From db2576547e344188f7adb9f8c94f933303e61954 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andrej=20Rama=C5=A1euski?= <andrej@x2.cz>
Date: Sun, 8 Aug 2021 22:42:46 +0200
Subject: [PATCH] Updaty pluginu pro verzi 4

---
 plugins/easy_mindmup/Gemfile                  |    4 +-
 plugins/easy_mindmup/README.md                |    1 -
 plugins/easy_mindmup/after_init.rb            |    2 +-
 .../app/helpers/easy_mindmup_helper.rb        |   14 -
 .../app/views/easy_mindmup/_includes.html.erb |  124 +-
 .../views/easy_mindmup/_js_prepare.html.erb   |    4 +-
 .../easy_mindmup/_test_includes.html.erb      |   28 -
 .../assets/javascripts/content_patch.js       |   49 -
 .../assets/javascripts/easy_mindmup.js        |   11 -
 .../assets/javascripts/external.js            |    2 -
 .../easy_mindmup/assets/javascripts/filter.js |   58 +-
 .../javascripts/jasmine/helpers/test.js       |  107 -
 .../javascripts/jasmine/jasmine_lib/boot.js   |  135 -
 .../jasmine/jasmine_lib/jasmine-html.js       |  473 --
 .../jasmine/jasmine_lib/jasmine.js            | 4941 --------------
 .../assets/javascripts/jasmine/main.js        |   66 -
 .../assets/javascripts/jasmine/parse_form.js  |   87 -
 .../javascripts/jasmine/saver_linearize.js    |   43 -
 .../assets/javascripts/layout_patch.js        |   26 +-
 .../easy_mindmup/assets/javascripts/legend.js |    2 -
 .../assets/javascripts/legend_events.js       |   47 +-
 .../easy_mindmup/assets/javascripts/loader.js |  138 +-
 .../easy_mindmup/assets/javascripts/main.js   |    2 -
 .../assets/javascripts/map_model_patch.js     |    8 +-
 .../assets/javascripts/mapjs_init.js          |   65 +-
 .../javascripts/mindmup/dom-map-widget.js     |    2 +-
 .../assets/javascripts/mindmup/layout.js      |   14 +-
 .../assets/javascripts/mm_context_menu.js     |   44 +-
 .../assets/javascripts/model_classes.js       |    6 +-
 .../assets/javascripts/node_patch.js          |    5 +-
 .../easy_mindmup/assets/javascripts/print.js  |   56 +-
 .../assets/javascripts/redrawer.js            |    8 +
 .../easy_mindmup/assets/javascripts/saver.js  |    2 +-
 .../assets/javascripts/storage.js             |  377 +-
 .../easy_mindmup/assets/javascripts/styles.js |   10 +-
 .../assets/javascripts/toolbar.js             |    2 +-
 .../easy_mindmup/assets/javascripts/utils.js  |   22 +-
 .../assets/stylesheets/easy_mindmup.css       |   14 +-
 .../stylesheets/easy_mindmup_sass.scss.erb    |    1 -
 .../assets/stylesheets/jasmine.css            |   58 -
 .../sass/_mindmup_default_variables.scss      |    2 +-
 .../stylesheets/sass/_mindmup_frame.scss      |   10 +-
 .../stylesheets/sass/_mindmup_legend.scss     |    3 -
 .../stylesheets/sass/_mindmup_sidebar.scss    |   16 +-
 plugins/easy_mindmup/config/locales/ja.yml    |  155 -
 plugins/easy_mindmup/init.rb                  |    2 +-
 .../lib/easy_mindmup/easy_mindmup.rb          |    6 -
 plugins/easy_wbs/README.md                    |    3 -
 plugins/easy_wbs/after_init.rb                |    2 +-
 .../app/controllers/easy_wbs_controller.rb    |    2 +-
 .../easy_queries/easy_wbs_easy_issue_query.rb |   53 -
 .../app/views/easy_wbs/_includes.html.erb     |   32 +-
 .../views/easy_wbs/_test_includes.html.erb    |    1 -
 .../app/views/easy_wbs/index.html.erb         |   60 +-
 .../easy_wbs/assets/javascripts/easy_wbs.js   |    3 -
 .../assets/javascripts/tests/encode_layout.js | 5827 -----------------
 .../easy_wbs/assets/javascripts/wbs_main.js   |    4 +-
 .../easy_wbs/assets/javascripts/wbs_modals.js |    2 +-
 .../assets/stylesheets/generated/easy_wbs.css |    1 -
 plugins/easy_wbs/config/locales/hu.yml        |   92 +-
 plugins/easy_wbs/config/locales/ja.yml        |   70 +-
 plugins/easy_wbs/init.rb                      |    4 +-
 .../controllers/queries_controller_patch.rb   |   12 +-
 .../helpers/application_helper_patch.rb       |    9 -
 .../redmine_patch/models/project_patch.rb     |    6 -
 .../easy_wbs/spec/features/jasmine_spec.rb    |   53 +-
 plugins/redmine_agile/.drone.yml              |   78 -
 plugins/redmine_agile/README.rdoc             |    0
 .../controllers/agile_boards_controller.rb    |   96 +-
 .../controllers/agile_charts_controller.rb    |   96 +-
 .../controllers/agile_colors_controller.rb    |   50 -
 .../agile_journal_details_controller.rb       |   13 +-
 .../controllers/agile_queries_controller.rb   |  124 -
 .../controllers/agile_versions_controller.rb  |   85 -
 .../app/helpers/agile_boards_helper.rb        |  143 +-
 .../app/helpers/agile_charts_helper.rb        |   13 +-
 .../app/helpers/agile_support_helper.rb       |   32 +-
 .../app/helpers/agile_versions_helper.rb      |   54 -
 .../app/models/agile_charts_query.rb          |  215 +-
 .../redmine_agile/app/models/agile_color.rb   |   68 -
 .../redmine_agile/app/models/agile_data.rb    |    3 +-
 .../redmine_agile/app/models/agile_query.rb   |  482 +-
 .../app/models/agile_versions_query.rb        |  150 -
 .../agile_boards/_add_issue_card.html.erb     |    5 -
 .../app/views/agile_boards/_board.html.erb    |   43 +-
 .../app/views/agile_boards/_index.html.erb    |   94 +-
 .../views/agile_boards/_issue_card.html.erb   |   29 +-
 .../views/agile_boards/_issues_links.html.erb |   16 +-
 .../agile_boards/_issues_sidebar.html.erb     |    8 -
 .../app/views/agile_boards/index.html.erb     |   49 -
 .../agile_boards/inline_comment.html.erb      |    2 +-
 .../views/agile_charts/_agile_charts.html.erb |   14 +-
 .../app/views/agile_charts/_chart.html.erb    |  100 +-
 .../agile_charts/_versions_show.html.erb      |   24 +-
 .../app/views/agile_charts/show.html.erb      |   35 +-
 .../app/views/agile_colors/index.html.erb     |   29 -
 .../agile_journal_details/status.html.erb     |   65 +-
 .../app/views/agile_queries/_columns.html.erb |   38 -
 .../app/views/agile_queries/_filters.html.erb |   28 -
 .../app/views/agile_queries/_form.html.erb    |   80 -
 .../app/views/agile_queries/edit.html.erb     |    6 -
 .../app/views/agile_queries/index.html.erb    |   25 -
 .../app/views/agile_queries/new.html.erb      |    6 -
 .../app/views/agile_versions/_board.html.erb  |   43 -
 .../agile_versions/_version_issues.html.erb   |   22 -
 .../views/agile_versions/autocomplete.js.erb  |    1 -
 .../app/views/agile_versions/index.html.erb   |   56 -
 .../app/views/agile_versions/load.js.erb      |    5 -
 .../context_menus/_agile_colors.html.erb      |   11 -
 .../views/issues/_agile_data_fields.html.erb  |   10 +-
 .../app/views/issues/_issue_color.html.erb    |   11 -
 .../views/issues/_issue_color_form.html.erb   |   14 -
 .../issues/_issue_story_points_form.html.erb  |   21 +-
 .../projects/_project_color_form.html.erb     |   11 -
 .../views/settings/agile/_general.html.erb    |   37 +-
 .../app/views/users/_user_color_form.html.erb |   11 -
 .../assets/images/pro_version_agile.png       |  Bin 49763 -> 0 bytes
 .../javascripts/jquery.simplecolorpicker.js   |    0
 .../assets/javascripts/redmine_agile.js       |  274 +-
 .../javascripts/redmine_agile_context_menu.js |  222 -
 .../assets/javascripts/visibility.min.js      |    1 -
 .../stylesheets/jquery.simplecolorpicker.css  |    0
 .../assets/stylesheets/redmine_agile.css      |  127 +-
 plugins/redmine_agile/config/locales/de.yml   |  116 +-
 plugins/redmine_agile/config/locales/en.yml   |   96 +-
 plugins/redmine_agile/config/locales/es.yml   |  199 +-
 .../redmine_agile/config/locales/pt-BR.yml    |  246 +-
 plugins/redmine_agile/config/locales/ru.yml   |   96 +-
 .../redmine_agile/config/locales/zh-TW.yml    |    4 +-
 plugins/redmine_agile/config/routes.rb        |   19 +-
 .../migrate/001_create_issue_status_orders.rb |    2 +-
 .../db/migrate/002_create_agile_colors.rb     |    2 +-
 .../migrate/003_rename_issue_status_orders.rb |    2 +-
 .../db/migrate/004_rename_agile_ranks.rb      |    2 +-
 .../005_add_story_points_to_agile_ranks.rb    |    2 +-
 plugins/redmine_agile/doc/CHANGELOG           |  127 +-
 plugins/redmine_agile/init.rb                 |   51 +-
 .../redmine_agile/lib/acts_as_colored/init.rb |   21 -
 .../acts_as_colored/lib/acts_as_colored.rb    |   69 -
 plugins/redmine_agile/lib/redmine_agile.rb    |   76 +-
 .../lib/redmine_agile/charts/agile_chart.rb   |  106 +-
 .../charts/average_lead_time_chart.rb         |   67 -
 .../redmine_agile/charts/burndown_chart.rb    |   19 +-
 .../lib/redmine_agile/charts/burnup_chart.rb  |   81 -
 .../charts/cumulative_flow_chart.rb           |   72 -
 .../redmine_agile/charts/lead_time_chart.rb   |   77 -
 .../charts/trackers_cumulative_flow_chart.rb  |   56 -
 .../redmine_agile/charts/velocity_chart.rb    |   60 -
 .../charts/work_burndown_chart.rb             |   11 +-
 .../redmine_agile/charts/work_burnup_chart.rb |  112 -
 .../lib/redmine_agile/helpers/agile_helper.rb |  139 +-
 .../hooks/controller_issue_hook.rb            |   23 +-
 .../redmine_agile/hooks/helper_issues_hook.rb |   34 -
 .../hooks/views_context_menus_hook.rb         |   26 -
 .../redmine_agile/hooks/views_issues_hook.rb  |   16 +-
 .../redmine_agile/hooks/views_layouts_hook.rb |    4 +-
 .../hooks/views_projects_form_hook.rb         |   26 -
 .../hooks/views_users_form_hook.rb            |   27 -
 .../hooks/views_versions_hook.rb              |    2 +-
 .../application_controller_patch.rb           |   41 -
 .../patches/compatibility_patch.rb            |  351 -
 .../lib/redmine_agile/patches/issue_patch.rb  |   22 +-
 .../patches/issue_priority_patch.rb           |   36 -
 .../patches/issue_query_patch.rb              |   62 -
 .../redmine_agile/patches/project_patch.rb    |   12 +-
 .../patches/queries_controller_patch.rb       |    3 +-
 .../redmine_agile/patches/tracker_patch.rb    |   36 -
 .../lib/redmine_agile/patches/user_patch.rb   |   39 -
 .../lib/redmine_agile/utils/header_tree.rb    |  118 -
 .../functional/agile_board_controller_test.rb |  963 ---
 .../agile_charts_controller_test.rb           |  189 +-
 .../agile_journal_details_controller_test.rb  |   23 +-
 .../agile_queries_controller_test.rb          |  134 -
 .../agile_versions_controller_test.rb         |  124 -
 .../test/functional/issues_controller_test.rb |   54 +-
 .../functional/projects_controller_test.rb    |   20 +-
 .../test/functional/users_controller_test.rb  |   17 +-
 .../test/integration/common_views_test.rb     |    2 +-
 plugins/redmine_agile/test/test_helper.rb     |   20 +-
 .../test/ui/agile_board_ui_test.rb            |    2 +-
 .../test/unit/agile_color_test.rb             |   80 -
 .../test/unit/agile_data_test.rb              |    2 +-
 .../test/unit/agile_versions_query_test.rb    |  138 -
 .../unit/helpers/agile_boards_helper_test.rb  |  131 +-
 ...hecklist_template_categories_controller.rb |   72 -
 .../checklist_templates_controller.rb         |   94 -
 .../app/controllers/checklists_controller.rb  |   43 +-
 .../app/helpers/checklists_helper.rb          |   17 +-
 .../app/models/checklist.rb                   |   12 +-
 .../app/models/checklist_template.rb          |   62 -
 .../app/models/checklist_template_category.rb |   33 -
 .../app/models/journal_checklist_history.rb   |   43 +-
 .../_form.html.erb                            |    6 -
 .../edit.html.erb                             |    6 -
 .../new.html.erb                              |    6 -
 .../views/checklist_templates/_form.html.erb  |   59 -
 .../views/checklist_templates/edit.html.erb   |    6 -
 .../views/checklist_templates/new.html.erb    |    6 -
 .../views/checklists/_checklist_item.html.erb |   15 +-
 .../app/views/checklists/done.js.erb          |   13 +
 .../app/views/issues/_checklist.html.erb      |   10 +-
 .../views/issues/_checklist_fields.html.erb   |   37 +-
 .../app/views/issues/_checklist_form.html.erb |    3 -
 .../issues/_checklist_templates.html.erb      |    4 -
 .../settings/_checklist_templates.html.erb    |   34 -
 .../settings/checklists/_checklists.html.erb  |    6 -
 .../checklists/_template_categories.html.erb  |   29 -
 .../settings/checklists/_templates.html.erb   |   44 -
 .../assets/javascripts/checklists.js          |  219 +-
 .../assets/stylesheets/checklists.css         |   69 +-
 .../redmine_checklists/config/locales/de.yml  |   14 +-
 .../redmine_checklists/config/locales/en.yml  |   12 +-
 .../redmine_checklists/config/locales/fr.yml  |    5 +
 .../config/locales/pt-BR.yml                  |   43 +-
 .../redmine_checklists/config/locales/ru.yml  |   13 +
 plugins/redmine_checklists/config/routes.rb   |    9 +-
 .../db/migrate/001_create_checklists.rb       |    2 +-
 .../002_add_time_stamps_to_checklists.rb      |    2 +-
 .../003_create_checklist_template_category.rb |    2 +-
 .../migrate/004_create_checklist_templates.rb |    2 +-
 .../005_modify_checklist_subject_length.rb    |    2 +-
 .../006_add_fields_to_checklist_template.rb   |    2 +-
 plugins/redmine_checklists/doc/CHANGELOG      |   60 +-
 plugins/redmine_checklists/init.rb            |   10 +-
 .../hooks/controller_issues_hook.rb           |   16 +-
 .../hooks/views_issues_hook.rb                |    2 +-
 .../hooks/views_layouts_hook.rb               |    2 +-
 .../add_helpers_for_checklists_patch.rb       |    2 +-
 .../2.1/redmine_api_test_patch.rb             |    2 +-
 .../application_controller_patch.rb           |   41 -
 .../compatibility/application_helper_patch.rb |    2 +-
 .../patches/compatibility/journal_patch.rb    |   27 +-
 .../compatibility/open_struct_patch.rb        |    2 +-
 .../patches/compatibility_patch.rb            |    2 +-
 .../redmine_checklists/patches/issue_patch.rb |   39 +-
 .../patches/issue_query_patch.rb              |   58 +-
 .../patches/issues_controller_patch.rb        |   61 +-
 .../patches/issues_helper_patch.rb            |   45 +-
 .../patches/notifiable_patch.rb               |   31 +-
 .../patches/project_patch.rb                  |   10 +-
 .../patches/projects_helper_patch.rb          |   52 -
 .../redmine_checklist_setting.rb              |   26 -
 .../redmine_checklists/redmine_checklists.rb  |   16 +-
 .../redmine_checklists/scripts/run_local.sh   |   12 -
 .../test/fixtures/checklists.yml              |   13 +-
 ...ist_template_categories_controller_test.rb |   92 -
 .../checklist_templates_controller_test.rb    |  135 -
 .../functional/checklists_controller_test.rb  |   15 +-
 .../test/functional/issues_controller_test.rb |  216 +-
 .../integration/api_test/checklists_test.rb   |   31 +-
 .../test/integration/common_issue_test.rb     |   19 +-
 .../redmine_checklists/test/test_helper.rb    |   13 +-
 .../unit/checklist_template_category_test.rb  |   80 -
 .../test/unit/checklist_template_test.rb      |   32 -
 .../test/unit/checklist_test.rb               |   51 +-
 .../test/unit/issue_test.rb                   |    6 +-
 .../unit/journal_checklist_history_test.rb    |  251 -
 .../test/unit/project_test.rb                 |    2 +-
 plugins/redmine_monitoring_controlling        |    1 -
 plugins/redmine_user_specific_theme           |    1 -
 plugins/redmine_work_time/.hgignore           |    3 -
 plugins/redmine_work_time/README.md           |   11 +-
 .../app/controllers/work_time_controller.rb   |   73 +-
 .../app/models/user_issue_month.rb            |    7 +-
 .../app/models/wt_daily_memo.rb               |    7 +-
 .../app/models/wt_holidays.rb                 |    7 +-
 .../app/models/wt_member_order.rb             |    7 +-
 .../app/models/wt_project_orders.rb           |    7 +-
 .../app/models/wt_ticket_relay.rb             |    7 +-
 .../work_time/_user_month_table.html.erb      |    4 +
 .../redmine_work_time/config/locales/ca.yml   |   95 +-
 .../redmine_work_time/config/locales/de.yml   |    1 +
 .../redmine_work_time/config/locales/en.yml   |    1 +
 .../redmine_work_time/config/locales/es.yml   |   47 +-
 .../redmine_work_time/config/locales/fr.yml   |    1 +
 .../redmine_work_time/config/locales/it.yml   |    1 +
 .../redmine_work_time/config/locales/ja.yml   |    1 +
 .../redmine_work_time/config/locales/ko.yml   |    1 +
 .../redmine_work_time/config/locales/no.yml   |    1 +
 .../redmine_work_time/config/locales/po.yml   |    1 +
 .../config/locales/pt-BR.yml                  |    1 +
 .../redmine_work_time/config/locales/ru.yml   |    1 +
 .../redmine_work_time/config/locales/tr.yml   |    1 +
 .../redmine_work_time/config/locales/zh.yml   |    1 +
 .../migrate/001_create_user_issue_months.rb   |    2 +-
 .../db/migrate/002_create_wt_member_orders.rb |    2 +-
 .../db/migrate/003_create_wt_ticket_relays.rb |    2 +-
 .../db/migrate/004_add_prj_to_mem_odr.rb      |    2 +-
 .../db/migrate/005_create_wt_daily_memos.rb   |    2 +-
 .../migrate/006_create_wt_project_orders.rb   |    2 +-
 .../db/migrate/007_create_wt_holidays.rb      |    2 +-
 .../008_remove_month_from_user_issue_month.rb |    2 +-
 .../009_remove_prj_from_wt_project_orders.rb  |    2 +-
 plugins/redmine_work_time/init.rb             |    2 +-
 .../lib/work_time_projects_helper_patch.rb    |   25 +-
 plugins/redmineup_tags/Gemfile.lock           |  108 -
 .../app/controllers/issue_tags_controller.rb  |   53 +-
 .../app/controllers/tags_controller.rb        |   26 +-
 .../app/helpers/issues_tags_helper.rb         |   20 +-
 .../redmineup_tags/app/helpers/tags_helper.rb |   71 +-
 .../views/context_menus/_issues_tags.html.erb |   15 +-
 .../app/views/issue_tags/_edit_modal.html.erb |   12 +-
 .../views/issues/._tags_sidebar.html.erb.swp  |  Bin 12288 -> 0 bytes
 .../app/views/issues/_tags.html.erb           |    2 +-
 .../app/views/issues/_tags_form.html.erb      |    6 +-
 .../app/views/issues/_tags_sidebar.html.erb   |    2 -
 .../app/views/tags/context_menu.html.erb      |    6 +-
 .../assets/stylesheets/redmine_tags.css       |    1 +
 plugins/redmineup_tags/config/locales/de.yml  |   39 +-
 plugins/redmineup_tags/config/locales/ru.yml  |   33 +-
 plugins/redmineup_tags/config/locales/zh.yml  |   18 +-
 plugins/redmineup_tags/config/routes.rb       |   18 +-
 .../db/migrate/001_add_tags_and_taggings.rb   |    2 +-
 .../db/migrate/002_create_tags.rb             |    2 +-
 plugins/redmineup_tags/doc/CHANGELOG          |   46 +-
 plugins/redmineup_tags/init.rb                |   52 +-
 .../redmineup_tags/lib/query_tags_column.rb   |    2 +-
 plugins/redmineup_tags/lib/redmine_tags.rb    |   27 -
 .../redmine_tags/hooks/model_issue_hook.rb    |   47 -
 .../hooks/views_context_menus_hook.rb         |   26 -
 .../redmine_tags/hooks/views_issues_hook.rb   |   30 -
 .../redmine_tags/hooks/views_layouts_hook.rb  |   27 -
 .../patches/action_controller_patch.rb        |   49 -
 .../add_helpers_for_issue_tags_patch.rb       |   29 -
 .../redmine_tags/patches/agile_query_patch.rb |   69 -
 .../auto_completes_controller_patch.rb        |   56 -
 .../lib/redmine_tags/patches/issue_patch.rb   |  115 -
 .../redmine_tags/patches/issue_query_patch.rb |   87 -
 .../patches/queries_helper_patch.rb           |   51 -
 .../auto_completes_controller_test.rb         |   33 +-
 .../functional/issue_tags_controller_test.rb  |  106 +-
 .../test/functional/issues_controller_test.rb |  155 +-
 .../test/functional/tags_controller_test.rb   |   43 +-
 plugins/redmineup_tags/test/test_helper.rb    |   22 +-
 .../redmineup_tags/test/unit/issue_test.rb    |   46 +-
 335 files changed, 4099 insertions(+), 21787 deletions(-)
 delete mode 100644 plugins/easy_mindmup/README.md
 delete mode 100644 plugins/easy_mindmup/app/views/easy_mindmup/_test_includes.html.erb
 delete mode 100644 plugins/easy_mindmup/assets/javascripts/easy_mindmup.js
 delete mode 100644 plugins/easy_mindmup/assets/javascripts/jasmine/helpers/test.js
 delete mode 100644 plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/boot.js
 delete mode 100644 plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/jasmine-html.js
 delete mode 100644 plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/jasmine.js
 delete mode 100644 plugins/easy_mindmup/assets/javascripts/jasmine/main.js
 delete mode 100644 plugins/easy_mindmup/assets/javascripts/jasmine/parse_form.js
 delete mode 100644 plugins/easy_mindmup/assets/javascripts/jasmine/saver_linearize.js
 delete mode 100644 plugins/easy_mindmup/assets/stylesheets/jasmine.css
 delete mode 100644 plugins/easy_mindmup/config/locales/ja.yml
 delete mode 100644 plugins/easy_wbs/README.md
 delete mode 100644 plugins/easy_wbs/app/models/easy_queries/easy_wbs_easy_issue_query.rb
 delete mode 100644 plugins/easy_wbs/assets/javascripts/easy_wbs.js
 delete mode 100644 plugins/easy_wbs/assets/javascripts/tests/encode_layout.js
 delete mode 100644 plugins/easy_wbs/assets/stylesheets/generated/easy_wbs.css
 delete mode 100644 plugins/redmine_agile/.drone.yml
 mode change 100755 => 100644 plugins/redmine_agile/README.rdoc
 mode change 100755 => 100644 plugins/redmine_agile/app/controllers/agile_boards_controller.rb
 delete mode 100644 plugins/redmine_agile/app/controllers/agile_colors_controller.rb
 delete mode 100644 plugins/redmine_agile/app/controllers/agile_queries_controller.rb
 delete mode 100644 plugins/redmine_agile/app/controllers/agile_versions_controller.rb
 delete mode 100644 plugins/redmine_agile/app/helpers/agile_versions_helper.rb
 delete mode 100644 plugins/redmine_agile/app/models/agile_color.rb
 mode change 100755 => 100644 plugins/redmine_agile/app/models/agile_data.rb
 delete mode 100644 plugins/redmine_agile/app/models/agile_versions_query.rb
 delete mode 100755 plugins/redmine_agile/app/views/agile_boards/_issues_sidebar.html.erb
 mode change 100755 => 100644 plugins/redmine_agile/app/views/agile_boards/index.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_colors/index.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_queries/_columns.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_queries/_filters.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_queries/_form.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_queries/edit.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_queries/index.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_queries/new.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_versions/_board.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_versions/_version_issues.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_versions/autocomplete.js.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_versions/index.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/agile_versions/load.js.erb
 delete mode 100644 plugins/redmine_agile/app/views/context_menus/_agile_colors.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/issues/_issue_color.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/issues/_issue_color_form.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/projects/_project_color_form.html.erb
 mode change 100755 => 100644 plugins/redmine_agile/app/views/settings/agile/_general.html.erb
 delete mode 100644 plugins/redmine_agile/app/views/users/_user_color_form.html.erb
 delete mode 100644 plugins/redmine_agile/assets/images/pro_version_agile.png
 mode change 100755 => 100644 plugins/redmine_agile/assets/javascripts/jquery.simplecolorpicker.js
 mode change 100755 => 100644 plugins/redmine_agile/assets/javascripts/redmine_agile.js
 delete mode 100644 plugins/redmine_agile/assets/javascripts/redmine_agile_context_menu.js
 delete mode 100644 plugins/redmine_agile/assets/javascripts/visibility.min.js
 mode change 100755 => 100644 plugins/redmine_agile/assets/stylesheets/jquery.simplecolorpicker.css
 mode change 100755 => 100644 plugins/redmine_agile/assets/stylesheets/redmine_agile.css
 mode change 100755 => 100644 plugins/redmine_agile/config/locales/en.yml
 mode change 100755 => 100644 plugins/redmine_agile/config/locales/ru.yml
 mode change 100755 => 100644 plugins/redmine_agile/config/routes.rb
 mode change 100755 => 100644 plugins/redmine_agile/db/migrate/001_create_issue_status_orders.rb
 mode change 100755 => 100644 plugins/redmine_agile/doc/CHANGELOG
 mode change 100755 => 100644 plugins/redmine_agile/init.rb
 delete mode 100644 plugins/redmine_agile/lib/acts_as_colored/init.rb
 delete mode 100644 plugins/redmine_agile/lib/acts_as_colored/lib/acts_as_colored.rb
 mode change 100755 => 100644 plugins/redmine_agile/lib/redmine_agile.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/charts/average_lead_time_chart.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/charts/burnup_chart.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/charts/cumulative_flow_chart.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/charts/lead_time_chart.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/charts/trackers_cumulative_flow_chart.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/charts/velocity_chart.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/charts/work_burnup_chart.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/hooks/helper_issues_hook.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/hooks/views_context_menus_hook.rb
 mode change 100755 => 100644 plugins/redmine_agile/lib/redmine_agile/hooks/views_issues_hook.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/hooks/views_projects_form_hook.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/hooks/views_users_form_hook.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/patches/compatibility/application_controller_patch.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/patches/compatibility_patch.rb
 mode change 100755 => 100644 plugins/redmine_agile/lib/redmine_agile/patches/issue_patch.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/patches/issue_priority_patch.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/patches/issue_query_patch.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/patches/tracker_patch.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/patches/user_patch.rb
 delete mode 100644 plugins/redmine_agile/lib/redmine_agile/utils/header_tree.rb
 delete mode 100755 plugins/redmine_agile/test/functional/agile_board_controller_test.rb
 delete mode 100644 plugins/redmine_agile/test/functional/agile_queries_controller_test.rb
 delete mode 100644 plugins/redmine_agile/test/functional/agile_versions_controller_test.rb
 delete mode 100644 plugins/redmine_agile/test/unit/agile_color_test.rb
 delete mode 100644 plugins/redmine_agile/test/unit/agile_versions_query_test.rb
 delete mode 100644 plugins/redmine_checklists/app/controllers/checklist_template_categories_controller.rb
 delete mode 100644 plugins/redmine_checklists/app/controllers/checklist_templates_controller.rb
 delete mode 100644 plugins/redmine_checklists/app/models/checklist_template.rb
 delete mode 100644 plugins/redmine_checklists/app/models/checklist_template_category.rb
 delete mode 100644 plugins/redmine_checklists/app/views/checklist_template_categories/_form.html.erb
 delete mode 100644 plugins/redmine_checklists/app/views/checklist_template_categories/edit.html.erb
 delete mode 100644 plugins/redmine_checklists/app/views/checklist_template_categories/new.html.erb
 delete mode 100644 plugins/redmine_checklists/app/views/checklist_templates/_form.html.erb
 delete mode 100644 plugins/redmine_checklists/app/views/checklist_templates/edit.html.erb
 delete mode 100644 plugins/redmine_checklists/app/views/checklist_templates/new.html.erb
 delete mode 100644 plugins/redmine_checklists/app/views/issues/_checklist_templates.html.erb
 delete mode 100644 plugins/redmine_checklists/app/views/projects/settings/_checklist_templates.html.erb
 delete mode 100644 plugins/redmine_checklists/app/views/settings/checklists/_template_categories.html.erb
 delete mode 100644 plugins/redmine_checklists/app/views/settings/checklists/_templates.html.erb
 mode change 100755 => 100644 plugins/redmine_checklists/config/locales/fr.yml
 delete mode 100644 plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/application_controller_patch.rb
 delete mode 100644 plugins/redmine_checklists/lib/redmine_checklists/patches/projects_helper_patch.rb
 delete mode 100644 plugins/redmine_checklists/lib/redmine_checklists/redmine_checklist_setting.rb
 delete mode 100755 plugins/redmine_checklists/scripts/run_local.sh
 delete mode 100644 plugins/redmine_checklists/test/functional/checklist_template_categories_controller_test.rb
 delete mode 100644 plugins/redmine_checklists/test/functional/checklist_templates_controller_test.rb
 delete mode 100644 plugins/redmine_checklists/test/unit/checklist_template_category_test.rb
 delete mode 100644 plugins/redmine_checklists/test/unit/checklist_template_test.rb
 delete mode 100644 plugins/redmine_checklists/test/unit/journal_checklist_history_test.rb
 delete mode 160000 plugins/redmine_monitoring_controlling
 delete mode 160000 plugins/redmine_user_specific_theme
 delete mode 100644 plugins/redmine_work_time/.hgignore
 mode change 100755 => 100644 plugins/redmine_work_time/app/controllers/work_time_controller.rb
 mode change 100755 => 100644 plugins/redmine_work_time/config/locales/zh.yml
 delete mode 100644 plugins/redmineup_tags/Gemfile.lock
 delete mode 100644 plugins/redmineup_tags/app/views/issues/._tags_sidebar.html.erb.swp
 mode change 100755 => 100644 plugins/redmineup_tags/doc/CHANGELOG
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags.rb
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags/hooks/model_issue_hook.rb
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags/hooks/views_context_menus_hook.rb
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags/hooks/views_issues_hook.rb
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags/hooks/views_layouts_hook.rb
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags/patches/action_controller_patch.rb
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags/patches/add_helpers_for_issue_tags_patch.rb
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags/patches/agile_query_patch.rb
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags/patches/auto_completes_controller_patch.rb
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags/patches/issue_patch.rb
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags/patches/issue_query_patch.rb
 delete mode 100644 plugins/redmineup_tags/lib/redmine_tags/patches/queries_helper_patch.rb

diff --git a/plugins/easy_mindmup/Gemfile b/plugins/easy_mindmup/Gemfile
index 0ab92e6..772940c 100644
--- a/plugins/easy_mindmup/Gemfile
+++ b/plugins/easy_mindmup/Gemfile
@@ -1,3 +1 @@
-unless %w(easyproject easy_gantt).any? { |plugin| Dir.exist?(File.expand_path("../../#{plugin}", __FILE__)) }
-  gem 'redmine_extensions', '~> 0.2.4'
-end
\ No newline at end of file
+gem 'redmine_extensions' unless Dir.exist?(File.expand_path('../../easyproject', __FILE__))
diff --git a/plugins/easy_mindmup/README.md b/plugins/easy_mindmup/README.md
deleted file mode 100644
index a89d93d..0000000
--- a/plugins/easy_mindmup/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Easy Mindmup
diff --git a/plugins/easy_mindmup/after_init.rb b/plugins/easy_mindmup/after_init.rb
index 837c915..560a27c 100644
--- a/plugins/easy_mindmup/after_init.rb
+++ b/plugins/easy_mindmup/after_init.rb
@@ -1,6 +1,6 @@
 app_dir = File.join(File.dirname(__FILE__), 'app')
 lib_dir = File.join(File.dirname(__FILE__), 'lib', 'easy_mindmup')
 
-ActionDispatch::Reloader.to_prepare do
+RedmineExtensions::Reloader.to_prepare do
   require 'easy_mindmup/easy_mindmup'
 end
diff --git a/plugins/easy_mindmup/app/helpers/easy_mindmup_helper.rb b/plugins/easy_mindmup/app/helpers/easy_mindmup_helper.rb
index 4bebec3..a72e35a 100644
--- a/plugins/easy_mindmup/app/helpers/easy_mindmup_helper.rb
+++ b/plugins/easy_mindmup/app/helpers/easy_mindmup_helper.rb
@@ -18,18 +18,4 @@ module EasyMindmupHelper
     end
   end
 
-  def prepare_test_includes(tests)
-    includes = []
-    tests.each do |test_file|
-      plugin = 'easy_mindmup'
-      if test_file.include?('/')
-        split = test_file.split('/')
-        plugin = split[0]
-        test_file = split[1]
-      end
-      includes << [test_file, plugin]
-    end
-    includes
-  end
-
 end
diff --git a/plugins/easy_mindmup/app/views/easy_mindmup/_includes.html.erb b/plugins/easy_mindmup/app/views/easy_mindmup/_includes.html.erb
index 3a24075..842e70d 100644
--- a/plugins/easy_mindmup/app/views/easy_mindmup/_includes.html.erb
+++ b/plugins/easy_mindmup/app/views/easy_mindmup/_includes.html.erb
@@ -1,71 +1,77 @@
 <% include_calendar_headers_tags %>
 <% heads_for_wiki_formatter %>
-<% if EasyMindmup.easy_extensions? %>
+<% if defined?(EasyExtensions) %>
   <%= stylesheet_link_tag('easy_mindmup', :media => 'all') %>
 <% else %>
+  <%= stylesheet_link_tag('generated/easy_mindmup', :plugin => 'easy_mindmup', :media => 'all') %>
+  <%#= stylesheet_link_tag('easy_mindmup', :plugin => 'easy_mindmup', :media => 'all') %>
   <%= stylesheet_link_tag('context_menu', :media => 'all') %>
 <% end %>
-<% if EasyMindmup.combine_by_pipeline?(params) %>
-  <%= javascript_include_tag('easy_mindmup') %>
+<%= javascript_include_tag('external', :plugin => 'easy_mindmup') %>
+<% if false && defined?(EasyExtensions) %>
+  <%= javascript_include_tag('easy_wbs', :plugin => 'easy_mindmup') %>
 <% else %>
-  <%= javascript_include_tag(
-          'external',
-
-          'mindmup/mapjs',
-          'mindmup/clipboard',
-          'mindmup/content',
-          'mindmup/dom-map-view',
-          'mindmup/dom-map-widget',
-          'mindmup/hammer-draggable',
-          # 'mindmup/image-drop-widget',
-          'mindmup/layout',
-          #'mindmup/link-edit-widget',
-          'mindmup/map-model',
-          'mindmup/map-toolbar-widget',
-          'mindmup/observable',
-          # 'mindmup/url-helper',
-
-          'polyfill',
-          'utils',
-          'main',
-          'mapjs_init',
-          'event_bus',
-          'redrawer',
-          'toolbar',
-          'history',
-          'data',
-          'links',
-          'loader',
-          # 'last_state',
-          'saver',
-          'autosave',
-          'save_progress',
-          'save_info',
-          'filter',
-          'node_patch',
-          'dom_patch',
-          'map_model_patch',
-          'model_classes',
-          'after_change',
-          'styles',
-          'logger',
-          # 'gateway',
-          # 'modals',
-          'validator',
-          'storage',
-          'legend',
-          'legend_events',
-          'expand_all',
-          'print',
-          'mm_context_menu',
-          'layout_patch',
-          'content_patch',
-          'links',
-          'link_edit_widget', :plugin => 'easy_mindmup')
-  %>
+    <script type="application/javascript">
+        window.easyDartLoaders = window.easyDartLoaders || [];
+    </script>
+  <%= begin javascript_include_tag(
+        'mindmup/mapjs',
+        'mindmup/clipboard',
+        'mindmup/content',
+        'mindmup/dom-map-view',
+        'mindmup/dom-map-widget',
+        'mindmup/hammer-draggable',
+        'mindmup/image-drop-widget',
+        'mindmup/layout',
+        #'mindmup/link-edit-widget',
+        'mindmup/map-model',
+        'mindmup/map-toolbar-widget',
+        'mindmup/observable',
+        'mindmup/url-helper', :plugin => 'easy_mindmup')
+      end %>
+  <%= begin javascript_include_tag(
+        :libs,
+        :polyfill,
+        :utils,
+        :main,
+        :mapjs_init,
+        :event_bus,
+        :redrawer,
+        :toolbar,
+        :history,
+        :data,
+        :links,
+        :loader,
+        # :last_state,
+        :saver,
+        :autosave,
+        :save_progress,
+        :save_info,
+        :filter,
+        :node_patch,
+        :dom_patch,
+        :map_model_patch,
+        :model_classes,
+        :after_change,
+        :styles,
+        :logger,
+        # :gateway,
+        # :modals,
+        :validator,
+        :storage,
+        :legend,
+        :legend_events,
+        :expand_all,
+        :print,
+        :mm_context_menu,
+        :layout_patch,
+        :content_patch,
+        :links,
+        :link_edit_widget, :plugin => 'easy_mindmup')
+      end %>
 <% end %>
 
 
-<% if EasyMindmup.easy_extensions?
+<% if defined?(EasyExtensions)
      include_front_end_commons if respond_to? :include_front_end_commons
    end %>
diff --git a/plugins/easy_mindmup/app/views/easy_mindmup/_js_prepare.html.erb b/plugins/easy_mindmup/app/views/easy_mindmup/_js_prepare.html.erb
index 0aaff39..14cb604 100644
--- a/plugins/easy_mindmup/app/views/easy_mindmup/_js_prepare.html.erb
+++ b/plugins/easy_mindmup/app/views/easy_mindmup/_js_prepare.html.erb
@@ -1,5 +1,5 @@
 <script type="text/javascript">
-  window.easyMindMupSetting = $.extend(true, window.easyMindMupSetting, <%= {
+  window.easyMindMupSetting = <%= {
     easyRedmine: EasyMindmup.easy_extensions?,
     rootID: @project.id,
     apiKey: User.current.api_key,
@@ -133,7 +133,7 @@
         </div>
       }
     }
-  }.to_json.html_safe %>);
+  }.to_json.html_safe %>
 
   $(document).ready(function(){
     $("p.nodata").remove()
diff --git a/plugins/easy_mindmup/app/views/easy_mindmup/_test_includes.html.erb b/plugins/easy_mindmup/app/views/easy_mindmup/_test_includes.html.erb
deleted file mode 100644
index 55bcea0..0000000
--- a/plugins/easy_mindmup/app/views/easy_mindmup/_test_includes.html.erb
+++ /dev/null
@@ -1,28 +0,0 @@
-<%=
-  javascript_include_tag(
-      # test framework
-      'jasmine/helpers/test',
-      'jasmine/jasmine_lib/jasmine',
-      'jasmine/jasmine_lib/jasmine-html',
-      'jasmine/jasmine_lib/boot',
-
-      # common tests
-      'jasmine/main',
-      'jasmine/parse_form',
-      'jasmine/saver_linearize',
-      plugin: :easy_mindmup)
-%>
-<% extra_test_names = params[:run_jasmine_tests]
-   if extra_test_names != 'true'
-     if extra_test_names.is_a?(String)
-       extra_tests = prepare_test_includes([extra_test_names])
-     elsif extra_test_names.is_a?(Array)
-       extra_tests = prepare_test_includes(extra_test_names)
-     else
-       extra_tests = []
-     end
-     extra_tests.each do |test, plugin| %>
-    <%= javascript_include_tag("jasmine/#{test}", plugin: plugin) %>
-  <% end %>
-<% end %>
-<%= stylesheet_link_tag('jasmine', media: 'all', plugin: :easy_mindmup) %>
diff --git a/plugins/easy_mindmup/assets/javascripts/content_patch.js b/plugins/easy_mindmup/assets/javascripts/content_patch.js
index f80f77b..373c849 100644
--- a/plugins/easy_mindmup/assets/javascripts/content_patch.js
+++ b/plugins/easy_mindmup/assets/javascripts/content_patch.js
@@ -2,11 +2,9 @@
   /**
    *
    * @param {MindMup} ysy
-   * @property {MindMup} ysy
    * @constructor
    */
   function ContentPatch(ysy) {
-    this.ysy = ysy;
     this.patch(ysy);
   }
 
@@ -15,7 +13,6 @@
    * @param {MindMup} ysy
    */
   ContentPatch.prototype.patch = function (ysy) {
-    var self = this;
     ysy.eventBus.register("TreeLoaded", /** @param {RootIdea} contentAggregate*/ function (contentAggregate) {
       var commandProcessors = contentAggregate.getCommandProcessors();
       contentAggregate.clone = function (subIdeaId) {
@@ -32,53 +29,7 @@
       commandProcessors.setCustomData = function (originId, idea, obj) {
         ysy.setCustomData(idea, obj);
       };
-      contentAggregate.updateOneSide = $.proxy(self.updateOneSide, self);
     });
   };
-  /**
-   *
-   * @param {RootIdea} idea
-   */
-  ContentPatch.prototype.updateOneSide = function (idea) {
-    var i;
-    var oneSideOn = this.ysy.settings.oneSideOn;
-    if (oneSideOn === idea.oneSideOn) return;
-    var ranks = Object.getOwnPropertyNames(idea.ideas).map(function (i) {
-      return parseFloat(i);
-    }).sort(function (a, b) {
-      return a - b
-    });
-    if (ranks.length === 0) return;
-    var constructed = {};
-    if (oneSideOn) {
-      var firstPositive = _.findIndex(ranks, function (rank) {
-        return rank > 0;
-      });
-      var pointer = 1;
-      for (i = firstPositive; i < ranks.length; i++) {
-        constructed[pointer++] = idea.ideas[ranks[i]];
-      }
-      for (i = 0; i < firstPositive; i++) {
-        constructed[pointer++] = idea.ideas[ranks[i]];
-      }
-    } else {
-      if (ranks[0] < 0) {
-        return;
-      }
-      var middlePoint = Math.ceil(ranks.length / 2);
-      //index = i % 2 !== 0 ? -i / 2 - 0.5 : i / 2 + 1;
-      for (i = 0; i < middlePoint; i++) {
-        constructed[i + 1] = idea.ideas[ranks[i]];
-      }
-      for (i = middlePoint; i < ranks.length; i++) {
-        constructed[i - ranks.length] = idea.ideas[ranks[i]];
-      }
-    }
-    idea.oneSideOn = oneSideOn;
-    idea.ideas = constructed;
-    if (idea.dispatchEvent) {
-      idea.dispatchEvent("changed");
-    }
-  };
   window.easyMindMupClasses.ContentPatch = ContentPatch;
 })();
\ No newline at end of file
diff --git a/plugins/easy_mindmup/assets/javascripts/easy_mindmup.js b/plugins/easy_mindmup/assets/javascripts/easy_mindmup.js
deleted file mode 100644
index ed30119..0000000
--- a/plugins/easy_mindmup/assets/javascripts/easy_mindmup.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/*
- * = require external
- * = require mindmup/mapjs
- * = require_directory ./mindmup
- * = require libs
- * = require polyfill
- * = require utils
- * = require_directory .
- * = stub mindmup/link-edit-widget
- * = stub jsdocs_external
- */
diff --git a/plugins/easy_mindmup/assets/javascripts/external.js b/plugins/easy_mindmup/assets/javascripts/external.js
index b267393..00f9b85 100644
--- a/plugins/easy_mindmup/assets/javascripts/external.js
+++ b/plugins/easy_mindmup/assets/javascripts/external.js
@@ -38,8 +38,6 @@
  * Licensed under the MIT license */
 
 !function(a,b){"use strict";function c(a,c){Date.now||(Date.now=function(){return(new Date).getTime()}),a.utils.each(["on","off"],function(d){a.utils[d]=function(a,e,f){c(a)[d](e,function(a){var d=c.extend({},a.originalEvent,a);d.button===b&&(d.button=a.which-1),f.call(this,d)})}}),a.Instance.prototype.trigger=function(a,b){var d=c(this.element);return d.has(b.target).length&&(d=c(b.target)),d.trigger({type:a,gesture:b})},c.fn.hammer=function(b){return this.each(function(){var d=c(this),e=d.data("hammer");e?e&&b&&a.utils.extend(e.options,b):d.data("hammer",new a(this,b||{}))})}}"function"==typeof define&&define.amd?define(["hammerjs","jquery"],c):c(a.Hammer,a.jQuery||a.Zepto)}(window);
-/* Mustache.js */
-(function defineMustache(global,factory){if(typeof exports==="object"&&exports&&typeof exports.nodeName!=="string"){factory(exports)}else if(typeof define==="function"&&define.amd){define(["exports"],factory)}else{global.Mustache={};factory(global.Mustache)}})(this,function mustacheFactory(mustache){var objectToString=Object.prototype.toString;var isArray=Array.isArray||function isArrayPolyfill(object){return objectToString.call(object)==="[object Array]"};function isFunction(object){return typeof object==="function"}function typeStr(obj){return isArray(obj)?"array":typeof obj}function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function hasProperty(obj,propName){return obj!=null&&typeof obj==="object"&&propName in obj}var regExpTest=RegExp.prototype.test;function testRegExp(re,string){return regExpTest.call(re,string)}var nonSpaceRe=/\S/;function isWhitespace(string){return!testRegExp(nonSpaceRe,string)}var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};function escapeHtml(string){return String(string).replace(/[&<>"'`=\/]/g,function fromEntityMap(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tagsToCompile){if(typeof tagsToCompile==="string")tagsToCompile=tagsToCompile.split(spaceRe,2);if(!isArray(tagsToCompile)||tagsToCompile.length!==2)throw new Error("Invalid tags: "+tagsToCompile);openingTagRe=new RegExp(escapeRegExp(tagsToCompile[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tagsToCompile[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tagsToCompile[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i<valueLength;++i){chr=value.charAt(i);if(isWhitespace(chr)){spaces.push(tokens.length)}else{nonSpace=true}tokens.push(["text",chr,start,start+1]);start+=1;if(chr==="\n")stripSpace()}}if(!scanner.scan(openingTagRe))break;hasTag=true;type=scanner.scan(tagRe)||"name";scanner.scan(whiteRe);if(type==="="){value=scanner.scanUntil(equalsRe);scanner.scan(equalsRe);scanner.scanUntil(closingTagRe)}else if(type==="{"){value=scanner.scanUntil(closingCurlyRe);scanner.scan(curlyRe);scanner.scanUntil(closingTagRe);type="&"}else{value=scanner.scanUntil(closingTagRe)}if(!scanner.scan(closingTagRe))throw new Error("Unclosed tag at "+scanner.pos);token=[type,value,start,scanner.pos];tokens.push(token);if(type==="#"||type==="^"){sections.push(token)}else if(type==="/"){openSection=sections.pop();if(!openSection)throw new Error('Unopened section "'+value+'" at '+start);if(openSection[1]!==value)throw new Error('Unclosed section "'+openSection[1]+'" at '+start)}else if(type==="name"||type==="{"||type==="&"){nonSpace=true}else if(type==="="){compileTags(value)}}openSection=sections.pop();if(openSection)throw new Error('Unclosed section "'+openSection[1]+'" at '+scanner.pos);return nestTokens(squashTokens(tokens))}function squashTokens(tokens){var squashedTokens=[];var token,lastToken;for(var i=0,numTokens=tokens.length;i<numTokens;++i){token=tokens[i];if(token){if(token[0]==="text"&&lastToken&&lastToken[0]==="text"){lastToken[1]+=token[1];lastToken[3]=token[3]}else{squashedTokens.push(token);lastToken=token}}}return squashedTokens}function nestTokens(tokens){var nestedTokens=[];var collector=nestedTokens;var sections=[];var token,section;for(var i=0,numTokens=tokens.length;i<numTokens;++i){token=tokens[i];switch(token[0]){case"#":case"^":collector.push(token);sections.push(token);collector=token[4]=[];break;case"/":section=sections.pop();section[5]=token[2];collector=sections.length>0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function eos(){return this.tail===""};Scanner.prototype.scan=function scan(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function scanUntil(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function push(view){return new Context(view,this)};Context.prototype.lookup=function lookup(name){var cache=this.cache;var value;if(cache.hasOwnProperty(name)){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index<names.length){if(index===names.length-1)lookupHit=hasProperty(value,names[index]);value=value[names[index++]]}}else{value=context.view[name];lookupHit=hasProperty(context.view,name)}if(lookupHit)break;context=context.parent}cache[name]=value}if(isFunction(value))value=value.call(this.view);return value};function Writer(){this.cache={}}Writer.prototype.clearCache=function clearCache(){this.cache={}};Writer.prototype.parse=function parse(template,tags){var cache=this.cache;var tokens=cache[template];if(tokens==null)tokens=cache[template]=parseTemplate(template,tags);return tokens};Writer.prototype.render=function render(template,view,partials){var tokens=this.parse(template);var context=view instanceof Context?view:new Context(view);return this.renderTokens(tokens,context,partials,template)};Writer.prototype.renderTokens=function renderTokens(tokens,context,partials,originalTemplate){var buffer="";var token,symbol,value;for(var i=0,numTokens=tokens.length;i<numTokens;++i){value=undefined;token=tokens[i];symbol=token[0];if(symbol==="#")value=this.renderSection(token,context,partials,originalTemplate);else if(symbol==="^")value=this.renderInverted(token,context,partials,originalTemplate);else if(symbol===">")value=this.renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this.unescapedValue(token,context);else if(symbol==="name")value=this.escapedValue(token,context);else if(symbol==="text")value=this.rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype.renderSection=function renderSection(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;j<valueLength;++j){buffer+=this.renderTokens(token[4],context.push(value[j]),partials,originalTemplate)}}else if(typeof value==="object"||typeof value==="string"||typeof value==="number"){buffer+=this.renderTokens(token[4],context.push(value),partials,originalTemplate)}else if(isFunction(value)){if(typeof originalTemplate!=="string")throw new Error("Cannot use higher-order sections without the original template");value=value.call(context.view,originalTemplate.slice(token[3],token[5]),subRender);if(value!=null)buffer+=value}else{buffer+=this.renderTokens(token[4],context,partials,originalTemplate)}return buffer};Writer.prototype.renderInverted=function renderInverted(token,context,partials,originalTemplate){var value=context.lookup(token[1]);if(!value||isArray(value)&&value.length===0)return this.renderTokens(token[4],context,partials,originalTemplate)};Writer.prototype.renderPartial=function renderPartial(token,context,partials){if(!partials)return;var value=isFunction(partials)?partials(token[1]):partials[token[1]];if(value!=null)return this.renderTokens(this.parse(value),context,partials,value)};Writer.prototype.unescapedValue=function unescapedValue(token,context){var value=context.lookup(token[1]);if(value!=null)return value};Writer.prototype.escapedValue=function escapedValue(token,context){var value=context.lookup(token[1]);if(value!=null)return mustache.escape(value)};Writer.prototype.rawValue=function rawValue(token){return token[1]};mustache.name="mustache.js";mustache.version="2.2.1";mustache.tags=["{{","}}"];var defaultWriter=new Writer;mustache.clearCache=function clearCache(){return defaultWriter.clearCache()};mustache.parse=function parse(template,tags){return defaultWriter.parse(template,tags)};mustache.render=function render(template,view,partials){if(typeof template!=="string"){throw new TypeError('Invalid template! Template should be a "string" '+'but "'+typeStr(template)+'" was given as the first '+"argument for mustache#render(template, view, partials)")}return defaultWriter.render(template,view,partials)};mustache.to_html=function to_html(template,view,partials,send){var result=mustache.render(template,view,partials);if(isFunction(send)){send(result)}else{return result}};mustache.escape=escapeHtml;mustache.Scanner=Scanner;mustache.Context=Context;mustache.Writer=Writer});
 /*
  * jQuery Hotkeys Plugin
  * Copyright 2010, John Resig
diff --git a/plugins/easy_mindmup/assets/javascripts/filter.js b/plugins/easy_mindmup/assets/javascripts/filter.js
index 5069334..b4c7206 100644
--- a/plugins/easy_mindmup/assets/javascripts/filter.js
+++ b/plugins/easy_mindmup/assets/javascripts/filter.js
@@ -6,45 +6,15 @@
    */
   function Filter(ysy) {
     this.ysy = ysy;
-    this.allowedValues = [];
-    this.init(ysy);
   }
 
-  Filter.prototype.init = function (ysy) {
-    var self = this;
-    ysy.eventBus.register("nodeStyleChanged", function () {
-      self.reset();
-    });
-  };
-
-  Filter.prototype.className = "mindmup-node-filtered";
-  Filter.prototype.pushAllowed = function (value) {
-    this.allowedValues.push(value || 0);
-    this.sweepNodes();
-  };
   Filter.prototype.isOn = function(){
-    return !!this.allowedValues.length;
-  };
-  Filter.prototype.removeAllowed = function (value) {
-    this.allowedValues = _.without(this.allowedValues, value);
-    this.sweepNodes();
-  };
-  Filter.prototype.toggleAllowed = function (value) {
-    var store = this.ysy.styles.getCurrentStyle();
-    if (store) this.store = store;
-    if (_.contains(this.allowedValues, value)) {
-      this.removeAllowed(value);
-    } else {
-      this.pushAllowed(value);
-    }
+    return false;
   };
   Filter.prototype.cssByBannedValue = function (value) {
-    if (!this.allowedValues.length) return "";
-    return _.contains(this.allowedValues, value) ? "" : " " + this.className;
+    return "";
   };
   Filter.prototype.reset = function () {
-    this.ysy.$container.find("." + this.className).removeClass(this.className);
-    this.allowedValues = [];
   };
   /**
    *
@@ -52,29 +22,7 @@
    * @return {boolean}
    */
   Filter.prototype.isBanned = function (idea) {
-    if (!this.allowedValues.length) return false;
-    if (idea.attr.isFresh) return false;
-    var data = this.ysy.getData(idea);
-    var value = this.store.value(data);
-    return this.allowedValues.indexOf(value) === -1;
-  };
-  Filter.prototype.sweepNodes = function () {
-    this.recursiveBanner(this.ysy.idea);
+    return false;
   };
-  /**
-   *
-   * @param {ModelEntity} idea
-   */
-  Filter.prototype.recursiveBanner = function (idea) {
-    var banned = this.isBanned(idea);
-    var node = this.ysy.getNodeElement(idea);
-    if (node.length) {
-      node.toggleClass(this.className, banned);
-    }
-    if (idea.ideas) {
-      _.each(idea.ideas, this.recursiveBanner, this)
-    }
-  };
-
   window.easyMindMupClasses.Filter = Filter;
 })();
diff --git a/plugins/easy_mindmup/assets/javascripts/jasmine/helpers/test.js b/plugins/easy_mindmup/assets/javascripts/jasmine/helpers/test.js
deleted file mode 100644
index e3357e9..0000000
--- a/plugins/easy_mindmup/assets/javascripts/jasmine/helpers/test.js
+++ /dev/null
@@ -1,107 +0,0 @@
-(function () {
-  /**
-   * Class responsible for Jasmine testing
-   * @param {MindMup} ysy
-   * @property {MindMup} ysy
-   * @constructor
-   */
-  function JasmineTests(ysy) {
-    this.ysy = ysy;
-    this._redrawRequested = true;
-    this.jasmineStarted = false;
-    this.startCounter = 0;
-    this.beatCallbacks = [];
-    this.extraTestNames = [];
-    this.extraTestFunctions = [];
-    this.init(ysy);
-  }
-
-  /**
-   *
-   * @param {MindMup} ysy
-   */
-  JasmineTests.prototype.init = function (ysy) {
-    ysy.eventBus.register("TreeLoaded",function () {
-      if (!this.jasmineStarted) {
-        this.jasmineStarted = true;
-        window.jasmine.jasmineStart(ysy);
-      }
-    });
-    ysy.repainter.redrawMe(this);
-    this.loadExtraTests();
-  };
-  JasmineTests.prototype._render = function () {
-    if (this.beatCallbacks.length) {
-      var newCallbacks = [];
-      for (var i = 0; i < this.beatCallbacks.length; i++) {
-        var callPack = this.beatCallbacks[i];
-        if (callPack.rounds === 1) {
-          callPack.callback();
-        } else {
-          callPack.rounds--;
-          newCallbacks.push(callPack);
-        }
-      }
-      this.beatCallbacks = newCallbacks;
-      if(newCallbacks.length){
-        this.ysy.repainter.redrawMe(this);
-      }
-    }
-  };
-  JasmineTests.prototype.fewBeatsAfter = function (callback, count) {
-    if (count === undefined) count = 2;
-    this.beatCallbacks.push({callback: callback, rounds: count});
-    this.ysy.repainter.redrawMe(this);
-  };
-  JasmineTests.prototype.loadExtraTests = function () {
-    var self = this;
-    describe("(EXTRA)", function () {
-      for (var i = 0; i < self.extraTestFunctions.length; i++) {
-        self.extraTestFunctions[i]();
-      }
-    });
-  };
-  JasmineTests.prototype.parseResult = function () {
-    var specs = window.jsApiReporter.specs();
-    var shortReport = "";
-    var report = "";
-    var allPassed = true;
-    var result = "";
-    for (var i = 0; i < specs.length; i++) {
-      var spec = specs[i];
-      if (spec.status === "passed") {
-        shortReport += ".";
-      } else {
-        allPassed = false;
-        shortReport += "X";
-        report += "__TEST " + spec.fullName + "______\n";
-        for (var j = 0; j < spec.failedExpectations.length; j++) {
-          var fail = spec.failedExpectations[j];
-          var split = fail.stack.split("\n");
-          result += window.location + "\n";
-          report += "   " + fail.message + "\n";
-          for (var k = 1; k < split.length; k++) {
-            if (split[k].indexOf("/jasmine_lib/") > -1) break;
-            report += split[k] + "\n";
-          }
-        }
-      }
-    }
-    if (allPassed) {
-      return "success";
-    }
-    result += " RESULTS: " + shortReport + "\n" + report;
-    $("#content").text(result.replace("\n", "<br>"));
-    return result;
-  };
-  easyMindMupClasses.JasmineTests = JasmineTests;
-})();
-window.describeExtra = function (file, func) {
-  if (file.indexOf("/") === -1) {
-    file = "easy_mindmup/" + file
-  }
-  jasmine.ysyInstance.tests.extraTestNames.push(file);
-  jasmine.ysyInstance.tests.extraTestFunctions.push(function () {
-    describe(file, func);
-  });
-};
diff --git a/plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/boot.js b/plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/boot.js
deleted file mode 100644
index c1fd802..0000000
--- a/plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/boot.js
+++ /dev/null
@@ -1,135 +0,0 @@
-/**
- Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
-
- If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
-
- The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
-
- [jasmine-gem]: http://github.com/pivotal/jasmine-gem
- */
-
-(function() {
-
-  /**
-   * ## Require &amp; Instantiate
-   *
-   * Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
-   */
-  window.jasmine = jasmineRequire.core(jasmineRequire);
-
-  /**
-   * Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
-   */
-  jasmineRequire.html(jasmine);
-
-  /**
-   * Create the Jasmine environment. This is used to run all specs in a project.
-   */
-  var env = jasmine.getEnv();
-
-  /**
-   * ## The Global Interface
-   *
-   * Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
-   */
-  var jasmineInterface = jasmineRequire.interface(jasmine, env);
-
-  /**
-   * Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
-   */
-  extend(window, jasmineInterface);
-
-  /**
-   * ## Runner Parameters
-   *
-   * More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
-   */
-
-  var queryString = new jasmine.QueryString({
-    getWindowLocation: function() { return window.location; }
-  });
-
-  var catchingExceptions = queryString.getParam("catch");
-  env.catchExceptions(typeof catchingExceptions === "undefined" ? true : catchingExceptions);
-
-  var throwingExpectationFailures = queryString.getParam("throwFailures");
-  env.throwOnExpectationFailure(throwingExpectationFailures);
-
-  var random = queryString.getParam("random");
-  env.randomizeTests(random);
-
-  var seed = queryString.getParam("seed");
-  if (seed) {
-    env.seed(seed);
-  }
-
-  /**
-   * ## Reporters
-   * The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
-   */
-  var htmlReporter = new jasmine.HtmlReporter({
-    env: env,
-    onRaiseExceptionsClick: function() { queryString.navigateWithNewParam("catch", !env.catchingExceptions()); },
-    onThrowExpectationsClick: function() { queryString.navigateWithNewParam("throwFailures", !env.throwingExpectationFailures()); },
-    onRandomClick: function() { queryString.navigateWithNewParam("random", !env.randomTests()); },
-    addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); },
-    getContainer: function() { return document.body; },
-    createElement: function() { return document.createElement.apply(document, arguments); },
-    createTextNode: function() { return document.createTextNode.apply(document, arguments); },
-    timer: new jasmine.Timer()
-  });
-
-  /**
-   * The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results  from JavaScript.
-   */
-  env.addReporter(jasmineInterface.jsApiReporter);
-  env.addReporter(htmlReporter);
-
-  /**
-   * Filter which specs will be run by matching the start of the full name against the `spec` query param.
-   */
-  var specFilter = new jasmine.HtmlSpecFilter({
-    filterString: function() { return queryString.getParam("spec"); }
-  });
-
-  env.specFilter = function(spec) {
-    return specFilter.matches(spec.getFullName());
-  };
-
-  /**
-   * Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
-   */
-  window.setTimeout = window.setTimeout;
-  window.setInterval = window.setInterval;
-  window.clearTimeout = window.clearTimeout;
-  window.clearInterval = window.clearInterval;
-
-  /**
-   * ## Execution
-   *
-   * Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
-   */
-  var currentWindowOnload = window.onload;
-  /**
-   * @type {MindMup}
-   */
-  jasmine.ysyInstance = null;
-  jasmine.jasmineStart = function(ysyInstance) {
-    jasmine.ysyInstance = ysyInstance;
-  // window.onload = function() {   // HOSEKP
-    // if (currentWindowOnload) {
-    //   currentWindowOnload();
-    // }                            // HOSEKP
-    htmlReporter.initialize();
-    env.execute();
-  };
-
-  /**
-   * Helper function for readability above.
-   */
-  function extend(destination, source) {
-    for (var property in source) destination[property] = source[property];
-    return destination;
-  }
-
-}());
diff --git a/plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/jasmine-html.js b/plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/jasmine-html.js
deleted file mode 100644
index da23532..0000000
--- a/plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/jasmine-html.js
+++ /dev/null
@@ -1,473 +0,0 @@
-/*
-Copyright (c) 2008-2015 Pivotal Labs
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-jasmineRequire.html = function(j$) {
-  j$.ResultsNode = jasmineRequire.ResultsNode();
-  j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
-  j$.QueryString = jasmineRequire.QueryString();
-  j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
-};
-
-jasmineRequire.HtmlReporter = function(j$) {
-
-  var noopTimer = {
-    start: function() {},
-    elapsed: function() { return 0; }
-  };
-
-  function HtmlReporter(options) {
-    var env = options.env || {},
-      getContainer = options.getContainer,
-      createElement = options.createElement,
-      createTextNode = options.createTextNode,
-      onRaiseExceptionsClick = options.onRaiseExceptionsClick || function() {},
-      onThrowExpectationsClick = options.onThrowExpectationsClick || function() {},
-      onRandomClick = options.onRandomClick || function() {},
-      addToExistingQueryString = options.addToExistingQueryString || defaultQueryString,
-      timer = options.timer || noopTimer,
-      results = [],
-      specsExecuted = 0,
-      failureCount = 0,
-      pendingSpecCount = 0,
-      htmlReporterMain,
-      symbols,
-      failedSuites = [];
-
-    this.initialize = function() {
-      clearPrior();
-      htmlReporterMain = createDom('div', {className: 'jasmine_html-reporter'},
-        createDom('div', {className: 'jasmine-banner'},
-          createDom('a', {className: 'jasmine-title', href: 'http://jasmine.github.io/', target: '_blank'}),
-          createDom('span', {className: 'jasmine-version'}, j$.version)
-        ),
-        createDom('ul', {className: 'jasmine-symbol-summary'}),
-        createDom('div', {className: 'jasmine-alert'}),
-        createDom('div', {className: 'jasmine-results'},
-          createDom('div', {className: 'jasmine-failures'})
-        )
-      );
-      getContainer().appendChild(htmlReporterMain);
-    };
-
-    var totalSpecsDefined;
-    this.jasmineStarted = function(options) {
-      totalSpecsDefined = options.totalSpecsDefined || 0;
-      timer.start();
-    };
-
-    var summary = createDom('div', {className: 'jasmine-summary'});
-
-    var topResults = new j$.ResultsNode({}, '', null),
-      currentParent = topResults;
-
-    this.suiteStarted = function(result) {
-      currentParent.addChild(result, 'suite');
-      currentParent = currentParent.last();
-    };
-
-    this.suiteDone = function(result) {
-      if (result.status == 'failed') {
-        failedSuites.push(result);
-      }
-
-      if (currentParent == topResults) {
-        return;
-      }
-
-      currentParent = currentParent.parent;
-    };
-
-    this.specStarted = function(result) {
-      currentParent.addChild(result, 'spec');
-    };
-
-    var failures = [];
-    this.specDone = function(result) {
-      if(noExpectations(result) && typeof console !== 'undefined' && typeof console.error !== 'undefined') {
-        console.error('Spec \'' + result.fullName + '\' has no expectations.');
-      }
-
-      if (result.status != 'disabled') {
-        specsExecuted++;
-      }
-
-      if (!symbols){
-        symbols = find('.jasmine-symbol-summary');
-      }
-
-      symbols.appendChild(createDom('li', {
-          className: noExpectations(result) ? 'jasmine-empty' : 'jasmine-' + result.status,
-          id: 'spec_' + result.id,
-          title: result.fullName
-        }
-      ));
-
-      if (result.status == 'failed') {
-        failureCount++;
-
-        var failure =
-          createDom('div', {className: 'jasmine-spec-detail jasmine-failed'},
-            createDom('div', {className: 'jasmine-description'},
-              createDom('a', {title: result.fullName, href: specHref(result)}, result.fullName)
-            ),
-            createDom('div', {className: 'jasmine-messages'})
-          );
-        var messages = failure.childNodes[1];
-
-        for (var i = 0; i < result.failedExpectations.length; i++) {
-          var expectation = result.failedExpectations[i];
-          messages.appendChild(createDom('div', {className: 'jasmine-result-message'}, expectation.message));
-          messages.appendChild(createDom('div', {className: 'jasmine-stack-trace'}, expectation.stack));
-        }
-
-        failures.push(failure);
-      }
-
-      if (result.status == 'pending') {
-        pendingSpecCount++;
-      }
-    };
-
-    this.jasmineDone = function(doneResult) {
-      var banner = find('.jasmine-banner');
-      var alert = find('.jasmine-alert');
-      var order = doneResult && doneResult.order;
-      alert.appendChild(createDom('span', {className: 'jasmine-duration'}, 'finished in ' + timer.elapsed() / 1000 + 's'));
-
-      banner.appendChild(
-        createDom('div', { className: 'jasmine-run-options' },
-          createDom('span', { className: 'jasmine-trigger' }, 'Options'),
-          createDom('div', { className: 'jasmine-payload' },
-            createDom('div', { className: 'jasmine-exceptions' },
-              createDom('input', {
-                className: 'jasmine-raise',
-                id: 'jasmine-raise-exceptions',
-                type: 'checkbox'
-              }),
-              createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')),
-            createDom('div', { className: 'jasmine-throw-failures' },
-              createDom('input', {
-                className: 'jasmine-throw',
-                id: 'jasmine-throw-failures',
-                type: 'checkbox'
-              }),
-              createDom('label', { className: 'jasmine-label', 'for': 'jasmine-throw-failures' }, 'stop spec on expectation failure')),
-            createDom('div', { className: 'jasmine-random-order' },
-              createDom('input', {
-                className: 'jasmine-random',
-                id: 'jasmine-random-order',
-                type: 'checkbox'
-              }),
-              createDom('label', { className: 'jasmine-label', 'for': 'jasmine-random-order' }, 'run tests in random order'))
-          )
-        ));
-
-      var raiseCheckbox = find('#jasmine-raise-exceptions');
-
-      raiseCheckbox.checked = !env.catchingExceptions();
-      raiseCheckbox.onclick = onRaiseExceptionsClick;
-
-      var throwCheckbox = find('#jasmine-throw-failures');
-      throwCheckbox.checked = env.throwingExpectationFailures();
-      throwCheckbox.onclick = onThrowExpectationsClick;
-
-      var randomCheckbox = find('#jasmine-random-order');
-      randomCheckbox.checked = env.randomTests();
-      randomCheckbox.onclick = onRandomClick;
-
-      var optionsMenu = find('.jasmine-run-options'),
-          optionsTrigger = optionsMenu.querySelector('.jasmine-trigger'),
-          optionsPayload = optionsMenu.querySelector('.jasmine-payload'),
-          isOpen = /\bjasmine-open\b/;
-
-      optionsTrigger.onclick = function() {
-        if (isOpen.test(optionsPayload.className)) {
-          optionsPayload.className = optionsPayload.className.replace(isOpen, '');
-        } else {
-          optionsPayload.className += ' jasmine-open';
-        }
-      };
-
-      if (specsExecuted < totalSpecsDefined) {
-        var skippedMessage = 'Ran ' + specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all';
-        alert.appendChild(
-          createDom('span', {className: 'jasmine-bar jasmine-skipped'},
-            createDom('a', {href: '?', title: 'Run all specs'}, skippedMessage)
-          )
-        );
-      }
-      var statusBarMessage = '';
-      var statusBarClassName = 'jasmine-bar ';
-
-      if (totalSpecsDefined > 0) {
-        statusBarMessage += pluralize('spec', specsExecuted) + ', ' + pluralize('failure', failureCount);
-        if (pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', pendingSpecCount); }
-        statusBarClassName += (failureCount > 0) ? 'jasmine-failed' : 'jasmine-passed';
-      } else {
-        statusBarClassName += 'jasmine-skipped';
-        statusBarMessage += 'No specs found';
-      }
-
-      var seedBar;
-      if (order && order.random) {
-        seedBar = createDom('span', {className: 'jasmine-seed-bar'},
-          ', randomized with seed ',
-          createDom('a', {title: 'randomized with seed ' + order.seed, href: seedHref(order.seed)}, order.seed)
-        );
-      }
-
-      alert.appendChild(createDom('span', {className: statusBarClassName}, statusBarMessage, seedBar));
-
-      for(i = 0; i < failedSuites.length; i++) {
-        var failedSuite = failedSuites[i];
-        for(var j = 0; j < failedSuite.failedExpectations.length; j++) {
-          var errorBarMessage = 'AfterAll ' + failedSuite.failedExpectations[j].message;
-          var errorBarClassName = 'jasmine-bar jasmine-errored';
-          alert.appendChild(createDom('span', {className: errorBarClassName}, errorBarMessage));
-        }
-      }
-
-      var results = find('.jasmine-results');
-      results.appendChild(summary);
-
-      summaryList(topResults, summary);
-
-      function summaryList(resultsTree, domParent) {
-        var specListNode;
-        for (var i = 0; i < resultsTree.children.length; i++) {
-          var resultNode = resultsTree.children[i];
-          if (resultNode.type == 'suite') {
-            var suiteListNode = createDom('ul', {className: 'jasmine-suite', id: 'suite-' + resultNode.result.id},
-              createDom('li', {className: 'jasmine-suite-detail'},
-                createDom('a', {href: specHref(resultNode.result)}, resultNode.result.description)
-              )
-            );
-
-            summaryList(resultNode, suiteListNode);
-            domParent.appendChild(suiteListNode);
-          }
-          if (resultNode.type == 'spec') {
-            if (domParent.getAttribute('class') != 'jasmine-specs') {
-              specListNode = createDom('ul', {className: 'jasmine-specs'});
-              domParent.appendChild(specListNode);
-            }
-            var specDescription = resultNode.result.description;
-            if(noExpectations(resultNode.result)) {
-              specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
-            }
-            if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') {
-              specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason;
-            }
-            specListNode.appendChild(
-              createDom('li', {
-                  className: 'jasmine-' + resultNode.result.status,
-                  id: 'spec-' + resultNode.result.id
-                },
-                createDom('a', {href: specHref(resultNode.result)}, specDescription)
-              )
-            );
-          }
-        }
-      }
-
-      if (failures.length) {
-        alert.appendChild(
-          createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-spec-list'},
-            createDom('span', {}, 'Spec List | '),
-            createDom('a', {className: 'jasmine-failures-menu', href: '#'}, 'Failures')));
-        alert.appendChild(
-          createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-failure-list'},
-            createDom('a', {className: 'jasmine-spec-list-menu', href: '#'}, 'Spec List'),
-            createDom('span', {}, ' | Failures ')));
-
-        find('.jasmine-failures-menu').onclick = function() {
-          setMenuModeTo('jasmine-failure-list');
-        };
-        find('.jasmine-spec-list-menu').onclick = function() {
-          setMenuModeTo('jasmine-spec-list');
-        };
-
-        setMenuModeTo('jasmine-failure-list');
-
-        var failureNode = find('.jasmine-failures');
-        for (var i = 0; i < failures.length; i++) {
-          failureNode.appendChild(failures[i]);
-        }
-      }
-    };
-
-    return this;
-
-    function find(selector) {
-      return getContainer().querySelector('.jasmine_html-reporter ' + selector);
-    }
-
-    function clearPrior() {
-      // return the reporter
-      var oldReporter = find('');
-
-      if(oldReporter) {
-        getContainer().removeChild(oldReporter);
-      }
-    }
-
-    function createDom(type, attrs, childrenVarArgs) {
-      var el = createElement(type);
-
-      for (var i = 2; i < arguments.length; i++) {
-        var child = arguments[i];
-
-        if (typeof child === 'string') {
-          el.appendChild(createTextNode(child));
-        } else {
-          if (child) {
-            el.appendChild(child);
-          }
-        }
-      }
-
-      for (var attr in attrs) {
-        if (attr == 'className') {
-          el[attr] = attrs[attr];
-        } else {
-          el.setAttribute(attr, attrs[attr]);
-        }
-      }
-
-      return el;
-    }
-
-    function pluralize(singular, count) {
-      var word = (count == 1 ? singular : singular + 's');
-
-      return '' + count + ' ' + word;
-    }
-
-    function specHref(result) {
-      return addToExistingQueryString('spec', result.fullName);
-    }
-
-    function seedHref(seed) {
-      return addToExistingQueryString('seed', seed);
-    }
-
-    function defaultQueryString(key, value) {
-      return '?' + key + '=' + value;
-    }
-
-    function setMenuModeTo(mode) {
-      htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode);
-    }
-
-    function noExpectations(result) {
-      return (result.failedExpectations.length + result.passedExpectations.length) === 0 &&
-        result.status === 'passed';
-    }
-  }
-
-  return HtmlReporter;
-};
-
-jasmineRequire.HtmlSpecFilter = function() {
-  function HtmlSpecFilter(options) {
-    var filterString = options && options.filterString() && options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
-    var filterPattern = new RegExp(filterString);
-
-    this.matches = function(specName) {
-      return filterPattern.test(specName);
-    };
-  }
-
-  return HtmlSpecFilter;
-};
-
-jasmineRequire.ResultsNode = function() {
-  function ResultsNode(result, type, parent) {
-    this.result = result;
-    this.type = type;
-    this.parent = parent;
-
-    this.children = [];
-
-    this.addChild = function(result, type) {
-      this.children.push(new ResultsNode(result, type, this));
-    };
-
-    this.last = function() {
-      return this.children[this.children.length - 1];
-    };
-  }
-
-  return ResultsNode;
-};
-
-jasmineRequire.QueryString = function() {
-  function QueryString(options) {
-
-    this.navigateWithNewParam = function(key, value) {
-      options.getWindowLocation().search = this.fullStringWithNewParam(key, value);
-    };
-
-    this.fullStringWithNewParam = function(key, value) {
-      var paramMap = queryStringToParamMap();
-      paramMap[key] = value;
-      return toQueryString(paramMap);
-    };
-
-    this.getParam = function(key) {
-      return queryStringToParamMap()[key];
-    };
-
-    return this;
-
-    function toQueryString(paramMap) {
-      var qStrPairs = [];
-      for (var prop in paramMap) {
-        qStrPairs.push(encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop]));
-      }
-      return '?' + qStrPairs.join('&');
-    }
-
-    function queryStringToParamMap() {
-      var paramStr = options.getWindowLocation().search.substring(1),
-        params = [],
-        paramMap = {};
-
-      if (paramStr.length > 0) {
-        params = paramStr.split('&');
-        for (var i = 0; i < params.length; i++) {
-          var p = params[i].split('=');
-          var value = decodeURIComponent(p[1]);
-          if (value === 'true' || value === 'false') {
-            value = JSON.parse(value);
-          }
-          paramMap[decodeURIComponent(p[0])] = value;
-        }
-      }
-
-      return paramMap;
-    }
-
-  }
-
-  return QueryString;
-};
diff --git a/plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/jasmine.js b/plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/jasmine.js
deleted file mode 100644
index 7156e66..0000000
--- a/plugins/easy_mindmup/assets/javascripts/jasmine/jasmine_lib/jasmine.js
+++ /dev/null
@@ -1,4941 +0,0 @@
-/*
- Copyright (c) 2008-2017 Pivotal Labs
-
- Permission is hereby granted, free of charge, to any person obtaining
- a copy of this software and associated documentation files (the
- "Software"), to deal in the Software without restriction, including
- without limitation the rights to use, copy, modify, merge, publish,
- distribute, sublicense, and/or sell copies of the Software, and to
- permit persons to whom the Software is furnished to do so, subject to
- the following conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-var getJasmineRequireObj = (function (jasmineGlobal) {
-  var jasmineRequire;
-
-  if (typeof module !== 'undefined' && module.exports && typeof exports !== 'undefined') {
-    if (typeof global !== 'undefined') {
-      jasmineGlobal = global;
-    } else {
-      jasmineGlobal = {};
-    }
-    jasmineRequire = exports;
-  } else {
-    if (typeof window !== 'undefined' && typeof window.toString === 'function' && window.toString() === '[object GjsGlobal]') {
-      jasmineGlobal = window;
-    }
-    jasmineRequire = jasmineGlobal.jasmineRequire = jasmineGlobal.jasmineRequire || {};
-  }
-
-  function getJasmineRequire() {
-    return jasmineRequire;
-  }
-
-  getJasmineRequire().core = function(jRequire) {
-    var j$ = {};
-
-    jRequire.base(j$, jasmineGlobal);
-    j$.util = jRequire.util();
-    j$.errors = jRequire.errors();
-    j$.formatErrorMsg = jRequire.formatErrorMsg();
-    j$.Any = jRequire.Any(j$);
-    j$.Anything = jRequire.Anything(j$);
-    j$.CallTracker = jRequire.CallTracker(j$);
-    j$.MockDate = jRequire.MockDate();
-    j$.getClearStack = jRequire.clearStack(j$);
-    j$.Clock = jRequire.Clock();
-    j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler();
-    j$.Env = jRequire.Env(j$);
-    j$.ExceptionFormatter = jRequire.ExceptionFormatter();
-    j$.Expectation = jRequire.Expectation();
-    j$.buildExpectationResult = jRequire.buildExpectationResult();
-    j$.JsApiReporter = jRequire.JsApiReporter();
-    j$.matchersUtil = jRequire.matchersUtil(j$);
-    j$.ObjectContaining = jRequire.ObjectContaining(j$);
-    j$.ArrayContaining = jRequire.ArrayContaining(j$);
-    j$.pp = jRequire.pp(j$);
-    j$.QueueRunner = jRequire.QueueRunner(j$);
-    j$.ReportDispatcher = jRequire.ReportDispatcher();
-    j$.Spec = jRequire.Spec(j$);
-    j$.Spy = jRequire.Spy(j$);
-    j$.SpyRegistry = jRequire.SpyRegistry(j$);
-    j$.SpyStrategy = jRequire.SpyStrategy(j$);
-    j$.StringMatching = jRequire.StringMatching(j$);
-    j$.Suite = jRequire.Suite(j$);
-    j$.Timer = jRequire.Timer();
-    j$.TreeProcessor = jRequire.TreeProcessor();
-    j$.version = jRequire.version();
-    j$.Order = jRequire.Order();
-    j$.DiffBuilder = jRequire.DiffBuilder(j$);
-    j$.NullDiffBuilder = jRequire.NullDiffBuilder(j$);
-    j$.ObjectPath = jRequire.ObjectPath(j$);
-    j$.GlobalErrors = jRequire.GlobalErrors(j$);
-
-    j$.matchers = jRequire.requireMatchers(jRequire, j$);
-
-    return j$;
-  };
-
-  return getJasmineRequire;
-})(this);
-
-getJasmineRequireObj().requireMatchers = function(jRequire, j$) {
-  var availableMatchers = [
-        'toBe',
-        'toBeCloseTo',
-        'toBeDefined',
-        'toBeFalsy',
-        'toBeGreaterThan',
-        'toBeGreaterThanOrEqual',
-        'toBeLessThan',
-        'toBeLessThanOrEqual',
-        'toBeNaN',
-        'toBeNegativeInfinity',
-        'toBeNull',
-        'toBePositiveInfinity',
-        'toBeTruthy',
-        'toBeUndefined',
-        'toContain',
-        'toEqual',
-        'toHaveBeenCalled',
-        'toHaveBeenCalledBefore',
-        'toHaveBeenCalledTimes',
-        'toHaveBeenCalledWith',
-        'toMatch',
-        'toThrow',
-        'toThrowError'
-      ],
-      matchers = {};
-
-  for (var i = 0; i < availableMatchers.length; i++) {
-    var name = availableMatchers[i];
-    matchers[name] = jRequire[name](j$);
-  }
-
-  return matchers;
-};
-
-getJasmineRequireObj().base = function(j$, jasmineGlobal) {
-  j$.unimplementedMethod_ = function() {
-    throw new Error('unimplemented method');
-  };
-
-  /**
-   * Maximum object depth the pretty printer will print to.
-   * Set this to a lower value to speed up pretty printing if you have large objects.
-   * @name jasmine.MAX_PRETTY_PRINT_DEPTH
-   */
-  j$.MAX_PRETTY_PRINT_DEPTH = 40;
-  /**
-   * Maximum number of array elements to display when pretty printing objects.
-   * Elements past this number will be ellipised.
-   * @name jasmine.MAX_PRETTY_PRINT_ARRAY_LENGTH
-   */
-  j$.MAX_PRETTY_PRINT_ARRAY_LENGTH = 100;
-  /**
-   * Default number of milliseconds Jasmine will wait for an asynchronous spec to complete.
-   * @name jasmine.DEFAULT_TIMEOUT_INTERVAL
-   */
-  j$.DEFAULT_TIMEOUT_INTERVAL = 5000;
-
-  j$.getGlobal = function() {
-    return jasmineGlobal;
-  };
-
-  /**
-   * Get the currently booted Jasmine Environment.
-   *
-   * @name jasmine.getEnv
-   * @function
-   * @return {Env}
-   */
-  j$.getEnv = function(options) {
-    var env = j$.currentEnv_ = j$.currentEnv_ || new j$.Env(options);
-    //jasmine. singletons in here (setTimeout blah blah).
-    return env;
-  };
-
-  j$.isArray_ = function(value) {
-    return j$.isA_('Array', value);
-  };
-
-  j$.isObject_ = function(value) {
-    return !j$.util.isUndefined(value) && value !== null && j$.isA_('Object', value);
-  };
-
-  j$.isString_ = function(value) {
-    return j$.isA_('String', value);
-  };
-
-  j$.isNumber_ = function(value) {
-    return j$.isA_('Number', value);
-  };
-
-  j$.isFunction_ = function(value) {
-    return j$.isA_('Function', value);
-  };
-
-  j$.isA_ = function(typeName, value) {
-    return j$.getType_(value) === '[object ' + typeName + ']';
-  };
-
-  j$.getType_ = function(value) {
-    return Object.prototype.toString.apply(value);
-  };
-
-  j$.isDomNode = function(obj) {
-    return obj.nodeType > 0;
-  };
-
-  j$.fnNameFor = function(func) {
-    if (func.name) {
-      return func.name;
-    }
-
-    var matches = func.toString().match(/^\s*function\s*(\w*)\s*\(/);
-    return matches ? matches[1] : '<anonymous>';
-  };
-
-  /**
-   * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
-   * that will succeed if the actual value being compared is an instance of the specified class/constructor.
-   * @name jasmine.any
-   * @function
-   * @param {Constructor} clazz - The constructor to check against.
-   */
-  j$.any = function(clazz) {
-    return new j$.Any(clazz);
-  };
-
-  /**
-   * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
-   * that will succeed if the actual value being compared is not `null` and not `undefined`.
-   * @name jasmine.anything
-   * @function
-   */
-  j$.anything = function() {
-    return new j$.Anything();
-  };
-
-  /**
-   * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
-   * that will succeed if the actual value being compared contains at least the keys and values.
-   * @name jasmine.objectContaining
-   * @function
-   * @param {Object} sample - The subset of properties that _must_ be in the actual.
-   */
-  j$.objectContaining = function(sample) {
-    return new j$.ObjectContaining(sample);
-  };
-
-  /**
-   * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
-   * that will succeed if the actual value is a `String` that matches the `RegExp` or `String`.
-   * @name jasmine.stringMatching
-   * @function
-   * @param {RegExp|String} expected
-   */
-  j$.stringMatching = function(expected) {
-    return new j$.StringMatching(expected);
-  };
-
-  /**
-   * Get a matcher, usable in any {@link matchers|matcher} that uses Jasmine's equality (e.g. {@link matchers#toEqual|toEqual}, {@link matchers#toContain|toContain}, or {@link matchers#toHaveBeenCalledWith|toHaveBeenCalledWith}),
-   * that will succeed if the actual value is an `Array` that contains at least the elements in the sample.
-   * @name jasmine.arrayContaining
-   * @function
-   * @param {Array} sample
-   */
-  j$.arrayContaining = function(sample) {
-    return new j$.ArrayContaining(sample);
-  };
-
-  /**
-   * Create a bare {@link Spy} object. This won't be installed anywhere and will not have any implementation behind it.
-   * @name jasmine.createSpy
-   * @function
-   * @param {String} [name] - Name to give the spy. This will be displayed in failure messages.
-   * @param {Function} [originalFn] - Function to act as the real implementation.
-   * @return {Spy}
-   */
-  j$.createSpy = function(name, originalFn) {
-    return j$.Spy(name, originalFn);
-  };
-
-  j$.isSpy = function(putativeSpy) {
-    if (!putativeSpy) {
-      return false;
-    }
-    return putativeSpy.and instanceof j$.SpyStrategy &&
-        putativeSpy.calls instanceof j$.CallTracker;
-  };
-
-  /**
-   * Create an object with multiple {@link Spy}s as its members.
-   * @name jasmine.createSpyObj
-   * @function
-   * @param {String} [baseName] - Base name for the spies in the object.
-   * @param {String[]|Object} methodNames - Array of method names to create spies for, or Object whose keys will be method names and values the {@link Spy#and#returnValue|returnValue}.
-   * @return {Object}
-   */
-  j$.createSpyObj = function(baseName, methodNames) {
-    var baseNameIsCollection = j$.isObject_(baseName) || j$.isArray_(baseName);
-
-    if (baseNameIsCollection && j$.util.isUndefined(methodNames)) {
-      methodNames = baseName;
-      baseName = 'unknown';
-    }
-
-    var obj = {};
-    var spiesWereSet = false;
-
-    if (j$.isArray_(methodNames)) {
-      for (var i = 0; i < methodNames.length; i++) {
-        obj[methodNames[i]] = j$.createSpy(baseName + '.' + methodNames[i]);
-        spiesWereSet = true;
-      }
-    } else if (j$.isObject_(methodNames)) {
-      for (var key in methodNames) {
-        if (methodNames.hasOwnProperty(key)) {
-          obj[key] = j$.createSpy(baseName + '.' + key);
-          obj[key].and.returnValue(methodNames[key]);
-          spiesWereSet = true;
-        }
-      }
-    }
-
-    if (!spiesWereSet) {
-      throw 'createSpyObj requires a non-empty array or object of method names to create spies for';
-    }
-
-    return obj;
-  };
-};
-
-getJasmineRequireObj().util = function() {
-
-  var util = {};
-
-  util.inherit = function(childClass, parentClass) {
-    var Subclass = function() {
-    };
-    Subclass.prototype = parentClass.prototype;
-    childClass.prototype = new Subclass();
-  };
-
-  util.htmlEscape = function(str) {
-    if (!str) {
-      return str;
-    }
-    return str.replace(/&/g, '&amp;')
-        .replace(/</g, '&lt;')
-        .replace(/>/g, '&gt;');
-  };
-
-  util.argsToArray = function(args) {
-    var arrayOfArgs = [];
-    for (var i = 0; i < args.length; i++) {
-      arrayOfArgs.push(args[i]);
-    }
-    return arrayOfArgs;
-  };
-
-  util.isUndefined = function(obj) {
-    return obj === void 0;
-  };
-
-  util.arrayContains = function(array, search) {
-    var i = array.length;
-    while (i--) {
-      if (array[i] === search) {
-        return true;
-      }
-    }
-    return false;
-  };
-
-  util.clone = function(obj) {
-    if (Object.prototype.toString.apply(obj) === '[object Array]') {
-      return obj.slice();
-    }
-
-    var cloned = {};
-    for (var prop in obj) {
-      if (obj.hasOwnProperty(prop)) {
-        cloned[prop] = obj[prop];
-      }
-    }
-
-    return cloned;
-  };
-
-  util.getPropertyDescriptor = function(obj, methodName) {
-    var descriptor,
-        proto = obj;
-
-    do {
-      descriptor = Object.getOwnPropertyDescriptor(proto, methodName);
-      proto = Object.getPrototypeOf(proto);
-    } while (!descriptor && proto);
-
-    return descriptor;
-  };
-
-  util.objectDifference = function(obj, toRemove) {
-    var diff = {};
-
-    for (var key in obj) {
-      if (util.has(obj, key) && !util.has(toRemove, key)) {
-        diff[key] = obj[key];
-      }
-    }
-
-    return diff;
-  };
-
-  util.has = function(obj, key) {
-    return Object.prototype.hasOwnProperty.call(obj, key);
-  };
-
-  return util;
-};
-
-getJasmineRequireObj().Spec = function(j$) {
-  function Spec(attrs) {
-    this.expectationFactory = attrs.expectationFactory;
-    this.resultCallback = attrs.resultCallback || function() {};
-    this.id = attrs.id;
-    this.description = attrs.description || '';
-    this.queueableFn = attrs.queueableFn;
-    this.beforeAndAfterFns = attrs.beforeAndAfterFns || function() { return {befores: [], afters: []}; };
-    this.userContext = attrs.userContext || function() { return {}; };
-    this.onStart = attrs.onStart || function() {};
-    this.getSpecName = attrs.getSpecName || function() { return ''; };
-    this.expectationResultFactory = attrs.expectationResultFactory || function() { };
-    this.queueRunnerFactory = attrs.queueRunnerFactory || function() {};
-    this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
-    this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
-
-    if (!this.queueableFn.fn) {
-      this.pend();
-    }
-
-    this.result = {
-      id: this.id,
-      description: this.description,
-      fullName: this.getFullName(),
-      failedExpectations: [],
-      passedExpectations: [],
-      pendingReason: ''
-    };
-  }
-
-  Spec.prototype.addExpectationResult = function(passed, data, isError) {
-    var expectationResult = this.expectationResultFactory(data);
-    if (passed) {
-      this.result.passedExpectations.push(expectationResult);
-    } else {
-      this.result.failedExpectations.push(expectationResult);
-
-      if (this.throwOnExpectationFailure && !isError) {
-        throw new j$.errors.ExpectationFailed();
-      }
-    }
-  };
-
-  Spec.prototype.expect = function(actual) {
-    return this.expectationFactory(actual, this);
-  };
-
-  Spec.prototype.execute = function(onComplete, enabled) {
-    var self = this;
-
-    this.onStart(this);
-
-    if (!this.isExecutable() || this.markedPending || enabled === false) {
-      complete(enabled);
-      return;
-    }
-
-    var fns = this.beforeAndAfterFns();
-    var allFns = fns.befores.concat(this.queueableFn).concat(fns.afters);
-
-    this.queueRunnerFactory({
-      queueableFns: allFns,
-      onException: function() { self.onException.apply(self, arguments); },
-      onComplete: complete,
-      userContext: this.userContext()
-    });
-
-    function complete(enabledAgain) {
-      self.result.status = self.status(enabledAgain);
-      self.resultCallback(self.result);
-
-      if (onComplete) {
-        onComplete();
-      }
-    }
-  };
-
-  Spec.prototype.onException = function onException(e) {
-    if (Spec.isPendingSpecException(e)) {
-      this.pend(extractCustomPendingMessage(e));
-      return;
-    }
-
-    if (e instanceof j$.errors.ExpectationFailed) {
-      return;
-    }
-
-    this.addExpectationResult(false, {
-      matcherName: '',
-      passed: false,
-      expected: '',
-      actual: '',
-      error: e
-    }, true);
-  };
-
-  Spec.prototype.disable = function() {
-    this.disabled = true;
-  };
-
-  Spec.prototype.pend = function(message) {
-    this.markedPending = true;
-    if (message) {
-      this.result.pendingReason = message;
-    }
-  };
-
-  Spec.prototype.getResult = function() {
-    this.result.status = this.status();
-    return this.result;
-  };
-
-  Spec.prototype.status = function(enabled) {
-    if (this.disabled || enabled === false) {
-      return 'disabled';
-    }
-
-    if (this.markedPending) {
-      return 'pending';
-    }
-
-    if (this.result.failedExpectations.length > 0) {
-      return 'failed';
-    } else {
-      return 'passed';
-    }
-  };
-
-  Spec.prototype.isExecutable = function() {
-    return !this.disabled;
-  };
-
-  Spec.prototype.getFullName = function() {
-    return this.getSpecName(this);
-  };
-
-  var extractCustomPendingMessage = function(e) {
-    var fullMessage = e.toString(),
-        boilerplateStart = fullMessage.indexOf(Spec.pendingSpecExceptionMessage),
-        boilerplateEnd = boilerplateStart + Spec.pendingSpecExceptionMessage.length;
-
-    return fullMessage.substr(boilerplateEnd);
-  };
-
-  Spec.pendingSpecExceptionMessage = '=> marked Pending';
-
-  Spec.isPendingSpecException = function(e) {
-    return !!(e && e.toString && e.toString().indexOf(Spec.pendingSpecExceptionMessage) !== -1);
-  };
-
-  return Spec;
-};
-
-if (typeof window == void 0 && typeof exports == 'object') {
-  exports.Spec = jasmineRequire.Spec;
-}
-
-/*jshint bitwise: false*/
-
-getJasmineRequireObj().Order = function() {
-  function Order(options) {
-    this.random = 'random' in options ? options.random : true;
-    var seed = this.seed = options.seed || generateSeed();
-    this.sort = this.random ? randomOrder : naturalOrder;
-
-    function naturalOrder(items) {
-      return items;
-    }
-
-    function randomOrder(items) {
-      var copy = items.slice();
-      copy.sort(function(a, b) {
-        return jenkinsHash(seed + a.id) - jenkinsHash(seed + b.id);
-      });
-      return copy;
-    }
-
-    function generateSeed() {
-      return String(Math.random()).slice(-5);
-    }
-
-    // Bob Jenkins One-at-a-Time Hash algorithm is a non-cryptographic hash function
-    // used to get a different output when the key changes slighly.
-    // We use your return to sort the children randomly in a consistent way when
-    // used in conjunction with a seed
-
-    function jenkinsHash(key) {
-      var hash, i;
-      for(hash = i = 0; i < key.length; ++i) {
-        hash += key.charCodeAt(i);
-        hash += (hash << 10);
-        hash ^= (hash >> 6);
-      }
-      hash += (hash << 3);
-      hash ^= (hash >> 11);
-      hash += (hash << 15);
-      return hash;
-    }
-
-  }
-
-  return Order;
-};
-
-getJasmineRequireObj().Env = function(j$) {
-  /**
-   * _Note:_ Do not construct this directly, Jasmine will make one during booting.
-   * @name Env
-   * @classdesc The Jasmine environment
-   * @constructor
-   */
-  function Env(options) {
-    options = options || {};
-
-    var self = this;
-    var global = options.global || j$.getGlobal();
-
-    var totalSpecsDefined = 0;
-
-    var catchExceptions = true;
-
-    var realSetTimeout = j$.getGlobal().setTimeout;
-    var realClearTimeout = j$.getGlobal().clearTimeout;
-    var clearStack = j$.getClearStack(j$.getGlobal());
-    this.clock = new j$.Clock(global, function () { return new j$.DelayedFunctionScheduler(); }, new j$.MockDate(global));
-
-    var runnableResources = {};
-
-    var currentSpec = null;
-    var currentlyExecutingSuites = [];
-    var currentDeclarationSuite = null;
-    var throwOnExpectationFailure = false;
-    var random = false;
-    var seed = null;
-
-    var currentSuite = function() {
-      return currentlyExecutingSuites[currentlyExecutingSuites.length - 1];
-    };
-
-    var currentRunnable = function() {
-      return currentSpec || currentSuite();
-    };
-
-    var reporter = new j$.ReportDispatcher([
-      'jasmineStarted',
-      'jasmineDone',
-      'suiteStarted',
-      'suiteDone',
-      'specStarted',
-      'specDone'
-    ]);
-
-    var globalErrors = new j$.GlobalErrors();
-
-    this.specFilter = function() {
-      return true;
-    };
-
-    this.addCustomEqualityTester = function(tester) {
-      if(!currentRunnable()) {
-        throw new Error('Custom Equalities must be added in a before function or a spec');
-      }
-      runnableResources[currentRunnable().id].customEqualityTesters.push(tester);
-    };
-
-    this.addMatchers = function(matchersToAdd) {
-      if(!currentRunnable()) {
-        throw new Error('Matchers must be added in a before function or a spec');
-      }
-      var customMatchers = runnableResources[currentRunnable().id].customMatchers;
-      for (var matcherName in matchersToAdd) {
-        customMatchers[matcherName] = matchersToAdd[matcherName];
-      }
-    };
-
-    j$.Expectation.addCoreMatchers(j$.matchers);
-
-    var nextSpecId = 0;
-    var getNextSpecId = function() {
-      return 'spec' + nextSpecId++;
-    };
-
-    var nextSuiteId = 0;
-    var getNextSuiteId = function() {
-      return 'suite' + nextSuiteId++;
-    };
-
-    var expectationFactory = function(actual, spec) {
-      return j$.Expectation.Factory({
-        util: j$.matchersUtil,
-        customEqualityTesters: runnableResources[spec.id].customEqualityTesters,
-        customMatchers: runnableResources[spec.id].customMatchers,
-        actual: actual,
-        addExpectationResult: addExpectationResult
-      });
-
-      function addExpectationResult(passed, result) {
-        return spec.addExpectationResult(passed, result);
-      }
-    };
-
-    var defaultResourcesForRunnable = function(id, parentRunnableId) {
-      var resources = {spies: [], customEqualityTesters: [], customMatchers: {}};
-
-      if(runnableResources[parentRunnableId]){
-        resources.customEqualityTesters = j$.util.clone(runnableResources[parentRunnableId].customEqualityTesters);
-        resources.customMatchers = j$.util.clone(runnableResources[parentRunnableId].customMatchers);
-      }
-
-      runnableResources[id] = resources;
-    };
-
-    var clearResourcesForRunnable = function(id) {
-      spyRegistry.clearSpies();
-      delete runnableResources[id];
-    };
-
-    var beforeAndAfterFns = function(suite) {
-      return function() {
-        var befores = [],
-            afters = [];
-
-        while(suite) {
-          befores = befores.concat(suite.beforeFns);
-          afters = afters.concat(suite.afterFns);
-
-          suite = suite.parentSuite;
-        }
-
-        return {
-          befores: befores.reverse(),
-          afters: afters
-        };
-      };
-    };
-
-    var getSpecName = function(spec, suite) {
-      var fullName = [spec.description],
-          suiteFullName = suite.getFullName();
-
-      if (suiteFullName !== '') {
-        fullName.unshift(suiteFullName);
-      }
-      return fullName.join(' ');
-    };
-
-    // TODO: we may just be able to pass in the fn instead of wrapping here
-    var buildExpectationResult = j$.buildExpectationResult,
-        exceptionFormatter = new j$.ExceptionFormatter(),
-        expectationResultFactory = function(attrs) {
-          attrs.messageFormatter = exceptionFormatter.message;
-          attrs.stackFormatter = exceptionFormatter.stack;
-
-          return buildExpectationResult(attrs);
-        };
-
-    // TODO: fix this naming, and here's where the value comes in
-    this.catchExceptions = function(value) {
-      catchExceptions = !!value;
-      return catchExceptions;
-    };
-
-    this.catchingExceptions = function() {
-      return catchExceptions;
-    };
-
-    var maximumSpecCallbackDepth = 20;
-    var currentSpecCallbackDepth = 0;
-
-    var catchException = function(e) {
-      return j$.Spec.isPendingSpecException(e) || catchExceptions;
-    };
-
-    this.throwOnExpectationFailure = function(value) {
-      throwOnExpectationFailure = !!value;
-    };
-
-    this.throwingExpectationFailures = function() {
-      return throwOnExpectationFailure;
-    };
-
-    this.randomizeTests = function(value) {
-      random = !!value;
-    };
-
-    this.randomTests = function() {
-      return random;
-    };
-
-    this.seed = function(value) {
-      if (value) {
-        seed = value;
-      }
-      return seed;
-    };
-
-    var queueRunnerFactory = function(options) {
-      options.catchException = catchException;
-      options.clearStack = options.clearStack || clearStack;
-      options.timeout = {setTimeout: realSetTimeout, clearTimeout: realClearTimeout};
-      options.fail = self.fail;
-      options.globalErrors = globalErrors;
-
-      new j$.QueueRunner(options).execute();
-    };
-
-    var topSuite = new j$.Suite({
-      env: this,
-      id: getNextSuiteId(),
-      description: 'Jasmine__TopLevel__Suite',
-      expectationFactory: expectationFactory,
-      expectationResultFactory: expectationResultFactory
-    });
-    defaultResourcesForRunnable(topSuite.id);
-    currentDeclarationSuite = topSuite;
-
-    this.topSuite = function() {
-      return topSuite;
-    };
-
-    this.execute = function(runnablesToRun) {
-      if(!runnablesToRun) {
-        if (focusedRunnables.length) {
-          runnablesToRun = focusedRunnables;
-        } else {
-          runnablesToRun = [topSuite.id];
-        }
-      }
-
-      var order = new j$.Order({
-        random: random,
-        seed: seed
-      });
-
-      var processor = new j$.TreeProcessor({
-        tree: topSuite,
-        runnableIds: runnablesToRun,
-        queueRunnerFactory: queueRunnerFactory,
-        nodeStart: function(suite) {
-          currentlyExecutingSuites.push(suite);
-          defaultResourcesForRunnable(suite.id, suite.parentSuite.id);
-          reporter.suiteStarted(suite.result);
-        },
-        nodeComplete: function(suite, result) {
-          if (!suite.markedPending) {
-            clearResourcesForRunnable(suite.id);
-          }
-          currentlyExecutingSuites.pop();
-          reporter.suiteDone(result);
-        },
-        orderChildren: function(node) {
-          return order.sort(node.children);
-        }
-      });
-
-      if(!processor.processTree().valid) {
-        throw new Error('Invalid order: would cause a beforeAll or afterAll to be run multiple times');
-      }
-
-      reporter.jasmineStarted({
-        totalSpecsDefined: totalSpecsDefined
-      });
-
-      currentlyExecutingSuites.push(topSuite);
-
-      globalErrors.install();
-      processor.execute(function() {
-        clearResourcesForRunnable(topSuite.id);
-        currentlyExecutingSuites.pop();
-        globalErrors.uninstall();
-
-        reporter.jasmineDone({
-          order: order,
-          failedExpectations: topSuite.result.failedExpectations
-        });
-      });
-    };
-
-    /**
-     * Add a custom reporter to the Jasmine environment.
-     * @name Env#addReporter
-     * @function
-     * @see custom_reporter
-     */
-    this.addReporter = function(reporterToAdd) {
-      reporter.addReporter(reporterToAdd);
-    };
-
-    this.provideFallbackReporter = function(reporterToAdd) {
-      reporter.provideFallbackReporter(reporterToAdd);
-    };
-
-    this.clearReporters = function() {
-      reporter.clearReporters();
-    };
-
-    var spyRegistry = new j$.SpyRegistry({currentSpies: function() {
-      if(!currentRunnable()) {
-        throw new Error('Spies must be created in a before function or a spec');
-      }
-      return runnableResources[currentRunnable().id].spies;
-    }});
-
-    this.allowRespy = function(allow){
-      spyRegistry.allowRespy(allow);
-    };
-
-    this.spyOn = function() {
-      return spyRegistry.spyOn.apply(spyRegistry, arguments);
-    };
-
-    this.spyOnProperty = function() {
-      return spyRegistry.spyOnProperty.apply(spyRegistry, arguments);
-    };
-
-    var ensureIsFunction = function(fn, caller) {
-      if (!j$.isFunction_(fn)) {
-        throw new Error(caller + ' expects a function argument; received ' + j$.getType_(fn));
-      }
-    };
-
-    var suiteFactory = function(description) {
-      var suite = new j$.Suite({
-        env: self,
-        id: getNextSuiteId(),
-        description: description,
-        parentSuite: currentDeclarationSuite,
-        expectationFactory: expectationFactory,
-        expectationResultFactory: expectationResultFactory,
-        throwOnExpectationFailure: throwOnExpectationFailure
-      });
-
-      return suite;
-    };
-
-    this.describe = function(description, specDefinitions) {
-      ensureIsFunction(specDefinitions, 'describe');
-      var suite = suiteFactory(description);
-      if (specDefinitions.length > 0) {
-        throw new Error('describe does not expect any arguments');
-      }
-      if (currentDeclarationSuite.markedPending) {
-        suite.pend();
-      }
-      addSpecsToSuite(suite, specDefinitions);
-      return suite;
-    };
-
-    this.xdescribe = function(description, specDefinitions) {
-      ensureIsFunction(specDefinitions, 'xdescribe');
-      var suite = suiteFactory(description);
-      suite.pend();
-      addSpecsToSuite(suite, specDefinitions);
-      return suite;
-    };
-
-    var focusedRunnables = [];
-
-    this.fdescribe = function(description, specDefinitions) {
-      ensureIsFunction(specDefinitions, 'fdescribe');
-      var suite = suiteFactory(description);
-      suite.isFocused = true;
-
-      focusedRunnables.push(suite.id);
-      unfocusAncestor();
-      addSpecsToSuite(suite, specDefinitions);
-
-      return suite;
-    };
-
-    function addSpecsToSuite(suite, specDefinitions) {
-      var parentSuite = currentDeclarationSuite;
-      parentSuite.addChild(suite);
-      currentDeclarationSuite = suite;
-
-      var declarationError = null;
-      try {
-        specDefinitions.call(suite);
-      } catch (e) {
-        declarationError = e;
-      }
-
-      if (declarationError) {
-        self.it('encountered a declaration exception', function() {
-          throw declarationError;
-        });
-      }
-
-      currentDeclarationSuite = parentSuite;
-    }
-
-    function findFocusedAncestor(suite) {
-      while (suite) {
-        if (suite.isFocused) {
-          return suite.id;
-        }
-        suite = suite.parentSuite;
-      }
-
-      return null;
-    }
-
-    function unfocusAncestor() {
-      var focusedAncestor = findFocusedAncestor(currentDeclarationSuite);
-      if (focusedAncestor) {
-        for (var i = 0; i < focusedRunnables.length; i++) {
-          if (focusedRunnables[i] === focusedAncestor) {
-            focusedRunnables.splice(i, 1);
-            break;
-          }
-        }
-      }
-    }
-
-    var specFactory = function(description, fn, suite, timeout) {
-      totalSpecsDefined++;
-      var spec = new j$.Spec({
-        id: getNextSpecId(),
-        beforeAndAfterFns: beforeAndAfterFns(suite),
-        expectationFactory: expectationFactory,
-        resultCallback: specResultCallback,
-        getSpecName: function(spec) {
-          return getSpecName(spec, suite);
-        },
-        onStart: specStarted,
-        description: description,
-        expectationResultFactory: expectationResultFactory,
-        queueRunnerFactory: queueRunnerFactory,
-        userContext: function() { return suite.clonedSharedUserContext(); },
-        queueableFn: {
-          fn: fn,
-          timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
-        },
-        throwOnExpectationFailure: throwOnExpectationFailure
-      });
-
-      if (!self.specFilter(spec)) {
-        spec.disable();
-      }
-
-      return spec;
-
-      function specResultCallback(result) {
-        clearResourcesForRunnable(spec.id);
-        currentSpec = null;
-        reporter.specDone(result);
-      }
-
-      function specStarted(spec) {
-        currentSpec = spec;
-        defaultResourcesForRunnable(spec.id, suite.id);
-        reporter.specStarted(spec.result);
-      }
-    };
-
-    this.it = function(description, fn, timeout) {
-      // it() sometimes doesn't have a fn argument, so only check the type if
-      // it's given.
-      if (arguments.length > 1 && typeof fn !== 'undefined') {
-        ensureIsFunction(fn, 'it');
-      }
-      var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
-      if (currentDeclarationSuite.markedPending) {
-        spec.pend();
-      }
-      currentDeclarationSuite.addChild(spec);
-      return spec;
-    };
-
-    this.xit = function(description, fn, timeout) {
-      // xit(), like it(), doesn't always have a fn argument, so only check the
-      // type when needed.
-      if (arguments.length > 1 && typeof fn !== 'undefined') {
-        ensureIsFunction(fn, 'xit');
-      }
-      var spec = this.it.apply(this, arguments);
-      spec.pend('Temporarily disabled with xit');
-      return spec;
-    };
-
-    this.fit = function(description, fn, timeout){
-      ensureIsFunction(fn, 'fit');
-      var spec = specFactory(description, fn, currentDeclarationSuite, timeout);
-      currentDeclarationSuite.addChild(spec);
-      focusedRunnables.push(spec.id);
-      unfocusAncestor();
-      return spec;
-    };
-
-    this.expect = function(actual) {
-      if (!currentRunnable()) {
-        throw new Error('\'expect\' was used when there was no current spec, this could be because an asynchronous test timed out');
-      }
-
-      return currentRunnable().expect(actual);
-    };
-
-    this.beforeEach = function(beforeEachFunction, timeout) {
-      ensureIsFunction(beforeEachFunction, 'beforeEach');
-      currentDeclarationSuite.beforeEach({
-        fn: beforeEachFunction,
-        timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
-      });
-    };
-
-    this.beforeAll = function(beforeAllFunction, timeout) {
-      ensureIsFunction(beforeAllFunction, 'beforeAll');
-      currentDeclarationSuite.beforeAll({
-        fn: beforeAllFunction,
-        timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
-      });
-    };
-
-    this.afterEach = function(afterEachFunction, timeout) {
-      ensureIsFunction(afterEachFunction, 'afterEach');
-      currentDeclarationSuite.afterEach({
-        fn: afterEachFunction,
-        timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
-      });
-    };
-
-    this.afterAll = function(afterAllFunction, timeout) {
-      ensureIsFunction(afterAllFunction, 'afterAll');
-      currentDeclarationSuite.afterAll({
-        fn: afterAllFunction,
-        timeout: function() { return timeout || j$.DEFAULT_TIMEOUT_INTERVAL; }
-      });
-    };
-
-    this.pending = function(message) {
-      var fullMessage = j$.Spec.pendingSpecExceptionMessage;
-      if(message) {
-        fullMessage += message;
-      }
-      throw fullMessage;
-    };
-
-    this.fail = function(error) {
-      if (!currentRunnable()) {
-        throw new Error('\'fail\' was used when there was no current spec, this could be because an asynchronous test timed out');
-      }
-
-      var message = 'Failed';
-      if (error) {
-        message += ': ';
-        if (error.message) {
-          message += error.message;
-        } else if (jasmine.isString_(error)) {
-          message += error;
-        } else {
-          // pretty print all kind of objects. This includes arrays.
-          message += jasmine.pp(error);
-        }
-      }
-
-      currentRunnable().addExpectationResult(false, {
-        matcherName: '',
-        passed: false,
-        expected: '',
-        actual: '',
-        message: message,
-        error: error && error.message ? error : null
-      });
-    };
-  }
-
-  return Env;
-};
-
-getJasmineRequireObj().JsApiReporter = function() {
-
-  var noopTimer = {
-    start: function(){},
-    elapsed: function(){ return 0; }
-  };
-
-  /**
-   * _Note:_ Do not construct this directly, use the global `jsApiReporter` to retrieve the instantiated object.
-   *
-   * @name jsApiReporter
-   * @classdesc Reporter added by default in `boot.js` to record results for retrieval in javascript code.
-   * @class
-   */
-  function JsApiReporter(options) {
-    var timer = options.timer || noopTimer,
-        status = 'loaded';
-
-    this.started = false;
-    this.finished = false;
-    this.runDetails = {};
-
-    this.jasmineStarted = function() {
-      this.started = true;
-      status = 'started';
-      timer.start();
-    };
-
-    var executionTime;
-
-    this.jasmineDone = function(runDetails) {
-      this.finished = true;
-      this.runDetails = runDetails;
-      executionTime = timer.elapsed();
-      status = 'done';
-    };
-
-    /**
-     * Get the current status for the Jasmine environment.
-     * @name jsApiReporter#status
-     * @function
-     * @return {String} - One of `loaded`, `started`, or `done`
-     */
-    this.status = function() {
-      return status;
-    };
-
-    var suites = [],
-        suites_hash = {};
-
-    this.suiteStarted = function(result) {
-      suites_hash[result.id] = result;
-    };
-
-    this.suiteDone = function(result) {
-      storeSuite(result);
-    };
-
-    /**
-     * Get the results for a set of suites.
-     *
-     * Retrievable in slices for easier serialization.
-     * @name jsApiReporter#suiteResults
-     * @function
-     * @param {Number} index - The position in the suites list to start from.
-     * @param {Number} length - Maximum number of suite results to return.
-     * @return {Object[]}
-     */
-    this.suiteResults = function(index, length) {
-      return suites.slice(index, index + length);
-    };
-
-    function storeSuite(result) {
-      suites.push(result);
-      suites_hash[result.id] = result;
-    }
-
-    /**
-     * Get all of the suites in a single object, with their `id` as the key.
-     * @name jsApiReporter#suites
-     * @function
-     * @return {Object}
-     */
-    this.suites = function() {
-      return suites_hash;
-    };
-
-    var specs = [];
-
-    this.specDone = function(result) {
-      specs.push(result);
-    };
-
-    /**
-     * Get the results for a set of specs.
-     *
-     * Retrievable in slices for easier serialization.
-     * @name jsApiReporter#specResults
-     * @function
-     * @param {Number} index - The position in the specs list to start from.
-     * @param {Number} length - Maximum number of specs results to return.
-     * @return {Object[]}
-     */
-    this.specResults = function(index, length) {
-      return specs.slice(index, index + length);
-    };
-
-    /**
-     * Get all spec results.
-     * @name jsApiReporter#specs
-     * @function
-     * @return {Object[]}
-     */
-    this.specs = function() {
-      return specs;
-    };
-
-    /**
-     * Get the number of milliseconds it took for the full Jasmine suite to run.
-     * @name jsApiReporter#executionTime
-     * @function
-     * @return {Number}
-     */
-    this.executionTime = function() {
-      return executionTime;
-    };
-
-  }
-
-  return JsApiReporter;
-};
-
-getJasmineRequireObj().Any = function(j$) {
-
-  function Any(expectedObject) {
-    if (typeof expectedObject === 'undefined') {
-      throw new TypeError(
-          'jasmine.any() expects to be passed a constructor function. ' +
-          'Please pass one or use jasmine.anything() to match any object.'
-      );
-    }
-    this.expectedObject = expectedObject;
-  }
-
-  Any.prototype.asymmetricMatch = function(other) {
-    if (this.expectedObject == String) {
-      return typeof other == 'string' || other instanceof String;
-    }
-
-    if (this.expectedObject == Number) {
-      return typeof other == 'number' || other instanceof Number;
-    }
-
-    if (this.expectedObject == Function) {
-      return typeof other == 'function' || other instanceof Function;
-    }
-
-    if (this.expectedObject == Object) {
-      return typeof other == 'object';
-    }
-
-    if (this.expectedObject == Boolean) {
-      return typeof other == 'boolean';
-    }
-
-    return other instanceof this.expectedObject;
-  };
-
-  Any.prototype.jasmineToString = function() {
-    return '<jasmine.any(' + j$.fnNameFor(this.expectedObject) + ')>';
-  };
-
-  return Any;
-};
-
-getJasmineRequireObj().Anything = function(j$) {
-
-  function Anything() {}
-
-  Anything.prototype.asymmetricMatch = function(other) {
-    return !j$.util.isUndefined(other) && other !== null;
-  };
-
-  Anything.prototype.jasmineToString = function() {
-    return '<jasmine.anything>';
-  };
-
-  return Anything;
-};
-
-getJasmineRequireObj().ArrayContaining = function(j$) {
-  function ArrayContaining(sample) {
-    this.sample = sample;
-  }
-
-  ArrayContaining.prototype.asymmetricMatch = function(other, customTesters) {
-    var className = Object.prototype.toString.call(this.sample);
-    if (className !== '[object Array]') { throw new Error('You must provide an array to arrayContaining, not \'' + this.sample + '\'.'); }
-
-    for (var i = 0; i < this.sample.length; i++) {
-      var item = this.sample[i];
-      if (!j$.matchersUtil.contains(other, item, customTesters)) {
-        return false;
-      }
-    }
-
-    return true;
-  };
-
-  ArrayContaining.prototype.jasmineToString = function () {
-    return '<jasmine.arrayContaining(' + jasmine.pp(this.sample) +')>';
-  };
-
-  return ArrayContaining;
-};
-
-getJasmineRequireObj().ObjectContaining = function(j$) {
-
-  function ObjectContaining(sample) {
-    this.sample = sample;
-  }
-
-  function getPrototype(obj) {
-    if (Object.getPrototypeOf) {
-      return Object.getPrototypeOf(obj);
-    }
-
-    if (obj.constructor.prototype == obj) {
-      return null;
-    }
-
-    return obj.constructor.prototype;
-  }
-
-  function hasProperty(obj, property) {
-    if (!obj) {
-      return false;
-    }
-
-    if (Object.prototype.hasOwnProperty.call(obj, property)) {
-      return true;
-    }
-
-    return hasProperty(getPrototype(obj), property);
-  }
-
-  ObjectContaining.prototype.asymmetricMatch = function(other, customTesters) {
-    if (typeof(this.sample) !== 'object') { throw new Error('You must provide an object to objectContaining, not \''+this.sample+'\'.'); }
-
-    for (var property in this.sample) {
-      if (!hasProperty(other, property) ||
-          !j$.matchersUtil.equals(this.sample[property], other[property], customTesters)) {
-        return false;
-      }
-    }
-
-    return true;
-  };
-
-  ObjectContaining.prototype.jasmineToString = function() {
-    return '<jasmine.objectContaining(' + j$.pp(this.sample) + ')>';
-  };
-
-  return ObjectContaining;
-};
-
-getJasmineRequireObj().StringMatching = function(j$) {
-
-  function StringMatching(expected) {
-    if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) {
-      throw new Error('Expected is not a String or a RegExp');
-    }
-
-    this.regexp = new RegExp(expected);
-  }
-
-  StringMatching.prototype.asymmetricMatch = function(other) {
-    return this.regexp.test(other);
-  };
-
-  StringMatching.prototype.jasmineToString = function() {
-    return '<jasmine.stringMatching(' + this.regexp + ')>';
-  };
-
-  return StringMatching;
-};
-
-getJasmineRequireObj().CallTracker = function(j$) {
-
-  /**
-   * @namespace Spy#calls
-   */
-  function CallTracker() {
-    var calls = [];
-    var opts = {};
-
-    function argCloner(context) {
-      var clonedArgs = [];
-      var argsAsArray = j$.util.argsToArray(context.args);
-      for(var i = 0; i < argsAsArray.length; i++) {
-        if(Object.prototype.toString.apply(argsAsArray[i]).match(/^\[object/)) {
-          clonedArgs.push(j$.util.clone(argsAsArray[i]));
-        } else {
-          clonedArgs.push(argsAsArray[i]);
-        }
-      }
-      context.args = clonedArgs;
-    }
-
-    this.track = function(context) {
-      if(opts.cloneArgs) {
-        argCloner(context);
-      }
-      calls.push(context);
-    };
-
-    /**
-     * Check whether this spy has been invoked.
-     * @name Spy#calls#any
-     * @function
-     * @return {Boolean}
-     */
-    this.any = function() {
-      return !!calls.length;
-    };
-
-    /**
-     * Get the number of invocations of this spy.
-     * @name Spy#calls#count
-     * @function
-     * @return {Integer}
-     */
-    this.count = function() {
-      return calls.length;
-    };
-
-    /**
-     * Get the arguments that were passed to a specific invocation of this spy.
-     * @name Spy#calls#argsFor
-     * @function
-     * @param {Integer} index The 0-based invocation index.
-     * @return {Array}
-     */
-    this.argsFor = function(index) {
-      var call = calls[index];
-      return call ? call.args : [];
-    };
-
-    /**
-     * Get the raw calls array for this spy.
-     * @name Spy#calls#all
-     * @function
-     * @return {Spy.callData[]}
-     */
-    this.all = function() {
-      return calls;
-    };
-
-    /**
-     * Get all of the arguments for each invocation of this spy in the order they were received.
-     * @name Spy#calls#allArgs
-     * @function
-     * @return {Array}
-     */
-    this.allArgs = function() {
-      var callArgs = [];
-      for(var i = 0; i < calls.length; i++){
-        callArgs.push(calls[i].args);
-      }
-
-      return callArgs;
-    };
-
-    /**
-     * Get the first invocation of this spy.
-     * @name Spy#calls#first
-     * @function
-     * @return {ObjecSpy.callData}
-     */
-    this.first = function() {
-      return calls[0];
-    };
-
-    /**
-     * Get the most recent invocation of this spy.
-     * @name Spy#calls#mostRecent
-     * @function
-     * @return {ObjecSpy.callData}
-     */
-    this.mostRecent = function() {
-      return calls[calls.length - 1];
-    };
-
-    /**
-     * Reset this spy as if it has never been called.
-     * @name Spy#calls#reset
-     * @function
-     */
-    this.reset = function() {
-      calls = [];
-    };
-
-    /**
-     * Set this spy to do a shallow clone of arguments passed to each invocation.
-     * @name Spy#calls#saveArgumentsByValue
-     * @function
-     */
-    this.saveArgumentsByValue = function() {
-      opts.cloneArgs = true;
-    };
-
-  }
-
-  return CallTracker;
-};
-
-getJasmineRequireObj().clearStack = function(j$) {
-  function messageChannelImpl(global) {
-    var channel = new global.MessageChannel(),
-        head = {},
-        tail = head;
-
-    channel.port1.onmessage = function() {
-      head = head.next;
-      var task = head.task;
-      delete head.task;
-      task();
-    };
-
-    return function clearStack(fn) {
-      tail = tail.next = { task: fn };
-      channel.port2.postMessage(0);
-    };
-  }
-
-  function getClearStack(global) {
-    if (j$.isFunction_(global.setImmediate)) {
-      var realSetImmediate = global.setImmediate;
-      return function(fn) {
-        realSetImmediate(fn);
-      };
-    } else if (!j$.util.isUndefined(global.MessageChannel)) {
-      return messageChannelImpl(global);
-    } else {
-      var realSetTimeout = global.setTimeout;
-      return function clearStack(fn) {
-        Function.prototype.apply.apply(realSetTimeout, [global, [fn, 0]]);
-      };
-    }
-  }
-
-  return getClearStack;
-};
-
-getJasmineRequireObj().Clock = function() {
-  /**
-   * _Note:_ Do not construct this directly, Jasmine will make one during booting. You can get the current clock with {@link jasmine.clock}.
-   * @class Clock
-   * @classdesc Jasmine's mock clock is used when testing time dependent code.
-   */
-  function Clock(global, delayedFunctionSchedulerFactory, mockDate) {
-    var self = this,
-        realTimingFunctions = {
-          setTimeout: global.setTimeout,
-          clearTimeout: global.clearTimeout,
-          setInterval: global.setInterval,
-          clearInterval: global.clearInterval
-        },
-        fakeTimingFunctions = {
-          setTimeout: setTimeout,
-          clearTimeout: clearTimeout,
-          setInterval: setInterval,
-          clearInterval: clearInterval
-        },
-        installed = false,
-        delayedFunctionScheduler,
-        timer;
-
-
-    /**
-     * Install the mock clock over the built-in methods.
-     * @name Clock#install
-     * @function
-     * @return {Clock}
-     */
-    self.install = function() {
-      if(!originalTimingFunctionsIntact()) {
-        throw new Error('Jasmine Clock was unable to install over custom global timer functions. Is the clock already installed?');
-      }
-      replace(global, fakeTimingFunctions);
-      timer = fakeTimingFunctions;
-      delayedFunctionScheduler = delayedFunctionSchedulerFactory();
-      installed = true;
-
-      return self;
-    };
-
-    /**
-     * Uninstall the mock clock, returning the built-in methods to their places.
-     * @name Clock#uninstall
-     * @function
-     */
-    self.uninstall = function() {
-      delayedFunctionScheduler = null;
-      mockDate.uninstall();
-      replace(global, realTimingFunctions);
-
-      timer = realTimingFunctions;
-      installed = false;
-    };
-
-    /**
-     * Execute a function with a mocked Clock
-     *
-     * The clock will be {@link Clock#install|install}ed before the function is called and {@link Clock#uninstall|uninstall}ed in a `finally` after the function completes.
-     * @name Clock#withMock
-     * @function
-     * @param {closure} Function The function to be called.
-     */
-    self.withMock = function(closure) {
-      this.install();
-      try {
-        closure();
-      } finally {
-        this.uninstall();
-      }
-    };
-
-    /**
-     * Instruct the installed Clock to also mock the date returned by `new Date()`
-     * @name Clock#mockDate
-     * @function
-     * @param {Date} [initialDate=now] The `Date` to provide.
-     */
-    self.mockDate = function(initialDate) {
-      mockDate.install(initialDate);
-    };
-
-    self.setTimeout = function(fn, delay, params) {
-      if (legacyIE()) {
-        if (arguments.length > 2) {
-          throw new Error('IE < 9 cannot support extra params to setTimeout without a polyfill');
-        }
-        return timer.setTimeout(fn, delay);
-      }
-      return Function.prototype.apply.apply(timer.setTimeout, [global, arguments]);
-    };
-
-    self.setInterval = function(fn, delay, params) {
-      if (legacyIE()) {
-        if (arguments.length > 2) {
-          throw new Error('IE < 9 cannot support extra params to setInterval without a polyfill');
-        }
-        return timer.setInterval(fn, delay);
-      }
-      return Function.prototype.apply.apply(timer.setInterval, [global, arguments]);
-    };
-
-    self.clearTimeout = function(id) {
-      return Function.prototype.call.apply(timer.clearTimeout, [global, id]);
-    };
-
-    self.clearInterval = function(id) {
-      return Function.prototype.call.apply(timer.clearInterval, [global, id]);
-    };
-
-    /**
-     * Tick the Clock forward, running any enqueued timeouts along the way
-     * @name Clock#tick
-     * @function
-     * @param {int} millis The number of milliseconds to tick.
-     */
-    self.tick = function(millis) {
-      if (installed) {
-        delayedFunctionScheduler.tick(millis, function(millis) { mockDate.tick(millis); });
-      } else {
-        throw new Error('Mock clock is not installed, use jasmine.clock().install()');
-      }
-    };
-
-    return self;
-
-    function originalTimingFunctionsIntact() {
-      return global.setTimeout === realTimingFunctions.setTimeout &&
-          global.clearTimeout === realTimingFunctions.clearTimeout &&
-          global.setInterval === realTimingFunctions.setInterval &&
-          global.clearInterval === realTimingFunctions.clearInterval;
-    }
-
-    function legacyIE() {
-      //if these methods are polyfilled, apply will be present
-      return !(realTimingFunctions.setTimeout || realTimingFunctions.setInterval).apply;
-    }
-
-    function replace(dest, source) {
-      for (var prop in source) {
-        dest[prop] = source[prop];
-      }
-    }
-
-    function setTimeout(fn, delay) {
-      return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
-    }
-
-    function clearTimeout(id) {
-      return delayedFunctionScheduler.removeFunctionWithId(id);
-    }
-
-    function setInterval(fn, interval) {
-      return delayedFunctionScheduler.scheduleFunction(fn, interval, argSlice(arguments, 2), true);
-    }
-
-    function clearInterval(id) {
-      return delayedFunctionScheduler.removeFunctionWithId(id);
-    }
-
-    function argSlice(argsObj, n) {
-      return Array.prototype.slice.call(argsObj, n);
-    }
-  }
-
-  return Clock;
-};
-
-getJasmineRequireObj().DelayedFunctionScheduler = function() {
-  function DelayedFunctionScheduler() {
-    var self = this;
-    var scheduledLookup = [];
-    var scheduledFunctions = {};
-    var currentTime = 0;
-    var delayedFnCount = 0;
-
-    self.tick = function(millis, tickDate) {
-      millis = millis || 0;
-      var endTime = currentTime + millis;
-
-      runScheduledFunctions(endTime, tickDate);
-      currentTime = endTime;
-    };
-
-    self.scheduleFunction = function(funcToCall, millis, params, recurring, timeoutKey, runAtMillis) {
-      var f;
-      if (typeof(funcToCall) === 'string') {
-        /* jshint evil: true */
-        f = function() { return eval(funcToCall); };
-        /* jshint evil: false */
-      } else {
-        f = funcToCall;
-      }
-
-      millis = millis || 0;
-      timeoutKey = timeoutKey || ++delayedFnCount;
-      runAtMillis = runAtMillis || (currentTime + millis);
-
-      var funcToSchedule = {
-        runAtMillis: runAtMillis,
-        funcToCall: f,
-        recurring: recurring,
-        params: params,
-        timeoutKey: timeoutKey,
-        millis: millis
-      };
-
-      if (runAtMillis in scheduledFunctions) {
-        scheduledFunctions[runAtMillis].push(funcToSchedule);
-      } else {
-        scheduledFunctions[runAtMillis] = [funcToSchedule];
-        scheduledLookup.push(runAtMillis);
-        scheduledLookup.sort(function (a, b) {
-          return a - b;
-        });
-      }
-
-      return timeoutKey;
-    };
-
-    self.removeFunctionWithId = function(timeoutKey) {
-      for (var runAtMillis in scheduledFunctions) {
-        var funcs = scheduledFunctions[runAtMillis];
-        var i = indexOfFirstToPass(funcs, function (func) {
-          return func.timeoutKey === timeoutKey;
-        });
-
-        if (i > -1) {
-          if (funcs.length === 1) {
-            delete scheduledFunctions[runAtMillis];
-            deleteFromLookup(runAtMillis);
-          } else {
-            funcs.splice(i, 1);
-          }
-
-          // intervals get rescheduled when executed, so there's never more
-          // than a single scheduled function with a given timeoutKey
-          break;
-        }
-      }
-    };
-
-    return self;
-
-    function indexOfFirstToPass(array, testFn) {
-      var index = -1;
-
-      for (var i = 0; i < array.length; ++i) {
-        if (testFn(array[i])) {
-          index = i;
-          break;
-        }
-      }
-
-      return index;
-    }
-
-    function deleteFromLookup(key) {
-      var value = Number(key);
-      var i = indexOfFirstToPass(scheduledLookup, function (millis) {
-        return millis === value;
-      });
-
-      if (i > -1) {
-        scheduledLookup.splice(i, 1);
-      }
-    }
-
-    function reschedule(scheduledFn) {
-      self.scheduleFunction(scheduledFn.funcToCall,
-          scheduledFn.millis,
-          scheduledFn.params,
-          true,
-          scheduledFn.timeoutKey,
-          scheduledFn.runAtMillis + scheduledFn.millis);
-    }
-
-    function forEachFunction(funcsToRun, callback) {
-      for (var i = 0; i < funcsToRun.length; ++i) {
-        callback(funcsToRun[i]);
-      }
-    }
-
-    function runScheduledFunctions(endTime, tickDate) {
-      tickDate = tickDate || function() {};
-      if (scheduledLookup.length === 0 || scheduledLookup[0] > endTime) {
-        tickDate(endTime - currentTime);
-        return;
-      }
-
-      do {
-        var newCurrentTime = scheduledLookup.shift();
-        tickDate(newCurrentTime - currentTime);
-
-        currentTime = newCurrentTime;
-
-        var funcsToRun = scheduledFunctions[currentTime];
-        delete scheduledFunctions[currentTime];
-
-        forEachFunction(funcsToRun, function(funcToRun) {
-          if (funcToRun.recurring) {
-            reschedule(funcToRun);
-          }
-        });
-
-        forEachFunction(funcsToRun, function(funcToRun) {
-          funcToRun.funcToCall.apply(null, funcToRun.params || []);
-        });
-      } while (scheduledLookup.length > 0 &&
-      // checking first if we're out of time prevents setTimeout(0)
-      // scheduled in a funcToRun from forcing an extra iteration
-      currentTime !== endTime  &&
-      scheduledLookup[0] <= endTime);
-
-      // ran out of functions to call, but still time left on the clock
-      if (currentTime !== endTime) {
-        tickDate(endTime - currentTime);
-      }
-    }
-  }
-
-  return DelayedFunctionScheduler;
-};
-
-getJasmineRequireObj().errors = function() {
-  function ExpectationFailed() {}
-
-  ExpectationFailed.prototype = new Error();
-  ExpectationFailed.prototype.constructor = ExpectationFailed;
-
-  return {
-    ExpectationFailed: ExpectationFailed
-  };
-};
-getJasmineRequireObj().ExceptionFormatter = function() {
-  function ExceptionFormatter() {
-    this.message = function(error) {
-      var message = '';
-
-      if (error.name && error.message) {
-        message += error.name + ': ' + error.message;
-      } else {
-        message += error.toString() + ' thrown';
-      }
-
-      if (error.fileName || error.sourceURL) {
-        message += ' in ' + (error.fileName || error.sourceURL);
-      }
-
-      if (error.line || error.lineNumber) {
-        message += ' (line ' + (error.line || error.lineNumber) + ')';
-      }
-
-      return message;
-    };
-
-    this.stack = function(error) {
-      return error ? error.stack : null;
-    };
-  }
-
-  return ExceptionFormatter;
-};
-
-getJasmineRequireObj().Expectation = function() {
-
-  /**
-   * Matchers that come with Jasmine out of the box.
-   * @namespace matchers
-   */
-  function Expectation(options) {
-    this.util = options.util || { buildFailureMessage: function() {} };
-    this.customEqualityTesters = options.customEqualityTesters || [];
-    this.actual = options.actual;
-    this.addExpectationResult = options.addExpectationResult || function(){};
-    this.isNot = options.isNot;
-
-    var customMatchers = options.customMatchers || {};
-    for (var matcherName in customMatchers) {
-      this[matcherName] = Expectation.prototype.wrapCompare(matcherName, customMatchers[matcherName]);
-    }
-  }
-
-  Expectation.prototype.wrapCompare = function(name, matcherFactory) {
-    return function() {
-      var args = Array.prototype.slice.call(arguments, 0),
-          expected = args.slice(0),
-          message = '';
-
-      args.unshift(this.actual);
-
-      var matcher = matcherFactory(this.util, this.customEqualityTesters),
-          matcherCompare = matcher.compare;
-
-      function defaultNegativeCompare() {
-        var result = matcher.compare.apply(null, args);
-        result.pass = !result.pass;
-        return result;
-      }
-
-      if (this.isNot) {
-        matcherCompare = matcher.negativeCompare || defaultNegativeCompare;
-      }
-
-      var result = matcherCompare.apply(null, args);
-
-      if (!result.pass) {
-        if (!result.message) {
-          args.unshift(this.isNot);
-          args.unshift(name);
-          message = this.util.buildFailureMessage.apply(null, args);
-        } else {
-          if (Object.prototype.toString.apply(result.message) === '[object Function]') {
-            message = result.message();
-          } else {
-            message = result.message;
-          }
-        }
-      }
-
-      if (expected.length == 1) {
-        expected = expected[0];
-      }
-
-      // TODO: how many of these params are needed?
-      this.addExpectationResult(
-          result.pass,
-          {
-            matcherName: name,
-            passed: result.pass,
-            message: message,
-            error: result.error,
-            actual: this.actual,
-            expected: expected // TODO: this may need to be arrayified/sliced
-          }
-      );
-    };
-  };
-
-  Expectation.addCoreMatchers = function(matchers) {
-    var prototype = Expectation.prototype;
-    for (var matcherName in matchers) {
-      var matcher = matchers[matcherName];
-      prototype[matcherName] = prototype.wrapCompare(matcherName, matcher);
-    }
-  };
-
-  Expectation.Factory = function(options) {
-    options = options || {};
-
-    var expect = new Expectation(options);
-
-    // TODO: this would be nice as its own Object - NegativeExpectation
-    // TODO: copy instead of mutate options
-    options.isNot = true;
-    expect.not = new Expectation(options);
-
-    return expect;
-  };
-
-  return Expectation;
-};
-
-//TODO: expectation result may make more sense as a presentation of an expectation.
-getJasmineRequireObj().buildExpectationResult = function() {
-  function buildExpectationResult(options) {
-    var messageFormatter = options.messageFormatter || function() {},
-        stackFormatter = options.stackFormatter || function() {};
-
-    var result = {
-      matcherName: options.matcherName,
-      message: message(),
-      stack: stack(),
-      passed: options.passed
-    };
-
-    if(!result.passed) {
-      result.expected = options.expected;
-      result.actual = options.actual;
-    }
-
-    return result;
-
-    function message() {
-      if (options.passed) {
-        return 'Passed.';
-      } else if (options.message) {
-        return options.message;
-      } else if (options.error) {
-        return messageFormatter(options.error);
-      }
-      return '';
-    }
-
-    function stack() {
-      if (options.passed) {
-        return '';
-      }
-
-      var error = options.error;
-      if (!error) {
-        try {
-          throw new Error(message());
-        } catch (e) {
-          error = e;
-        }
-      }
-      return stackFormatter(error);
-    }
-  }
-
-  return buildExpectationResult;
-};
-
-getJasmineRequireObj().formatErrorMsg = function() {
-  function generateErrorMsg(domain, usage) {
-    var usageDefinition = usage ? '\nUsage: ' + usage : '';
-
-    return function errorMsg(msg) {
-      return domain + ' : ' + msg + usageDefinition;
-    };
-  }
-
-  return generateErrorMsg;
-};
-
-getJasmineRequireObj().GlobalErrors = function(j$) {
-  function GlobalErrors(global) {
-    var handlers = [];
-    global = global || j$.getGlobal();
-
-    var onerror = function onerror() {
-      var handler = handlers[handlers.length - 1];
-      handler.apply(null, Array.prototype.slice.call(arguments, 0));
-    };
-
-    this.uninstall = function noop() {};
-
-    this.install = function install() {
-      if (global.process && global.process.listeners && j$.isFunction_(global.process.on)) {
-        var originalHandlers = global.process.listeners('uncaughtException');
-        global.process.removeAllListeners('uncaughtException');
-        global.process.on('uncaughtException', onerror);
-
-        this.uninstall = function uninstall() {
-          global.process.removeListener('uncaughtException', onerror);
-          for (var i = 0; i < originalHandlers.length; i++) {
-            global.process.on('uncaughtException', originalHandlers[i]);
-          }
-        };
-      } else {
-        var originalHandler = global.onerror;
-        global.onerror = onerror;
-
-        this.uninstall = function uninstall() {
-          global.onerror = originalHandler;
-        };
-      }
-    };
-
-    this.pushListener = function pushListener(listener) {
-      handlers.push(listener);
-    };
-
-    this.popListener = function popListener() {
-      handlers.pop();
-    };
-  }
-
-  return GlobalErrors;
-};
-
-getJasmineRequireObj().DiffBuilder = function(j$) {
-  return function DiffBuilder() {
-    var path = new j$.ObjectPath(),
-        mismatches = [];
-
-    return {
-      record: function (actual, expected, formatter) {
-        formatter = formatter || defaultFormatter;
-        mismatches.push(formatter(actual, expected, path));
-      },
-
-      getMessage: function () {
-        return mismatches.join('\n');
-      },
-
-      withPath: function (pathComponent, block) {
-        var oldPath = path;
-        path = path.add(pathComponent);
-        block();
-        path = oldPath;
-      }
-    };
-
-    function defaultFormatter (actual, expected, path) {
-      return 'Expected ' +
-          path + (path.depth() ? ' = ' : '') +
-          j$.pp(actual) +
-          ' to equal ' +
-          j$.pp(expected) +
-          '.';
-    }
-  };
-};
-
-getJasmineRequireObj().matchersUtil = function(j$) {
-  // TODO: what to do about jasmine.pp not being inject? move to JSON.stringify? gut PrettyPrinter?
-
-  return {
-    equals: equals,
-
-    contains: function(haystack, needle, customTesters) {
-      customTesters = customTesters || [];
-
-      if ((Object.prototype.toString.apply(haystack) === '[object Set]')) {
-        return haystack.has(needle);
-      }
-
-      if ((Object.prototype.toString.apply(haystack) === '[object Array]') ||
-          (!!haystack && !haystack.indexOf))
-      {
-        for (var i = 0; i < haystack.length; i++) {
-          if (equals(haystack[i], needle, customTesters)) {
-            return true;
-          }
-        }
-        return false;
-      }
-
-      return !!haystack && haystack.indexOf(needle) >= 0;
-    },
-
-    buildFailureMessage: function() {
-      var args = Array.prototype.slice.call(arguments, 0),
-          matcherName = args[0],
-          isNot = args[1],
-          actual = args[2],
-          expected = args.slice(3),
-          englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });
-
-      var message = 'Expected ' +
-          j$.pp(actual) +
-          (isNot ? ' not ' : ' ') +
-          englishyPredicate;
-
-      if (expected.length > 0) {
-        for (var i = 0; i < expected.length; i++) {
-          if (i > 0) {
-            message += ',';
-          }
-          message += ' ' + j$.pp(expected[i]);
-        }
-      }
-
-      return message + '.';
-    }
-  };
-
-  function isAsymmetric(obj) {
-    return obj && j$.isA_('Function', obj.asymmetricMatch);
-  }
-
-  function asymmetricMatch(a, b, customTesters, diffBuilder) {
-    var asymmetricA = isAsymmetric(a),
-        asymmetricB = isAsymmetric(b),
-        result;
-
-    if (asymmetricA && asymmetricB) {
-      return undefined;
-    }
-
-    if (asymmetricA) {
-      result = a.asymmetricMatch(b, customTesters);
-      diffBuilder.record(a, b);
-      return result;
-    }
-
-    if (asymmetricB) {
-      result = b.asymmetricMatch(a, customTesters);
-      diffBuilder.record(a, b);
-      return result;
-    }
-  }
-
-  function equals(a, b, customTesters, diffBuilder) {
-    customTesters = customTesters || [];
-    diffBuilder = diffBuilder || j$.NullDiffBuilder();
-
-    return eq(a, b, [], [], customTesters, diffBuilder);
-  }
-
-  // Equality function lovingly adapted from isEqual in
-  //   [Underscore](http://underscorejs.org)
-  function eq(a, b, aStack, bStack, customTesters, diffBuilder) {
-    var result = true, i;
-
-    var asymmetricResult = asymmetricMatch(a, b, customTesters, diffBuilder);
-    if (!j$.util.isUndefined(asymmetricResult)) {
-      return asymmetricResult;
-    }
-
-    for (i = 0; i < customTesters.length; i++) {
-      var customTesterResult = customTesters[i](a, b);
-      if (!j$.util.isUndefined(customTesterResult)) {
-        if (!customTesterResult) {
-          diffBuilder.record(a, b);
-        }
-        return customTesterResult;
-      }
-    }
-
-    if (a instanceof Error && b instanceof Error) {
-      result = a.message == b.message;
-      if (!result) {
-        diffBuilder.record(a, b);
-      }
-      return result;
-    }
-
-    // Identical objects are equal. `0 === -0`, but they aren't identical.
-    // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
-    if (a === b) {
-      result = a !== 0 || 1 / a == 1 / b;
-      if (!result) {
-        diffBuilder.record(a, b);
-      }
-      return result;
-    }
-    // A strict comparison is necessary because `null == undefined`.
-    if (a === null || b === null) {
-      result = a === b;
-      if (!result) {
-        diffBuilder.record(a, b);
-      }
-      return result;
-    }
-    var className = Object.prototype.toString.call(a);
-    if (className != Object.prototype.toString.call(b)) {
-      diffBuilder.record(a, b);
-      return false;
-    }
-    switch (className) {
-        // Strings, numbers, dates, and booleans are compared by value.
-      case '[object String]':
-        // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
-        // equivalent to `new String("5")`.
-        result = a == String(b);
-        if (!result) {
-          diffBuilder.record(a, b);
-        }
-        return result;
-      case '[object Number]':
-        // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
-        // other numeric values.
-        result = a != +a ? b != +b : (a === 0 ? 1 / a == 1 / b : a == +b);
-        if (!result) {
-          diffBuilder.record(a, b);
-        }
-        return result;
-      case '[object Date]':
-      case '[object Boolean]':
-        // Coerce dates and booleans to numeric primitive values. Dates are compared by their
-        // millisecond representations. Note that invalid dates with millisecond representations
-        // of `NaN` are not equivalent.
-        result = +a == +b;
-        if (!result) {
-          diffBuilder.record(a, b);
-        }
-        return result;
-        // RegExps are compared by their source patterns and flags.
-      case '[object RegExp]':
-        return a.source == b.source &&
-            a.global == b.global &&
-            a.multiline == b.multiline &&
-            a.ignoreCase == b.ignoreCase;
-    }
-    if (typeof a != 'object' || typeof b != 'object') {
-      diffBuilder.record(a, b);
-      return false;
-    }
-
-    var aIsDomNode = j$.isDomNode(a);
-    var bIsDomNode = j$.isDomNode(b);
-    if (aIsDomNode && bIsDomNode) {
-      // At first try to use DOM3 method isEqualNode
-      if (a.isEqualNode) {
-        result = a.isEqualNode(b);
-        if (!result) {
-          diffBuilder.record(a, b);
-        }
-        return result;
-      }
-      // IE8 doesn't support isEqualNode, try to use outerHTML && innerText
-      var aIsElement = a instanceof Element;
-      var bIsElement = b instanceof Element;
-      if (aIsElement && bIsElement) {
-        result = a.outerHTML == b.outerHTML;
-        if (!result) {
-          diffBuilder.record(a, b);
-        }
-        return result;
-      }
-      if (aIsElement || bIsElement) {
-        diffBuilder.record(a, b);
-        return false;
-      }
-      result = a.innerText == b.innerText && a.textContent == b.textContent;
-      if (!result) {
-        diffBuilder.record(a, b);
-      }
-      return result;
-    }
-    if (aIsDomNode || bIsDomNode) {
-      diffBuilder.record(a, b);
-      return false;
-    }
-
-    // Assume equality for cyclic structures. The algorithm for detecting cyclic
-    // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
-    var length = aStack.length;
-    while (length--) {
-      // Linear search. Performance is inversely proportional to the number of
-      // unique nested structures.
-      if (aStack[length] == a) { return bStack[length] == b; }
-    }
-    // Add the first object to the stack of traversed objects.
-    aStack.push(a);
-    bStack.push(b);
-    var size = 0;
-    // Recursively compare objects and arrays.
-    // Compare array lengths to determine if a deep comparison is necessary.
-    if (className == '[object Array]') {
-      size = a.length;
-      if (size !== b.length) {
-        diffBuilder.record(a, b);
-        return false;
-      }
-
-      for (i = 0; i < size; i++) {
-        diffBuilder.withPath(i, function() {
-          result = eq(a[i], b[i], aStack, bStack, customTesters, diffBuilder) && result;
-        });
-      }
-      if (!result) {
-        return false;
-      }
-    } else if (className == '[object Set]') {
-      if (a.size != b.size) {
-        diffBuilder.record(a, b);
-        return false;
-      }
-      var iterA = a.values(), iterB = b.values();
-      var valA, valB;
-      do {
-        valA = iterA.next();
-        valB = iterB.next();
-        if (!eq(valA.value, valB.value, aStack, bStack, customTesters, j$.NullDiffBuilder())) {
-          diffBuilder.record(a, b);
-          return false;
-        }
-      } while (!valA.done && !valB.done);
-    } else {
-
-      // Objects with different constructors are not equivalent, but `Object`s
-      // or `Array`s from different frames are.
-      var aCtor = a.constructor, bCtor = b.constructor;
-      if (aCtor !== bCtor &&
-          isFunction(aCtor) && isFunction(bCtor) &&
-          a instanceof aCtor && b instanceof bCtor &&
-          !(aCtor instanceof aCtor && bCtor instanceof bCtor)) {
-
-        diffBuilder.record(a, b, constructorsAreDifferentFormatter);
-        return false;
-      }
-    }
-
-    // Deep compare objects.
-    var aKeys = keys(a, className == '[object Array]'), key;
-    size = aKeys.length;
-
-    // Ensure that both objects contain the same number of properties before comparing deep equality.
-    if (keys(b, className == '[object Array]').length !== size) {
-      diffBuilder.record(a, b, objectKeysAreDifferentFormatter);
-      return false;
-    }
-
-    for (i = 0; i < size; i++) {
-      key = aKeys[i];
-      // Deep compare each member
-      if (!j$.util.has(b, key)) {
-        diffBuilder.record(a, b, objectKeysAreDifferentFormatter);
-        result = false;
-        continue;
-      }
-
-      diffBuilder.withPath(key, function() {
-        if(!eq(a[key], b[key], aStack, bStack, customTesters, diffBuilder)) {
-          result = false;
-        }
-      });
-    }
-
-    if (!result) {
-      return false;
-    }
-
-    // Remove the first object from the stack of traversed objects.
-    aStack.pop();
-    bStack.pop();
-
-    return result;
-  }
-
-  function keys(obj, isArray) {
-    var allKeys = Object.keys ? Object.keys(obj) :
-        (function(o) {
-          var keys = [];
-          for (var key in o) {
-            if (j$.util.has(o, key)) {
-              keys.push(key);
-            }
-          }
-          return keys;
-        })(obj);
-
-    if (!isArray) {
-      return allKeys;
-    }
-
-    if (allKeys.length === 0) {
-      return allKeys;
-    }
-
-    var extraKeys = [];
-    for (var i = 0; i < allKeys.length; i++) {
-      if (!/^[0-9]+$/.test(allKeys[i])) {
-        extraKeys.push(allKeys[i]);
-      }
-    }
-
-    return extraKeys;
-  }
-
-  function has(obj, key) {
-    return Object.prototype.hasOwnProperty.call(obj, key);
-  }
-
-  function isFunction(obj) {
-    return typeof obj === 'function';
-  }
-
-  function objectKeysAreDifferentFormatter(actual, expected, path) {
-    var missingProperties = j$.util.objectDifference(expected, actual),
-        extraProperties = j$.util.objectDifference(actual, expected),
-        missingPropertiesMessage = formatKeyValuePairs(missingProperties),
-        extraPropertiesMessage = formatKeyValuePairs(extraProperties),
-        messages = [];
-
-    if (!path.depth()) {
-      path = 'object';
-    }
-
-    if (missingPropertiesMessage.length) {
-      messages.push('Expected ' + path + ' to have properties' + missingPropertiesMessage);
-    }
-
-    if (extraPropertiesMessage.length) {
-      messages.push('Expected ' + path + ' not to have properties' + extraPropertiesMessage);
-    }
-
-    return messages.join('\n');
-  }
-
-  function constructorsAreDifferentFormatter(actual, expected, path) {
-    if (!path.depth()) {
-      path = 'object';
-    }
-
-    return 'Expected ' +
-        path + ' to be a kind of ' +
-        j$.fnNameFor(expected.constructor) +
-        ', but was ' + j$.pp(actual) + '.';
-  }
-
-  function formatKeyValuePairs(obj) {
-    var formatted = '';
-    for (var key in obj) {
-      formatted += '\n    ' + key + ': ' + j$.pp(obj[key]);
-    }
-    return formatted;
-  }
-};
-
-getJasmineRequireObj().NullDiffBuilder = function(j$) {
-  return function() {
-    return {
-      withPath: function(_, block) {
-        block();
-      },
-      record: function() {}
-    };
-  };
-};
-
-getJasmineRequireObj().ObjectPath = function(j$) {
-  function ObjectPath(components) {
-    this.components = components || [];
-  }
-
-  ObjectPath.prototype.toString = function() {
-    if (this.components.length) {
-      return '$' + map(this.components, formatPropertyAccess).join('');
-    } else {
-      return '';
-    }
-  };
-
-  ObjectPath.prototype.add = function(component) {
-    return new ObjectPath(this.components.concat([component]));
-  };
-
-  ObjectPath.prototype.depth = function() {
-    return this.components.length;
-  };
-
-  function formatPropertyAccess(prop) {
-    if (typeof prop === 'number') {
-      return '[' + prop + ']';
-    }
-
-    if (isValidIdentifier(prop)) {
-      return '.' + prop;
-    }
-
-    return '[\'' + prop + '\']';
-  }
-
-  function map(array, fn) {
-    var results = [];
-    for (var i = 0; i < array.length; i++) {
-      results.push(fn(array[i]));
-    }
-    return results;
-  }
-
-  function isValidIdentifier(string) {
-    return /^[A-Za-z\$_][A-Za-z0-9\$_]*$/.test(string);
-  }
-
-  return ObjectPath;
-};
-
-getJasmineRequireObj().toBe = function() {
-  /**
-   * {@link expect} the actual value to be `===` to the expected value.
-   * @function
-   * @name matchers#toBe
-   * @param {Object} expected - The expected value to compare against.
-   * @example
-   * expect(thing).toBe(realThing);
-   */
-  function toBe() {
-    return {
-      compare: function(actual, expected) {
-        return {
-          pass: actual === expected
-        };
-      }
-    };
-  }
-
-  return toBe;
-};
-
-getJasmineRequireObj().toBeCloseTo = function() {
-  /**
-   * {@link expect} the actual value to be within a specified precision of the expected value.
-   * @function
-   * @name matchers#toBeCloseTo
-   * @param {Object} expected - The expected value to compare against.
-   * @param {Number} [precision=2] - The number of decimal points to check.
-   * @example
-   * expect(number).toBeCloseTo(42.2, 3);
-   */
-  function toBeCloseTo() {
-    return {
-      compare: function(actual, expected, precision) {
-        if (precision !== 0) {
-          precision = precision || 2;
-        }
-
-        return {
-          pass: Math.abs(expected - actual) < (Math.pow(10, -precision) / 2)
-        };
-      }
-    };
-  }
-
-  return toBeCloseTo;
-};
-
-getJasmineRequireObj().toBeDefined = function() {
-  /**
-   * {@link expect} the actual value to be defined. (Not `undefined`)
-   * @function
-   * @name matchers#toBeDefined
-   * @example
-   * expect(result).toBeDefined();
-   */
-  function toBeDefined() {
-    return {
-      compare: function(actual) {
-        return {
-          pass: (void 0 !== actual)
-        };
-      }
-    };
-  }
-
-  return toBeDefined;
-};
-
-getJasmineRequireObj().toBeFalsy = function() {
-  /**
-   * {@link expect} the actual value to be falsy
-   * @function
-   * @name matchers#toBeFalsy
-   * @example
-   * expect(result).toBeFalsy();
-   */
-  function toBeFalsy() {
-    return {
-      compare: function(actual) {
-        return {
-          pass: !!!actual
-        };
-      }
-    };
-  }
-
-  return toBeFalsy;
-};
-
-getJasmineRequireObj().toBeGreaterThan = function() {
-  /**
-   * {@link expect} the actual value to be greater than the expected value.
-   * @function
-   * @name matchers#toBeGreaterThan
-   * @param {Number} expected - The value to compare against.
-   * @example
-   * expect(result).toBeGreaterThan(3);
-   */
-  function toBeGreaterThan() {
-    return {
-      compare: function(actual, expected) {
-        return {
-          pass: actual > expected
-        };
-      }
-    };
-  }
-
-  return toBeGreaterThan;
-};
-
-
-getJasmineRequireObj().toBeGreaterThanOrEqual = function() {
-  /**
-   * {@link expect} the actual value to be greater than or equal to the expected value.
-   * @function
-   * @name matchers#toBeGreaterThanOrEqual
-   * @param {Number} expected - The expected value to compare against.
-   * @example
-   * expect(result).toBeGreaterThanOrEqual(25);
-   */
-  function toBeGreaterThanOrEqual() {
-    return {
-      compare: function(actual, expected) {
-        return {
-          pass: actual >= expected
-        };
-      }
-    };
-  }
-
-  return toBeGreaterThanOrEqual;
-};
-
-getJasmineRequireObj().toBeLessThan = function() {
-  /**
-   * {@link expect} the actual value to be less than the expected value.
-   * @function
-   * @name matchers#toBeLessThan
-   * @param {Number} expected - The expected value to compare against.
-   * @example
-   * expect(result).toBeLessThan(0);
-   */
-  function toBeLessThan() {
-    return {
-
-      compare: function(actual, expected) {
-        return {
-          pass: actual < expected
-        };
-      }
-    };
-  }
-
-  return toBeLessThan;
-};
-
-getJasmineRequireObj().toBeLessThanOrEqual = function() {
-  /**
-   * {@link expect} the actual value to be less than or equal to the expected value.
-   * @function
-   * @name matchers#toBeLessThanOrEqual
-   * @param {Number} expected - The expected value to compare against.
-   * @example
-   * expect(result).toBeLessThanOrEqual(123);
-   */
-  function toBeLessThanOrEqual() {
-    return {
-
-      compare: function(actual, expected) {
-        return {
-          pass: actual <= expected
-        };
-      }
-    };
-  }
-
-  return toBeLessThanOrEqual;
-};
-
-getJasmineRequireObj().toBeNaN = function(j$) {
-  /**
-   * {@link expect} the actual value to be `NaN` (Not a Number).
-   * @function
-   * @name matchers#toBeNaN
-   * @example
-   * expect(thing).toBeNaN();
-   */
-  function toBeNaN() {
-    return {
-      compare: function(actual) {
-        var result = {
-          pass: (actual !== actual)
-        };
-
-        if (result.pass) {
-          result.message = 'Expected actual not to be NaN.';
-        } else {
-          result.message = function() { return 'Expected ' + j$.pp(actual) + ' to be NaN.'; };
-        }
-
-        return result;
-      }
-    };
-  }
-
-  return toBeNaN;
-};
-
-getJasmineRequireObj().toBeNegativeInfinity = function(j$) {
-  /**
-   * {@link expect} the actual value to be `-Infinity` (-infinity).
-   * @function
-   * @name matchers#toBeNegativeInfinity
-   * @example
-   * expect(thing).toBeNegativeInfinity();
-   */
-  function toBeNegativeInfinity() {
-    return {
-      compare: function(actual) {
-        var result = {
-          pass: (actual === Number.NEGATIVE_INFINITY)
-        };
-
-        if (result.pass) {
-          result.message = 'Expected actual to be -Infinity.';
-        } else {
-          result.message = function() { return 'Expected ' + j$.pp(actual) + ' not to be -Infinity.'; };
-        }
-
-        return result;
-      }
-    };
-  }
-
-  return toBeNegativeInfinity;
-};
-
-getJasmineRequireObj().toBeNull = function() {
-  /**
-   * {@link expect} the actual value to be `null`.
-   * @function
-   * @name matchers#toBeNull
-   * @example
-   * expect(result).toBeNull();
-   */
-  function toBeNull() {
-    return {
-      compare: function(actual) {
-        return {
-          pass: actual === null
-        };
-      }
-    };
-  }
-
-  return toBeNull;
-};
-
-getJasmineRequireObj().toBePositiveInfinity = function(j$) {
-  /**
-   * {@link expect} the actual value to be `Infinity` (infinity).
-   * @function
-   * @name matchers#toBePositiveInfinity
-   * @example
-   * expect(thing).toBePositiveInfinity();
-   */
-  function toBePositiveInfinity() {
-    return {
-      compare: function(actual) {
-        var result = {
-          pass: (actual === Number.POSITIVE_INFINITY)
-        };
-
-        if (result.pass) {
-          result.message = 'Expected actual to be Infinity.';
-        } else {
-          result.message = function() { return 'Expected ' + j$.pp(actual) + ' not to be Infinity.'; };
-        }
-
-        return result;
-      }
-    };
-  }
-
-  return toBePositiveInfinity;
-};
-
-getJasmineRequireObj().toBeTruthy = function() {
-  /**
-   * {@link expect} the actual value to be truthy.
-   * @function
-   * @name matchers#toBeTruthy
-   * @example
-   * expect(thing).toBeTruthy();
-   */
-  function toBeTruthy() {
-    return {
-      compare: function(actual) {
-        return {
-          pass: !!actual
-        };
-      }
-    };
-  }
-
-  return toBeTruthy;
-};
-
-getJasmineRequireObj().toBeUndefined = function() {
-  /**
-   * {@link expect} the actual value to be `undefined`.
-   * @function
-   * @name matchers#toBeUndefined
-   * @example
-   * expect(result).toBeUndefined():
-   */
-  function toBeUndefined() {
-    return {
-      compare: function(actual) {
-        return {
-          pass: void 0 === actual
-        };
-      }
-    };
-  }
-
-  return toBeUndefined;
-};
-
-getJasmineRequireObj().toContain = function() {
-  /**
-   * {@link expect} the actual value to contain a specific value.
-   * @function
-   * @name matchers#toContain
-   * @param {Object} expected - The value to look for.
-   * @example
-   * expect(array).toContain(anElement);
-   * expect(string).toContain(substring);
-   */
-  function toContain(util, customEqualityTesters) {
-    customEqualityTesters = customEqualityTesters || [];
-
-    return {
-      compare: function(actual, expected) {
-
-        return {
-          pass: util.contains(actual, expected, customEqualityTesters)
-        };
-      }
-    };
-  }
-
-  return toContain;
-};
-
-getJasmineRequireObj().toEqual = function(j$) {
-  /**
-   * {@link expect} the actual value to be equal to the expected, using deep equality comparison.
-   * @function
-   * @name matchers#toEqual
-   * @param {Object} expected - Expected value
-   * @example
-   * expect(bigObject).toEqual({"foo": ['bar', 'baz']});
-   */
-  function toEqual(util, customEqualityTesters) {
-    customEqualityTesters = customEqualityTesters || [];
-
-    return {
-      compare: function(actual, expected) {
-        var result = {
-              pass: false
-            },
-            diffBuilder = j$.DiffBuilder();
-
-        result.pass = util.equals(actual, expected, customEqualityTesters, diffBuilder);
-
-        // TODO: only set error message if test fails
-        result.message = diffBuilder.getMessage();
-
-        return result;
-      }
-    };
-  }
-
-  return toEqual;
-};
-
-getJasmineRequireObj().toHaveBeenCalled = function(j$) {
-
-  var getErrorMsg = j$.formatErrorMsg('<toHaveBeenCalled>', 'expect(<spyObj>).toHaveBeenCalled()');
-
-  /**
-   * {@link expect} the actual (a {@link Spy}) to have been called.
-   * @function
-   * @name matchers#toHaveBeenCalled
-   * @example
-   * expect(mySpy).toHaveBeenCalled();
-   * expect(mySpy).not.toHaveBeenCalled();
-   */
-  function toHaveBeenCalled() {
-    return {
-      compare: function(actual) {
-        var result = {};
-
-        if (!j$.isSpy(actual)) {
-          throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.'));
-        }
-
-        if (arguments.length > 1) {
-          throw new Error(getErrorMsg('Does not take arguments, use toHaveBeenCalledWith'));
-        }
-
-        result.pass = actual.calls.any();
-
-        result.message = result.pass ?
-            'Expected spy ' + actual.and.identity() + ' not to have been called.' :
-            'Expected spy ' + actual.and.identity() + ' to have been called.';
-
-        return result;
-      }
-    };
-  }
-
-  return toHaveBeenCalled;
-};
-
-getJasmineRequireObj().toHaveBeenCalledBefore = function(j$) {
-
-  var getErrorMsg = j$.formatErrorMsg('<toHaveBeenCalledBefore>', 'expect(<spyObj>).toHaveBeenCalledBefore(<spyObj>)');
-
-  /**
-   * {@link expect} the actual value (a {@link Spy}) to have been called before another {@link Spy}.
-   * @function
-   * @name matchers#toHaveBeenCalledBefore
-   * @param {Spy} expected - {@link Spy} that should have been called after the `actual` {@link Spy}.
-   * @example
-   * expect(mySpy).toHaveBeenCalledBefore(otherSpy);
-   */
-  function toHaveBeenCalledBefore() {
-    return {
-      compare: function(firstSpy, latterSpy) {
-        if (!j$.isSpy(firstSpy)) {
-          throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(firstSpy) + '.'));
-        }
-        if (!j$.isSpy(latterSpy)) {
-          throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(latterSpy) + '.'));
-        }
-
-        var result = { pass: false };
-
-        if (!firstSpy.calls.count()) {
-          result.message = 'Expected spy ' +  firstSpy.and.identity() + ' to have been called.';
-          return result;
-        }
-        if (!latterSpy.calls.count()) {
-          result.message = 'Expected spy ' +  latterSpy.and.identity() + ' to have been called.';
-          return result;
-        }
-
-        var latest1stSpyCall = firstSpy.calls.mostRecent().invocationOrder;
-        var first2ndSpyCall = latterSpy.calls.first().invocationOrder;
-
-        result.pass = latest1stSpyCall < first2ndSpyCall;
-
-        if (result.pass) {
-          result.message = 'Expected spy ' + firstSpy.and.identity() + ' to not have been called before spy ' + latterSpy.and.identity() + ', but it was';
-        } else {
-          var first1stSpyCall = firstSpy.calls.first().invocationOrder;
-          var latest2ndSpyCall = latterSpy.calls.mostRecent().invocationOrder;
-
-          if(first1stSpyCall < first2ndSpyCall) {
-            result.message = 'Expected latest call to spy ' + firstSpy.and.identity() + ' to have been called before first call to spy ' + latterSpy.and.identity() + ' (no interleaved calls)';
-          } else if (latest2ndSpyCall > latest1stSpyCall) {
-            result.message = 'Expected first call to spy ' + latterSpy.and.identity() + ' to have been called after latest call to spy ' + firstSpy.and.identity() + ' (no interleaved calls)';
-          } else {
-            result.message = 'Expected spy ' + firstSpy.and.identity() + ' to have been called before spy ' + latterSpy.and.identity();
-          }
-        }
-
-        return result;
-      }
-    };
-  }
-
-  return toHaveBeenCalledBefore;
-};
-
-getJasmineRequireObj().toHaveBeenCalledTimes = function(j$) {
-
-  var getErrorMsg = j$.formatErrorMsg('<toHaveBeenCalledTimes>', 'expect(<spyObj>).toHaveBeenCalledTimes(<Number>)');
-
-  /**
-   * {@link expect} the actual (a {@link Spy}) to have been called the specified number of times.
-   * @function
-   * @name matchers#toHaveBeenCalledTimes
-   * @param {Number} expected - The number of invocations to look for.
-   * @example
-   * expect(mySpy).toHaveBeenCalledTimes(3);
-   */
-  function toHaveBeenCalledTimes() {
-    return {
-      compare: function(actual, expected) {
-        if (!j$.isSpy(actual)) {
-          throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.'));
-        }
-
-        var args = Array.prototype.slice.call(arguments, 0),
-            result = { pass: false };
-
-        if (!j$.isNumber_(expected)){
-          throw new Error(getErrorMsg('The expected times failed is a required argument and must be a number.'));
-        }
-
-        actual = args[0];
-        var calls = actual.calls.count();
-        var timesMessage = expected === 1 ? 'once' : expected + ' times';
-        result.pass = calls === expected;
-        result.message = result.pass ?
-            'Expected spy ' + actual.and.identity() + ' not to have been called ' + timesMessage + '. It was called ' +  calls + ' times.' :
-            'Expected spy ' + actual.and.identity() + ' to have been called ' + timesMessage + '. It was called ' +  calls + ' times.';
-        return result;
-      }
-    };
-  }
-
-  return toHaveBeenCalledTimes;
-};
-
-getJasmineRequireObj().toHaveBeenCalledWith = function(j$) {
-
-  var getErrorMsg = j$.formatErrorMsg('<toHaveBeenCalledWith>', 'expect(<spyObj>).toHaveBeenCalledWith(...arguments)');
-
-  /**
-   * {@link expect} the actual (a {@link Spy}) to have been called with particular arguments at least once.
-   * @function
-   * @name matchers#toHaveBeenCalledWith
-   * @param {...Object} - The arguments to look for
-   * @example
-   * expect(mySpy).toHaveBeenCalledWith('foo', 'bar', 2);
-   */
-  function toHaveBeenCalledWith(util, customEqualityTesters) {
-    return {
-      compare: function() {
-        var args = Array.prototype.slice.call(arguments, 0),
-            actual = args[0],
-            expectedArgs = args.slice(1),
-            result = { pass: false };
-
-        if (!j$.isSpy(actual)) {
-          throw new Error(getErrorMsg('Expected a spy, but got ' + j$.pp(actual) + '.'));
-        }
-
-        if (!actual.calls.any()) {
-          result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but it was never called.'; };
-          return result;
-        }
-
-        if (util.contains(actual.calls.allArgs(), expectedArgs, customEqualityTesters)) {
-          result.pass = true;
-          result.message = function() { return 'Expected spy ' + actual.and.identity() + ' not to have been called with ' + j$.pp(expectedArgs) + ' but it was.'; };
-        } else {
-          result.message = function() { return 'Expected spy ' + actual.and.identity() + ' to have been called with ' + j$.pp(expectedArgs) + ' but actual calls were ' + j$.pp(actual.calls.allArgs()).replace(/^\[ | \]$/g, '') + '.'; };
-        }
-
-        return result;
-      }
-    };
-  }
-
-  return toHaveBeenCalledWith;
-};
-
-getJasmineRequireObj().toMatch = function(j$) {
-
-  var getErrorMsg = j$.formatErrorMsg('<toMatch>', 'expect(<expectation>).toMatch(<string> || <regexp>)');
-
-  /**
-   * {@link expect} the actual value to match a regular expression
-   * @function
-   * @name matchers#toMatch
-   * @param {RegExp|String} expected - Value to look for in the string.
-   * @example
-   * expect("my string").toMatch(/string$/);
-   * expect("other string").toMatch("her");
-   */
-  function toMatch() {
-    return {
-      compare: function(actual, expected) {
-        if (!j$.isString_(expected) && !j$.isA_('RegExp', expected)) {
-          throw new Error(getErrorMsg('Expected is not a String or a RegExp'));
-        }
-
-        var regexp = new RegExp(expected);
-
-        return {
-          pass: regexp.test(actual)
-        };
-      }
-    };
-  }
-
-  return toMatch;
-};
-
-getJasmineRequireObj().toThrow = function(j$) {
-
-  var getErrorMsg = j$.formatErrorMsg('<toThrow>', 'expect(function() {<expectation>}).toThrow()');
-
-  /**
-   * {@link expect} a function to `throw` something.
-   * @function
-   * @name matchers#toThrow
-   * @param {Object} [expected] - Value that should be thrown. If not provided, simply the fact that something was thrown will be checked.
-   * @example
-   * expect(function() { return 'things'; }).toThrow('foo');
-   * expect(function() { return 'stuff'; }).toThrow();
-   */
-  function toThrow(util) {
-    return {
-      compare: function(actual, expected) {
-        var result = { pass: false },
-            threw = false,
-            thrown;
-
-        if (typeof actual != 'function') {
-          throw new Error(getErrorMsg('Actual is not a Function'));
-        }
-
-        try {
-          actual();
-        } catch (e) {
-          threw = true;
-          thrown = e;
-        }
-
-        if (!threw) {
-          result.message = 'Expected function to throw an exception.';
-          return result;
-        }
-
-        if (arguments.length == 1) {
-          result.pass = true;
-          result.message = function() { return 'Expected function not to throw, but it threw ' + j$.pp(thrown) + '.'; };
-
-          return result;
-        }
-
-        if (util.equals(thrown, expected)) {
-          result.pass = true;
-          result.message = function() { return 'Expected function not to throw ' + j$.pp(expected) + '.'; };
-        } else {
-          result.message = function() { return 'Expected function to throw ' + j$.pp(expected) + ', but it threw ' +  j$.pp(thrown) + '.'; };
-        }
-
-        return result;
-      }
-    };
-  }
-
-  return toThrow;
-};
-
-getJasmineRequireObj().toThrowError = function(j$) {
-
-  var getErrorMsg =  j$.formatErrorMsg('<toThrowError>', 'expect(function() {<expectation>}).toThrowError(<ErrorConstructor>, <message>)');
-
-  /**
-   * {@link expect} a function to `throw` an `Error`.
-   * @function
-   * @name matchers#toThrowError
-   * @param {Error} [expected] - `Error` constructor the object that was thrown needs to be an instance of. If not provided, `Error` will be used.
-   * @param {RegExp|String} [message] - The message that should be set on the thrown `Error`
-   * @example
-   * expect(function() { return 'things'; }).toThrowError(MyCustomError, 'message');
-   * expect(function() { return 'things'; }).toThrowError(MyCustomError, /bar/);
-   * expect(function() { return 'stuff'; }).toThrowError(MyCustomError);
-   * expect(function() { return 'other'; }).toThrowError(/foo/);
-   * expect(function() { return 'other'; }).toThrowError();
-   */
-  function toThrowError () {
-    return {
-      compare: function(actual) {
-        var threw = false,
-            pass = {pass: true},
-            fail = {pass: false},
-            thrown;
-
-        if (typeof actual != 'function') {
-          throw new Error(getErrorMsg('Actual is not a Function'));
-        }
-
-        var errorMatcher = getMatcher.apply(null, arguments);
-
-        try {
-          actual();
-        } catch (e) {
-          threw = true;
-          thrown = e;
-        }
-
-        if (!threw) {
-          fail.message = 'Expected function to throw an Error.';
-          return fail;
-        }
-
-        // Get Error constructor of thrown
-        if (!isErrorObject(thrown)) {
-          fail.message = function() { return 'Expected function to throw an Error, but it threw ' + j$.pp(thrown) + '.'; };
-          return fail;
-        }
-
-        if (errorMatcher.hasNoSpecifics()) {
-          pass.message = 'Expected function not to throw an Error, but it threw ' + j$.fnNameFor(thrown) + '.';
-          return pass;
-        }
-
-        if (errorMatcher.matches(thrown)) {
-          pass.message = function() {
-            return 'Expected function not to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() + '.';
-          };
-          return pass;
-        } else {
-          fail.message = function() {
-            return 'Expected function to throw ' + errorMatcher.errorTypeDescription + errorMatcher.messageDescription() +
-                ', but it threw ' + errorMatcher.thrownDescription(thrown) + '.';
-          };
-          return fail;
-        }
-      }
-    };
-
-    function getMatcher() {
-      var expected = null,
-          errorType = null;
-
-      if (arguments.length == 2) {
-        expected = arguments[1];
-        if (isAnErrorType(expected)) {
-          errorType = expected;
-          expected = null;
-        }
-      } else if (arguments.length > 2) {
-        errorType = arguments[1];
-        expected = arguments[2];
-        if (!isAnErrorType(errorType)) {
-          throw new Error(getErrorMsg('Expected error type is not an Error.'));
-        }
-      }
-
-      if (expected && !isStringOrRegExp(expected)) {
-        if (errorType) {
-          throw new Error(getErrorMsg('Expected error message is not a string or RegExp.'));
-        } else {
-          throw new Error(getErrorMsg('Expected is not an Error, string, or RegExp.'));
-        }
-      }
-
-      function messageMatch(message) {
-        if (typeof expected == 'string') {
-          return expected == message;
-        } else {
-          return expected.test(message);
-        }
-      }
-
-      return {
-        errorTypeDescription: errorType ? j$.fnNameFor(errorType) : 'an exception',
-        thrownDescription: function(thrown) {
-          var thrownName = errorType ? j$.fnNameFor(thrown.constructor) : 'an exception',
-              thrownMessage = '';
-
-          if (expected) {
-            thrownMessage = ' with message ' + j$.pp(thrown.message);
-          }
-
-          return thrownName + thrownMessage;
-        },
-        messageDescription: function() {
-          if (expected === null) {
-            return '';
-          } else if (expected instanceof RegExp) {
-            return ' with a message matching ' + j$.pp(expected);
-          } else {
-            return ' with message ' + j$.pp(expected);
-          }
-        },
-        hasNoSpecifics: function() {
-          return expected === null && errorType === null;
-        },
-        matches: function(error) {
-          return (errorType === null || error instanceof errorType) &&
-              (expected === null || messageMatch(error.message));
-        }
-      };
-    }
-
-    function isStringOrRegExp(potential) {
-      return potential instanceof RegExp || (typeof potential == 'string');
-    }
-
-    function isAnErrorType(type) {
-      if (typeof type !== 'function') {
-        return false;
-      }
-
-      var Surrogate = function() {};
-      Surrogate.prototype = type.prototype;
-      return isErrorObject(new Surrogate());
-    }
-
-    function isErrorObject(thrown) {
-      if (thrown instanceof Error) {
-        return true;
-      }
-      if (thrown && thrown.constructor && thrown.constructor.constructor &&
-          (thrown instanceof (thrown.constructor.constructor('return this')()).Error)) {
-        return true;
-      }
-      return false;
-    }
-  }
-
-  return toThrowError;
-};
-
-getJasmineRequireObj().MockDate = function() {
-  function MockDate(global) {
-    var self = this;
-    var currentTime = 0;
-
-    if (!global || !global.Date) {
-      self.install = function() {};
-      self.tick = function() {};
-      self.uninstall = function() {};
-      return self;
-    }
-
-    var GlobalDate = global.Date;
-
-    self.install = function(mockDate) {
-      if (mockDate instanceof GlobalDate) {
-        currentTime = mockDate.getTime();
-      } else {
-        currentTime = new GlobalDate().getTime();
-      }
-
-      global.Date = FakeDate;
-    };
-
-    self.tick = function(millis) {
-      millis = millis || 0;
-      currentTime = currentTime + millis;
-    };
-
-    self.uninstall = function() {
-      currentTime = 0;
-      global.Date = GlobalDate;
-    };
-
-    createDateProperties();
-
-    return self;
-
-    function FakeDate() {
-      switch(arguments.length) {
-        case 0:
-          return new GlobalDate(currentTime);
-        case 1:
-          return new GlobalDate(arguments[0]);
-        case 2:
-          return new GlobalDate(arguments[0], arguments[1]);
-        case 3:
-          return new GlobalDate(arguments[0], arguments[1], arguments[2]);
-        case 4:
-          return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3]);
-        case 5:
-          return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
-              arguments[4]);
-        case 6:
-          return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
-              arguments[4], arguments[5]);
-        default:
-          return new GlobalDate(arguments[0], arguments[1], arguments[2], arguments[3],
-              arguments[4], arguments[5], arguments[6]);
-      }
-    }
-
-    function createDateProperties() {
-      FakeDate.prototype = GlobalDate.prototype;
-
-      FakeDate.now = function() {
-        if (GlobalDate.now) {
-          return currentTime;
-        } else {
-          throw new Error('Browser does not support Date.now()');
-        }
-      };
-
-      FakeDate.toSource = GlobalDate.toSource;
-      FakeDate.toString = GlobalDate.toString;
-      FakeDate.parse = GlobalDate.parse;
-      FakeDate.UTC = GlobalDate.UTC;
-    }
-  }
-
-  return MockDate;
-};
-
-getJasmineRequireObj().pp = function(j$) {
-
-  function PrettyPrinter() {
-    this.ppNestLevel_ = 0;
-    this.seen = [];
-  }
-
-  function hasCustomToString(value) {
-    // value.toString !== Object.prototype.toString if value has no custom toString but is from another context (e.g.
-    // iframe, web worker)
-    return value.toString !== Object.prototype.toString && (value.toString() !== Object.prototype.toString.call(value));
-  }
-
-  PrettyPrinter.prototype.format = function(value) {
-    this.ppNestLevel_++;
-    try {
-      if (j$.util.isUndefined(value)) {
-        this.emitScalar('undefined');
-      } else if (value === null) {
-        this.emitScalar('null');
-      } else if (value === 0 && 1/value === -Infinity) {
-        this.emitScalar('-0');
-      } else if (value === j$.getGlobal()) {
-        this.emitScalar('<global>');
-      } else if (value.jasmineToString) {
-        this.emitScalar(value.jasmineToString());
-      } else if (typeof value === 'string') {
-        this.emitString(value);
-      } else if (j$.isSpy(value)) {
-        this.emitScalar('spy on ' + value.and.identity());
-      } else if (value instanceof RegExp) {
-        this.emitScalar(value.toString());
-      } else if (typeof value === 'function') {
-        this.emitScalar('Function');
-      } else if (typeof value.nodeType === 'number') {
-        this.emitScalar('HTMLNode');
-      } else if (value instanceof Date) {
-        this.emitScalar('Date(' + value + ')');
-      } else if (value.toString && value.toString() == '[object Set]') {
-        this.emitSet(value);
-      } else if (value.toString && typeof value === 'object' && !j$.isArray_(value) && hasCustomToString(value)) {
-        this.emitScalar(value.toString());
-      } else if (j$.util.arrayContains(this.seen, value)) {
-        this.emitScalar('<circular reference: ' + (j$.isArray_(value) ? 'Array' : 'Object') + '>');
-      } else if (j$.isArray_(value) || j$.isA_('Object', value)) {
-        this.seen.push(value);
-        if (j$.isArray_(value)) {
-          this.emitArray(value);
-        } else {
-          this.emitObject(value);
-        }
-        this.seen.pop();
-      } else {
-        this.emitScalar(value.toString());
-      }
-    } finally {
-      this.ppNestLevel_--;
-    }
-  };
-
-  PrettyPrinter.prototype.iterateObject = function(obj, fn) {
-    for (var property in obj) {
-      if (!Object.prototype.hasOwnProperty.call(obj, property)) { continue; }
-      fn(property, obj.__lookupGetter__ ? (!j$.util.isUndefined(obj.__lookupGetter__(property)) &&
-      obj.__lookupGetter__(property) !== null) : false);
-    }
-  };
-
-  PrettyPrinter.prototype.emitArray = j$.unimplementedMethod_;
-  PrettyPrinter.prototype.emitSet = j$.unimplementedMethod_;
-  PrettyPrinter.prototype.emitObject = j$.unimplementedMethod_;
-  PrettyPrinter.prototype.emitScalar = j$.unimplementedMethod_;
-  PrettyPrinter.prototype.emitString = j$.unimplementedMethod_;
-
-  function StringPrettyPrinter() {
-    PrettyPrinter.call(this);
-
-    this.string = '';
-  }
-
-  j$.util.inherit(StringPrettyPrinter, PrettyPrinter);
-
-  StringPrettyPrinter.prototype.emitScalar = function(value) {
-    this.append(value);
-  };
-
-  StringPrettyPrinter.prototype.emitString = function(value) {
-    this.append('\'' + value + '\'');
-  };
-
-  StringPrettyPrinter.prototype.emitArray = function(array) {
-    if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
-      this.append('Array');
-      return;
-    }
-    var length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
-    this.append('[ ');
-    for (var i = 0; i < length; i++) {
-      if (i > 0) {
-        this.append(', ');
-      }
-      this.format(array[i]);
-    }
-    if(array.length > length){
-      this.append(', ...');
-    }
-
-    var self = this;
-    var first = array.length === 0;
-    this.iterateObject(array, function(property, isGetter) {
-      if (property.match(/^\d+$/)) {
-        return;
-      }
-
-      if (first) {
-        first = false;
-      } else {
-        self.append(', ');
-      }
-
-      self.formatProperty(array, property, isGetter);
-    });
-
-    this.append(' ]');
-  };
-
-  StringPrettyPrinter.prototype.emitSet = function(set) {
-    if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
-      this.append('Set');
-      return;
-    }
-    this.append('Set( ');
-    var size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH);
-    var iter = set.values();
-    for (var i = 0; i < size; i++) {
-      if (i > 0) {
-        this.append(', ');
-      }
-      this.format(iter.next().value);
-    }
-    if (set.size > size){
-      this.append(', ...');
-    }
-    this.append(' )');
-  };
-
-  StringPrettyPrinter.prototype.emitObject = function(obj) {
-    var ctor = obj.constructor,
-        constructorName;
-
-    constructorName = typeof ctor === 'function' && obj instanceof ctor ?
-        j$.fnNameFor(obj.constructor) :
-        'null';
-
-    this.append(constructorName);
-
-    if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) {
-      return;
-    }
-
-    var self = this;
-    this.append('({ ');
-    var first = true;
-
-    this.iterateObject(obj, function(property, isGetter) {
-      if (first) {
-        first = false;
-      } else {
-        self.append(', ');
-      }
-
-      self.formatProperty(obj, property, isGetter);
-    });
-
-    this.append(' })');
-  };
-
-  StringPrettyPrinter.prototype.formatProperty = function(obj, property, isGetter) {
-    this.append(property);
-    this.append(': ');
-    if (isGetter) {
-      this.append('<getter>');
-    } else {
-      this.format(obj[property]);
-    }
-  };
-
-  StringPrettyPrinter.prototype.append = function(value) {
-    this.string += value;
-  };
-
-  return function(value) {
-    var stringPrettyPrinter = new StringPrettyPrinter();
-    stringPrettyPrinter.format(value);
-    return stringPrettyPrinter.string;
-  };
-};
-
-getJasmineRequireObj().QueueRunner = function(j$) {
-
-  function once(fn) {
-    var called = false;
-    return function() {
-      if (!called) {
-        called = true;
-        fn();
-      }
-      return null;
-    };
-  }
-
-  function QueueRunner(attrs) {
-    this.queueableFns = attrs.queueableFns || [];
-    this.onComplete = attrs.onComplete || function() {};
-    this.clearStack = attrs.clearStack || function(fn) {fn();};
-    this.onException = attrs.onException || function() {};
-    this.catchException = attrs.catchException || function() { return true; };
-    this.userContext = attrs.userContext || {};
-    this.timeout = attrs.timeout || {setTimeout: setTimeout, clearTimeout: clearTimeout};
-    this.fail = attrs.fail || function() {};
-    this.globalErrors = attrs.globalErrors || { pushListener: function() {}, popListener: function() {} };
-  }
-
-  QueueRunner.prototype.execute = function() {
-    this.run(this.queueableFns, 0);
-  };
-
-  QueueRunner.prototype.run = function(queueableFns, recursiveIndex) {
-    var length = queueableFns.length,
-        self = this,
-        iterativeIndex;
-
-
-    for(iterativeIndex = recursiveIndex; iterativeIndex < length; iterativeIndex++) {
-      var queueableFn = queueableFns[iterativeIndex];
-      if (queueableFn.fn.length > 0) {
-        attemptAsync(queueableFn);
-        return;
-      } else {
-        attemptSync(queueableFn);
-      }
-    }
-
-    this.clearStack(this.onComplete);
-
-    function attemptSync(queueableFn) {
-      try {
-        queueableFn.fn.call(self.userContext);
-      } catch (e) {
-        handleException(e, queueableFn);
-      }
-    }
-
-    function attemptAsync(queueableFn) {
-      var clearTimeout = function () {
-            Function.prototype.apply.apply(self.timeout.clearTimeout, [j$.getGlobal(), [timeoutId]]);
-          },
-          handleError = function(error) {
-            onException(error);
-            next();
-          },
-          next = once(function () {
-            clearTimeout(timeoutId);
-            self.globalErrors.popListener(handleError);
-            self.run(queueableFns, iterativeIndex + 1);
-          }),
-          timeoutId;
-
-      next.fail = function() {
-        self.fail.apply(null, arguments);
-        next();
-      };
-
-      self.globalErrors.pushListener(handleError);
-
-      if (queueableFn.timeout) {
-        timeoutId = Function.prototype.apply.apply(self.timeout.setTimeout, [j$.getGlobal(), [function() {
-          var error = new Error('Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.');
-          onException(error);
-          next();
-        }, queueableFn.timeout()]]);
-      }
-
-      try {
-        queueableFn.fn.call(self.userContext, next);
-      } catch (e) {
-        handleException(e, queueableFn);
-        next();
-      }
-    }
-
-    function onException(e) {
-      self.onException(e);
-    }
-
-    function handleException(e, queueableFn) {
-      onException(e);
-      if (!self.catchException(e)) {
-        //TODO: set a var when we catch an exception and
-        //use a finally block to close the loop in a nice way..
-        throw e;
-      }
-    }
-  };
-
-  return QueueRunner;
-};
-
-getJasmineRequireObj().ReportDispatcher = function() {
-  function ReportDispatcher(methods) {
-
-    var dispatchedMethods = methods || [];
-
-    for (var i = 0; i < dispatchedMethods.length; i++) {
-      var method = dispatchedMethods[i];
-      this[method] = (function(m) {
-        return function() {
-          dispatch(m, arguments);
-        };
-      }(method));
-    }
-
-    var reporters = [];
-    var fallbackReporter = null;
-
-    this.addReporter = function(reporter) {
-      reporters.push(reporter);
-    };
-
-    this.provideFallbackReporter = function(reporter) {
-      fallbackReporter = reporter;
-    };
-
-    this.clearReporters = function() {
-      reporters = [];
-    };
-
-    return this;
-
-    function dispatch(method, args) {
-      if (reporters.length === 0 && fallbackReporter !== null) {
-        reporters.push(fallbackReporter);
-      }
-      for (var i = 0; i < reporters.length; i++) {
-        var reporter = reporters[i];
-        if (reporter[method]) {
-          reporter[method].apply(reporter, args);
-        }
-      }
-    }
-  }
-
-  return ReportDispatcher;
-};
-
-
-getJasmineRequireObj().interface = function(jasmine, env) {
-  var jasmineInterface = {
-    /**
-     * Create a group of specs (often called a suite).
-     *
-     * Calls to `describe` can be nested within other calls to compose your suite as a tree.
-     * @name describe
-     * @function
-     * @global
-     * @param {String} description Textual description of the group
-     * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites a specs
-     */
-    describe: function(description, specDefinitions) {
-      return env.describe(description, specDefinitions);
-    },
-
-    /**
-     * A temporarily disabled [`describe`]{@link describe}
-     *
-     * Specs within an `xdescribe` will be marked pending and not executed
-     * @name xdescribe
-     * @function
-     * @global
-     * @param {String} description Textual description of the group
-     * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites a specs
-     */
-    xdescribe: function(description, specDefinitions) {
-      return env.xdescribe(description, specDefinitions);
-    },
-
-    /**
-     * A focused [`describe`]{@link describe}
-     *
-     * If suites or specs are focused, only those that are focused will be executed
-     * @see fit
-     * @name fdescribe
-     * @function
-     * @global
-     * @param {String} description Textual description of the group
-     * @param {Function} specDefinitions Function for Jasmine to invoke that will define inner suites a specs
-     */
-    fdescribe: function(description, specDefinitions) {
-      return env.fdescribe(description, specDefinitions);
-    },
-
-    /**
-     * Define a single spec. A spec should contain one or more {@link expect|expectations} that test the state of the code.
-     *
-     * A spec whose expectations all succeed will be passing and a spec with any failures will fail.
-     * @name it
-     * @function
-     * @global
-     * @param {String} description Textual description of what this spec is checking
-     * @param {Function} [testFunction] Function that contains the code of your test. If not provided the test will be `pending`.
-     * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec.
-     */
-    it: function() {
-      return env.it.apply(env, arguments);
-    },
-
-    /**
-     * A temporarily disabled [`it`]{@link it}
-     *
-     * The spec will report as `pending` and will not be executed.
-     * @name xit
-     * @function
-     * @global
-     * @param {String} description Textual description of what this spec is checking.
-     * @param {Function} [testFunction] Function that contains the code of your test. Will not be executed.
-     */
-    xit: function() {
-      return env.xit.apply(env, arguments);
-    },
-
-    /**
-     * A focused [`it`]{@link it}
-     *
-     * If suites or specs are focused, only those that are focused will be executed.
-     * @name fit
-     * @function
-     * @global
-     * @param {String} description Textual description of what this spec is checking.
-     * @param {Function} testFunction Function that contains the code of your test.
-     * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async spec.
-     */
-    fit: function() {
-      return env.fit.apply(env, arguments);
-    },
-
-    /**
-     * Run some shared setup before each of the specs in the {@link describe} in which it is called.
-     * @name beforeEach
-     * @function
-     * @global
-     * @param {Function} [function] Function that contains the code to setup your specs.
-     * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeEach.
-     */
-    beforeEach: function() {
-      return env.beforeEach.apply(env, arguments);
-    },
-
-    /**
-     * Run some shared teardown after each of the specs in the {@link describe} in which it is called.
-     * @name afterEach
-     * @function
-     * @global
-     * @param {Function} [function] Function that contains the code to teardown your specs.
-     * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterEach.
-     */
-    afterEach: function() {
-      return env.afterEach.apply(env, arguments);
-    },
-
-    /**
-     * Run some shared setup once before all of the specs in the {@link describe} are run.
-     *
-     * _Note:_ Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
-     * @name beforeAll
-     * @function
-     * @global
-     * @param {Function} [function] Function that contains the code to setup your specs.
-     * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async beforeAll.
-     */
-    beforeAll: function() {
-      return env.beforeAll.apply(env, arguments);
-    },
-
-    /**
-     * Run some shared teardown once before all of the specs in the {@link describe} are run.
-     *
-     * _Note:_ Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
-     * @name afterAll
-     * @function
-     * @global
-     * @param {Function} [function] Function that contains the code to teardown your specs.
-     * @param {Int} [timeout={@link jasmine.DEFAULT_TIMEOUT_INTERVAL}] Custom timeout for an async afterAll.
-     */
-    afterAll: function() {
-      return env.afterAll.apply(env, arguments);
-    },
-
-    /**
-     * Create an expectation for a spec.
-     * @name expect
-     * @function
-     * @global
-     * @param {Object} actual - Actual computed value to test expectations against.
-     * @return {matchers}
-     */
-    expect: function(actual) {
-      return env.expect(actual);
-    },
-
-    /**
-     * Mark a spec as pending, expectation results will be ignored.
-     * @name pending
-     * @function
-     * @global
-     * @param {String} [message] - Reason the spec is pending.
-     */
-    pending: function() {
-      return env.pending.apply(env, arguments);
-    },
-
-    /**
-     * Explicitly mark a spec as failed.
-     * @name fail
-     * @function
-     * @global
-     * @param {String|Error} [error] - Reason for the failure.
-     */
-    fail: function() {
-      return env.fail.apply(env, arguments);
-    },
-
-    /**
-     * Install a spy onto an existing object.
-     * @name spyOn
-     * @function
-     * @global
-     * @param {Object} obj - The object upon which to install the {@link Spy}.
-     * @param {String} methodName - The name of the method to replace with a {@link Spy}.
-     * @returns {Spy}
-     */
-    spyOn: function(obj, methodName) {
-      return env.spyOn(obj, methodName);
-    },
-
-    /**
-     * Install a spy on a property onto an existing object.
-     * @name spyOnProperty
-     * @function
-     * @global
-     * @param {Object} obj - The object upon which to install the {@link Spy}
-     * @param {String} propertyName - The name of the property to replace with a {@link Spy}.
-     * @param {String} [accessType=get] - The access type (get|set) of the property to {@link Spy} on.
-     * @returns {Spy}
-     */
-    spyOnProperty: function(obj, methodName, accessType) {
-      return env.spyOnProperty(obj, methodName, accessType);
-    },
-
-    jsApiReporter: new jasmine.JsApiReporter({
-      timer: new jasmine.Timer()
-    }),
-
-    /**
-     * @namespace jasmine
-     */
-    jasmine: jasmine
-  };
-
-  /**
-   * Add a custom equality tester for the current scope of specs.
-   *
-   * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}.
-   * @name jasmine.addCustomEqualityTester
-   * @function
-   * @param {Function} tester - A function which takes two arguments to compare and returns a `true` or `false` comparison result if it knows how to compare them, and `undefined` otherwise.
-   * @see custom_equality
-   */
-  jasmine.addCustomEqualityTester = function(tester) {
-    env.addCustomEqualityTester(tester);
-  };
-
-  /**
-   * Add custom matchers for the current scope of specs.
-   *
-   * _Note:_ This is only callable from within a {@link beforeEach}, {@link it}, or {@link beforeAll}.
-   * @name jasmine.addMatchers
-   * @function
-   * @param {Object} matchers - Keys from this object will be the new matcher names.
-   * @see custom_matcher
-   */
-  jasmine.addMatchers = function(matchers) {
-    return env.addMatchers(matchers);
-  };
-
-  /**
-   * Get the currently booted mock {Clock} for this Jasmine environment.
-   * @name jasmine.clock
-   * @function
-   * @returns {Clock}
-   */
-  jasmine.clock = function() {
-    return env.clock;
-  };
-
-  return jasmineInterface;
-};
-
-getJasmineRequireObj().Spy = function (j$) {
-
-  var nextOrder = (function() {
-    var order = 0;
-
-    return function() {
-      return order++;
-    };
-  })();
-
-  /**
-   * _Note:_ Do not construct this directly, use {@link spyOn}, {@link spyOnProperty}, {@link jasmine.createSpy}, or {@link jasmine.createSpyObj}
-   * @constructor
-   * @name Spy
-   */
-  function Spy(name, originalFn) {
-    var numArgs = (typeof originalFn === 'function' ? originalFn.length : 0),
-        wrapper = makeFunc(numArgs, function () {
-          return spy.apply(this, Array.prototype.slice.call(arguments));
-        }),
-        spyStrategy = new j$.SpyStrategy({
-          name: name,
-          fn: originalFn,
-          getSpy: function () {
-            return wrapper;
-          }
-        }),
-        callTracker = new j$.CallTracker(),
-        spy = function () {
-          /**
-           * @name Spy.callData
-           * @property {object} object - `this` context for the invocation.
-           * @property {number} invocationOrder - Order of the invocation.
-           * @property {Array} args - The arguments passed for this invocation.
-           */
-          var callData = {
-            object: this,
-            invocationOrder: nextOrder(),
-            args: Array.prototype.slice.apply(arguments)
-          };
-
-          callTracker.track(callData);
-          var returnValue = spyStrategy.exec.apply(this, arguments);
-          callData.returnValue = returnValue;
-
-          return returnValue;
-        };
-
-    function makeFunc(length, fn) {
-      switch (length) {
-        case 1 : return function (a) { return fn.apply(this, arguments); };
-        case 2 : return function (a,b) { return fn.apply(this, arguments); };
-        case 3 : return function (a,b,c) { return fn.apply(this, arguments); };
-        case 4 : return function (a,b,c,d) { return fn.apply(this, arguments); };
-        case 5 : return function (a,b,c,d,e) { return fn.apply(this, arguments); };
-        case 6 : return function (a,b,c,d,e,f) { return fn.apply(this, arguments); };
-        case 7 : return function (a,b,c,d,e,f,g) { return fn.apply(this, arguments); };
-        case 8 : return function (a,b,c,d,e,f,g,h) { return fn.apply(this, arguments); };
-        case 9 : return function (a,b,c,d,e,f,g,h,i) { return fn.apply(this, arguments); };
-        default : return function () { return fn.apply(this, arguments); };
-      }
-    }
-
-    for (var prop in originalFn) {
-      if (prop === 'and' || prop === 'calls') {
-        throw new Error('Jasmine spies would overwrite the \'and\' and \'calls\' properties on the object being spied upon');
-      }
-
-      wrapper[prop] = originalFn[prop];
-    }
-
-    wrapper.and = spyStrategy;
-    wrapper.calls = callTracker;
-
-    return wrapper;
-  }
-
-  return Spy;
-};
-
-getJasmineRequireObj().SpyRegistry = function(j$) {
-
-  var getErrorMsg = j$.formatErrorMsg('<spyOn>', 'spyOn(<object>, <methodName>)');
-
-  function SpyRegistry(options) {
-    options = options || {};
-    var currentSpies = options.currentSpies || function() { return []; };
-
-    this.allowRespy = function(allow){
-      this.respy = allow;
-    };
-
-    this.spyOn = function(obj, methodName) {
-
-      if (j$.util.isUndefined(obj) || obj === null) {
-        throw new Error(getErrorMsg('could not find an object to spy upon for ' + methodName + '()'));
-      }
-
-      if (j$.util.isUndefined(methodName) || methodName === null) {
-        throw new Error(getErrorMsg('No method name supplied'));
-      }
-
-      if (j$.util.isUndefined(obj[methodName])) {
-        throw new Error(getErrorMsg(methodName + '() method does not exist'));
-      }
-
-      if (obj[methodName] && j$.isSpy(obj[methodName])  ) {
-        if ( !!this.respy ){
-          return obj[methodName];
-        }else {
-          throw new Error(getErrorMsg(methodName + ' has already been spied upon'));
-        }
-      }
-
-      var descriptor;
-      try {
-        descriptor = Object.getOwnPropertyDescriptor(obj, methodName);
-      } catch(e) {
-        // IE 8 doesn't support `definePropery` on non-DOM nodes
-      }
-
-      if (descriptor && !(descriptor.writable || descriptor.set)) {
-        throw new Error(getErrorMsg(methodName + ' is not declared writable or has no setter'));
-      }
-
-      var originalMethod = obj[methodName],
-          spiedMethod = j$.createSpy(methodName, originalMethod),
-          restoreStrategy;
-
-      if (Object.prototype.hasOwnProperty.call(obj, methodName)) {
-        restoreStrategy = function() {
-          obj[methodName] = originalMethod;
-        };
-      } else {
-        restoreStrategy = function() {
-          if (!delete obj[methodName]) {
-            obj[methodName] = originalMethod;
-          }
-        };
-      }
-
-      currentSpies().push({
-        restoreObjectToOriginalState: restoreStrategy
-      });
-
-      obj[methodName] = spiedMethod;
-
-      return spiedMethod;
-    };
-
-    this.spyOnProperty = function (obj, propertyName, accessType) {
-      accessType = accessType || 'get';
-
-      if (j$.util.isUndefined(obj)) {
-        throw new Error('spyOn could not find an object to spy upon for ' + propertyName + '');
-      }
-
-      if (j$.util.isUndefined(propertyName)) {
-        throw new Error('No property name supplied');
-      }
-
-      var descriptor;
-      try {
-        descriptor = j$.util.getPropertyDescriptor(obj, propertyName);
-      } catch(e) {
-        // IE 8 doesn't support `definePropery` on non-DOM nodes
-      }
-
-      if (!descriptor) {
-        throw new Error(propertyName + ' property does not exist');
-      }
-
-      if (!descriptor.configurable) {
-        throw new Error(propertyName + ' is not declared configurable');
-      }
-
-      if(!descriptor[accessType]) {
-        throw new Error('Property ' + propertyName + ' does not have access type ' + accessType);
-      }
-
-      if (j$.isSpy(descriptor[accessType])) {
-        //TODO?: should this return the current spy? Downside: may cause user confusion about spy state
-        throw new Error(propertyName + ' has already been spied upon');
-      }
-
-      var originalDescriptor = j$.util.clone(descriptor),
-          spy = j$.createSpy(propertyName, descriptor[accessType]),
-          restoreStrategy;
-
-      if (Object.prototype.hasOwnProperty.call(obj, propertyName)) {
-        restoreStrategy = function() {
-          Object.defineProperty(obj, propertyName, originalDescriptor);
-        };
-      } else {
-        restoreStrategy = function() {
-          delete obj[propertyName];
-        };
-      }
-
-      currentSpies().push({
-        restoreObjectToOriginalState: restoreStrategy
-      });
-
-      descriptor[accessType] = spy;
-
-      Object.defineProperty(obj, propertyName, descriptor);
-
-      return spy;
-    };
-
-    this.clearSpies = function() {
-      var spies = currentSpies();
-      for (var i = spies.length - 1; i >= 0; i--) {
-        var spyEntry = spies[i];
-        spyEntry.restoreObjectToOriginalState();
-      }
-    };
-  }
-
-  return SpyRegistry;
-};
-
-getJasmineRequireObj().SpyStrategy = function(j$) {
-
-  /**
-   * @namespace Spy#and
-   */
-  function SpyStrategy(options) {
-    options = options || {};
-
-    var identity = options.name || 'unknown',
-        originalFn = options.fn || function() {},
-        getSpy = options.getSpy || function() {},
-        plan = function() {};
-
-    /**
-     * Return the identifying information for the spy.
-     * @name Spy#and#identity
-     * @function
-     * @returns {String}
-     */
-    this.identity = function() {
-      return identity;
-    };
-
-    /**
-     * Execute the current spy strategy.
-     * @name Spy#and#exec
-     * @function
-     */
-    this.exec = function() {
-      return plan.apply(this, arguments);
-    };
-
-    /**
-     * Tell the spy to call through to the real implementation when invoked.
-     * @name Spy#and#callThrough
-     * @function
-     */
-    this.callThrough = function() {
-      plan = originalFn;
-      return getSpy();
-    };
-
-    /**
-     * Tell the spy to return the value when invoked.
-     * @name Spy#and#returnValue
-     * @function
-     * @param {*} value The value to return.
-     */
-    this.returnValue = function(value) {
-      plan = function() {
-        return value;
-      };
-      return getSpy();
-    };
-
-    /**
-     * Tell the spy to return one of the specified values (sequentially) each time the spy is invoked.
-     * @name Spy#and#returnValues
-     * @function
-     * @param {...*} values - Values to be returned on subsequent calls to the spy.
-     */
-    this.returnValues = function() {
-      var values = Array.prototype.slice.call(arguments);
-      plan = function () {
-        return values.shift();
-      };
-      return getSpy();
-    };
-
-    /**
-     * Tell the spy to throw an error when invoked.
-     * @name Spy#and#throwError
-     * @function
-     * @param {Error|String} something Thing to throw
-     */
-    this.throwError = function(something) {
-      var error = (something instanceof Error) ? something : new Error(something);
-      plan = function() {
-        throw error;
-      };
-      return getSpy();
-    };
-
-    /**
-     * Tell the spy to call a fake implementation when invoked.
-     * @name Spy#and#callFake
-     * @function
-     * @param {Function} fn The function to invoke with the passed parameters.
-     */
-    this.callFake = function(fn) {
-      if(!j$.isFunction_(fn)) {
-        throw new Error('Argument passed to callFake should be a function, got ' + fn);
-      }
-      plan = fn;
-      return getSpy();
-    };
-
-    /**
-     * Tell the spy to do nothing when invoked. This is the default.
-     * @name Spy#and#stub
-     * @function
-     */
-    this.stub = function(fn) {
-      plan = function() {};
-      return getSpy();
-    };
-  }
-
-  return SpyStrategy;
-};
-
-getJasmineRequireObj().Suite = function(j$) {
-  function Suite(attrs) {
-    this.env = attrs.env;
-    this.id = attrs.id;
-    this.parentSuite = attrs.parentSuite;
-    this.description = attrs.description;
-    this.expectationFactory = attrs.expectationFactory;
-    this.expectationResultFactory = attrs.expectationResultFactory;
-    this.throwOnExpectationFailure = !!attrs.throwOnExpectationFailure;
-
-    this.beforeFns = [];
-    this.afterFns = [];
-    this.beforeAllFns = [];
-    this.afterAllFns = [];
-
-    this.children = [];
-
-    this.result = {
-      id: this.id,
-      description: this.description,
-      fullName: this.getFullName(),
-      failedExpectations: []
-    };
-  }
-
-  Suite.prototype.expect = function(actual) {
-    return this.expectationFactory(actual, this);
-  };
-
-  Suite.prototype.getFullName = function() {
-    var fullName = [];
-    for (var parentSuite = this; parentSuite; parentSuite = parentSuite.parentSuite) {
-      if (parentSuite.parentSuite) {
-        fullName.unshift(parentSuite.description);
-      }
-    }
-    return fullName.join(' ');
-  };
-
-  Suite.prototype.pend = function() {
-    this.markedPending = true;
-  };
-
-  Suite.prototype.beforeEach = function(fn) {
-    this.beforeFns.unshift(fn);
-  };
-
-  Suite.prototype.beforeAll = function(fn) {
-    this.beforeAllFns.push(fn);
-  };
-
-  Suite.prototype.afterEach = function(fn) {
-    this.afterFns.unshift(fn);
-  };
-
-  Suite.prototype.afterAll = function(fn) {
-    this.afterAllFns.unshift(fn);
-  };
-
-  Suite.prototype.addChild = function(child) {
-    this.children.push(child);
-  };
-
-  Suite.prototype.status = function() {
-    if (this.markedPending) {
-      return 'pending';
-    }
-
-    if (this.result.failedExpectations.length > 0) {
-      return 'failed';
-    } else {
-      return 'finished';
-    }
-  };
-
-  Suite.prototype.isExecutable = function() {
-    return !this.markedPending;
-  };
-
-  Suite.prototype.canBeReentered = function() {
-    return this.beforeAllFns.length === 0 && this.afterAllFns.length === 0;
-  };
-
-  Suite.prototype.getResult = function() {
-    this.result.status = this.status();
-    return this.result;
-  };
-
-  Suite.prototype.sharedUserContext = function() {
-    if (!this.sharedContext) {
-      this.sharedContext = this.parentSuite ? clone(this.parentSuite.sharedUserContext()) : {};
-    }
-
-    return this.sharedContext;
-  };
-
-  Suite.prototype.clonedSharedUserContext = function() {
-    return clone(this.sharedUserContext());
-  };
-
-  Suite.prototype.onException = function() {
-    if (arguments[0] instanceof j$.errors.ExpectationFailed) {
-      return;
-    }
-
-    if(isAfterAll(this.children)) {
-      var data = {
-        matcherName: '',
-        passed: false,
-        expected: '',
-        actual: '',
-        error: arguments[0]
-      };
-      this.result.failedExpectations.push(this.expectationResultFactory(data));
-    } else {
-      for (var i = 0; i < this.children.length; i++) {
-        var child = this.children[i];
-        child.onException.apply(child, arguments);
-      }
-    }
-  };
-
-  Suite.prototype.addExpectationResult = function () {
-    if(isAfterAll(this.children) && isFailure(arguments)){
-      var data = arguments[1];
-      this.result.failedExpectations.push(this.expectationResultFactory(data));
-      if(this.throwOnExpectationFailure) {
-        throw new j$.errors.ExpectationFailed();
-      }
-    } else {
-      for (var i = 0; i < this.children.length; i++) {
-        var child = this.children[i];
-        try {
-          child.addExpectationResult.apply(child, arguments);
-        } catch(e) {
-          // keep going
-        }
-      }
-    }
-  };
-
-  function isAfterAll(children) {
-    return children && children[0].result.status;
-  }
-
-  function isFailure(args) {
-    return !args[0];
-  }
-
-  function clone(obj) {
-    var clonedObj = {};
-    for (var prop in obj) {
-      if (obj.hasOwnProperty(prop)) {
-        clonedObj[prop] = obj[prop];
-      }
-    }
-
-    return clonedObj;
-  }
-
-  return Suite;
-};
-
-if (typeof window == void 0 && typeof exports == 'object') {
-  exports.Suite = jasmineRequire.Suite;
-}
-
-getJasmineRequireObj().Timer = function() {
-  var defaultNow = (function(Date) {
-    return function() { return new Date().getTime(); };
-  })(Date);
-
-  function Timer(options) {
-    options = options || {};
-
-    var now = options.now || defaultNow,
-        startTime;
-
-    this.start = function() {
-      startTime = now();
-    };
-
-    this.elapsed = function() {
-      return now() - startTime;
-    };
-  }
-
-  return Timer;
-};
-
-getJasmineRequireObj().TreeProcessor = function() {
-  function TreeProcessor(attrs) {
-    var tree = attrs.tree,
-        runnableIds = attrs.runnableIds,
-        queueRunnerFactory = attrs.queueRunnerFactory,
-        nodeStart = attrs.nodeStart || function() {},
-        nodeComplete = attrs.nodeComplete || function() {},
-        orderChildren = attrs.orderChildren || function(node) { return node.children; },
-        stats = { valid: true },
-        processed = false,
-        defaultMin = Infinity,
-        defaultMax = 1 - Infinity;
-
-    this.processTree = function() {
-      processNode(tree, false);
-      processed = true;
-      return stats;
-    };
-
-    this.execute = function(done) {
-      if (!processed) {
-        this.processTree();
-      }
-
-      if (!stats.valid) {
-        throw 'invalid order';
-      }
-
-      var childFns = wrapChildren(tree, 0);
-
-      queueRunnerFactory({
-        queueableFns: childFns,
-        userContext: tree.sharedUserContext(),
-        onException: function() {
-          tree.onException.apply(tree, arguments);
-        },
-        onComplete: done
-      });
-    };
-
-    function runnableIndex(id) {
-      for (var i = 0; i < runnableIds.length; i++) {
-        if (runnableIds[i] === id) {
-          return i;
-        }
-      }
-    }
-
-    function processNode(node, parentEnabled) {
-      var executableIndex = runnableIndex(node.id);
-
-      if (executableIndex !== undefined) {
-        parentEnabled = true;
-      }
-
-      parentEnabled = parentEnabled && node.isExecutable();
-
-      if (!node.children) {
-        stats[node.id] = {
-          executable: parentEnabled && node.isExecutable(),
-          segments: [{
-            index: 0,
-            owner: node,
-            nodes: [node],
-            min: startingMin(executableIndex),
-            max: startingMax(executableIndex)
-          }]
-        };
-      } else {
-        var hasExecutableChild = false;
-
-        var orderedChildren = orderChildren(node);
-
-        for (var i = 0; i < orderedChildren.length; i++) {
-          var child = orderedChildren[i];
-
-          processNode(child, parentEnabled);
-
-          if (!stats.valid) {
-            return;
-          }
-
-          var childStats = stats[child.id];
-
-          hasExecutableChild = hasExecutableChild || childStats.executable;
-        }
-
-        stats[node.id] = {
-          executable: hasExecutableChild
-        };
-
-        segmentChildren(node, orderedChildren, stats[node.id], executableIndex);
-
-        if (!node.canBeReentered() && stats[node.id].segments.length > 1) {
-          stats = { valid: false };
-        }
-      }
-    }
-
-    function startingMin(executableIndex) {
-      return executableIndex === undefined ? defaultMin : executableIndex;
-    }
-
-    function startingMax(executableIndex) {
-      return executableIndex === undefined ? defaultMax : executableIndex;
-    }
-
-    function segmentChildren(node, orderedChildren, nodeStats, executableIndex) {
-      var currentSegment = { index: 0, owner: node, nodes: [], min: startingMin(executableIndex), max: startingMax(executableIndex) },
-          result = [currentSegment],
-          lastMax = defaultMax,
-          orderedChildSegments = orderChildSegments(orderedChildren);
-
-      function isSegmentBoundary(minIndex) {
-        return lastMax !== defaultMax && minIndex !== defaultMin && lastMax < minIndex - 1;
-      }
-
-      for (var i = 0; i < orderedChildSegments.length; i++) {
-        var childSegment = orderedChildSegments[i],
-            maxIndex = childSegment.max,
-            minIndex = childSegment.min;
-
-        if (isSegmentBoundary(minIndex)) {
-          currentSegment = {index: result.length, owner: node, nodes: [], min: defaultMin, max: defaultMax};
-          result.push(currentSegment);
-        }
-
-        currentSegment.nodes.push(childSegment);
-        currentSegment.min = Math.min(currentSegment.min, minIndex);
-        currentSegment.max = Math.max(currentSegment.max, maxIndex);
-        lastMax = maxIndex;
-      }
-
-      nodeStats.segments = result;
-    }
-
-    function orderChildSegments(children) {
-      var specifiedOrder = [],
-          unspecifiedOrder = [];
-
-      for (var i = 0; i < children.length; i++) {
-        var child = children[i],
-            segments = stats[child.id].segments;
-
-        for (var j = 0; j < segments.length; j++) {
-          var seg = segments[j];
-
-          if (seg.min === defaultMin) {
-            unspecifiedOrder.push(seg);
-          } else {
-            specifiedOrder.push(seg);
-          }
-        }
-      }
-
-      specifiedOrder.sort(function(a, b) {
-        return a.min - b.min;
-      });
-
-      return specifiedOrder.concat(unspecifiedOrder);
-    }
-
-    function executeNode(node, segmentNumber) {
-      if (node.children) {
-        return {
-          fn: function(done) {
-            nodeStart(node);
-
-            queueRunnerFactory({
-              onComplete: function() {
-                nodeComplete(node, node.getResult());
-                done();
-              },
-              queueableFns: wrapChildren(node, segmentNumber),
-              userContext: node.sharedUserContext(),
-              onException: function() {
-                node.onException.apply(node, arguments);
-              }
-            });
-          }
-        };
-      } else {
-        return {
-          fn: function(done) { node.execute(done, stats[node.id].executable); }
-        };
-      }
-    }
-
-    function wrapChildren(node, segmentNumber) {
-      var result = [],
-          segmentChildren = stats[node.id].segments[segmentNumber].nodes;
-
-      for (var i = 0; i < segmentChildren.length; i++) {
-        result.push(executeNode(segmentChildren[i].owner, segmentChildren[i].index));
-      }
-
-      if (!stats[node.id].executable) {
-        return result;
-      }
-
-      return node.beforeAllFns.concat(result).concat(node.afterAllFns);
-    }
-  }
-
-  return TreeProcessor;
-};
-
-getJasmineRequireObj().version = function() {
-  return '2.6.1';
-};
diff --git a/plugins/easy_mindmup/assets/javascripts/jasmine/main.js b/plugins/easy_mindmup/assets/javascripts/jasmine/main.js
deleted file mode 100644
index db22637..0000000
--- a/plugins/easy_mindmup/assets/javascripts/jasmine/main.js
+++ /dev/null
@@ -1,66 +0,0 @@
-describe("Test framework", function () {
-  it("should load", function () {
-    expect(true).toBe(true);
-    // expect(false).toBe(true);
-  });
-
-  it("should start after mindmup is loaded", function () {
-    expect($("#node_1").length).toBe(1);
-  });
-  it("should handle long tests", function (done) {
-    setTimeout(function () {
-      expect(true).toBe(true);
-      done();
-    }, 100);
-  });
-  var getQueryString = function () {
-    var query_string = {};
-    var query = window.location.search.substring(1);
-    var vars = query.split("&");
-    for (var i = 0; i < vars.length; i++) {
-      var pair = vars[i].split("=");
-      // If first entry with this name
-      if (typeof query_string[pair[0]] === "undefined") {
-        query_string[pair[0]] = decodeURIComponent(pair[1]);
-        // If second entry with this name
-      } else if (typeof query_string[pair[0]] === "string") {
-        query_string[pair[0]] = [query_string[pair[0]], decodeURIComponent(pair[1])];
-        // If third or later entry with this name
-      } else {
-        query_string[pair[0]].push(decodeURIComponent(pair[1]));
-      }
-    }
-    return query_string;
-  };
-  var prefixTestFile = function (file) {
-    if (file.indexOf("/") > -1) return file;
-    return "easy_gantt/" + file;
-  };
-  it("should load extra tests if any", function () {
-    var params = getQueryString();
-    var requestedTests = params["run_jasmine_tests"] || params["run_jasmine_tests[]"] || params["run_jasmine_tests%5B%5D"];
-    if (requestedTests === "true") {
-      requestedTests = [];
-    } else if (typeof(requestedTests) === "string") {
-      requestedTests = [prefixTestFile(requestedTests)];
-    } else if (typeof(requestedTests) === "object" && requestedTests.length !== undefined) {
-      requestedTests = requestedTests.map(function (requestedTest) {
-        return prefixTestFile(requestedTest);
-      })
-    } else {
-      throw "Wrong type of run_jasmine_tests - \""+requestedTests+"\" is not true|string|Array<String>";
-    }
-    var extraTests = jasmine.ysyInstance.tests.extraTestNames;
-    if (requestedTests.length > extraTests.length) {
-      for (var i = 0; i < requestedTests.length; i++) {
-        expect(extraTests).toContain(requestedTests[i], "extraTests missing " + requestedTests[i]);
-      }
-      return;
-    }
-    expect(requestedTests.length).toBe(extraTests.length, "requested tests !== loaded tests");
-    for (i = 0; i < extraTests.length; i++) {
-      expect(requestedTests).toContain(extraTests[i]);
-    }
-
-  });
-});
\ No newline at end of file
diff --git a/plugins/easy_mindmup/assets/javascripts/jasmine/parse_form.js b/plugins/easy_mindmup/assets/javascripts/jasmine/parse_form.js
deleted file mode 100644
index 3693e5f..0000000
--- a/plugins/easy_mindmup/assets/javascripts/jasmine/parse_form.js
+++ /dev/null
@@ -1,87 +0,0 @@
-describe("parseForm", function () {
-  it("should convert FormArray to json", function () {
-    var formData = [{"name": "utf8", "value": "âś“"}, {
-      "name": "_method",
-      "value": "patch"
-    }, {
-      "name": "authenticity_token",
-      "value": "AyUhWxdKyCnB5V5FgkMZecRtDucWOsFRAq+RmxhPjclTrjNy3VngNdHp5tiS+iVqOqp4+7PXyrJNrDAX2rWGmA=="
-    }, {"name": "form_update_triggered_by", "value": ""}, {
-      "name": "issue[subject]",
-      "value": "level 1d"
-    }, {"name": "issue[tracker_id]", "value": "15"}, {
-      "name": "issue[author_id]",
-      "value": "5"
-    }, {"name": "issue[fixed_version_id]", "value": ""}, {
-      "name": "issue[old_fixed_version_id]",
-      "value": ""
-    }, {"name": "issue[parent_issue_id]", "value": ""}, {
-      "name": "issue[parent_issue_id]",
-      "value": ""
-    }, {"name": "issue[start_date]", "value": "2016-12-08"}, {
-      "name": "issue[easy_repeat_settings][simple_period]",
-      "value": ""
-    }, {
-      "name": "issue[easy_repeat_settings][end_date]",
-      "value": ""
-    }, {
-      "name": "issue[easy_repeat_settings][endtype_count_x]",
-      "value": ""
-    }, {"name": "issue[custom_field_values][29][]", "value": "material2"}, {
-      "name": "issue[custom_field_values][29][]",
-      "value": "material4"
-    }, {"name": "issue[custom_field_values][29][]", "value": ""}, {
-      "name": "issue[custom_field_values][83]",
-      "value": ""
-    }, {"name": "issue[status_id]", "value": "1"}, {
-      "name": "issue[done_ratio]",
-      "value": "0"
-    }, {"name": "issue[priority_id]", "value": "9"}, {"name": "issue[due_date]", "value": ""}, {
-      "name": "issue[notes]",
-      "value": ""
-    }, {"name": "version[project_id]", "value": "118"}, {
-      "name": "issue[private_notes]",
-      "value": "0"
-    }, {"name": "issue[private_notes]", "value": "1"}, {
-      "name": "issue[update_repeat_entity_attributes]",
-      "value": "1"
-    }, {"name": "issue[lock_version]", "value": "4"}];
-    var json = {
-      "utf8": "âś“",
-      "_method": "patch",
-      "authenticity_token": "AyUhWxdKyCnB5V5FgkMZecRtDucWOsFRAq+RmxhPjclTrjNy3VngNdHp5tiS+iVqOqp4+7PXyrJNrDAX2rWGmA==",
-      "form_update_triggered_by": "",
-      "issue": {
-        "subject": "level 1d",
-        "tracker_id": "15",
-        "author_id": "5",
-        "fixed_version_id": "",
-        "old_fixed_version_id": "",
-        "parent_issue_id": "",
-        "start_date": "2016-12-08",
-        "easy_repeat_settings": {
-          "simple_period": "",
-          "end_date": "",
-          "endtype_count_x": ""
-        },
-        "custom_field_values": {
-          "29": ["material2", "material4", ""],
-          "83": ""
-        },
-        "status_id": "1",
-        "done_ratio": "0",
-        "priority_id": "9",
-        "due_date": "",
-        "notes": "",
-        "private_notes": "1",
-        "update_repeat_entity_attributes": "1",
-        "lock_version": "4"
-      },
-      "version": {
-        "project_id": "118"
-      }
-    };
-    var result = jasmine.ysyInstance.util.formToJson(formData);
-    expect(result).toEqual(json);
-  });
-});
diff --git a/plugins/easy_mindmup/assets/javascripts/jasmine/saver_linearize.js b/plugins/easy_mindmup/assets/javascripts/jasmine/saver_linearize.js
deleted file mode 100644
index 92c1073..0000000
--- a/plugins/easy_mindmup/assets/javascripts/jasmine/saver_linearize.js
+++ /dev/null
@@ -1,43 +0,0 @@
-describe("Saver linearize", function () {
-  it("should return [] for only project", function () {
-    var ysy = jasmine.ysyInstance;
-    var project = new window.easyMindMupClasses.RootIdea(ysy);
-    project.fromServer(5, "Project A", "project", false, {id: 5});
-    var result = [];
-    ysy.saver.linearizeTree(project, null, null, result, true);
-    expect(result).toEqual([]);
-  });
-  it("should return 1 unsafe pack for 1 issue if unsafe", function () {
-    var ysy = jasmine.ysyInstance;
-    var project = new window.easyMindMupClasses.RootIdea(ysy);
-    project.fromServer(5, "Project A", "project", false, {id: 5});
-    var issue = new window.easyMindMupClasses.ModelEntity(ysy);
-    issue.fromServer(6, "Issue A", "issue", true, {
-      id: 6, project_id: 5,
-      subject: "Issue A"
-    });
-    project.ideas = {5: issue};
-    var result = [];
-    ysy.saver.linearizeTree(project, null, null, result, true);
-    expect(result.length).toEqual(1);
-    var pack = result[0];
-    expect(pack.node).toBe(issue);
-    expect(pack.parent).toBe(project);
-    expect(pack.isSame).toBe(false);
-    expect(pack.isSafe).toBe(false);
-  });
-  it("should return 0 packs for 1 unchanged issue if safe", function () {
-    var ysy = jasmine.ysyInstance;
-    var project = new window.easyMindMupClasses.RootIdea(ysy);
-    project.fromServer(5, "Project A", "project", false, {id: 5});
-    var issue = new window.easyMindMupClasses.ModelEntity(ysy);
-    issue.fromServer(6, "Issue A", "issue", true, {
-      id: 6, project_id: 5,
-      subject: "Issue A"
-    });
-    project.ideas = {5: issue};
-    var result = [];
-    ysy.saver.linearizeTree(project, null, null, result, false);
-    expect(result.length).toEqual(0);
-  });
-});
\ No newline at end of file
diff --git a/plugins/easy_mindmup/assets/javascripts/layout_patch.js b/plugins/easy_mindmup/assets/javascripts/layout_patch.js
index 1f694f0..305a038 100644
--- a/plugins/easy_mindmup/assets/javascripts/layout_patch.js
+++ b/plugins/easy_mindmup/assets/javascripts/layout_patch.js
@@ -16,7 +16,7 @@
       self.preComputeDimensions(contentAggregate, self, ysy);
       return MAPJS.calculateLayout(contentAggregate, function (idea) {
         return self.nodeCacheMarks[idea.id]
-      });
+      }, undefined, self.layoutPredicate);
     };
     jQuery.fn.queueFadeOut = function (options) {
       var element = this;
@@ -27,6 +27,27 @@
       }, options));
     }
   };
+  /**
+   * function which separate nodes into two categories:
+   * LeftTree => positive = false
+   * RightTree => positive = true;
+   * @param {RootIdea} idea
+   * @param {boolean} positive
+   * @return {Function}
+   */
+  LayoutPatch.prototype.layoutPredicate = function (idea, positive) {
+    if (idea.oneSideOn) {
+      return function () {
+        return positive;
+      }
+    }
+    return function (rank, parent) {
+      if (parent === idea) {
+        if (rank < 0 === positive) return false;
+      }
+      return true;
+    };
+  };
   /**
    *
    * @param {RootIdea} superIdea
@@ -51,8 +72,7 @@
     var bigHtml = '<div id="dimension_compute_cont">';
     for (var i = 0; i < nodes.length; i++) {
       var idea = nodes[i];
-      var text = ysy.util.escapeHtml(ysy.nodePatch.getNodeText(idea));
-      bigHtml += '<div id="compute_node_' + idea.id + '" class="mapjs-node" style="visibility: hidden;position: absolute"><span>' + text + '</span></div>'
+      bigHtml += '<div id="compute_node_' + idea.id + '" class="mapjs-node" style="visibility: hidden;position: absolute"><span>' + ysy.nodePatch.getNodeText(idea) + '</span></div>'
     }
     bigHtml += '</div>';
     $(bigHtml).appendTo('body');
diff --git a/plugins/easy_mindmup/assets/javascripts/legend.js b/plugins/easy_mindmup/assets/javascripts/legend.js
index 64018a4..0fa54ab 100644
--- a/plugins/easy_mindmup/assets/javascripts/legend.js
+++ b/plugins/easy_mindmup/assets/javascripts/legend.js
@@ -169,9 +169,7 @@
           <div data-item_id="{{item.id}}" class="mindmup-legend-item-cont">\
             <div class="mindmup-legend-color-box{{banned}}{{scheme}}"></div>\
             {{#avatar}}\
-             <span class="avatar-container">\
               <img width="64" height="64" alt="{{item.name}}" class="gravatar" src="{{{avatarUrl}}}">\
-              </span>\
             {{/avatar}}\
             {{item.name}}\
           </div>';
diff --git a/plugins/easy_mindmup/assets/javascripts/legend_events.js b/plugins/easy_mindmup/assets/javascripts/legend_events.js
index 11ef9c1..dcb9a4e 100644
--- a/plugins/easy_mindmup/assets/javascripts/legend_events.js
+++ b/plugins/easy_mindmup/assets/javascripts/legend_events.js
@@ -35,7 +35,7 @@
     ysy.$menu.find(".mindmup__legend-trigger").on("click", function (e) {
       legend.toggle();
     });
-    ysy.$menu.find(this.legendHeaderTogglerSelector).on("click", function () {
+    ysy.$menu.find(this.legendHeaderTogglerSelector).on("click",function () {
       legend.headerToggle();
     });
     this.$element
@@ -90,9 +90,7 @@
         })
         .off("click." + this.domain)
         .on("click." + this.domain, this.filterSelector, function () {
-          var id = $(this).data("item_id");
-          ysy.filter.toggleAllowed(id);
-          ysy.repainter.redrawMe(ysy.legends);
+          ysy.util.showUpgradeModal("filtering");
         });
     ysy.eventBus.register('resize', $.proxy(legend.resize, legend));
   };
@@ -104,9 +102,7 @@
    */
   LegendEvents.prototype.down = function ($sourceElement, x, y) {
     this.maxDistance = 0;
-    var $stageElement = this.ysy.$container.children("[data-mapjs-role=\"stage\"]");
-    this.stageData = $stageElement.data();
-    this.stageOffset = $stageElement.offset();
+    this.stageOffset = this.ysy.$container.children("[data-mapjs-role=\"stage\"]").offset();
     this.startX = x - window.scrollX;
     this.startY = y - window.scrollY;
     // this.startTime = Date.now();
@@ -167,15 +163,12 @@
     }
     if (this.avatar) {
       this.avatar.moveAvatar(this.actualX, this.actualY);
-      var oldDropTarget = this.currentDropTarget;
+      if (this.currentDropTarget) {
+        this.currentDropTarget.removeClass(this.hoverClass);
+      }
       this.currentDropTarget = this.getCurrentTarget(x, y);
-      if (this.currentDropTarget !== oldDropTarget) {
-        if (oldDropTarget) {
-          oldDropTarget.removeClass(this.hoverClass);
-        }
-        if (this.currentDropTarget) {
-          this.currentDropTarget.addClass(this.hoverClass);
-        }
+      if (this.currentDropTarget) {
+        this.currentDropTarget.addClass(this.hoverClass);
       }
     }
   };
@@ -189,11 +182,9 @@
     this.actionActive = false;
 
     if (this.moveDistance() < this.maxDistanceToClick) {
-      var id = this.$sourceElement.data("item_id");
-      this.ysy.filter.toggleAllowed(id);
-      this.ysy.repainter.redrawMe(this.legend);
+      this.ysy.util.showUpgradeModal("filtering");
     } else {
-      this._drop();
+      this.ysy.util.showUpgradeModal("dnd_property");
     }
     if (this.$overlay) {
       if (this.avatar) {
@@ -230,29 +221,16 @@
     for (var i = 0; i < $possibles.length; i++) {
       var $possible = $possibles[i];
       var data = $possible.data();
-      var transformedX = (x - this.stageOffset.left) / this.stageData.scale;
+      var transformedX = x - this.stageOffset.left;
       if (transformedX < data.x) continue;
       if (transformedX > data.x + data.width) continue;
-      var transformedY = (y - this.stageOffset.top) / this.stageData.scale;
+      var transformedY = y - this.stageOffset.top;
       if (transformedY < data.y) continue;
       if (transformedY > data.y + data.height) continue;
       return $possible;
     }
     return null;
   };
-  /**
-   * run at the end of drag event
-   * @private
-   */
-  LegendEvents.prototype._drop = function () {
-    if (!this.currentDropTarget) return;
-    var ysy = this.ysy;
-    var idea = ysy.mapModel.findIdeaById(this.currentDropTarget.attr("id").split("_")[1]);
-    if (ysy.setData(idea, this.changeObject)) {
-      ysy.mapModel.selectNode(idea.id);
-      ysy.idea.dispatchEvent('changed');
-    }
-  };
   LegendEvents.prototype.moveDistance = function () {
     if (this.actualX == null) {
       return 0;
@@ -265,7 +243,6 @@
   };
 
   window.easyMindMupClasses.LegendEvents = LegendEvents;
-
   //####################################################################################################################
   /**
    *
diff --git a/plugins/easy_mindmup/assets/javascripts/loader.js b/plugins/easy_mindmup/assets/javascripts/loader.js
index 0920166..58c9578 100644
--- a/plugins/easy_mindmup/assets/javascripts/loader.js
+++ b/plugins/easy_mindmup/assets/javascripts/loader.js
@@ -22,10 +22,6 @@
    */
   Loader.prototype.load = function () {
     // var self = this;
-    this.ysy.storage.clear();
-    if (this.ysy.idea) {
-      this.ysy.idea.resetHistory();
-    }
     // var last = this.ysy.storage.lastState.getSavedIdea();
     // if (last) {
     //   var storedDeferred = this.openStoredModal(last);
@@ -63,8 +59,8 @@
     if (this.ysy.idea) {
       return this._updateIdeaByData(this.ysy.idea, data);
     }
+    this.ysy.storage.extra.positionExtract = data["layout"];  // position of all nodes
     var convertedData = this.convertData(data);
-    this.ysy.storage.extra.setLayout(data["layout"]);  // position of all nodes
     var enhancedData = this.ysy.storage.extra.enhanceData(convertedData, convertedData[this._rootId]);
     var links = this.ysy.links.convertRelations(data, enhancedData);
     var rearranged = this.rearrangeData(enhancedData);
@@ -73,6 +69,7 @@
     var initedData = MAPJS.content(rearranged);
     // var diff = ysy.storage.lastState.compareIdea(initedData, 'server');
     // this.prepareLastStateMessages(diff, last, initedData);
+    this.ysy.storage.settings.load(initedData);
     this.ysy.eventBus.fireEvent("IdeaConstructed", initedData);
     this.setIdea(initedData);
   };
@@ -84,12 +81,12 @@
    */
   Loader.prototype._updateIdeaByData = function (idea, data) {
     var convertedData = this.convertData(data);
-    this.ysy.storage.extra.setLayout(data["layout"]);  // position of all nodes
     var enhancedData = this.ysy.storage.extra.enhanceData(convertedData, convertedData[this._rootId]);
     var links = this.ysy.links.convertRelations(data, enhancedData);
     var rearranged = this.rearrangeData(enhancedData, idea);
     var initedData = MAPJS.content(rearranged);
     idea.ideas = initedData.ideas;
+    idea.resetHistory();
     this.ysy.links.attachLinks(idea, links);
     this.ysy.eventBus.fireEvent("IdeaConstructed", idea);
     this.ysy.eventBus.fireEvent("TreeUpdated", idea);
@@ -211,14 +208,12 @@
    * @return {RootIdea}
    */
   Loader.prototype.rearrangeData = function (convertedEntities, oldIdea) {
-    /** @type {Array.<ModelEntity>} rootChildren */
-    var rootChildren = [];
+    var detachedIssues = [];
+    var index = 0;
     /** @type {ModelEntity} entity */
     var entity;
     /** @type {ModelEntity} */
     var parent;
-    /** @type {RootIdea}*/
-    var root = /** @type {RootIdea}*/ convertedEntities[this._rootId];
     var entitiesToProcess;
     if (oldIdea) {
       var idLookup = {};
@@ -240,104 +235,60 @@
       parent = entity.parent;
       if (!parent) continue;
       var parentIdeas = parent.ideas;
+      parent.nChild++;
       if (entity.rank) {
         if (parentIdeas[entity.rank]) {
-          var detachedEntity = parentIdeas[entity.rank];
-          parentIdeas[entity.rank] = entity;
-          while (parent.nextChildIndex === 0 || parentIdeas[parent.nextChildIndex]) {
-            parent.nextChildIndex++;
-          }
-          parentIdeas[parent.nextChildIndex++] = detachedEntity;
-          delete entity.rank;
-          continue;
-        } else {
-          if (parent.nextChildIndex === 0) parent.nextChildIndex = 1;
-          parentIdeas[entity.rank] = entity;
+          detachedIssues.push(parentIdeas[entity.rank])
         }
+        parentIdeas[entity.rank] = entity;
         delete entity.rank;
       } else {
         if (parent.id === this._rootId) {
-          rootChildren.push(entity);
+          index = parent.nChild % 2 === 0 ? -parent.nChild / 2 : parent.nChild / 2 + 0.5;
+          //console.log("index "+index + " nChild "+parent.nChild);
+          if (parentIdeas[index]) {
+            detachedIssues.push(entity);
+          } else {
+            parentIdeas[index] = entity;
+          }
         } else {
-          while (parent.nextChildIndex === 0 || parentIdeas[parent.nextChildIndex]) {
-            parent.nextChildIndex++;
+          if (parentIdeas[parent.nChild]) {
+            detachedIssues.push(entity);
+          } else {
+            parentIdeas[parent.nChild] = entity;
           }
-          parentIdeas[parent.nextChildIndex++] = entity;
+        }
+      }
+      if (parent.id === this._rootId) {
+        var counter = 0;
+        index = 0;
+        while (detachedIssues.length > 0) {
+          counter++;
+          index = index > 0 ? index - counter : index + counter;
+          if (parentIdeas[index]) continue;
+          parentIdeas[index] = detachedIssues.shift();
+        }
+      } else {
+        index = 0;
+        while (detachedIssues.length > 0) {
+          index++;
+          if (parentIdeas[index]) continue;
+          parentIdeas[index] = detachedIssues.shift();
         }
       }
     }
-
-    this._fillRootChildren(root, rootChildren);
-
     for (id = 1; id < convertedEntities.length; id++) {
       entity = convertedEntities[id];
       delete entity.parent;
       if (entity.attr.collapsed === undefined) {
         if (id === this._rootId) continue;
-        if (entity.nextChildIndex) {
+        if (entity.nChild) {
           entity.attr.collapsed = true;
         }
-      }
-      delete entity.nextChildIndex;
-    }
-    return root;
-  };
-  /**
-   *
-   * @param {RootIdea} idea
-   * @param {Array.<ModelEntity>} children
-   * @private
-   */
-  Loader.prototype._fillRootChildren = function (idea, children) {
-    if (children.length === 0){
-      this.ysy.contentPatch.updateOneSide(idea);
-      return;
-    }
-    var parentIdeas = idea.ideas;
-    var ranks = Object.getOwnPropertyNames(idea.ideas).map(function (i) {
-      return parseInt(i);
-    }).sort(function (a, b) {
-      return a - b
-    });
-    var positiveIndex = 1;
-    var nextPositive = function () {
-      while (parentIdeas[positiveIndex]) {
-        positiveIndex++;
-      }
-      return positiveIndex;
-    };
-    if(this.ysy.settings.oneSideOn){
-      var firstPositive = _.findIndex(ranks, function (rank) {
-        return rank > 0;
-      });
-      for (i = 0; i < children.length; i++) {
-        parentIdeas[nextPositive()]=children[i];
-      }
-      for (i = 0; i < firstPositive; i++) {
-        parentIdeas[nextPositive()] = parentIdeas[ranks[i]];
-        delete parentIdeas[ranks[i]];
-      }
-    }
-    var halfCount = Math.ceil((ranks.length + children.length) / 2);
-    var positives = 0;
-    var negativeIndex = -1;
-    for (var i = 0; i < ranks.length; i++) {
-      if (ranks[i] > 0) {
-        positives++;
-      }
-    }
-    for (i = 0; i < children.length; i++) {
-      if (halfCount >= positives) {
-        parentIdeas[nextPositive()] = children[i];
-        positives++;
-      } else {
-        while (parentIdeas[negativeIndex]) {
-          negativeIndex--;
-        }
-        parentIdeas[negativeIndex] = children[i];
-        negativeIndex--;
+        delete entity.nChild;
       }
     }
+    return /** @type {RootIdea}*/ convertedEntities[this._rootId];
   };
   /**
    *
@@ -347,23 +298,22 @@
    */
   Loader.prototype.rearrangeByIdea = function (oldIdea, newIdea, idLookup) {
     if (!_.isEmpty(oldIdea.ideas)) {
-      var oldRanks = Object.getOwnPropertyNames(oldIdea.ideas);
+      var ranks = Object.getOwnPropertyNames(oldIdea.ideas);
       var oldParentId = this.ysy.getData(oldIdea).id;
-      for (var i = 0; i < oldRanks.length; i++) {
-        var rank = oldRanks[i];
+      for (var i = 0; i < ranks.length; i++) {
+        var rank = ranks[i];
         var oldChild = oldIdea.ideas[rank];
         var oldChildId = this.ysy.getData(oldChild).id;
         if (!oldChildId) continue;
         var newChild = idLookup[oldChildId];
         if (!newChild) continue;
         var newParentId = this.ysy.getData(newChild.parent).id;
-        if (oldParentId !== newParentId) continue;
+        if(oldParentId !== newParentId) continue;
         if (!newIdea.ideas) {
           newIdea.ideas = {};
         }
         newIdea.ideas[rank] = newChild;
-        if (!newIdea.nextChildIndex) newIdea.nextChildIndex++;
-        newIdea.nextChildIndex++;
+        newIdea.nChild++;
         this.rearrangeByIdea(oldChild, newChild, idLookup);
       }
     }
diff --git a/plugins/easy_mindmup/assets/javascripts/main.js b/plugins/easy_mindmup/assets/javascripts/main.js
index ddcdeef..db481e1 100644
--- a/plugins/easy_mindmup/assets/javascripts/main.js
+++ b/plugins/easy_mindmup/assets/javascripts/main.js
@@ -30,8 +30,6 @@
     this.log = new easyMindMupClasses.Logger(this);
     /** @type {Repainter} */
     this.repainter = new easyMindMupClasses.Repainter(this);
-    /** @type {JasmineTests} */
-    this.tests = easyMindMupClasses.JasmineTests && new easyMindMupClasses.JasmineTests(this);
   };
   MindMup.prototype.init = function () {
     if (!this.helperInited) {
diff --git a/plugins/easy_mindmup/assets/javascripts/map_model_patch.js b/plugins/easy_mindmup/assets/javascripts/map_model_patch.js
index c36cb5b..8f7a497 100644
--- a/plugins/easy_mindmup/assets/javascripts/map_model_patch.js
+++ b/plugins/easy_mindmup/assets/javascripts/map_model_patch.js
@@ -56,10 +56,10 @@
       };
       mapModel.toggleOneSide = function (source) {
         var idea = ysy.idea;
-        var targetState = !self.ysy.settings.oneSideOn;
-        self.ysy.settings.oneSideOn = targetState;
-        idea.updateOneSide(idea);
-        self.ysy.eventBus.fireEvent("saveOneSideOn", targetState);
+        idea.oneSideOn = !idea.oneSideOn;
+        //idea.toggleOneSide();
+        idea.dispatchEvent("changed");
+        mapModel.dispatchEvent("saveSettings", idea);
       };
       /** prevent scroll jumping after deselecting node while editing */
       mapModel.addEventListener('inputEnabledChanged', function (canInput, holdFocus) {
diff --git a/plugins/easy_mindmup/assets/javascripts/mapjs_init.js b/plugins/easy_mindmup/assets/javascripts/mapjs_init.js
index 3984ac6..e95a24f 100644
--- a/plugins/easy_mindmup/assets/javascripts/mapjs_init.js
+++ b/plugins/easy_mindmup/assets/javascripts/mapjs_init.js
@@ -13,11 +13,11 @@
    * @param {MindMup} ysy
    */
   MMInitiator.prototype.init = function (ysy) {
-    // var imageInsertController = new MAPJS.ImageInsertController("http://localhost:4999?u=");
+    var imageInsertController = new MAPJS.ImageInsertController("http://localhost:4999?u=");
     var mapModel = new MAPJS.MapModel(ysy.layoutPatch.layoutCalculator, []);
-    jQuery(ysy.$container).domMapWidget(console, mapModel, false, null, undefined, ysy);
+    jQuery(ysy.$container).domMapWidget(console, mapModel, false, imageInsertController, undefined, ysy);
     jQuery(ysy.$menu).mapToolbarWidget(mapModel, ysy);
-    MAPJS.DOMRender.stageMargin = {top: 300, left: 300, bottom: 300, right: 300};
+    MAPJS.DOMRender.stageMargin = {top: 50, left: 50, bottom: 50, right: 50};
     MAPJS.DOMRender.linkConnectorPath = ysy.links.outerPath;
     MAPJS.DOMRender.nodeConnectorPath = ysy.domPatch.curvedPath;
     ysy.mapModel = mapModel;
@@ -31,3 +31,62 @@
 
   window.easyMindMupClasses.MMInitiator = MMInitiator;
 })();
+
+
+MAPJS.initAll = function (container) {
+  //jQuery.fn.attachmentEditorWidget = function (mapModel) {
+  //  'use strict';
+  //  return this.each(function () {
+  //    var element = jQuery(this);
+  //    mapModel.addEventListener('attachmentOpened', function (nodeId, attachment) {
+  //      mapModel.setAttachment(
+  //          'attachmentEditorWidget',
+  //          nodeId, {
+  //            contentType: 'text/html',
+  //            content: prompt('attachment', attachment && attachment.content)
+  //          }
+  //      );
+  //    });
+  //  });
+  //};
+
+
+  window.onerror = ysy.log.error;
+  // var container = jQuery('#container'),
+  //idea = MAPJS.content(test_tree()),
+  var imageInsertController = new MAPJS.ImageInsertController("http://localhost:4999?u="),
+      mapModel = new MAPJS.MapModel(MAPJS.DOMRender.layoutCalculator, []);
+  jQuery(container).domMapWidget(console, mapModel, false, imageInsertController, undefined, ysy);
+  jQuery('#wbs_menu').mapToolbarWidget(mapModel);
+  //jQuery('body').attachmentEditorWidget(mapModel);
+  //$("[data-mm-action='export-image']").click(function () {
+  //  MAPJS.pngExport(idea).then(function (url) {
+  //    window.open(url, '_blank');
+  //  });
+  //});
+  //mapModel.setIdea(idea);  // < HOSEK
+  MAPJS.DOMRender.stageMargin = {top: 50, left: 50, bottom: 50, right: 50};
+  MAPJS.DOMRender.linkConnectorPath = MAPJS.DOMRender.outerPath;
+  ysy.mapjs.mapModel = mapModel;
+  //jQuery('.arrow').click(function () {
+  //  jQuery(this).toggleClass('active');
+  //});
+  imageInsertController.addEventListener('imageInsertError', function (reason) {
+    ysy.log.error('image insert error', reason);
+  });
+  //container.on('drop', function (e) {
+  //  var dataTransfer = e.originalEvent.dataTransfer;
+  //  e.stopPropagation();
+  //  e.preventDefault();
+  //  if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
+  //    var fileInfo = dataTransfer.files[0];
+  //    if (/\.mup$/.test(fileInfo.name)) {
+  //      var oFReader = new FileReader();
+  //      oFReader.onload = function (oFREvent) {
+  //        mapModel.setIdea(MAPJS.content(JSON.parse(oFREvent.target.result)));
+  //      };
+  //      oFReader.readAsText(fileInfo, 'UTF-8');
+  //    }
+  //  }
+  //});
+};
diff --git a/plugins/easy_mindmup/assets/javascripts/mindmup/dom-map-widget.js b/plugins/easy_mindmup/assets/javascripts/mindmup/dom-map-widget.js
index 5f19274..be6b7dd 100644
--- a/plugins/easy_mindmup/assets/javascripts/mindmup/dom-map-widget.js
+++ b/plugins/easy_mindmup/assets/javascripts/mindmup/dom-map-widget.js
@@ -106,7 +106,7 @@ $.fn.domMapWidget = function (activityLog, mapModel, touchEnabled, imageInsertCo
           element.css('overflow', 'auto');
         }
       });
-      //element.imageDropWidget(imageInsertController);
+      element.imageDropWidget(imageInsertController);
     } else {
       element.on('doubletap', function (event) {
         if (mapModel.requestContextMenu(event.gesture.center.pageX, event.gesture.center.pageY)) {
diff --git a/plugins/easy_mindmup/assets/javascripts/mindmup/layout.js b/plugins/easy_mindmup/assets/javascripts/mindmup/layout.js
index eca20aa..080f4f9 100644
--- a/plugins/easy_mindmup/assets/javascripts/mindmup/layout.js
+++ b/plugins/easy_mindmup/assets/javascripts/mindmup/layout.js
@@ -266,7 +266,7 @@ MAPJS.calculateTree = function (content, dimensionProvider, margin, rankAndParen
       includedSubIdeaKeys = function () {
         var allRanks = _.map(_.keys(content.ideas), parseFloat),
             includedRanks = rankAndParentPredicate ? _.filter(allRanks, function (rank) {
-              return rankAndParentPredicate(rank, content.id);
+              return rankAndParentPredicate(rank, content);
             }) : allRanks;
         return _.sortBy(includedRanks, Math.abs);
       },
@@ -320,21 +320,17 @@ MAPJS.calculateTree = function (content, dimensionProvider, margin, rankAndParen
   return new MAPJS.Tree(options);
 };
 
-MAPJS.calculateLayout = function (idea, dimensionProvider, margin) {
+MAPJS.calculateLayout = function (idea, dimensionProvider, margin,layoutPredicate) {
   'use strict';
-  var positiveTree, negativeTree, layout, negativeLayout,
+  var positiveTree, negativeTree, layout, negativeLayout, positive, negative,
       setDefaultStyles = function (nodes) {
         _.each(nodes, function (node) {
           node.attr = node.attr || {};
           node.attr.style = _.extend({}, MAPJS.defaultStyles[(node.level === 1) ? 'root' : 'nonRoot'], node.attr.style);
         });
-      },
-      positive = function (rank, parentId) {
-        return parentId !== idea.id || rank > 0;
-      },
-      negative = function (rank, parentId) {
-        return parentId !== idea.id || rank < 0;
       };
+  positive = layoutPredicate(idea, true);
+  negative = layoutPredicate(idea, false);
   margin = margin || 20;
   positiveTree = MAPJS.calculateTree(idea, dimensionProvider, margin, positive);
   negativeTree = MAPJS.calculateTree(idea, dimensionProvider, margin, negative);
diff --git a/plugins/easy_mindmup/assets/javascripts/mm_context_menu.js b/plugins/easy_mindmup/assets/javascripts/mm_context_menu.js
index 66d1a8c..5b0c73d 100644
--- a/plugins/easy_mindmup/assets/javascripts/mm_context_menu.js
+++ b/plugins/easy_mindmup/assets/javascripts/mm_context_menu.js
@@ -26,7 +26,7 @@
     if ($element.length === 0) {
       $element =
           $('<div id="context-menu" class="mindmup__context_menu ' + (this.ysy.settings.easyRedmine ? "easy" : "redmine") + '"></div>')
-          .appendTo('#content').hide();
+          .appendTo('body').hide();
     }
     this.$element = $element;
     var self = this;
@@ -108,52 +108,14 @@
    */
   ContextMenu.prototype.bindEvents = function (primaryNode, $element) {
     var ysy = this.ysy;
-    var self = this;
     var mapModel = ysy.mapModel;
     $element.mapToolbarWidget(mapModel, ysy);
     $element.find(".mindmup-data-value-changer:not(.disabled)").on('tap click', function () {
-      var $this = $(this);
-      var obj = {};
-      obj[$this.data("key")] = $this.data("value");
-      var activatedIds = ysy.mapModel.getActivatedNodeIds();
-      for (var i = 0; i < activatedIds.length; i++) {
-        var node = ysy.idea;
-        if (activatedIds[i] !== 1) {
-          node = node.findSubIdeaById(activatedIds[i]);
-        }
-        if (node.attr.entityType !== primaryNode.attr.entityType) continue;
-        ysy.setData(node, obj);
-      }
-      //ysy.mapModel.setData(primaryNode, obj);
-      ysy.idea.dispatchEvent('changed');
+      ysy.util.showUpgradeModal("context_menu");
     });
     $element.find(".mindmup-data-value-input-link:not(.disabled)").on('tap click', function () {
-      var $this = $(this);
-      $this.hide();
-      var $input = $this.parent().find(".mindmup-data-value-input");
-      if ($input.length === 0) return;
-      $input.show().focus();
-      $input.on("change", function () {
-        var obj = {};
-        var newValue = $input.val();
-        if ($input.attr("type") === "number") {
-          newValue = parseFloat(newValue);
-        }
-        obj[$input.data("key")] = newValue;
-        var activatedIds = ysy.mapModel.getActivatedNodeIds();
-        for (var i = 0; i < activatedIds.length; i++) {
-          var node = ysy.mapModel.findIdeaById(activatedIds[i]);
-          if (node.attr.entityType !== primaryNode.attr.entityType) continue;
-          ysy.setData(node, obj);
-        }
-        self.innerHide();
-        ysy.idea.dispatchEvent('changed');
-
-      });
+      ysy.util.showUpgradeModal("context_menu");
       return false;
-      // var obj = {};
-      // obj[$this.data("key")] = $this.data("value");
-      //ysy.mapModel.setData(primaryNode, obj);
     });
   };
   ContextMenu.prototype.window_size = function () {
diff --git a/plugins/easy_mindmup/assets/javascripts/model_classes.js b/plugins/easy_mindmup/assets/javascripts/model_classes.js
index 25525d7..6f6f86a 100644
--- a/plugins/easy_mindmup/assets/javascripts/model_classes.js
+++ b/plugins/easy_mindmup/assets/javascripts/model_classes.js
@@ -97,7 +97,7 @@
    * Universal entity from which model tree is generated
    * @property {number} id
    * @property {String} title
-   * @property {number} nextChildIndex
+   * @property {number} nChild
    * @property {Object.<String, ModelEntity>} ideas
    * @property {ModelEntityAttr} attr
    * @property {ModelEntity} parent - temporary = used only for generating of model tree and saving
@@ -108,7 +108,7 @@
   function ModelEntity() {
     this.id = 0;
     this.title = "No title";
-    this.nextChildIndex = 0;
+    this.nChild = 0;
     this.ideas = {};
     this.attr = new ModelEntityAttr();
     this.parent = null;
@@ -206,8 +206,6 @@
   };
   RootIdea.prototype.resetHistory = function () {
   };
-  RootIdea.prototype.updateOneSide = function () {
-  };
 
 
   window.easyMindMupClasses.RootIdea = RootIdea;
diff --git a/plugins/easy_mindmup/assets/javascripts/node_patch.js b/plugins/easy_mindmup/assets/javascripts/node_patch.js
index a2b0aa3..eb5fcb0 100644
--- a/plugins/easy_mindmup/assets/javascripts/node_patch.js
+++ b/plugins/easy_mindmup/assets/javascripts/node_patch.js
@@ -46,7 +46,7 @@
       if (assigneeIndex > -1) {
         var user = users[assigneeIndex];
         var avatarUrl = user.avatar_url ? user.avatar_url : "/plugin_assets/easy_extensions/images/avatar.jpg";
-        return '<span class="avatar-container"><img width="64" height="64" alt="' + user.name + '" class="gravatar" src="' + avatarUrl + '"></span>';
+        return '<img width="64" height="64" alt="' + user.name + '" class="gravatar" src="' + avatarUrl + '">';
       }
     });
   };
@@ -70,7 +70,8 @@
   var getNodeText = function (nodeContent) {
     var MAX_URL_LENGTH = 25;
     var title = nodeContent.title;
-    var text = title.length < MAX_URL_LENGTH ? title : (title.substring(0, MAX_URL_LENGTH) + '...');
+    var text = MAPJS.URLHelper.stripLink(title) ||
+        (title.length < MAX_URL_LENGTH ? title : (title.substring(0, MAX_URL_LENGTH) + '...'));
     return text.trim();
   };
   NodePatch.prototype.getNodeText = getNodeText;
diff --git a/plugins/easy_mindmup/assets/javascripts/print.js b/plugins/easy_mindmup/assets/javascripts/print.js
index 4357270..54127c0 100644
--- a/plugins/easy_mindmup/assets/javascripts/print.js
+++ b/plugins/easy_mindmup/assets/javascripts/print.js
@@ -30,14 +30,13 @@
     window.print();
     this.afterPrint();
   };
-  Print.prototype.beforePrint = function () {
+  Print.prototype.beforePrint = function (isCompact) {
     if (this.printReady) return;
     this.ysy.mapModel.resetView();
-    this.$area = this.createPrintArea();
+    this.$area = isCompact? this.createCompactArea():this.createPrintArea();
     $("body").append(this.$area);
     $("#wrapper").hide();
     this.printReady = true;
-    return this.$area;
   };
   Print.prototype.afterPrint = function () {
     if (!this.printReady) return;
@@ -53,9 +52,11 @@
     var stripWidth = 330;
     var children = $stage.children(":not(:hidden)");
     var dims = this.getStageDims(children);
-    var $area = $('<div id="mindmup__print-area" class="mindmup__print-area mindmup__print-area--stripped scheme-by-' + this.ysy.styles.setting + '"></div>');
+    var $area = $('<div class="mindmup-print-area scheme-by-' + this.ysy.styles.setting + '" \
+        style="height:' + (dims.bottom - dims.top + this.margins.top + this.margins.bottom) + 'px"></div>');
     for (var p = dims.left - this.margins.left; p < dims.right + this.margins.right; p += stripWidth) {
       $area.append(this.createStrip(children, dims, p, p + stripWidth));
+      //p -= 2;
     }
     return $area;
   };
@@ -63,25 +64,26 @@
     /* start can be negative*/
     if (end <= start) return null;
     // var stageOffset = $stage.height();
-    var $strip = $('<div class="mindmup__print-strip" style="height:' + (dims.bottom - dims.top + this.margins.top + this.margins.bottom) + 'px;width:' + (end - start) + 'px"></div>');
+    var $strip = $('<div class="mindmup-print-strip" style="height:' + (dims.bottom - dims.top + this.margins.top + this.margins.bottom) + 'px;width:' + (end - start) + 'px"></div>');
     // var children = $stage.children(":not(:hidden)");
     var added = 0;
-    var topEdge = dims.top - this.margins.top;
     for (var i = 0; i < children.length; i++) {
       var child = children[i];
       var left = parseInt(child.style.left);
       var width = child.offsetWidth;
-      if (left > end + 5) continue;
-      if (left + width < start - 5) continue;
+      var top = parseInt(child.style.top) - dims.top + this.margins.top;
+      var height = child.offsetHeight;
+      if (left > end) continue;
+      if (left + width < start) continue;
       added++;
+      var $child = $(child);
+      var transform = $child.css("transform");
+      if (transform === "none") transform = "";
+      transform = "translate(" + (left - start) + "px," + top + "px) " + transform;
       $strip.append(
-          $(child)
+          $child
               .clone()
-              .css({
-                left: left - start,
-                top: parseInt(child.style.top) - topEdge,
-                width: width
-              })
+              .css({left: 0, top: 0, transform: transform})
       );
     }
     if (!added) return null;
@@ -107,6 +109,32 @@
     }
     return dims;
   };
+  Print.prototype.createCompactArea = function () {
+    var $stage = this.ysy.$container.children();
+    var children = $stage.children(":not(:hidden)");
+    var dims = this.getStageDims(children);
+    var leftEdge = dims.left - this.margins.left;
+    var $area = $('<div class="mindmup-pdf-print-area scheme-by-' + this.ysy.styles.setting + '" style="'
+        + 'height:' + (dims.bottom - dims.top + this.margins.top + this.margins.bottom) + 'px;'
+        + 'width:' + (dims.right - leftEdge + this.margins.right) + 'px"></div>');
+    for (var i = 0; i < children.length; i++) {
+      var child = children[i];
+      var left = parseInt(child.style.left);
+      var width = child.offsetWidth;
+      var top = parseInt(child.style.top) - dims.top + this.margins.top;
+      var height = child.offsetHeight;
+      var $child = $(child);
+      var transform = $child.css("transform");
+      if (transform === "none") transform = "";
+      transform = "translate(" + (left - leftEdge) + "px," + top + "px) " + transform;
+      $area.append(
+          $child
+              .clone()
+              .css({left: 0, top: 0, transform: transform})
+      );
+    }
+    return $area;
+  };
 
   window.easyMindMupClasses.Print = Print;
   //####################################################################################################################
diff --git a/plugins/easy_mindmup/assets/javascripts/redrawer.js b/plugins/easy_mindmup/assets/javascripts/redrawer.js
index 0fcd9a3..b9b4919 100644
--- a/plugins/easy_mindmup/assets/javascripts/redrawer.js
+++ b/plugins/easy_mindmup/assets/javascripts/redrawer.js
@@ -8,6 +8,14 @@
     this.ysy = ysy;
     this.onRepaint = [];
     var self = this;
+    var requestAnimationFrame = (function () {
+      return window.requestAnimationFrame ||
+          window.webkitRequestAnimationFrame ||
+          window.mozRequestAnimationFrame ||
+          function (callback) {
+            window.setTimeout(callback, 1000 / 60);
+          };
+    })();
     var animationLoop = function () {
       var queue = self.onRepaint;
       if (queue.length > 0) {
diff --git a/plugins/easy_mindmup/assets/javascripts/saver.js b/plugins/easy_mindmup/assets/javascripts/saver.js
index 81222a7..4309313 100644
--- a/plugins/easy_mindmup/assets/javascripts/saver.js
+++ b/plugins/easy_mindmup/assets/javascripts/saver.js
@@ -238,7 +238,7 @@
   Saver.prototype.saveLayout = function () {
     if (!this.layoutKey) throw "Missing layoutKey";
     var self = this;
-    var layout = this.ysy.storage.extra.getLayout();
+    var layout = this.ysy.storage.extra.positionExtract;
     var requestData = {easy_setting: {}};
     requestData.easy_setting[this.layoutKey] = layout;
     var xhr = $.ajax({
diff --git a/plugins/easy_mindmup/assets/javascripts/storage.js b/plugins/easy_mindmup/assets/javascripts/storage.js
index 4162465..cf859b9 100644
--- a/plugins/easy_mindmup/assets/javascripts/storage.js
+++ b/plugins/easy_mindmup/assets/javascripts/storage.js
@@ -4,7 +4,6 @@
    *
    * @param {MindMup} ysy
    * @property {StorageExtra} extra
-   * @property {StorageLastState} lastState
    * @property {StorageSettings} settings
    * @constructor
    */
@@ -12,31 +11,18 @@
     this._scope = ysy.id + "-";
     this.ysy = ysy;
     this.extra = new StorageExtra(this);
-    var lastState = new StorageLastState(this);
-    var settings = new StorageSettings(this);
-    this.lastState = lastState;
-    this.settings = settings;
+    this.settings = new StorageSettings(this);
     var self = this;
     //if (this._scope == null) throw "_scope is not defined! Scope is used for separation of xBS products in localStorage";
 
     ysy.eventBus.register("TreeLoaded", function (idea) {
       idea.addEventListener('changed', function () {
-        lastState.remove();
         self.save(idea);
       });
     });
 
   }
 
-  Storage.prototype.getSessionData = function (key) {
-    return window.sessionStorage.getItem(this._scope + key);
-  };
-  Storage.prototype.saveSessionData = function (key, value) {
-    window.sessionStorage.setItem(this._scope + key, value);
-  };
-  Storage.prototype.resetSessionData = function (key) {
-    window.sessionStorage.removeItem(this._scope + key);
-  };
   Storage.prototype.getPersistentData = function (key) {
     return window.localStorage.getItem(this._scope + key);
   };
@@ -48,10 +34,8 @@
   };
   Storage.prototype.save = function (idea) {
     this.extra.save(idea);
-    this.lastState.save(idea);
   };
   Storage.prototype.clear = function () {
-    this.lastState.remove();
     this.extra.positionExtract = null;
   };
   window.easyMindMupClasses = window.easyMindMupClasses || {};
@@ -83,7 +67,6 @@
    * @param {RootIdea} idea
    */
   StorageExtra.prototype.save = function (idea) {
-    if (!idea) return;
     var extract = this._extractFromNode(idea);
     this.positionExtract = extract.positions;
     this.collapseExtract = extract.collapses;
@@ -142,79 +125,6 @@
     }
     return data;
   };
-  /**
-   * @param {{code?:String}} layout
-   */
-  StorageExtra.prototype.setLayout = function (layout) {
-    this.positionExtract = this._decodeLayout(layout);
-  };
-  /**
-   * @return {{code?: String}}
-   */
-  StorageExtra.prototype.getLayout = function () {
-    return this._encodeLayout(this.positionExtract);
-  };
-  /**
-   * encode layout if it contains more than 50 keys.
-   * result is capped if it reaches 60000 characters
-   * @param {{}} decodedLayout
-   * @return {{code?:String}|null}
-   */
-  StorageExtra.prototype._encodeLayout = function (decodedLayout) {
-    if(!decodedLayout) return null;
-    var keys = Object.getOwnPropertyNames(decodedLayout);
-    if (keys.length <= 50) return decodedLayout;
-    var i, key, pos, result = "";
-    if (keys.length > 2000) {
-      for (i = 0; i < keys.length; i++) {
-        key = keys[i];
-        pos = decodedLayout[key].position ? ("[" + decodedLayout[key].position + "]") : "";
-        result += key + "{" + (decodedLayout[key].rank || "") + pos + "}";
-        if (result.length > 60000) return {code: result};
-      }
-    } else {
-      for (i = 0; i < keys.length; i++) {
-        key = keys[i];
-        pos = decodedLayout[key].position ? ("[" + decodedLayout[key].position + "]") : "";
-        result += key + "{" + (decodedLayout[key].rank || "") + pos + "}";
-      }
-
-    }
-    return {code: result};
-  };
-  /**
-   * decode layout if contains "code" key
-   * @param {{code?:String}} encodedLayout
-   * @return {{}|null}
-   */
-  StorageExtra.prototype._decodeLayout = function (encodedLayout) {
-    if (!encodedLayout) return null;
-    if (!encodedLayout.code) return encodedLayout;
-    var issueStrings = encodedLayout.code.split("}");
-    var result = {};
-    for (var i = 0; i < issueStrings.length; i++) {
-      var issueString = issueStrings[i];
-      var p = issueString.indexOf("{");
-      if (p === -1) continue;
-      var issueResult = {};
-      var key = issueString.substring(0, p);
-      result[key] = issueResult;
-      var k = issueString.indexOf("[");
-      var issueRank;
-      if (k > -1) {
-        var posString = issueString.substring(k);
-        issueResult.position = JSON.parse(posString);
-        issueRank = issueString.substring(p + 1, k);
-      } else {
-        issueRank = issueString.substring(p + 1);
-      }
-      if (issueRank) {
-        issueResult.rank = parseInt(issueRank);
-      }
-    }
-    return result;
-  };
-
   /**
    *
    * @param {ModelEntity} node
@@ -249,177 +159,6 @@
     return extract;
   };
 //#######################################################################################
-  /**
-   *
-   * @param {Storage} storage
-   * @constructor
-   */
-  function StorageLastState(storage) {
-    this.storage = storage;
-    this._binded = false;
-    this.ysy = storage.ysy;
-  }
-
-  StorageLastState.prototype._dataKey = "last-data";
-  StorageLastState.prototype._idKey = "last-id";
-  StorageLastState.prototype.save = function (idea) {
-    // this.storage.savePersistentData(this._dataKey, JSON.stringify(idea));
-    // this.storage.savePersistentData(this._idKey, this.ysy.settings.rootID);
-    // if (this._binded)return;
-    // this._binded = true;
-    // var storage = this.storage;
-    // $(window).unbind('beforeunload').bind('beforeunload', function (e) {
-    //   var message = "Some changes are not saved!";
-    //   e.returnValue = message;
-    //   return message;
-    // }).unbind('unload').bind('unload', function () {
-    //   storage.lastState.remove();
-    // });
-  };
-  /**
-   *
-   * @return {RootIdea}
-   */
-  StorageLastState.prototype.getSavedIdea = function () {
-    var oldId = this.storage.getPersistentData(this._idKey);
-    if (oldId && parseInt(oldId) !== this.ysy.settings.rootID) {
-      this.remove();
-    }
-    var ideaJson = this.storage.getPersistentData(this._dataKey);
-    if (!ideaJson) return null;
-
-    var idea = new window.easyMindMupClasses.RootIdea(this.ysy).fromJson(JSON.parse(ideaJson));
-    return MAPJS.content(idea);
-  };
-  StorageLastState.prototype.remove = function () {
-    this.storage.resetPersistentData(this._dataKey);
-    this.storage.resetPersistentData(this._idKey);
-    if (this._binded) {
-      $(window).unbind('beforeunload');
-      $(window).unbind('unload');
-      this._binded = false;
-    }
-  };
-  StorageLastState.prototype.compareIdea = function (idea, diffType, savedIdea) {
-    if (!savedIdea) {
-      savedIdea = this.getSavedIdea();
-      if (savedIdea === null) {
-        return null;
-      }
-    }
-    return recursiveDiff(savedIdea, idea, {
-      softParams: diffType === 'all' || diffType === 'soft',
-      data: diffType === 'all' || diffType === 'data' || diffType === 'server',
-      structure: diffType === 'all' || diffType === 'structure' || diffType === 'server'
-    });
-  };
-  var recursiveDiff = function (oldIdea, newIdea, diffTypes) {
-    var isDifferent = false;
-    var oldDiff = {};
-    var newDiff = {};
-    var keys = ["title"];
-    for (var i = 0; i < keys.length; i++) {
-      var key = keys[i];
-      if (oldIdea[key] !== newIdea[key]) {
-        oldDiff[key] = oldIdea[key];
-        newDiff[key] = newIdea[key];
-        isDifferent = true;
-      }
-    }
-    if (diffTypes.data) {
-      var oldData = this.ysy.getData(oldIdea);
-      var newData = this.ysy.getData(newIdea);
-      keys = _.union(Object.getOwnPropertyNames(oldData), Object.getOwnPropertyNames(newData));
-      for (i = 0; i < keys.length; i++) {
-        key = keys[i];
-        if (typeof oldData[key] === "object" || typeof newData[key] === "object") {
-          continue;
-        }
-        if (oldData[key] !== newData[key]) {
-          if (oldDiff.attr === undefined) {
-            oldDiff.attr = {data: {}};
-            newDiff.attr = {data: {}};
-          }
-          oldDiff.attr.data[key] = oldData[key];
-          newDiff.attr.data[key] = newData[key];
-          isDifferent = true;
-        }
-      }
-    }
-    if (diffTypes.softParams) {
-      keys = ["collapsed", "position"];
-      for (i = 0; i < keys.length; i++) {
-        key = keys[i];
-        if (oldIdea.attr[key] !== newIdea.attr[key]) {
-          if (oldDiff.attr === undefined) {
-            oldDiff.attr = {};
-            newDiff.attr = {};
-          }
-          oldDiff.attr[key] = oldIdea.attr[key];
-          newDiff.attr[key] = newIdea.attr[key];
-          isDifferent = true;
-        }
-      }
-    }
-    if (_.isEmpty(oldIdea.ideas) && !_.isEmpty(newIdea.ideas)) {
-      newDiff.ideas = newIdea.ideas;
-      isDifferent = true;
-    } else if (_.isEmpty(newIdea.ideas) && !_.isEmpty(oldIdea.ideas)) {
-      oldDiff.ideas = oldIdea.ideas;
-      isDifferent = true;
-    } else {
-      var oldIdeas = $.extend({}, oldIdea.ideas);
-      var newIdeas = $.extend({}, newIdea.ideas);
-      var oldKeys = Object.getOwnPropertyNames(oldIdeas);
-      var newKeys = Object.getOwnPropertyNames(newIdeas);
-      for (var j = 0; j < oldKeys.length; j++) {
-        var oldKey = oldKeys[j];
-        var oldSubIdea = oldIdea.ideas[oldKey];
-        var oldSubIdeaId = this.ysy.getData(oldSubIdea).id;
-        for (var k = 0; k < newKeys.length; k++) {
-          var newKey = newKeys[k];
-          if (!newIdeas.hasOwnProperty(newKey)) continue;
-          var newSubIdea = newIdea.ideas[newKey];
-          var newSubIdeaId = this.ysy.getData(newSubIdea).id;
-          if (oldSubIdeaId !== newSubIdeaId) continue;
-          var diff = recursiveDiff(oldSubIdea, newSubIdea, diffTypes);
-          if (diff && diff.oldDiff) {
-            if (oldDiff.ideas === undefined) {
-              oldDiff.ideas = {};
-            }
-            oldDiff.ideas[oldKey] = diff.oldDiff;
-            isDifferent = true;
-          }
-          if (diff && diff.newDiff) {
-            if (newDiff.ideas === undefined) {
-              newDiff.ideas = {};
-            }
-            newDiff.ideas[newKey] = diff.newDiff;
-            isDifferent = true;
-          }
-          delete oldIdeas[oldKey];
-          delete newIdeas[newKey];
-        }
-        if (oldIdeas[oldKey]) {
-          if (!oldDiff.ideas) oldDiff.ideas = {};
-          oldDiff.ideas[oldKey] = oldIdeas[oldKey];
-          isDifferent = true;
-        }
-      }
-      for (newKey in newIdeas) {
-        if (!newIdeas.hasOwnProperty(newKey)) continue;
-        if (!newDiff.ideas) newDiff.ideas = {};
-        newDiff.ideas[newKey] = newIdeas[newKey];
-        isDifferent = true;
-      }
-    }
-    if (!isDifferent) return null;
-    var ret = {};
-    if (!_.isEmpty(oldDiff)) ret.oldDiff = oldDiff;
-    if (!_.isEmpty(newDiff)) ret.newDiff = newDiff;
-    return ret;
-  };
-//######################################################################################
   /**
    *
    * @param {Storage} storage
@@ -427,126 +166,26 @@
    * @constructor
    */
   function StorageSettings(storage) {
-    this.storage = storage;
-    this.ysy = storage.ysy;
-    this.init(storage.ysy);
   }
-
-  StorageSettings.prototype._key = "settings";
   /**
-   * @param {MindMup} ysy
+   * @param {RootIdea} idea
    */
-  StorageSettings.prototype.init = function (ysy) {
-    var self = this;
-    ysy.eventBus.register("saveOneSideOn",function(targetState){
-      self.saveOneSide(targetState);
-    });
-    ysy.eventBus.register("nodeStyleChanged", function () {
-      self.saveStyle();
-    });
-    ysy.eventBus.register("legendToggled", function (opened) {
-      self._save({legendHidden: !opened}, false, null);
-    });
-    ysy.eventBus.register("legendHeaderToggled", function (hidden) {
-      self._save({legendHeaderHidden: hidden}, false, null);
-    });
-    ysy.eventBus.register("BeforeServerClassInit",function () {
-      self.load();
-    });
-  };
-  StorageSettings.prototype.load = function () {
-    this.ysy.settings.oneSideOn = this._load("oneSideOn", null);
-    this.ysy.toolbar.redraw("toggleOneSide");
+  StorageSettings.prototype.load = function (idea) {
   };
   StorageSettings.prototype.loadStyle = function () {
-    return this._load("defaultStyle", this.ysy.settings.rootID);
+    return undefined;
   };
   StorageSettings.prototype.loadLegendHidden = function () {
-    return this._load("legendHidden", null);
+    return undefined
   };
   StorageSettings.prototype.loadLegendHeaderHidden = function () {
-    return this._load("legendHeaderHidden", null);
-  };
-  /**
-   *
-   * @param {String|Array.<String>} keys
-   * @param {String|null} rootKey
-   * @private
-   */
-  StorageSettings.prototype._load = function (keys, rootKey) {
-    if (!rootKey) {
-      rootKey = this._key;
-    } else {
-      rootKey = this._key + '-' + rootKey;
-    }
-    var extractString = this.storage.getPersistentData(rootKey);
-    if (!extractString) return;
-    var extract = JSON.parse(extractString);
-    if (typeof keys === "string") {
-      return extract[keys];
-    }
-    return _.pick(extract, keys);
+    return undefined
   };
   /**
-   * @param {boolean} oneSideOn
+   * @param {RootIdea} idea
    */
-  StorageSettings.prototype.saveOneSide = function (oneSideOn) {
-    this._save({oneSideOn: oneSideOn}, false, null);
+  StorageSettings.prototype.saveOneSide = function (idea) {
   };
   StorageSettings.prototype.saveStyle = function () {
-    var stylesClass = this.ysy.styles;
-    this._save(
-        {defaultStyle: stylesClass.setting === stylesClass.defaultStyle ? undefined : stylesClass.setting},
-        false,
-        this.ysy.settings.rootID
-    );
-  };
-  /**
-   * @param {Object} map
-   * @param {boolean} keepFalse
-   * @param {(string|null)} rootKey
-   * @param {boolean} [map.oneSideOn]
-   * @param {String} [map.defaultStyle]
-   * @param {boolean} [map.legendToggle]
-   */
-  StorageSettings.prototype._save = function (map, keepFalse, rootKey) {
-    if (!rootKey) {
-      rootKey = this._key;
-    } else {
-      rootKey = this._key + '-' + rootKey;
-    }
-    var extractString = this.storage.getPersistentData(rootKey);
-    if (!extractString) {
-      var updatedExtract = {};
-    } else {
-      var extract = JSON.parse(extractString);
-      updatedExtract = _.clone(extract);
-    }
-    var updatingKeys = _.keys(map);
-    if (keepFalse) {
-      for (var i = 0; i < updatingKeys.length; i++) {
-        var key = updatingKeys[i];
-        if (map[key] === undefined) {
-          delete updatedExtract[key];
-        } else {
-          updatedExtract[key] = map[key];
-        }
-      }
-    } else {
-      for (i = 0; i < updatingKeys.length; i++) {
-        key = updatingKeys[i];
-        if (!map[key]) {
-          delete updatedExtract[key];
-        } else {
-          updatedExtract[key] = map[key];
-        }
-      }
-    }
-    if (_.isEqual(extract, updatedExtract)) return;
-    if (_.isEmpty(updatedExtract)) {
-      this.storage.resetPersistentData(rootKey);
-      return;
-    }
-    this.storage.savePersistentData(rootKey, JSON.stringify(updatedExtract));
   };
 })();
diff --git a/plugins/easy_mindmup/assets/javascripts/styles.js b/plugins/easy_mindmup/assets/javascripts/styles.js
index fc93c4b..40b5a7a 100644
--- a/plugins/easy_mindmup/assets/javascripts/styles.js
+++ b/plugins/easy_mindmup/assets/javascripts/styles.js
@@ -41,10 +41,12 @@
       self.setColor(defaultStyle);
     });
 
-    $select.change(function () {
-      var value = $(this).val();
-      self.setColor(value);
-    });
+    $select
+        .attr("title", this.ysy.settings.labels.free.headerNotAvailable)
+        .on("change", function () {
+          self.ysy.util.showUpgradeModal("coloring");
+          $(this).val(self.defaultStyle);
+        });
   };
   /**
    *
diff --git a/plugins/easy_mindmup/assets/javascripts/toolbar.js b/plugins/easy_mindmup/assets/javascripts/toolbar.js
index 161d763..6d0e414 100644
--- a/plugins/easy_mindmup/assets/javascripts/toolbar.js
+++ b/plugins/easy_mindmup/assets/javascripts/toolbar.js
@@ -82,7 +82,7 @@
 
   OneSideButton.prototype.triggerName = "toggleOneSide";
   OneSideButton.prototype._render = function () {
-    var isActive = this.ysy.settings.oneSideOn;
+    var isActive = this.ysy.idea && this.ysy.idea.oneSideOn;
     if (!isActive) isActive = false;
     this.$element.find("a").toggleClass("active", isActive);
   };
diff --git a/plugins/easy_mindmup/assets/javascripts/utils.js b/plugins/easy_mindmup/assets/javascripts/utils.js
index 67255e6..e525b95 100644
--- a/plugins/easy_mindmup/assets/javascripts/utils.js
+++ b/plugins/easy_mindmup/assets/javascripts/utils.js
@@ -80,7 +80,7 @@
    */
   Util.prototype.showUpgradeModal= function (feature) {
     var ysy = this.ysy;
-    var $target = ysy.util.getModal("form-modal", "auto");
+    var $target = ysy.util.getModal("upgrade-modal", "auto");
     var template = ysy.settings.templates.upgrade;
     var freeLabels = ysy.settings.labels.free;
     var obj = {
@@ -90,7 +90,7 @@
     obj[feature] = true;
     var rendered = Mustache.render(template, obj);
     $target.html(rendered);
-    showModal("form-modal");
+    showModal("upgrade-modal");
     $target.dialog({
       buttons: [
         {
@@ -310,23 +310,5 @@
     }
     return null;
   };
-
-  var entityMap = {
-    '&': '&amp;',
-    '<': '&lt;',
-    '>': '&gt;',
-    '"': '&quot;',
-    "'": '&#39;',
-    '/': '&#x2F;',
-    '`': '&#x60;',
-    '=': '&#x3D;'
-  };
-
-  Util.prototype.escapeHtml = function(string) {
-    return String(string).replace(/[&<>"'`=\/]/g, function (s) {
-      return entityMap[s];
-    });
-  };
-
   window.easyMindMupClasses.Util = Util;
 })();
\ No newline at end of file
diff --git a/plugins/easy_mindmup/assets/stylesheets/easy_mindmup.css b/plugins/easy_mindmup/assets/stylesheets/easy_mindmup.css
index 506686d..4bb9515 100644
--- a/plugins/easy_mindmup/assets/stylesheets/easy_mindmup.css
+++ b/plugins/easy_mindmup/assets/stylesheets/easy_mindmup.css
@@ -43,6 +43,16 @@
   vertical-align: sub;
 }
 
+.mindmup-node-icon-avatar .gravatar,
+.mindmup-legend-item-cont .gravatar {
+  padding: 1px;
+  border: 1px solid #777;
+}
+
+.mindmup-legend-item-cont .gravatar {
+  margin-top: -7px;
+}
+
 .mindmup-node-icon-avatar .gravatar:hover,
 .mindmup-legend-item-cont .gravatar:hover {
   transform: scale(2);
@@ -144,7 +154,10 @@
 }
 
 mindmup-sidebar {
+  position: absolute;
   display: block;
+  top: 0;
+  right: 0;
 }
 
 .date-picker__date-wrap .input-append {
@@ -160,4 +173,3 @@ mindmup-sidebar {
 easy-lookup .easy-lookup-values-wrapper {
   height: auto;
 }
-
diff --git a/plugins/easy_mindmup/assets/stylesheets/easy_mindmup_sass.scss.erb b/plugins/easy_mindmup/assets/stylesheets/easy_mindmup_sass.scss.erb
index 5c00743..366ff0d 100644
--- a/plugins/easy_mindmup/assets/stylesheets/easy_mindmup_sass.scss.erb
+++ b/plugins/easy_mindmup/assets/stylesheets/easy_mindmup_sass.scss.erb
@@ -1,5 +1,4 @@
 <% redmine_sass = false
-   redmine_sass = true
    ep_com = Redmine::Plugin.installed?(:easy_project_com)
  %>
 <% if ep_com %>
diff --git a/plugins/easy_mindmup/assets/stylesheets/jasmine.css b/plugins/easy_mindmup/assets/stylesheets/jasmine.css
deleted file mode 100644
index 6319982..0000000
--- a/plugins/easy_mindmup/assets/stylesheets/jasmine.css
+++ /dev/null
@@ -1,58 +0,0 @@
-body { overflow-y: scroll; }
-
-.jasmine_html-reporter { background-color: #eee; padding: 5px; margin: -8px; font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333; }
-.jasmine_html-reporter a { text-decoration: none; }
-.jasmine_html-reporter a:hover { text-decoration: underline; }
-.jasmine_html-reporter p, .jasmine_html-reporter h1, .jasmine_html-reporter h2, .jasmine_html-reporter h3, .jasmine_html-reporter h4, .jasmine_html-reporter h5, .jasmine_html-reporter h6 { margin: 0; line-height: 14px; }
-.jasmine_html-reporter .jasmine-banner, .jasmine_html-reporter .jasmine-symbol-summary, .jasmine_html-reporter .jasmine-summary, .jasmine_html-reporter .jasmine-result-message, .jasmine_html-reporter .jasmine-spec .jasmine-description, .jasmine_html-reporter .jasmine-spec-detail .jasmine-description, .jasmine_html-reporter .jasmine-alert .jasmine-bar, .jasmine_html-reporter .jasmine-stack-trace { padding-left: 9px; padding-right: 9px; }
-.jasmine_html-reporter .jasmine-banner { position: relative; }
-.jasmine_html-reporter .jasmine-banner .jasmine-title { background: url('data:image/png;base64,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') no-repeat; background: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhLS0gQ3JlYXRlZCB3aXRoIElua3NjYXBlIChodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy8pIC0tPgoKPHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSIKICAgdmVyc2lvbj0iMS4xIgogICB3aWR0aD0iNjgxLjk2MjUyIgogICBoZWlnaHQ9IjE4Ny41IgogICBpZD0ic3ZnMiIKICAgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PG1ldGFkYXRhCiAgICAgaWQ9Im1ldGFkYXRhOCI+PHJkZjpSREY+PGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz48L2NjOldvcms+PC9yZGY6UkRGPjwvbWV0YWRhdGE+PGRlZnMKICAgICBpZD0iZGVmczYiPjxjbGlwUGF0aAogICAgICAgaWQ9ImNsaXBQYXRoMTgiPjxwYXRoCiAgICAgICAgIGQ9Ik0gMCwxNTAwIDAsMCBsIDU0NTUuNzQsMCAwLDE1MDAgTCAwLDE1MDAgeiIKICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgaWQ9InBhdGgyMCIgLz48L2NsaXBQYXRoPjwvZGVmcz48ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDEuMjUsMCwwLC0xLjI1LDAsMTg3LjUpIgogICAgIGlkPSJnMTAiPjxnCiAgICAgICB0cmFuc2Zvcm09InNjYWxlKDAuMSwwLjEpIgogICAgICAgaWQ9ImcxMiI+PGcKICAgICAgICAgaWQ9ImcxNCI+PGcKICAgICAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgxOCkiCiAgICAgICAgICAgaWQ9ImcxNiI+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTU0NCw1OTkuNDM0IGMgMC45MiwtNDAuMzUyIDI1LjY4LC04MS42MDIgNzEuNTMsLTgxLjYwMiAyNy41MSwwIDQ3LjY4LDEyLjgzMiA2MS40NCwzNS43NTQgMTIuODMsMjIuOTMgMTIuODMsNTYuODUyIDEyLjgzLDgyLjUyNyBsIDAsMzI5LjE4NCAtNzEuNTIsMCAwLDEwNC41NDMgMjY2LjgzLDAgMCwtMTA0LjU0MyAtNzAuNiwwIDAsLTM0NC43NyBjIDAsLTU4LjY5MSAtMy42OCwtMTA0LjUzMSAtNDQuOTMsLTE1Mi4yMTggLTM2LjY4LC00Mi4xOCAtOTYuMjgsLTY2LjAyIC0xNTMuMTQsLTY2LjAyIC0xMTcuMzcsMCAtMjA3LjI0LDc3Ljk0MSAtMjAyLjY0LDE5Ny4xNDUgbCAxMzAuMiwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMjIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDIzMDEuNCw2NjIuNjk1IGMgMCw4MC43MDMgLTY2Ljk0LDE0NS44MTMgLTE0Ny42MywxNDUuODEzIC04My40NCwwIC0xNDcuNjMsLTY4Ljc4MSAtMTQ3LjYzLC0xNTEuMzAxIDAsLTc5Ljc4NSA2Ni45NCwtMTQ1LjgwMSAxNDUuOCwtMTQ1LjgwMSA4NC4zNSwwIDE0OS40Niw2Ny44NTIgMTQ5LjQ2LDE1MS4yODkgeiBtIC0xLjgzLC0xODEuNTQ3IGMgLTM1Ljc3LC01NC4wOTcgLTkzLjUzLC03OC44NTkgLTE1Ny43MiwtNzguODU5IC0xNDAuMywwIC0yNTEuMjQsMTE2LjQ0OSAtMjUxLjI0LDI1NC45MTggMCwxNDIuMTI5IDExMy43LDI2MC40MSAyNTYuNzQsMjYwLjQxIDYzLjI3LDAgMTE4LjI5LC0yOS4zMzYgMTUyLjIyLC04Mi41MjMgbCAwLDY5LjY4NyAxNzUuMTQsMCAwLC0xMDQuNTI3IC02MS40NCwwIDAsLTI4MC41OTggNjEuNDQsMCAwLC0xMDQuNTI3IC0xNzUuMTQsMCAwLDY2LjAxOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAyNjIyLjMzLDU1Ny4yNTggYyAzLjY3LC00NC4wMTYgMzMuMDEsLTczLjM0OCA3OC44NiwtNzMuMzQ4IDMzLjkzLDAgNjYuOTMsMjMuODI0IDY2LjkzLDYwLjUwNCAwLDQ4LjYwNiAtNDUuODQsNTYuODU2IC04My40NCw2Ni45NDEgLTg1LjI4LDIyLjAwNCAtMTc4LjgxLDQ4LjYwNiAtMTc4LjgxLDE1NS44NzkgMCw5My41MzYgNzguODYsMTQ3LjYzMyAxNjUuOTgsMTQ3LjYzMyA0NCwwIDgzLjQzLC05LjE3NiAxMTAuOTQsLTQ0LjAwOCBsIDAsMzMuOTIyIDgyLjUzLDAgMCwtMTMyLjk2NSAtMTA4LjIxLDAgYyAtMS44MywzNC44NTYgLTI4LjQyLDU3Ljc3NCAtNjMuMjYsNTcuNzc0IC0zMC4yNiwwIC02Mi4zNSwtMTcuNDIyIC02Mi4zNSwtNTEuMzQ4IDAsLTQ1Ljg0NyA0NC45MywtNTUuOTMgODAuNjksLTY0LjE4IDg4LjAyLC0yMC4xNzUgMTgyLjQ3LC00Ny42OTUgMTgyLjQ3LC0xNTcuNzM0IDAsLTk5LjAyNyAtODMuNDQsLTE1NC4wMzkgLTE3NS4xMywtMTU0LjAzOSAtNDkuNTMsMCAtOTQuNDYsMTUuNTgyIC0xMjYuNTUsNTMuMTggbCAwLC00MC4zNCAtODUuMjcsMCAwLDE0Mi4xMjkgMTE0LjYyLDAiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGgyNiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMjk4OC4xOCw4MDAuMjU0IC02My4yNiwwIDAsMTA0LjUyNyAxNjUuMDUsMCAwLC03My4zNTUgYyAzMS4xOCw1MS4zNDcgNzguODYsODUuMjc3IDE0MS4yMSw4NS4yNzcgNjcuODUsMCAxMjQuNzEsLTQxLjI1OCAxNTIuMjEsLTEwMi42OTkgMjYuNiw2Mi4zNTEgOTIuNjIsMTAyLjY5OSAxNjAuNDcsMTAyLjY5OSA1My4xOSwwIDEwNS40NiwtMjIgMTQxLjIxLC02Mi4zNTEgMzguNTIsLTQ0LjkzOCAzOC41MiwtOTMuNTMyIDM4LjUyLC0xNDkuNDU3IGwgMCwtMTg1LjIzOSA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40MiwwIDAsMTA0LjUyNyA2My4yOCwwIDAsMTU3LjcxNSBjIDAsMzIuMTAyIDAsNjAuNTI3IC0xNC42Nyw4OC45NTcgLTE4LjM0LDI2LjU4MiAtNDguNjEsNDAuMzQ0IC03OS43Nyw0MC4zNDQgLTMwLjI2LDAgLTYzLjI4LC0xMi44NDQgLTgyLjUzLC0zNi42NzIgLTIyLjkzLC0yOS4zNTUgLTIyLjkzLC01Ni44NjMgLTIyLjkzLC05Mi42MjkgbCAwLC0xNTcuNzE1IDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM4LjQxLDAgMCwxMDQuNTI3IDYzLjI4LDAgMCwxNTAuMzgzIGMgMCwyOS4zNDggMCw2Ni4wMjMgLTE0LjY3LDkxLjY5OSAtMTUuNTksMjkuMzM2IC00Ny42OSw0NC45MzQgLTgwLjcsNDQuOTM0IC0zMS4xOCwwIC01Ny43NywtMTEuMDA4IC03Ny45NCwtMzUuNzc0IC0yNC43NywtMzAuMjUzIC0yNi42LC02Mi4zNDMgLTI2LjYsLTk5Ljk0MSBsIDAsLTE1MS4zMDEgNjMuMjcsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNiwwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDI4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSAzOTk4LjY2LDk1MS41NDcgLTExMS44NywwIDAsMTE4LjI5MyAxMTEuODcsMCAwLC0xMTguMjkzIHogbSAwLC00MzEuODkxIDYzLjI3LDAgMCwtMTA0LjUyNyAtMjM5LjMzLDAgMCwxMDQuNTI3IDY0LjE5LDAgMCwyODAuNTk4IC02My4yNywwIDAsMTA0LjUyNyAxNzUuMTQsMCAwLC0zODUuMTI1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzAiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDQxNTkuMTIsODAwLjI1NCAtNjMuMjcsMCAwLDEwNC41MjcgMTc1LjE0LDAgMCwtNjkuNjg3IGMgMjkuMzUsNTQuMTAxIDg0LjM2LDgwLjY5OSAxNDQuODcsODAuNjk5IDUzLjE5LDAgMTA1LjQ1LC0yMi4wMTYgMTQxLjIyLC02MC41MjcgNDAuMzQsLTQ0LjkzNCA0MS4yNiwtODguMDMyIDQxLjI2LC0xNDMuOTU3IGwgMCwtMTkxLjY1MyA2My4yNywwIDAsLTEwNC41MjcgLTIzOC40LDAgMCwxMDQuNTI3IDYzLjI2LDAgMCwxNTguNjM3IGMgMCwzMC4yNjIgMCw2MS40MzQgLTE5LjI2LDg4LjAzNSAtMjAuMTcsMjYuNTgyIC01My4xOCwzOS40MTQgLTg2LjE5LDM5LjQxNCAtMzMuOTMsMCAtNjguNzcsLTEzLjc1IC04OC45NCwtNDEuMjUgLTIxLjA5LC0yNy41IC0yMS4wOSwtNjkuNjg3IC0yMS4wOSwtMTAyLjcwNyBsIDAsLTE0Mi4xMjkgNjMuMjYsMCAwLC0xMDQuNTI3IC0yMzguNCwwIDAsMTA0LjUyNyA2My4yNywwIDAsMjgwLjU5OCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDMyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA1MDgyLjQ4LDcwMy45NjUgYyAtMTkuMjQsNzAuNjA1IC04MS42LDExNS41NDcgLTE1NC4wNCwxMTUuNTQ3IC02Ni4wNCwwIC0xMjkuMywtNTEuMzQ4IC0xNDMuMDUsLTExNS41NDcgbCAyOTcuMDksMCB6IG0gODUuMjcsLTE0NC44ODMgYyAtMzguNTEsLTkzLjUyMyAtMTI5LjI3LC0xNTYuNzkzIC0yMzEuMDUsLTE1Ni43OTMgLTE0My4wNywwIC0yNTcuNjgsMTExLjg3MSAtMjU3LjY4LDI1NS44MzYgMCwxNDQuODgzIDEwOS4xMiwyNjEuMzI4IDI1NC45MSwyNjEuMzI4IDY3Ljg3LDAgMTM1LjcyLC0zMC4yNTggMTgzLjM5LC03OC44NjMgNDguNjIsLTUxLjM0NCA2OC43OSwtMTEzLjY5NSA2OC43OSwtMTgzLjM4MyBsIC0zLjY3LC0zOS40MzQgLTM5Ni4xMywwIGMgMTQuNjcsLTY3Ljg2MyA3Ny4wMywtMTE3LjM2MyAxNDYuNzIsLTExNy4zNjMgNDguNTksMCA5MC43NiwxOC4zMjggMTE4LjI4LDU4LjY3MiBsIDExNi40NCwwIgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDY5MC44OTUsODUwLjcwMyA5MC43NSwwIDIyLjU0MywzMS4wMzUgMCwyNDMuMTIyIC0xMzUuODI5LDAgMCwtMjQzLjE0MSAyMi41MzYsLTMxLjAxNiIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDM2IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA2MzIuMzk1LDc0Mi4yNTggMjguMDM5LDg2LjMwNCAtMjIuNTUxLDMxLjA0IC0yMzEuMjIzLDc1LjEyOCAtNDEuOTc2LC0xMjkuMTgzIDIzMS4yNTcsLTc1LjEzNyAzNi40NTQsMTEuODQ4IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoMzgiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDcxNy40NDksNjUzLjEwNSAtNzMuNDEsNTMuMzYgLTM2LjQ4OCwtMTEuODc1IC0xNDIuOTAzLC0xOTYuNjkyIDEwOS44ODMsLTc5LjgyOCAxNDIuOTE4LDE5Ni43MDMgMCwzOC4zMzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0MCIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gODI4LjUyLDcwNi40NjUgLTczLjQyNiwtNTMuMzQgMC4wMTEsLTM4LjM1OSBMIDg5OC4wMDQsNDE4LjA3IDEwMDcuOSw0OTcuODk4IDg2NC45NzMsNjk0LjYwOSA4MjguNTIsNzA2LjQ2NSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQyIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA4MTIuMDg2LDgyOC41ODYgMjguMDU1LC04Ni4zMiAzNi40ODQsLTExLjgzNiAyMzEuMjI1LDc1LjExNyAtNDEuOTcsMTI5LjE4MyAtMjMxLjIzOSwtNzUuMTQgLTIyLjU1NSwtMzEuMDA0IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNDQiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDczNi4zMDEsMTMzNS44OCBjIC0zMjMuMDQ3LDAgLTU4NS44NzUsLTI2Mi43OCAtNTg1Ljg3NSwtNTg1Ljc4MiAwLC0zMjMuMTE4IDI2Mi44MjgsLTU4NS45NzcgNTg1Ljg3NSwtNTg1Ljk3NyAzMjMuMDE5LDAgNTg1LjgwOSwyNjIuODU5IDU4NS44MDksNTg1Ljk3NyAwLDMyMy4wMDIgLTI2Mi43OSw1ODUuNzgyIC01ODUuODA5LDU4NS43ODIgbCAwLDAgeiBtIDAsLTExOC42MSBjIDI1Ny45NzIsMCA0NjcuMTg5LC0yMDkuMTMgNDY3LjE4OSwtNDY3LjE3MiAwLC0yNTguMTI5IC0yMDkuMjE3LC00NjcuMzQ4IC00NjcuMTg5LC00NjcuMzQ4IC0yNTguMDc0LDAgLTQ2Ny4yNTQsMjA5LjIxOSAtNDY3LjI1NCw0NjcuMzQ4IDAsMjU4LjA0MiAyMDkuMTgsNDY3LjE3MiA0NjcuMjU0LDQ2Ny4xNzIiCiAgICAgICAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIgogICAgICAgICAgICAgaWQ9InBhdGg0NiIKICAgICAgICAgICAgIHN0eWxlPSJmaWxsOiM4YTQxODI7ZmlsbC1vcGFjaXR5OjE7ZmlsbC1ydWxlOm5vbnplcm87c3Ryb2tlOm5vbmUiIC8+PHBhdGgKICAgICAgICAgICAgIGQ9Im0gMTA5MS4xMyw2MTkuODgzIC0xNzUuNzcxLDU3LjEyMSAxMS42MjksMzUuODA4IDE3NS43NjIsLTU3LjEyMSAtMTEuNjIsLTM1LjgwOCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDQ4IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA4NjYuOTU3LDkwMi4wNzQgODM2LjUsOTI0LjE5OSA5NDUuMTIxLDEwNzMuNzMgOTc1LjU4NiwxMDUxLjYxIDg2Ni45NTcsOTAyLjA3NCIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDUwIgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0iTSA2MDcuNDY1LDkwMy40NDUgNDk4Ljg1NSwxMDUyLjk3IDUyOS4zMiwxMDc1LjEgNjM3LjkzLDkyNS41NjYgNjA3LjQ2NSw5MDMuNDQ1IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTIiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjxwYXRoCiAgICAgICAgICAgICBkPSJtIDM4MC42ODgsNjIyLjEyOSAtMTEuNjI2LDM1LjgwMSAxNzUuNzU4LDU3LjA5IDExLjYyMSwtMzUuODAxIC0xNzUuNzUzLC01Ny4wOSIKICAgICAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiCiAgICAgICAgICAgICBpZD0icGF0aDU0IgogICAgICAgICAgICAgc3R5bGU9ImZpbGw6IzhhNDE4MjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybztzdHJva2U6bm9uZSIgLz48cGF0aAogICAgICAgICAgICAgZD0ibSA3MTYuMjg5LDM3Ni41OSAzNy42NDA2LDAgMCwxODQuODE2IC0zNy42NDA2LDAgMCwtMTg0LjgxNiB6IgogICAgICAgICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgICAgICAgIGlkPSJwYXRoNTYiCiAgICAgICAgICAgICBzdHlsZT0iZmlsbDojOGE0MTgyO2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpub256ZXJvO3N0cm9rZTpub25lIiAvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=') no-repeat, none; -moz-background-size: 100%; -o-background-size: 100%; -webkit-background-size: 100%; background-size: 100%; display: block; float: left; width: 90px; height: 25px; }
-.jasmine_html-reporter .jasmine-banner .jasmine-version { margin-left: 14px; position: relative; top: 6px; }
-.jasmine_html-reporter #jasmine_content { position: fixed; right: 100%; }
-.jasmine_html-reporter .jasmine-version { color: #aaa; }
-.jasmine_html-reporter .jasmine-banner { margin-top: 14px; }
-.jasmine_html-reporter .jasmine-duration { color: #fff; float: right; line-height: 28px; padding-right: 9px; }
-.jasmine_html-reporter .jasmine-symbol-summary { overflow: hidden; *zoom: 1; margin: 14px 0; }
-.jasmine_html-reporter .jasmine-symbol-summary li { display: inline-block; height: 10px; width: 14px; font-size: 16px; }
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed { font-size: 14px; }
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-passed:before { color: #007069; content: "\02022"; }
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed { line-height: 9px; }
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-failed:before { color: #ca3a11; content: "\d7"; font-weight: bold; margin-left: -1px; }
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-disabled { font-size: 14px; }
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-disabled:before { color: #bababa; content: "\02022"; }
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending { line-height: 17px; }
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-pending:before { color: #ba9d37; content: "*"; }
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty { font-size: 14px; }
-.jasmine_html-reporter .jasmine-symbol-summary li.jasmine-empty:before { color: #ba9d37; content: "\02022"; }
-.jasmine_html-reporter .jasmine-run-options { float: right; margin-right: 5px; border: 1px solid #8a4182; color: #8a4182; position: relative; line-height: 20px; }
-.jasmine_html-reporter .jasmine-run-options .jasmine-trigger { cursor: pointer; padding: 8px 16px; }
-.jasmine_html-reporter .jasmine-run-options .jasmine-payload { position: absolute; display: none; right: -1px; border: 1px solid #8a4182; background-color: #eee; white-space: nowrap; padding: 4px 8px; }
-.jasmine_html-reporter .jasmine-run-options .jasmine-payload.jasmine-open { display: block; }
-.jasmine_html-reporter .jasmine-bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
-.jasmine_html-reporter .jasmine-bar.jasmine-failed { background-color: #ca3a11; }
-.jasmine_html-reporter .jasmine-bar.jasmine-passed { background-color: #007069; }
-.jasmine_html-reporter .jasmine-bar.jasmine-skipped { background-color: #bababa; }
-.jasmine_html-reporter .jasmine-bar.jasmine-errored { background-color: #ca3a11; }
-.jasmine_html-reporter .jasmine-bar.jasmine-menu { background-color: #fff; color: #aaa; }
-.jasmine_html-reporter .jasmine-bar.jasmine-menu a { color: #333; }
-.jasmine_html-reporter .jasmine-bar a { color: white; }
-.jasmine_html-reporter.jasmine-spec-list .jasmine-bar.jasmine-menu.jasmine-failure-list, .jasmine_html-reporter.jasmine-spec-list .jasmine-results .jasmine-failures { display: none; }
-.jasmine_html-reporter.jasmine-failure-list .jasmine-bar.jasmine-menu.jasmine-spec-list, .jasmine_html-reporter.jasmine-failure-list .jasmine-summary { display: none; }
-.jasmine_html-reporter .jasmine-results { margin-top: 14px; }
-.jasmine_html-reporter .jasmine-summary { margin-top: 14px; }
-.jasmine_html-reporter .jasmine-summary ul { list-style-type: none; margin-left: 14px; padding-top: 0; padding-left: 0; }
-.jasmine_html-reporter .jasmine-summary ul.jasmine-suite { margin-top: 7px; margin-bottom: 7px; }
-.jasmine_html-reporter .jasmine-summary li.jasmine-passed a { color: #007069; }
-.jasmine_html-reporter .jasmine-summary li.jasmine-failed a { color: #ca3a11; }
-.jasmine_html-reporter .jasmine-summary li.jasmine-empty a { color: #ba9d37; }
-.jasmine_html-reporter .jasmine-summary li.jasmine-pending a { color: #ba9d37; }
-.jasmine_html-reporter .jasmine-summary li.jasmine-disabled a { color: #bababa; }
-.jasmine_html-reporter .jasmine-description + .jasmine-suite { margin-top: 0; }
-.jasmine_html-reporter .jasmine-suite { margin-top: 14px; }
-.jasmine_html-reporter .jasmine-suite a { color: #333; }
-.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail { margin-bottom: 28px; }
-.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description { background-color: #ca3a11; }
-.jasmine_html-reporter .jasmine-failures .jasmine-spec-detail .jasmine-description a { color: white; }
-.jasmine_html-reporter .jasmine-result-message { padding-top: 14px; color: #333; white-space: pre; }
-.jasmine_html-reporter .jasmine-result-message span.jasmine-result { display: block; }
-.jasmine_html-reporter .jasmine-stack-trace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666; border: 1px solid #ddd; background: white; white-space: pre; }
diff --git a/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_default_variables.scss b/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_default_variables.scss
index 16f1519..4ef0ca3 100644
--- a/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_default_variables.scss
+++ b/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_default_variables.scss
@@ -1,6 +1,6 @@
 $form-input-height: 25px !default;
 $mup-background-menu: white !default;
-$mindmup-node-shadow: 0px 3px 3px 0px rgba(0,0,0,0.3) !default;
+$mindmup-node-shadow: 5px 5px 5px 0 gray !default;
 
 $retrace: .75 * $box-padding !default;
 $retrace-mod: (0.25)*$box-padding !default;
diff --git a/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_frame.scss b/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_frame.scss
index da89cc8..cefc367 100644
--- a/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_frame.scss
+++ b/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_frame.scss
@@ -88,12 +88,6 @@ $legend-width: 260px;
     //@include respond-to(min-medium-screen){
     //  padding-left: $gap + $box-padding + $legend-width;
     //}
-    &_addons{
-      position: absolute;
-      right: 0;
-      top: 2 * $box-padding + 31;
-      z-index: 5;
-    }
     &-item {
       display: inline-block;
       text-align: left;
@@ -152,8 +146,8 @@ $legend-width: 260px;
         text-align: center;
         font-size: 1.5em;
         position: absolute;
-        top: $box-padding;
-        left: -$box-padding - 90;
+        bottom: -2*$box-padding;
+        right: $box-padding;
         line-height: $box-padding;
         @include respond-to(max-small-screen) {
           display: none;
diff --git a/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_legend.scss b/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_legend.scss
index 8a4ac23..7d689ac 100644
--- a/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_legend.scss
+++ b/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_legend.scss
@@ -31,9 +31,6 @@
       &-cont{
         cursor: pointer;
         margin-top: 0.5*$gap;
-        .avatar-container{
-          float: none;
-        }
       }
     }
     .hotkey_link {
diff --git a/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_sidebar.scss b/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_sidebar.scss
index 61e39eb..58466a1 100644
--- a/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_sidebar.scss
+++ b/plugins/easy_mindmup/assets/stylesheets/sass/_mindmup_sidebar.scss
@@ -1,10 +1,13 @@
 &-sidebar {
+
   %info-block {
     background-color: #ffffaa;
     border: 1px solid #bfb23f;
     padding: 5px;
     text-align: center;
   }
+
+  display: block;
   &__empty-title {
     @extend %info-block;
     display: block;
@@ -25,7 +28,10 @@
     border-radius: 5px;
   }
   &__root {
-    display: block;
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    z-index: 5;
   }
   &__toggler {
     position: absolute;
@@ -37,12 +43,6 @@
       background: #d94838;
       color: #fff;
     }
-    &.easy-mindmup__icon {
-      padding-left: 15px;
-      &:before{
-        width: auto;
-      }
-    }
   }
   &__container {
     border-left: 1px solid #dfccaf;
@@ -105,7 +105,7 @@
     }
     &-label {
       display: inline-block;
-      width: 100px;
+      width: 90px;
       margin-top: 5px;
     }
     .spaceholder {
diff --git a/plugins/easy_mindmup/config/locales/ja.yml b/plugins/easy_mindmup/config/locales/ja.yml
deleted file mode 100644
index 2f99861..0000000
--- a/plugins/easy_mindmup/config/locales/ja.yml
+++ /dev/null
@@ -1,155 +0,0 @@
----
-ja:
-  easy_mindmup:
-    button_add_child: 子ノードの追加
-    button_add_parent: 親ノードの追加
-    button_add_sibling: 兄弟ノードの追加
-    button_all_icons: アイコン
-    button_collapse: フォルダツリー折りたたみ
-    button_collapse_all: すべて折りたたみ
-    button_cut: 切り取り
-    button_edit_data: データ編集
-    button_expand: フォルダツリー展開
-    button_expand_all: すべて展開する
-    button_expand_collapse: 展開/折りたたみ
-    button_hotkeys: ホットキー
-    button_legend: 凡例
-    button_one_side: 片側
-    button_paste: 貼り付け
-    button_project_menu: Easy MindMup
-    button_redo: 元に戻す
-    button_remove_node: ノードを取り除く
-    button_show_links: リンクを表示する
-    button_undo: 取り消し
-    error_create: 作成できませんでした
-    error_delete: 削除できませんでした
-    error_update: 更新できませんでした
-    errors:
-      no_rest_api: REST APIが無効になっています。管理でこれを有効にしてください。
-      not_subtaskable: チケット種別の設定のせいで、タスク “%{task_name}”をサブタスクできませんでした
-    free:
-      button_upgrade: 完全版を取得する
-      feature_coloring: プロパティによるノードの色付け
-      feature_context_menu: コンテキスト・メニューによるノード・プロパティの変更
-      feature_dnd_property: ドラッグ・アンド・ドロップによるノード・プロパティの変更
-      feature_filtering: プロパティによるノードのフィルタリング
-      header_not_available: 完全版でのみ提供可能
-    hotkeys:
-      info_mac_metakey: Mac OS Xでは、下記Ctrlと記載されたショートカットは、Ctrl + Cmdキーとなります。一部のブラウザーは、一部のキーバインドを使用することができません。よって、例えば、お客様のブラウザーでCmd + Spaceが機能しない場合は、Ctrl + Spaceを試してみてください。
-      keyboard:
-      - title: ノード操作
-        hotkeys:
-        - hotkey: Enter
-          info: 兄弟ノードの追加
-        - hotkey: Shift + Enter
-          info: 上記の兄弟ノード追加または改行(ノードの名前を変更する場合)
-        - hotkey: Tabまたはインサート・キー
-          info: 子ノードの追加
-        - hotkey: Shift + Tab
-          info: 親ノードの挿入
-        - hotkey: Space  
-          info: ノードのリネーム
-        - hotkey: Shift + Space
-          info: ノード・データの編集
-        - hotkey: バックスペースまたは削除
-          info: ノードを取り除く
-        - hotkey: Ctrl + 上/下キー
-          info: 上/下キーでノードの移動
-      - title: 編集
-        hotkeys:
-        - hotkey: Ctrl + S
-          info: 保存
-        - hotkey: Ctrl + X またはC
-          info: 切り取り
-        - hotkey: Ctrl + CまたはY
-          info: コピー
-        - hotkey: Ctrl + VまたはP
-          info: 貼り付け
-        - hotkey: UまたはCtrl+Z
-          info: 取り消し
-        - hotkey: RまたはCtrl+YまたはCtrl+Shift+Z
-          info: 元に戻す
-      - title: 選択
-        hotkeys:
-        - hotkey: 矢印キー
-          info: 現在選択しているノードの上/下/左/右を選択する
-        - hotkey: Shift + 矢印キー
-          info: 選択しているノードに上/下/左/右のノードを追加する(兄弟ノード複数選択には便利です)
-        - hotkey: "{"
-          info: カレント・ノードとその下のサブツリー全部の選択
-        - hotkey: "["
-          info: カレント・ノードの下のサブツリーのみ選択(そのノードそのものではなく)
-        - hotkey: "="
-          info: カレント・ノードの兄弟ノード全部を複数選択(同じ親を持つもの)
-        - hotkey: .
-          info: 複数選択を解除し、再びカレント・ノードのみ選択する
-        - hotkey: 1 - 9
-          info: 特定の改装のノードすべてを選択する(例:1で第一階層すべてを選ぶ)
-      - title: ナビゲーションと画面
-        hotkeys:
-        - hotkey: /またはF
-          info: ノードの展開または折りたたみ(子ノードを含める、または含めない)
-        - hotkey: Ctrl + またはZ
-          info: ズームイン
-        - hotkey: Ctrl − またはShift Z
-          info: ズームアウト
-        - hotkey: Esc、0、Ctrl + 0
-          info: "マップ表示のリセット - ルート・ノードを選択し、画面の中央に移動します"
-      mouse:
-      - action: マップを移動する
-        gesture: センター・ノードのクリック・アンド・ドラッグする、背景のクリック・アンド・ドラッグ、またはトラックパッド/タッチパッドでスクロールする
-      - action: ノードの選択
-        gesture: ノードをタップまたはクリックする
-      - action: 複数のノードの選択
-        gesture: Shift + クリック
-      - action: ノードの並べ替え
-        gesture: ノードをドラッグし、兄弟ノード間で水平に並べ替えに近い位置に移動する並べ替えでは、黒いアローポイントが表示される
-      - action: 手動でノードの位置を決める
-        gesture: ノードを並べ替えのためのアローポイントが消えるところ迄ドラッグする並べ替えアローポイントが表示されている場合に手動で位置を強制するには、ドラッグ中にShiftキーを押し続けるルート・ノードの子ノードはどの方向にもドラッグすることが可能ですが、それより下の階層のノードは、ルートに対して、それらの親ノードの方向にのみ配置することが可能だということに留意ください。
-      - action: ノード・リネーム
-        gesture: ノードをダブルクリックまたはダブルタップする
-      - action: 操作のコンテキスト・メニューの表示
-        gesture: ノードを右クリック(マウスで)、または背景をダブルタップ、またはノードを押し続けて、表示する(」タッチスクリーン上で)
-      - action: 親ノードの変更
-        gesture: ノードを他のノードの上にドラッグ・アンド・ドロップする(無限循環となるドロップはできません。したがって、子ノードまたは子孫ノード上にノードをドロップすることはできません)
-      - action: 課題やプロジェクトを別ウィンドウで開く
-        gesture: Alt + クリックする
-      title_key_shortcuts: キーボード操作
-      title_mouse_shortcuts: マウスとタッチによる操作
-      title_shortcuts: ショートカット
-    info_all_saved: すべての保存に成功しました
-    info_any_failed: リクエストの一部に失敗しました
-    info_no_permission: これを行うための必要な権限を持っていません
-    label_color_by: による色付け
-    label_go_to: に進む
-    label_hide_unused: 未使用のものを隠す
-    label_less: 未満
-    label_or: または
-    label_save_progress: しばらくお待ちください。全エンティティーの保存中です
-    label_show_unused: 未使用の項目を表示
-    last_state_modal:
-      label_differencies: サーバー上の相違は、
-      message_changed: "異なる属性を持つ(ブラウザー =>サーバー({{changes}})"
-      message_missing: "親から欠落しています “{{from}}"
-      message_moved: "の子供です “{{to}}”、ではない “{{from}}”"
-      message_present: "の子供としてあります “{{to}}”"
-      text_reload_appeal: サーバーからステートを再読込しますか?
-      title: Mind mapの最新のクライアント・ステートがサーバー・ステートと異なっています。
-    reload_modal:
-      label_errors: エラー
-      text_reload_appeal: 保存されていない項目を無視して、サーバーからデータの再読込をしますか?
-      title: Mind mapが正しく保存されませんでした
-    save_info:
-      at: に
-      autosaved: 自動保存
-      loaded: ロード終了
-      saved: 保存終了
-    stored_modal:
-      button_local: ローカルバージョン
-      button_server: サーバーバージョン
-      text_load_appeal: ロードしますか?それとも、サーバーから新しいステートをロードしますか?
-      title: 保存されていないロカールバージョンが見つかりました
-    title_node_changed: このノードが変更され、保存されます。
-    warning_delete_node: ノード{{name}}とそのすべての子孫を削除しますか?
-    warning_delete_nodes: これらのノードとそのすべての子孫を削除しますか?
-    warning_not_saved: は正しく保存されていません
\ No newline at end of file
diff --git a/plugins/easy_mindmup/init.rb b/plugins/easy_mindmup/init.rb
index f9ba9b3..a6b7bd9 100644
--- a/plugins/easy_mindmup/init.rb
+++ b/plugins/easy_mindmup/init.rb
@@ -2,7 +2,7 @@ Redmine::Plugin.register :easy_mindmup do
   name 'Easy MindMup plugin'
   author 'Easy Software Ltd'
   description 'Mind map view based on MindMup'
-  version '1.4'
+  version '1.0'
   url 'www.easyredmine.com'
   author_url 'www.easysoftware.cz'
 
diff --git a/plugins/easy_mindmup/lib/easy_mindmup/easy_mindmup.rb b/plugins/easy_mindmup/lib/easy_mindmup/easy_mindmup.rb
index 17f7c53..e767709 100644
--- a/plugins/easy_mindmup/lib/easy_mindmup/easy_mindmup.rb
+++ b/plugins/easy_mindmup/lib/easy_mindmup/easy_mindmup.rb
@@ -4,10 +4,4 @@ module EasyMindmup
     Redmine::Plugin.installed?(:easy_extensions)
   end
 
-  def self.combine_by_pipeline?(params)
-    return false unless easy_extensions?
-    return params[:combine_by_pipeline].to_s.to_boolean if params.key?(:combine_by_pipeline)
-    Rails.env.production?
-  end
-
 end
diff --git a/plugins/easy_wbs/README.md b/plugins/easy_wbs/README.md
deleted file mode 100644
index 7647453..0000000
--- a/plugins/easy_wbs/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Easy WBS
-
-For documentation and requirements, go to https://www.easyredmine.com/redmine-wbs-plugin
diff --git a/plugins/easy_wbs/after_init.rb b/plugins/easy_wbs/after_init.rb
index 47d281f..51781c2 100644
--- a/plugins/easy_wbs/after_init.rb
+++ b/plugins/easy_wbs/after_init.rb
@@ -25,7 +25,7 @@ Redmine::AccessControl.map do |map|
   end
 end
 
-ActionDispatch::Reloader.to_prepare do
+RedmineExtensions::Reloader.to_prepare do
   require 'easy_wbs/hooks'
   require 'easy_wbs/easy_wbs'
 
diff --git a/plugins/easy_wbs/app/controllers/easy_wbs_controller.rb b/plugins/easy_wbs/app/controllers/easy_wbs_controller.rb
index 48479a0..bf12aea 100644
--- a/plugins/easy_wbs/app/controllers/easy_wbs_controller.rb
+++ b/plugins/easy_wbs/app/controllers/easy_wbs_controller.rb
@@ -98,7 +98,7 @@ class EasyWbsController < ApplicationController
     end
 
     def load_versions
-      @versions = @projects.flat_map(&:shared_versions).select(&:open?).uniq
+      @versions = @projects.flat_map(&:shared_versions).uniq
     end
 
     def load_relations
diff --git a/plugins/easy_wbs/app/models/easy_queries/easy_wbs_easy_issue_query.rb b/plugins/easy_wbs/app/models/easy_queries/easy_wbs_easy_issue_query.rb
deleted file mode 100644
index 60f320d..0000000
--- a/plugins/easy_wbs/app/models/easy_queries/easy_wbs_easy_issue_query.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-class EasyWbsEasyIssueQuery < EasyIssueQuery
-
-  def easy_query_entity_controller
-    'easy_wbs'
-  end
-
-  def easy_query_entity_action
-    'index'
-  end
-
-  def columns
-    []
-  end
-
-  def groupable_columns
-    []
-  end
-
-  def project_scope
-    super.where.not(status: [Project::STATUS_CLOSED, Project::STATUS_ARCHIVED])
-  end
-
-  def entity_easy_query_path(options={})
-    if project
-      project_easy_wbs_index_path(project, options)
-    end
-  end
-
-  def query_after_initialize
-    super
-
-    self.display_filter_columns_on_index = false
-    self.display_filter_group_by_on_index = false
-    self.display_filter_sort_on_index = false
-    self.display_filter_settings_on_index = false
-
-    self.display_filter_columns_on_edit = false
-    self.display_filter_group_by_on_edit = false
-    self.display_filter_sort_on_edit = false
-    self.display_filter_settings_on_edit = false
-
-    self.display_show_sum_row = false
-    self.display_load_groups_opened = false
-
-    self.export_formats = {}
-    self.is_tagged = true if new_record?
-
-    # self.display_filter_fullscreen_button =
-    # self.display_save_button =
-    # self.display_project_column_if_project_missing =
-  end
-
-end
diff --git a/plugins/easy_wbs/app/views/easy_wbs/_includes.html.erb b/plugins/easy_wbs/app/views/easy_wbs/_includes.html.erb
index efc9f7c..321f373 100644
--- a/plugins/easy_wbs/app/views/easy_wbs/_includes.html.erb
+++ b/plugins/easy_wbs/app/views/easy_wbs/_includes.html.erb
@@ -1,22 +1,14 @@
-<% if EasyMindmup.easy_extensions? %>
-  <%= stylesheet_link_tag('easy_wbs', media: 'all') %>
-<% else %>
-  <%= stylesheet_link_tag('generated/easy_wbs', plugin: 'easy_wbs', media: 'all') %>
-<% end %>
+<%= stylesheet_link_tag('easy_wbs', plugin: 'easy_wbs', media: 'all') %>
+<%= javascript_include_tag(
+      :wbs_main,
+      :wbs_context_menu,
+      :wbs_loader,
+      :wbs_node_patch,
+      :wbs_saver,
+      :wbs_modals,
+      :wbs_styles,
+      :wbs_validator,
+      plugin: 'easy_wbs'
 
-<% if EasyMindmup.combine_by_pipeline?(params) %>
-  <%= javascript_include_tag('easy_wbs') %>
-<% else %>
-  <%= javascript_include_tag(
-          :wbs_main,
-          :wbs_context_menu,
-          :wbs_loader,
-          :wbs_node_patch,
-          :wbs_saver,
-          :wbs_modals,
-          :wbs_styles,
-          :wbs_validator,
-          plugin: 'easy_wbs'
-      ) %>
-<% end %>
+) %>
 <% include_galereya_headers_tags if defined?(include_galereya_headers_tags) %>
diff --git a/plugins/easy_wbs/app/views/easy_wbs/_test_includes.html.erb b/plugins/easy_wbs/app/views/easy_wbs/_test_includes.html.erb
index 7b1f822..54a36eb 100644
--- a/plugins/easy_wbs/app/views/easy_wbs/_test_includes.html.erb
+++ b/plugins/easy_wbs/app/views/easy_wbs/_test_includes.html.erb
@@ -1,5 +1,4 @@
 <%= javascript_include_tag(
-        'tests/encode_layout',
         'tests/saving_test',
         'tests/printing',
         'tests/parse_form',
diff --git a/plugins/easy_wbs/app/views/easy_wbs/index.html.erb b/plugins/easy_wbs/app/views/easy_wbs/index.html.erb
index dd24dab..79c6c54 100644
--- a/plugins/easy_wbs/app/views/easy_wbs/index.html.erb
+++ b/plugins/easy_wbs/app/views/easy_wbs/index.html.erb
@@ -29,7 +29,7 @@
               <a href="javascript:void(0)" class="easy-mindmup__icon easy-mindmup__icon--legend mindmup__legend-trigger"><span class="tip"><%= l(:button_legend, :scope => [:easy_wbs]) %></span></a>
             </div>
           </div>
-          <div class="mindmup-legend mindmup__legend-body"></div>
+          <div class="mindmup-legend"></div>
         </div>
         <div class="mindmup__menu-group mindmup__menu-group--tooltiped mindmup__menu-item mindmup__menu-group-display">
           <a href="javascript:void(0)" class="button button-2 easy-mindmup__icon easy-mindmup__icon--display"><span><%= l(:button_display, :scope => [:easy_wbs]) %></span></a>
@@ -53,20 +53,44 @@
           </ul>
         </div>
       </div>
-      <div class="mindmup__menu_addons">
-        <div class="mindmup__menu-group mindmup__menu-group--sizing mindmup__menu-item">
-          <ul>
-            <li class="scaleUp">
-              <a href="javascript:void(0)" class="easy-mindmup__icon easy-mindmup__icon--zoom_in"></a>
-            </li>
-            <li class="resetView">
-              <a href="javascript:void(0)" class="easy-mindmup__icon easy-mindmup__icon--refresh_view"></a>
-            </li>
-            <li class="scaleDown">
-              <a href="javascript:void(0)" class="easy-mindmup__icon easy-mindmup__icon--zoom_out"></a>
-            </li>
-          </ul>
-        </div>
+      <% if defined?(EasyExtensions) && !EasySetting.value(:easy_wbs_no_sidebar)%>
+        <script type="application/javascript">
+          window.easyDartLoaders = window.easyDartLoaders || [];
+          document.write('<ng-root-' + window.easyDartLoaders.length + ' class="mindmup-sidebar__root"></ng-root-' + window.easyDartLoaders.length + '>');
+          window.easyDartLoaders.push(<%= {
+            subtype: :wbs,
+            id:'WBS classic',
+            plugin: 'mindmupSidebar',
+            data: {
+              lang: wbs_translations,
+              paths: {
+                # home_path: (home_path=='/')?home_path:(home_path+'/'),
+                home_path: home_path,
+                issue_path: issue_path('__issueId',key: User.current.api_key,format: :json),
+                project_path: project_path('__projectId',key: User.current.api_key,format: :json),
+                easy_issue_form_fields: url_for(controller: :easy_issues, action: :form_fields,id: '__issueId',format: :json,project_id: nil,key: User.current.api_key),
+                easy_issue_project_form_fields: url_for(controller: :easy_issues, action: :form_fields,format: :json,project_id: '__projectId',key: User.current.api_key),
+                user_json: user_path('__userId',key: User.current.api_key,format: :json),
+                user_html: user_path('__userId'),
+                user_profile_html:profile_user_path('__userId')
+              }
+            }.to_json.to_s
+            # there should be String value in key data - it is faster to iterop with dart part
+          }.to_json.html_safe %>);
+        </script>
+      <% end %>
+      <div class="mindmup__menu-group mindmup__menu-group--sizing mindmup__menu-item">
+        <ul>
+          <li class="scaleUp">
+            <a href="javascript:void(0)" class="easy-mindmup__icon easy-mindmup__icon--zoom_in"></a>
+          </li>
+          <li class="resetView">
+            <a href="javascript:void(0)" class="easy-mindmup__icon easy-mindmup__icon--refresh_view"></a>
+          </li>
+          <li class="scaleDown">
+            <a href="javascript:void(0)" class="easy-mindmup__icon easy-mindmup__icon--zoom_out"></a>
+          </li>
+        </ul>
       </div>
       <div class="push-right">
         <div class="mindmup__menu-group mindmup__menu-group--tooltiped mindmup__menu-item">
@@ -111,12 +135,10 @@
 <% heads_for_wiki_formatter %>
 <%= content_for :header_tags do %>
   <%= render 'easy_mindmup/includes' %>
-  <%= render 'easy_wbs/includes' %>
   <%= render 'easy_mindmup/js_prepare' %>
-  <%= render 'easy_mindmup/test_includes' if params[:run_jasmine_tests] || params[:spec] %>
   <%= render 'easy_wbs/js_prepare' %>
-  <% # helper functions for user testing %>
-  <%= render 'easy_wbs/test_includes' if params[:include_tests] %>
+  <%= render 'easy_wbs/includes' %>
+  <%= render 'easy_wbs/test_includes' %>
 
   <script type="application/javascript">
     window.easyMindMupSetting.paths.data = "<%= data_path.html_safe %>";
diff --git a/plugins/easy_wbs/assets/javascripts/easy_wbs.js b/plugins/easy_wbs/assets/javascripts/easy_wbs.js
deleted file mode 100644
index e4cb518..0000000
--- a/plugins/easy_wbs/assets/javascripts/easy_wbs.js
+++ /dev/null
@@ -1,3 +0,0 @@
-/*
- * = require_directory .
- */
\ No newline at end of file
diff --git a/plugins/easy_wbs/assets/javascripts/tests/encode_layout.js b/plugins/easy_wbs/assets/javascripts/tests/encode_layout.js
deleted file mode 100644
index 6801f41..0000000
--- a/plugins/easy_wbs/assets/javascripts/tests/encode_layout.js
+++ /dev/null
@@ -1,5827 +0,0 @@
-/**
- * Created by hosekp on 6/12/17.
- */
-window.easyTests = $.extend(window.easyTests, {
-  encodeLayout: function () {
-    var smallLayout = {
-      "i2280": {"rank": 1},
-      "i2281": {"rank": 2},
-      "i2282": {"rank": 3},
-      "i2279": {"rank": 1},
-      "i2285": {"rank": 1},
-      "i2286": {"rank": 2},
-      "i2287": {"rank": 3},
-      "i2288": {"rank": 4},
-      "i2289": {"rank": 5},
-      "i2284": {"rank": 1},
-      "i2291": {"rank": 1},
-      "i2292": {"rank": 2},
-      "i2293": {"rank": 3},
-      "i2295": {"rank": 1},
-      "i2296": {"rank": 2},
-      "i2294": {"rank": 4},
-      "i2297": {"rank": 5},
-      "i2290": {"rank": 2},
-      "i2299": {"rank": 1},
-      "i2298": {"rank": 3},
-      "i2301": {"rank": 1},
-      "i2302": {"rank": 2},
-      "i2303": {"rank": 3},
-      "i2304": {"rank": 4},
-      "i2300": {"rank": 4},
-      "i2306": {"rank": 1},
-      "i2307": {"rank": 2},
-      "i2305": {"rank": 5},
-      "i2309": {"rank": 1},
-      "i2310": {"rank": 2},
-      "i2308": {"rank": 6},
-      "i2283": {"rank": 2},
-      "i2313": {"rank": 1},
-      "i2314": {"rank": 2},
-      "i2312": {"rank": 1},
-      "i2316": {"rank": 1},
-      "i2315": {"rank": 2},
-      "i2318": {"rank": 1},
-      "i2317": {"rank": 3},
-      "i2320": {"rank": 1},
-      "i2321": {"rank": 2},
-      "i2319": {"rank": 4},
-      "i2323": {"rank": 1},
-      "i2322": {"rank": 5},
-      "i2311": {"rank": 3},
-      "i2325": {"rank": 1},
-      "i2326": {"rank": 2},
-      "i2327": {"rank": 3},
-      "i2328": {"rank": 4},
-      "i2329": {"rank": 5},
-      "i2330": {"rank": 6},
-      "i2331": {"rank": 7},
-      "i2324": {"rank": 4},
-      "i2278": {"rank": 1},
-      "i2335": {"rank": 1},
-      "i2336": {"rank": 2},
-      "i2337": {"rank": 3},
-      "i2338": {"rank": 4},
-      "i2339": {"rank": 5},
-      "i2334": {"rank": 1},
-      "i2341": {"rank": 1},
-      "i2342": {"rank": 2},
-      "i2343": {"rank": 3},
-      "i2344": {"rank": 4},
-      "i2340": {"rank": 2},
-      "i2346": {"rank": 1},
-      "i2347": {"rank": 2},
-      "i2345": {"rank": 3},
-      "i2333": {"rank": 1},
-      "i2350": {"rank": 1},
-      "i2351": {"rank": 2},
-      "i2352": {"rank": 3},
-      "i2349": {"rank": 1},
-      "i2354": {"rank": 1},
-      "i2355": {"rank": 2},
-      "i2353": {"rank": 2},
-      "i2357": {"rank": 1},
-      "i2358": {"rank": 2},
-      "i2356": {"rank": 3},
-      "i2360": {"rank": 1},
-      "i2361": {"rank": 2},
-      "i2359": {"rank": 4},
-      "i2348": {"rank": 2},
-      "i2363": {"rank": 1},
-      "i2364": {"rank": 2},
-      "i2365": {"rank": 3},
-      "i2366": {"rank": 4},
-      "i2367": {"rank": 5},
-      "i2368": {"rank": 6},
-      "i2362": {"rank": 3},
-      "i2332": {"rank": 2},
-      "i2371": {"rank": 1},
-      "i2372": {"rank": 2},
-      "i2373": {"rank": 3},
-      "i2374": {"rank": 4},
-      "i2375": {"rank": 5},
-      "i2376": {"rank": 6},
-      "i2370": {"rank": 1},
-      "i2378": {"rank": 1},
-      "i2379": {"rank": 2},
-      "i2380": {"rank": 3},
-      "i2377": {"rank": 2},
-      "i2382": {"rank": 1},
-      "i2383": {"rank": 2},
-      "i2384": {"rank": 3},
-      "i2381": {"rank": 3},
-      "i2386": {"rank": 1},
-      "i2387": {"rank": 2},
-      "i2388": {"rank": 3},
-      "i2385": {"rank": 4},
-      "i2369": {"rank": 3},
-      "i2391": {"rank": 1},
-      "i2392": {"rank": 2},
-      "i2393": {"rank": 3},
-      "i2390": {"rank": 1},
-      "i2395": {"rank": 1},
-      "i2396": {"rank": 2},
-      "i2397": {"rank": 3},
-      "i2394": {"rank": 2},
-      "i2399": {"rank": 1},
-      "i2400": {"rank": 2},
-      "i2401": {"rank": 3},
-      "i2398": {"rank": 3},
-      "i2389": {"rank": 4},
-      "i2402": {"rank": 5},
-      "p85": {"rank": -6},
-      "i1655": {"rank": 1},
-      "i1656": {"rank": 2},
-      "i1657": {"rank": 3},
-      "i1654": {"rank": 1},
-      "i1660": {"rank": 1},
-      "i1661": {"rank": 2},
-      "i1662": {"rank": 3},
-      "i1663": {"rank": 4},
-      "i1664": {"rank": 5},
-      "i1659": {"rank": 1},
-      "i1666": {"rank": 1},
-      "i1667": {"rank": 2},
-      "i1668": {"rank": 3},
-      "i1670": {"rank": 1},
-      "i1671": {"rank": 2},
-      "i1669": {"rank": 4},
-      "i1672": {"rank": 5},
-      "i1665": {"rank": 2},
-      "i1674": {"rank": 1},
-      "i1673": {"rank": 3},
-      "i1676": {"rank": 1},
-      "i1677": {"rank": 2},
-      "i1678": {"rank": 3},
-      "i1679": {"rank": 4},
-      "i1675": {"rank": 4},
-      "i1681": {"rank": 1},
-      "i1682": {"rank": 2},
-      "i1680": {"rank": 5},
-      "i1684": {"rank": 1},
-      "i1685": {"rank": 2},
-      "i1683": {"rank": 6},
-      "i1658": {"rank": 2},
-      "i1688": {"rank": 1},
-      "i1689": {"rank": 2},
-      "i1687": {"rank": 1},
-      "i1691": {"rank": 1},
-      "i1690": {"rank": 2},
-      "i1693": {"rank": 1},
-      "i1692": {"rank": 3},
-      "i1695": {"rank": 1},
-      "i1696": {"rank": 2},
-      "i1694": {"rank": 4},
-      "i1698": {"rank": 1},
-      "i1697": {"rank": 5},
-      "i1686": {"rank": 3},
-      "i1700": {"rank": 1},
-      "i1701": {"rank": 2},
-      "i1702": {"rank": 3},
-      "i1703": {"rank": 4},
-      "i1704": {"rank": 5},
-      "i1705": {"rank": 6},
-      "i1706": {"rank": 7},
-      "i1699": {"rank": 4},
-      "i1653": {"rank": 1},
-      "i1710": {"rank": 1},
-      "i1711": {"rank": 2},
-      "i1712": {"rank": 3},
-      "i1713": {"rank": 4},
-      "i1714": {"rank": 5},
-      "i1709": {"rank": 1},
-      "i1716": {"rank": 1},
-      "i1717": {"rank": 2},
-      "i1718": {"rank": 3},
-      "i1719": {"rank": 4},
-      "i1715": {"rank": 2},
-      "i1721": {"rank": 1},
-      "i1722": {"rank": 2},
-      "i1720": {"rank": 3},
-      "i1708": {"rank": 1},
-      "i1725": {"rank": 1},
-      "i1726": {"rank": 2},
-      "i1727": {"rank": 3},
-      "i1724": {"rank": 1},
-      "i1729": {"rank": 1},
-      "i1730": {"rank": 2},
-      "i1728": {"rank": 2},
-      "i1732": {"rank": 1},
-      "i1733": {"rank": 2},
-      "i1731": {"rank": 3},
-      "i1735": {"rank": 1},
-      "i1736": {"rank": 2},
-      "i1734": {"rank": 4},
-      "i1723": {"rank": 2},
-      "i1738": {"rank": 1},
-      "i1739": {"rank": 2},
-      "i1740": {"rank": 3},
-      "i1741": {"rank": 4},
-      "i1742": {"rank": 5},
-      "i1743": {"rank": 6},
-      "i1737": {"rank": 3},
-      "i1707": {"rank": 2},
-      "i1746": {"rank": 1},
-      "i1747": {"rank": 2},
-      "i1748": {"rank": 3},
-      "i1749": {"rank": 4},
-      "i1750": {"rank": 5},
-      "i1751": {"rank": 6},
-      "i1745": {"rank": 1},
-      "i1753": {"rank": 1},
-      "i1754": {"rank": 2},
-      "i1755": {"rank": 3},
-      "i1752": {"rank": 2},
-      "i1757": {"rank": 1},
-      "i1758": {"rank": 2},
-      "i1759": {"rank": 3},
-      "i1756": {"rank": 3},
-      "i1761": {"rank": 1},
-      "i1762": {"rank": 2},
-      "i1763": {"rank": 3},
-      "i1760": {"rank": 4},
-      "i1744": {"rank": 3},
-      "i1766": {"rank": 1},
-      "i1767": {"rank": 2},
-      "i1768": {"rank": 3},
-      "i1765": {"rank": 1},
-      "i1770": {"rank": 1},
-      "i1771": {"rank": 2},
-      "i1772": {"rank": 3},
-      "i1769": {"rank": 2},
-      "i1774": {"rank": 1},
-      "i1775": {"rank": 2},
-      "i1776": {"rank": 3},
-      "i1773": {"rank": 3},
-      "i1764": {"rank": 4},
-      "i1777": {"rank": 5},
-      "p80": {"rank": -5},
-      "i2530": {"rank": 1},
-      "i2531": {"rank": 2},
-      "i2532": {"rank": 3},
-      "i2529": {"rank": 1},
-      "i2535": {"rank": 1},
-      "i2536": {"rank": 2},
-      "i2537": {"rank": 3},
-      "i2538": {"rank": 4},
-      "i2539": {"rank": 5},
-      "i2534": {"rank": 1},
-      "i2541": {"rank": 1},
-      "i2542": {"rank": 2},
-      "i2543": {"rank": 3},
-      "i2545": {"rank": 1},
-      "i2546": {"rank": 2},
-      "i2544": {"rank": 4},
-      "i2547": {"rank": 5},
-      "i2540": {"rank": 2},
-      "i2549": {"rank": 1},
-      "i2548": {"rank": 3},
-      "i2551": {"rank": 1},
-      "i2552": {"rank": 2},
-      "i2553": {"rank": 3},
-      "i2554": {"rank": 4},
-      "i2550": {"rank": 4},
-      "i2556": {"rank": 1},
-      "i2557": {"rank": 2},
-      "i2555": {"rank": 5},
-      "i2559": {"rank": 1},
-      "i2560": {"rank": 2},
-      "i2558": {"rank": 6},
-      "i2533": {"rank": 2},
-      "i2563": {"rank": 1},
-      "i2564": {"rank": 2},
-      "i2562": {"rank": 1},
-      "i2566": {"rank": 1},
-      "i2565": {"rank": 2},
-      "i2568": {"rank": 1},
-      "i2567": {"rank": 3},
-      "i2570": {"rank": 1},
-      "i2571": {"rank": 2},
-      "i2569": {"rank": 4},
-      "i2573": {"rank": 1},
-      "i2572": {"rank": 5},
-      "i2561": {"rank": 3},
-      "i2575": {"rank": 1},
-      "i2576": {"rank": 2},
-      "i2577": {"rank": 3},
-      "i2578": {"rank": 4},
-      "i2579": {"rank": 5},
-      "i2580": {"rank": 6},
-      "i2581": {"rank": 7},
-      "i2574": {"rank": 4},
-      "i2528": {"rank": 1},
-      "i2585": {"rank": 1},
-      "i2586": {"rank": 2},
-      "i2587": {"rank": 3},
-      "i2588": {"rank": 4},
-      "i2589": {"rank": 5},
-      "i2584": {"rank": 1},
-      "i2591": {"rank": 1},
-      "i2592": {"rank": 2},
-      "i2593": {"rank": 3},
-      "i2594": {"rank": 4},
-      "i2590": {"rank": 2},
-      "i2596": {"rank": 1},
-      "i2597": {"rank": 2},
-      "i2595": {"rank": 3},
-      "i2583": {"rank": 1},
-      "i2600": {"rank": 1},
-      "i2601": {"rank": 2},
-      "i2602": {"rank": 3},
-      "i2599": {"rank": 1},
-      "i2604": {"rank": 1},
-      "i2605": {"rank": 2},
-      "i2603": {"rank": 2},
-      "i2607": {"rank": 1},
-      "i2608": {"rank": 2},
-      "i2606": {"rank": 3},
-      "i2610": {"rank": 1},
-      "i2611": {"rank": 2},
-      "i2609": {"rank": 4},
-      "i2598": {"rank": 2},
-      "i2613": {"rank": 1},
-      "i2614": {"rank": 2},
-      "i2615": {"rank": 3},
-      "i2616": {"rank": 4},
-      "i2617": {"rank": 5},
-      "i2618": {"rank": 6},
-      "i2612": {"rank": 3},
-      "i2582": {"rank": 2},
-      "i2621": {"rank": 1},
-      "i2622": {"rank": 2},
-      "i2623": {"rank": 3},
-      "i2624": {"rank": 4},
-      "i2625": {"rank": 5},
-      "i2626": {"rank": 6},
-      "i2620": {"rank": 1},
-      "i2628": {"rank": 1},
-      "i2629": {"rank": 2},
-      "i2630": {"rank": 3},
-      "i2627": {"rank": 2},
-      "i2632": {"rank": 1},
-      "i2633": {"rank": 2},
-      "i2634": {"rank": 3},
-      "i2631": {"rank": 3},
-      "i2636": {"rank": 1},
-      "i2637": {"rank": 2},
-      "i2638": {"rank": 3},
-      "i2635": {"rank": 4},
-      "i2619": {"rank": 3},
-      "i2641": {"rank": 1},
-      "i2642": {"rank": 2},
-      "i2643": {"rank": 3},
-      "i2640": {"rank": 1},
-      "i2645": {"rank": 1},
-      "i2646": {"rank": 2},
-      "i2647": {"rank": 3},
-      "i2644": {"rank": 2},
-      "i2649": {"rank": 1},
-      "i2650": {"rank": 2},
-      "i2651": {"rank": 3},
-      "i2648": {"rank": 3},
-      "i2639": {"rank": 4},
-      "i2652": {"rank": 5},
-      "p87": {"rank": -4},
-      "i2905": {"rank": 1},
-      "i2906": {"rank": 2},
-      "i2907": {"rank": 3},
-      "i2904": {"rank": 1},
-      "i2910": {"rank": 1},
-      "i2911": {"rank": 2},
-      "i2912": {"rank": 3},
-      "i2913": {"rank": 4},
-      "i2914": {"rank": 5},
-      "i2909": {"rank": 1},
-      "i2916": {"rank": 1},
-      "i2917": {"rank": 2},
-      "i2918": {"rank": 3},
-      "i2920": {"rank": 1},
-      "i2921": {"rank": 2},
-      "i2919": {"rank": 4},
-      "i2922": {"rank": 5},
-      "i2915": {"rank": 2},
-      "i2924": {"rank": 1},
-      "i2923": {"rank": 3},
-      "i2926": {"rank": 1},
-      "i2927": {"rank": 2},
-      "i2928": {"rank": 3},
-      "i2929": {"rank": 4},
-      "i2925": {"rank": 4},
-      "i2931": {"rank": 1},
-      "i2932": {"rank": 2},
-      "i2930": {"rank": 5},
-      "i2934": {"rank": 1},
-      "i2935": {"rank": 2},
-      "i2933": {"rank": 6},
-      "i2908": {"rank": 2},
-      "i2938": {"rank": 1},
-      "i2939": {"rank": 2},
-      "i2937": {"rank": 1},
-      "i2941": {"rank": 1},
-      "i2940": {"rank": 2},
-      "i2943": {"rank": 1},
-      "i2942": {"rank": 3},
-      "i2945": {"rank": 1},
-      "i2946": {"rank": 2},
-      "i2944": {"rank": 4},
-      "i2948": {"rank": 1},
-      "i2947": {"rank": 5},
-      "i2936": {"rank": 3},
-      "i2950": {"rank": 1},
-      "i2951": {"rank": 2},
-      "i2952": {"rank": 3},
-      "i2953": {"rank": 4},
-      "i2954": {"rank": 5},
-      "i2955": {"rank": 6},
-      "i2956": {"rank": 7},
-      "i2949": {"rank": 4},
-      "i2903": {"rank": 1},
-      "i2960": {"rank": 1},
-      "i2961": {"rank": 2},
-      "i2962": {"rank": 3},
-      "i2963": {"rank": 4},
-      "i2964": {"rank": 5},
-      "i2959": {"rank": 1},
-      "i2966": {"rank": 1},
-      "i2967": {"rank": 2},
-      "i2968": {"rank": 3},
-      "i2969": {"rank": 4},
-      "i2965": {"rank": 2},
-      "i2971": {"rank": 1},
-      "i2972": {"rank": 2},
-      "i2970": {"rank": 3},
-      "i2958": {"rank": 1},
-      "i2975": {"rank": 1},
-      "i2976": {"rank": 2},
-      "i2977": {"rank": 3},
-      "i2974": {"rank": 1},
-      "i2979": {"rank": 1},
-      "i2980": {"rank": 2},
-      "i2978": {"rank": 2},
-      "i2982": {"rank": 1},
-      "i2983": {"rank": 2},
-      "i2981": {"rank": 3},
-      "i2985": {"rank": 1},
-      "i2986": {"rank": 2},
-      "i2984": {"rank": 4},
-      "i2973": {"rank": 2},
-      "i2988": {"rank": 1},
-      "i2989": {"rank": 2},
-      "i2990": {"rank": 3},
-      "i2991": {"rank": 4},
-      "i2992": {"rank": 5},
-      "i2993": {"rank": 6},
-      "i2987": {"rank": 3},
-      "i2957": {"rank": 2},
-      "i2996": {"rank": 1},
-      "i2997": {"rank": 2},
-      "i2998": {"rank": 3},
-      "i2999": {"rank": 4},
-      "i3000": {"rank": 5},
-      "i3001": {"rank": 6},
-      "i2995": {"rank": 1},
-      "i3003": {"rank": 1},
-      "i3004": {"rank": 2},
-      "i3005": {"rank": 3},
-      "i3002": {"rank": 2},
-      "i3007": {"rank": 1},
-      "i3008": {"rank": 2},
-      "i3009": {"rank": 3},
-      "i3006": {"rank": 3},
-      "i3011": {"rank": 1},
-      "i3012": {"rank": 2},
-      "i3013": {"rank": 3},
-      "i3010": {"rank": 4},
-      "i2994": {"rank": 3},
-      "i3016": {"rank": 1},
-      "i3017": {"rank": 2},
-      "i3018": {"rank": 3},
-      "i3015": {"rank": 1},
-      "i3020": {"rank": 1},
-      "i3021": {"rank": 2},
-      "i3022": {"rank": 3},
-      "i3019": {"rank": 2},
-      "i3024": {"rank": 1},
-      "i3025": {"rank": 2},
-      "i3026": {"rank": 3},
-      "i3023": {"rank": 3},
-      "i3014": {"rank": 4},
-      "i3027": {"rank": 5, "position": [808.5, 111.25, 1]},
-      "p90": {"rank": -3},
-      "i1905": {"rank": 1},
-      "i1906": {"rank": 2},
-      "i1907": {"rank": 3},
-      "i1904": {"rank": 1},
-      "i1910": {"rank": 1},
-      "i1911": {"rank": 2},
-      "i1912": {"rank": 3},
-      "i1913": {"rank": 4},
-      "i1914": {"rank": 5},
-      "i1909": {"rank": 1},
-      "i1916": {"rank": 1},
-      "i1917": {"rank": 2},
-      "i1918": {"rank": 3},
-      "i1920": {"rank": 1},
-      "i1921": {"rank": 2},
-      "i1919": {"rank": 4},
-      "i1922": {"rank": 5},
-      "i1915": {"rank": 2},
-      "i1924": {"rank": 1},
-      "i1923": {"rank": 3},
-      "i1926": {"rank": 1},
-      "i1927": {"rank": 2},
-      "i1928": {"rank": 3},
-      "i1929": {"rank": 4},
-      "i1925": {"rank": 4},
-      "i1931": {"rank": 1},
-      "i1932": {"rank": 2},
-      "i1930": {"rank": 5},
-      "i1934": {"rank": 1},
-      "i1935": {"rank": 2},
-      "i1933": {"rank": 6},
-      "i1908": {"rank": 2},
-      "i1938": {"rank": 1},
-      "i1939": {"rank": 2},
-      "i1937": {"rank": 1},
-      "i1941": {"rank": 1},
-      "i1940": {"rank": 2},
-      "i1943": {"rank": 1},
-      "i1942": {"rank": 3},
-      "i1945": {"rank": 1},
-      "i1946": {"rank": 2},
-      "i1944": {"rank": 4},
-      "i1948": {"rank": 1},
-      "i1947": {"rank": 5},
-      "i1936": {"rank": 3},
-      "i1950": {"rank": 1},
-      "i1951": {"rank": 2},
-      "i1952": {"rank": 3},
-      "i1953": {"rank": 4},
-      "i1954": {"rank": 5},
-      "i1955": {"rank": 6},
-      "i1956": {"rank": 7},
-      "i1949": {"rank": 4},
-      "i1903": {"rank": 1},
-      "i1960": {"rank": 1},
-      "i1961": {"rank": 2},
-      "i1962": {"rank": 3},
-      "i1963": {"rank": 4},
-      "i1964": {"rank": 5},
-      "i1959": {"rank": 1},
-      "i1966": {"rank": 1},
-      "i1967": {"rank": 2},
-      "i1968": {"rank": 3},
-      "i1969": {"rank": 4},
-      "i1965": {"rank": 2},
-      "i1971": {"rank": 1},
-      "i1972": {"rank": 2},
-      "i1970": {"rank": 3},
-      "i1958": {"rank": 1},
-      "i1975": {"rank": 1},
-      "i1976": {"rank": 2},
-      "i1977": {"rank": 3},
-      "i1974": {"rank": 1},
-      "i1979": {"rank": 1},
-      "i1980": {"rank": 2},
-      "i1978": {"rank": 2},
-      "i1982": {"rank": 1},
-      "i1983": {"rank": 2},
-      "i1981": {"rank": 3},
-      "i1985": {"rank": 1},
-      "i1986": {"rank": 2},
-      "i1984": {"rank": 4},
-      "i1973": {"rank": 2},
-      "i1988": {"rank": 1},
-      "i1989": {"rank": 2},
-      "i1990": {"rank": 3},
-      "i1991": {"rank": 4},
-      "i1992": {"rank": 5},
-      "i1993": {"rank": 6},
-      "i1987": {"rank": 3},
-      "i1957": {"rank": 2},
-      "i1996": {"rank": 1},
-      "i1997": {"rank": 2},
-      "i1998": {"rank": 3},
-      "i1999": {"rank": 4},
-      "i2000": {"rank": 5},
-      "i2001": {"rank": 6},
-      "i1995": {"rank": 1},
-      "i2003": {"rank": 1},
-      "i2004": {"rank": 2},
-      "i2005": {"rank": 3},
-      "i2002": {"rank": 2},
-      "i2007": {"rank": 1},
-      "i2008": {"rank": 2},
-      "i2009": {"rank": 3},
-      "i2006": {"rank": 3},
-      "i2011": {"rank": 1},
-      "i2012": {"rank": 2},
-      "i2013": {"rank": 3},
-      "i2010": {"rank": 4},
-      "i1994": {"rank": 3},
-      "i2016": {"rank": 1},
-      "i2017": {"rank": 2},
-      "i2018": {"rank": 3},
-      "i2015": {"rank": 1},
-      "i2020": {"rank": 1},
-      "i2021": {"rank": 2},
-      "i2022": {"rank": 3},
-      "i2019": {"rank": 2},
-      "i2024": {"rank": 1},
-      "i2025": {"rank": 2},
-      "i2026": {"rank": 3},
-      "i2023": {"rank": 3},
-      "i2014": {"rank": 4},
-      "i2027": {"rank": 5},
-      "p82": {"rank": -2},
-      "i2780": {"rank": 1},
-      "i2781": {"rank": 2},
-      "i2782": {"rank": 3},
-      "i2779": {"rank": 1},
-      "i2785": {"rank": 1},
-      "i2786": {"rank": 2},
-      "i2787": {"rank": 3},
-      "i2788": {"rank": 4},
-      "i2789": {"rank": 5},
-      "i2784": {"rank": 1},
-      "i2791": {"rank": 1},
-      "i2792": {"rank": 2},
-      "i2793": {"rank": 3},
-      "i2795": {"rank": 1},
-      "i2796": {"rank": 2},
-      "i2794": {"rank": 4},
-      "i2797": {"rank": 5},
-      "i2790": {"rank": 2},
-      "i2799": {"rank": 1},
-      "i2798": {"rank": 3},
-      "i2801": {"rank": 1},
-      "i2802": {"rank": 2},
-      "i2803": {"rank": 3},
-      "i2804": {"rank": 4},
-      "i2800": {"rank": 4},
-      "i2806": {"rank": 1},
-      "i2807": {"rank": 2},
-      "i2805": {"rank": 5},
-      "i2809": {"rank": 1},
-      "i2810": {"rank": 2},
-      "i2808": {"rank": 6},
-      "i2783": {"rank": 2},
-      "i2813": {"rank": 1},
-      "i2814": {"rank": 2},
-      "i2812": {"rank": 1},
-      "i2816": {"rank": 1},
-      "i2815": {"rank": 2},
-      "i2818": {"rank": 1},
-      "i2817": {"rank": 3},
-      "i2820": {"rank": 1},
-      "i2821": {"rank": 2},
-      "i2819": {"rank": 4},
-      "i2823": {"rank": 1},
-      "i2822": {"rank": 5},
-      "i2811": {"rank": 3},
-      "i2825": {"rank": 1},
-      "i2826": {"rank": 2},
-      "i2827": {"rank": 3},
-      "i2828": {"rank": 4},
-      "i2829": {"rank": 5},
-      "i2830": {"rank": 6},
-      "i2831": {"rank": 7},
-      "i2824": {"rank": 4},
-      "i2778": {"rank": 1},
-      "i2835": {"rank": 1},
-      "i2836": {"rank": 2},
-      "i2837": {"rank": 3},
-      "i2838": {"rank": 4},
-      "i2839": {"rank": 5},
-      "i2834": {"rank": 1},
-      "i2841": {"rank": 1},
-      "i2842": {"rank": 2},
-      "i2843": {"rank": 3},
-      "i2844": {"rank": 4},
-      "i2840": {"rank": 2},
-      "i2846": {"rank": 1},
-      "i2847": {"rank": 2},
-      "i2845": {"rank": 3},
-      "i2833": {"rank": 1},
-      "i2850": {"rank": 1},
-      "i2851": {"rank": 2},
-      "i2852": {"rank": 3},
-      "i2849": {"rank": 1},
-      "i2854": {"rank": 1},
-      "i2855": {"rank": 2},
-      "i2853": {"rank": 2},
-      "i2857": {"rank": 1},
-      "i2858": {"rank": 2},
-      "i2856": {"rank": 3},
-      "i2860": {"rank": 1},
-      "i2861": {"rank": 2},
-      "i2859": {"rank": 4},
-      "i2848": {"rank": 2},
-      "i2863": {"rank": 1},
-      "i2864": {"rank": 2},
-      "i2865": {"rank": 3},
-      "i2866": {"rank": 4},
-      "i2867": {"rank": 5},
-      "i2868": {"rank": 6},
-      "i2862": {"rank": 3},
-      "i2832": {"rank": 2},
-      "i2871": {"rank": 1},
-      "i2872": {"rank": 2},
-      "i2873": {"rank": 3},
-      "i2874": {"rank": 4},
-      "i2875": {"rank": 5},
-      "i2876": {"rank": 6},
-      "i2870": {"rank": 1},
-      "i2878": {"rank": 1},
-      "i2879": {"rank": 2},
-      "i2880": {"rank": 3},
-      "i2877": {"rank": 2},
-      "i2882": {"rank": 1},
-      "i2883": {"rank": 2},
-      "i2884": {"rank": 3},
-      "i2881": {"rank": 3},
-      "i2886": {"rank": 1},
-      "i2887": {"rank": 2},
-      "i2888": {"rank": 3},
-      "i2885": {"rank": 4},
-      "i2869": {"rank": 3},
-      "i2891": {"rank": 1},
-      "i2892": {"rank": 2},
-      "i2893": {"rank": 3},
-      "i2890": {"rank": 1},
-      "i2895": {"rank": 1},
-      "i2896": {"rank": 2},
-      "i2897": {"rank": 3},
-      "i2894": {"rank": 2},
-      "i2899": {"rank": 1},
-      "i2900": {"rank": 2},
-      "i2901": {"rank": 3},
-      "i2898": {"rank": 3},
-      "i2889": {"rank": 4},
-      "i2902": {"rank": 5},
-      "p89": {"rank": -1},
-      "i1530": {"rank": 1},
-      "i1531": {"rank": 2},
-      "i1532": {"rank": 3},
-      "i1529": {"rank": 1},
-      "i1535": {"rank": 1},
-      "i1536": {"rank": 2},
-      "i1537": {"rank": 3},
-      "i1538": {"rank": 4},
-      "i1539": {"rank": 5},
-      "i1534": {"rank": 1},
-      "i1541": {"rank": 1},
-      "i1542": {"rank": 2},
-      "i1543": {"rank": 3},
-      "i1545": {"rank": 1},
-      "i1546": {"rank": 2},
-      "i1544": {"rank": 4},
-      "i1547": {"rank": 5},
-      "i1540": {"rank": 2},
-      "i1549": {"rank": 1},
-      "i1548": {"rank": 3},
-      "i1551": {"rank": 1},
-      "i1552": {"rank": 2},
-      "i1553": {"rank": 3},
-      "i1554": {"rank": 4},
-      "i1550": {"rank": 4},
-      "i1556": {"rank": 1},
-      "i1557": {"rank": 2},
-      "i1555": {"rank": 5},
-      "i1559": {"rank": 1},
-      "i1560": {"rank": 2},
-      "i1558": {"rank": 6},
-      "i1533": {"rank": 2},
-      "i1563": {"rank": 1},
-      "i1564": {"rank": 2},
-      "i1562": {"rank": 1},
-      "i1566": {"rank": 1},
-      "i1565": {"rank": 2},
-      "i1568": {"rank": 1},
-      "i1567": {"rank": 3},
-      "i1570": {"rank": 1},
-      "i1571": {"rank": 2},
-      "i1569": {"rank": 4},
-      "i1573": {"rank": 1},
-      "i1572": {"rank": 5},
-      "i1561": {"rank": 3},
-      "i1575": {"rank": 1},
-      "i1576": {"rank": 2},
-      "i1577": {"rank": 3},
-      "i1578": {"rank": 4},
-      "i1579": {"rank": 5},
-      "i1580": {"rank": 6},
-      "i1581": {"rank": 7},
-      "i1574": {"rank": 4},
-      "i1528": {"rank": 1},
-      "i1585": {"rank": 1},
-      "i1586": {"rank": 2},
-      "i1587": {"rank": 3},
-      "i1588": {"rank": 4},
-      "i1589": {"rank": 5},
-      "i1584": {"rank": 1},
-      "i1591": {"rank": 1},
-      "i1592": {"rank": 2},
-      "i1593": {"rank": 3},
-      "i1594": {"rank": 4},
-      "i1590": {"rank": 2},
-      "i1596": {"rank": 1},
-      "i1597": {"rank": 2},
-      "i1595": {"rank": 3},
-      "i1583": {"rank": 1},
-      "i1600": {"rank": 1},
-      "i1601": {"rank": 2},
-      "i1602": {"rank": 3},
-      "i1599": {"rank": 1},
-      "i1604": {"rank": 1},
-      "i1605": {"rank": 2},
-      "i1603": {"rank": 2},
-      "i1607": {"rank": 1},
-      "i1608": {"rank": 2},
-      "i1606": {"rank": 3},
-      "i1610": {"rank": 1},
-      "i1611": {"rank": 2},
-      "i1609": {"rank": 4},
-      "i1598": {"rank": 2},
-      "i1613": {"rank": 1},
-      "i1614": {"rank": 2},
-      "i1615": {"rank": 3},
-      "i1616": {"rank": 4},
-      "i1617": {"rank": 5},
-      "i1618": {"rank": 6},
-      "i1612": {"rank": 3},
-      "i1582": {"rank": 2},
-      "i1621": {"rank": 1},
-      "i1622": {"rank": 2},
-      "i1623": {"rank": 3},
-      "i1624": {"rank": 4},
-      "i1625": {"rank": 5},
-      "i1626": {"rank": 6},
-      "i1620": {"rank": 1},
-      "i1628": {"rank": 1},
-      "i1629": {"rank": 2},
-      "i1630": {"rank": 3},
-      "i1627": {"rank": 2},
-      "i1632": {"rank": 1},
-      "i1633": {"rank": 2},
-      "i1634": {"rank": 3},
-      "i1631": {"rank": 3},
-      "i1636": {"rank": 1},
-      "i1637": {"rank": 2},
-      "i1638": {"rank": 3},
-      "i1635": {"rank": 4},
-      "i1619": {"rank": 3},
-      "i1641": {"rank": 1},
-      "i1642": {"rank": 2},
-      "i1643": {"rank": 3},
-      "i1640": {"rank": 1},
-      "i1645": {"rank": 1},
-      "i1646": {"rank": 2},
-      "i1647": {"rank": 3},
-      "i1644": {"rank": 2},
-      "i1649": {"rank": 1},
-      "i1650": {"rank": 2},
-      "i1651": {"rank": 3},
-      "i1648": {"rank": 3},
-      "i1639": {"rank": 4},
-      "i1652": {"rank": 5, "position": [727.5, 333.5, 1]},
-      "p79": {"rank": 1},
-      "i1405": {"rank": 1},
-      "i1406": {"rank": 2},
-      "i1407": {"rank": 3},
-      "i1404": {"rank": 1},
-      "i1410": {"rank": 1},
-      "i1411": {"rank": 2},
-      "i1412": {"rank": 3},
-      "i1413": {"rank": 4},
-      "i1414": {"rank": 5},
-      "i1409": {"rank": 1},
-      "i1416": {"rank": 1},
-      "i1417": {"rank": 2},
-      "i1418": {"rank": 3},
-      "i1420": {"rank": 1},
-      "i1421": {"rank": 2},
-      "i1419": {"rank": 4},
-      "i1422": {"rank": 5},
-      "i1415": {"rank": 2},
-      "i1424": {"rank": 1},
-      "i1423": {"rank": 3},
-      "i1426": {"rank": 1},
-      "i1427": {"rank": 2},
-      "i1428": {"rank": 3},
-      "i1429": {"rank": 4},
-      "i1425": {"rank": 4},
-      "i1431": {"rank": 1},
-      "i1432": {"rank": 2},
-      "i1430": {"rank": 5},
-      "i1434": {"rank": 1},
-      "i1435": {"rank": 2},
-      "i1433": {"rank": 6},
-      "i1408": {"rank": 2},
-      "i1438": {"rank": 1},
-      "i1439": {"rank": 2},
-      "i1437": {"rank": 1},
-      "i1441": {"rank": 1},
-      "i1440": {"rank": 2},
-      "i1443": {"rank": 1},
-      "i1442": {"rank": 3},
-      "i1445": {"rank": 1},
-      "i1446": {"rank": 2},
-      "i1444": {"rank": 4},
-      "i1448": {"rank": 1},
-      "i1447": {"rank": 5},
-      "i1436": {"rank": 3},
-      "i1450": {"rank": 1},
-      "i1451": {"rank": 2},
-      "i1452": {"rank": 3},
-      "i1453": {"rank": 4},
-      "i1454": {"rank": 5},
-      "i1455": {"rank": 6},
-      "i1456": {"rank": 7},
-      "i1449": {"rank": 4},
-      "i1403": {"rank": 1},
-      "i1460": {"rank": 1},
-      "i1461": {"rank": 2},
-      "i1462": {"rank": 3},
-      "i1463": {"rank": 4},
-      "i1464": {"rank": 5},
-      "i1459": {"rank": 1},
-      "i1466": {"rank": 1},
-      "i1467": {"rank": 2},
-      "i1468": {"rank": 3},
-      "i1469": {"rank": 4},
-      "i1465": {"rank": 2},
-      "i1471": {"rank": 1},
-      "i1472": {"rank": 2},
-      "i1470": {"rank": 3},
-      "i1458": {"rank": 1},
-      "i1475": {"rank": 1},
-      "i1476": {"rank": 2},
-      "i1477": {"rank": 3},
-      "i1474": {"rank": 1},
-      "i1479": {"rank": 1},
-      "i1480": {"rank": 2},
-      "i1478": {"rank": 2},
-      "i1482": {"rank": 1},
-      "i1483": {"rank": 2},
-      "i1481": {"rank": 3},
-      "i1485": {"rank": 1},
-      "i1486": {"rank": 2},
-      "i1484": {"rank": 4},
-      "i1473": {"rank": 2},
-      "i1488": {"rank": 1},
-      "i1489": {"rank": 2},
-      "i1490": {"rank": 3},
-      "i1491": {"rank": 4},
-      "i1492": {"rank": 5},
-      "i1493": {"rank": 6},
-      "i1487": {"rank": 3},
-      "i1457": {"rank": 2},
-      "i1496": {"rank": 1},
-      "i1497": {"rank": 2},
-      "i1498": {"rank": 3},
-      "i1499": {"rank": 4},
-      "i1500": {"rank": 5},
-      "i1501": {"rank": 6},
-      "i1495": {"rank": 1},
-      "i1503": {"rank": 1},
-      "i1504": {"rank": 2},
-      "i1505": {"rank": 3},
-      "i1502": {"rank": 2},
-      "i1507": {"rank": 1},
-      "i1508": {"rank": 2},
-      "i1509": {"rank": 3},
-      "i1506": {"rank": 3},
-      "i1511": {"rank": 1},
-      "i1512": {"rank": 2},
-      "i1513": {"rank": 3},
-      "i1510": {"rank": 4},
-      "i1494": {"rank": 3},
-      "i1516": {"rank": 1},
-      "i1517": {"rank": 2},
-      "i1518": {"rank": 3},
-      "i1515": {"rank": 1},
-      "i1520": {"rank": 1},
-      "i1521": {"rank": 2},
-      "i1522": {"rank": 3},
-      "i1519": {"rank": 2},
-      "i1524": {"rank": 1},
-      "i1525": {"rank": 2},
-      "i1526": {"rank": 3},
-      "i1523": {"rank": 3},
-      "i1514": {"rank": 4},
-      "i1527": {"rank": 5},
-      "p78": {"rank": 2},
-      "i3363": {"rank": 1},
-      "i3364": {"rank": 2},
-      "i3365": {"rank": 3},
-      "i3366": {"rank": 4},
-      "i2155": {"rank": 1},
-      "i2156": {"rank": 2},
-      "i2157": {"rank": 3},
-      "i2154": {"rank": 1},
-      "i2160": {"rank": 1},
-      "i2161": {"rank": 2},
-      "i2162": {"rank": 3},
-      "i2163": {"rank": 4},
-      "i2164": {"rank": 5},
-      "i2159": {"rank": 1},
-      "i2166": {"rank": 1},
-      "i2167": {"rank": 2},
-      "i2168": {"rank": 3},
-      "i2170": {"rank": 1},
-      "i2171": {"rank": 2},
-      "i2169": {"rank": 4},
-      "i2172": {"rank": 5},
-      "i2165": {"rank": 2},
-      "i2174": {"rank": 1},
-      "i2173": {"rank": 3},
-      "i2176": {"rank": 1},
-      "i2177": {"rank": 2},
-      "i2178": {"rank": 3},
-      "i2179": {"rank": 4},
-      "i2175": {"rank": 4},
-      "i2181": {"rank": 1},
-      "i2182": {"rank": 2},
-      "i2180": {"rank": 5},
-      "i2184": {"rank": 1},
-      "i2185": {"rank": 2},
-      "i2183": {"rank": 6},
-      "i2158": {"rank": 2},
-      "i2188": {"rank": 1},
-      "i2189": {"rank": 2},
-      "i2187": {"rank": 1},
-      "i2191": {"rank": 1},
-      "i2190": {"rank": 2},
-      "i2193": {"rank": 1},
-      "i2192": {"rank": 3},
-      "i2195": {"rank": 1},
-      "i2196": {"rank": 2},
-      "i2194": {"rank": 4},
-      "i2198": {"rank": 1},
-      "i2197": {"rank": 5},
-      "i2186": {"rank": 3},
-      "i2200": {"rank": 1},
-      "i2201": {"rank": 2},
-      "i2202": {"rank": 3},
-      "i2203": {"rank": 4},
-      "i2204": {"rank": 5},
-      "i2205": {"rank": 6},
-      "i2206": {"rank": 7},
-      "i2199": {"rank": 4},
-      "i2153": {"rank": 1},
-      "i2210": {"rank": 1},
-      "i2211": {"rank": 2},
-      "i2212": {"rank": 3},
-      "i2213": {"rank": 4},
-      "i2214": {"rank": 5},
-      "i2209": {"rank": 1},
-      "i2216": {"rank": 1},
-      "i2217": {"rank": 2},
-      "i2218": {"rank": 3},
-      "i2219": {"rank": 4},
-      "i2215": {"rank": 2},
-      "i2221": {"rank": 1},
-      "i2222": {"rank": 2},
-      "i2220": {"rank": 3},
-      "i2208": {"rank": 1},
-      "i2225": {"rank": 1},
-      "i2226": {"rank": 2},
-      "i2227": {"rank": 3},
-      "i2224": {"rank": 1},
-      "i2229": {"rank": 1},
-      "i2230": {"rank": 2},
-      "i2228": {"rank": 2},
-      "i2232": {"rank": 1},
-      "i2233": {"rank": 2},
-      "i2231": {"rank": 3},
-      "i2235": {"rank": 1},
-      "i2236": {"rank": 2},
-      "i2234": {"rank": 4},
-      "i2223": {"rank": 2},
-      "i2238": {"rank": 1},
-      "i2239": {"rank": 2},
-      "i2240": {"rank": 3},
-      "i2241": {"rank": 4},
-      "i2242": {"rank": 5},
-      "i2243": {"rank": 6},
-      "i2237": {"rank": 3},
-      "i2207": {"rank": 2},
-      "i2246": {"rank": 1},
-      "i2247": {"rank": 2},
-      "i2248": {"rank": 3},
-      "i2249": {"rank": 4},
-      "i2250": {"rank": 5},
-      "i2251": {"rank": 6},
-      "i2245": {"rank": 1},
-      "i2253": {"rank": 1},
-      "i2254": {"rank": 2},
-      "i2255": {"rank": 3},
-      "i2252": {"rank": 2},
-      "i2257": {"rank": 1},
-      "i2258": {"rank": 2},
-      "i2259": {"rank": 3},
-      "i2256": {"rank": 3},
-      "i2261": {"rank": 1},
-      "i2262": {"rank": 2},
-      "i2263": {"rank": 3},
-      "i2260": {"rank": 4},
-      "i2244": {"rank": 3},
-      "i2266": {"rank": 1},
-      "i2267": {"rank": 2},
-      "i2268": {"rank": 3},
-      "i2265": {"rank": 1},
-      "i2270": {"rank": 1},
-      "i2271": {"rank": 2},
-      "i2272": {"rank": 3},
-      "i2269": {"rank": 2},
-      "i2274": {"rank": 1},
-      "i2275": {"rank": 2},
-      "i2276": {"rank": 3},
-      "i2273": {"rank": 3},
-      "i2264": {"rank": 4},
-      "i2277": {"rank": 5},
-      "p84": {"rank": 3},
-      "i2405": {"rank": 1},
-      "i2406": {"rank": 2},
-      "i2407": {"rank": 3},
-      "i2404": {"rank": 1},
-      "i2410": {"rank": 1},
-      "i2411": {"rank": 2},
-      "i2412": {"rank": 3},
-      "i2413": {"rank": 4},
-      "i2414": {"rank": 5},
-      "i2409": {"rank": 1},
-      "i2416": {"rank": 1},
-      "i2417": {"rank": 2},
-      "i2418": {"rank": 3},
-      "i2420": {"rank": 1},
-      "i2421": {"rank": 2},
-      "i2419": {"rank": 4},
-      "i2422": {"rank": 5},
-      "i2415": {"rank": 2},
-      "i2424": {"rank": 1},
-      "i2423": {"rank": 3},
-      "i2426": {"rank": 1},
-      "i2427": {"rank": 2},
-      "i2428": {"rank": 3},
-      "i2429": {"rank": 4},
-      "i2425": {"rank": 4},
-      "i2431": {"rank": 1},
-      "i2432": {"rank": 2},
-      "i2430": {"rank": 5},
-      "i2434": {"rank": 1},
-      "i2435": {"rank": 2},
-      "i2433": {"rank": 6},
-      "i2408": {"rank": 2},
-      "i2438": {"rank": 1},
-      "i2439": {"rank": 2},
-      "i2437": {"rank": 1},
-      "i2441": {"rank": 1},
-      "i2440": {"rank": 2},
-      "i2443": {"rank": 1},
-      "i2442": {"rank": 3},
-      "i2445": {"rank": 1},
-      "i2446": {"rank": 2},
-      "i2444": {"rank": 4},
-      "i2448": {"rank": 1},
-      "i2447": {"rank": 5},
-      "i2436": {"rank": 3},
-      "i2450": {"rank": 1},
-      "i2451": {"rank": 2},
-      "i2452": {"rank": 3},
-      "i2453": {"rank": 4},
-      "i2454": {"rank": 5},
-      "i2455": {"rank": 6},
-      "i2456": {"rank": 7},
-      "i2449": {"rank": 4},
-      "i2403": {"rank": 1},
-      "i2460": {"rank": 1},
-      "i2461": {"rank": 2},
-      "i2462": {"rank": 3},
-      "i2463": {"rank": 4},
-      "i2464": {"rank": 5},
-      "i2459": {"rank": 1},
-      "i2466": {"rank": 1},
-      "i2467": {"rank": 2},
-      "i2468": {"rank": 3},
-      "i2469": {"rank": 4},
-      "i2465": {"rank": 2},
-      "i2471": {"rank": 1},
-      "i2472": {"rank": 2},
-      "i2470": {"rank": 3},
-      "i2458": {"rank": 1},
-      "i2475": {"rank": 1},
-      "i2476": {"rank": 2},
-      "i2477": {"rank": 3},
-      "i2474": {"rank": 1},
-      "i2479": {"rank": 1},
-      "i2480": {"rank": 2},
-      "i2478": {"rank": 2},
-      "i2482": {"rank": 1},
-      "i2483": {"rank": 2},
-      "i2481": {"rank": 3},
-      "i2485": {"rank": 1},
-      "i2486": {"rank": 2},
-      "i2484": {"rank": 4},
-      "i2473": {"rank": 2},
-      "i2488": {"rank": 1},
-      "i2489": {"rank": 2},
-      "i2490": {"rank": 3},
-      "i2491": {"rank": 4},
-      "i2492": {"rank": 5},
-      "i2493": {"rank": 6},
-      "i2487": {"rank": 3},
-      "i2457": {"rank": 2},
-      "i2496": {"rank": 1},
-      "i2497": {"rank": 2},
-      "i2498": {"rank": 3},
-      "i2499": {"rank": 4},
-      "i2500": {"rank": 5},
-      "i2501": {"rank": 6},
-      "i2495": {"rank": 1},
-      "i2503": {"rank": 1},
-      "i2504": {"rank": 2},
-      "i2505": {"rank": 3},
-      "i2502": {"rank": 2},
-      "i2507": {"rank": 1},
-      "i2508": {"rank": 2},
-      "i2509": {"rank": 3},
-      "i2506": {"rank": 3},
-      "i2511": {"rank": 1},
-      "i2512": {"rank": 2},
-      "i2513": {"rank": 3},
-      "i2510": {"rank": 4},
-      "i2494": {"rank": 3},
-      "i2516": {"rank": 1},
-      "i2517": {"rank": 2},
-      "i2518": {"rank": 3},
-      "i2515": {"rank": 1},
-      "i2514": {"rank": 4},
-      "i2527": {"rank": 5, "position": [291.99426556054686, 101.83236694335938, 1]},
-      "p86": {"rank": 4},
-      "i2030": {"rank": 1},
-      "i2031": {"rank": 2},
-      "i2032": {"rank": 3},
-      "i2029": {"rank": 1},
-      "i2035": {"rank": 1},
-      "i2036": {"rank": 2},
-      "i2037": {"rank": 3},
-      "i2038": {"rank": 4},
-      "i2039": {"rank": 5},
-      "i2034": {"rank": 1},
-      "i2033": {"rank": 2},
-      "i2063": {"rank": 1},
-      "i2064": {"rank": 2},
-      "i2062": {"rank": 1},
-      "i2068": {"rank": 1},
-      "i2067": {"rank": 2},
-      "i2073": {"rank": 1},
-      "i2072": {"rank": 3},
-      "i2061": {"rank": 3},
-      "i2075": {"rank": 1},
-      "i2076": {"rank": 2},
-      "i2077": {"rank": 3},
-      "i2078": {"rank": 4},
-      "i2079": {"rank": 5},
-      "i2080": {"rank": 6},
-      "i2081": {"rank": 7},
-      "i2074": {"rank": 4},
-      "i2524": {"rank": 1},
-      "i2525": {"rank": 2},
-      "i2526": {"rank": 3},
-      "i2523": {"rank": 5},
-      "i2028": {"rank": 1},
-      "i2085": {"rank": 1},
-      "i2086": {"rank": 2},
-      "i2087": {"rank": 3},
-      "i2088": {"rank": 4},
-      "i2089": {"rank": 5},
-      "i2084": {"rank": 1},
-      "i2091": {"rank": 1},
-      "i2092": {"rank": 2},
-      "i2093": {"rank": 3},
-      "i2094": {"rank": 4},
-      "i2090": {"rank": 2},
-      "i2096": {"rank": 1},
-      "i2097": {"rank": 2},
-      "i2095": {"rank": 3},
-      "i2083": {"rank": 1},
-      "i2100": {"rank": 1},
-      "i2101": {"rank": 2},
-      "i2102": {"rank": 3},
-      "i2099": {"rank": 1},
-      "i2104": {"rank": 1},
-      "i2105": {"rank": 2},
-      "i2103": {"rank": 2},
-      "i2107": {"rank": 1},
-      "i2108": {"rank": 2},
-      "i2106": {"rank": 3},
-      "i2110": {"rank": 1},
-      "i2111": {"rank": 2},
-      "i2109": {"rank": 4},
-      "i2098": {"rank": 2},
-      "i2113": {"rank": 1},
-      "i2114": {"rank": 2},
-      "i2115": {"rank": 3},
-      "i2116": {"rank": 4},
-      "i2117": {"rank": 5},
-      "i2118": {"rank": 6},
-      "i2112": {"rank": 3},
-      "i2082": {"rank": 1},
-      "i2121": {"rank": 1},
-      "i2122": {"rank": 2},
-      "i2123": {"rank": 3},
-      "i2124": {"rank": 4},
-      "i2125": {"rank": 5},
-      "i2126": {"rank": 6},
-      "i2120": {"rank": 2},
-      "i2132": {"rank": 1},
-      "i2133": {"rank": 2},
-      "i2134": {"rank": 3},
-      "i2131": {"rank": 3},
-      "i2136": {"rank": 1},
-      "i2137": {"rank": 2},
-      "i2138": {"rank": 3},
-      "i2135": {"rank": 4},
-      "i2119": {"rank": 2},
-      "i2141": {"rank": 1},
-      "i2142": {"rank": 2},
-      "i2143": {"rank": 3},
-      "i2140": {"rank": 1},
-      "i2145": {"rank": 1},
-      "i2146": {"rank": 2},
-      "i2147": {"rank": 3},
-      "i2144": {"rank": 2},
-      "i2149": {"rank": 1},
-      "i2150": {"rank": 2},
-      "i2151": {"rank": 3},
-      "i2148": {"rank": 3},
-      "i2139": {"rank": 3},
-      "i2152": {"rank": 4},
-      "i2520": {"rank": 1},
-      "i2521": {"rank": 2},
-      "i2522": {"rank": 3},
-      "i2519": {"rank": 5},
-      "p83": {"rank": 5},
-      "i1780": {"rank": 1},
-      "i1781": {"rank": 2},
-      "i1782": {"rank": 3},
-      "i1779": {"rank": 1},
-      "i1785": {"rank": 1},
-      "i1786": {"rank": 2},
-      "i1787": {"rank": 3},
-      "i1788": {"rank": 4},
-      "i1789": {"rank": 5},
-      "i1784": {"rank": 1},
-      "i1791": {"rank": 1},
-      "i1792": {"rank": 2},
-      "i1793": {"rank": 3},
-      "i1795": {"rank": 1},
-      "i1796": {"rank": 2},
-      "i1794": {"rank": 4},
-      "i1797": {"rank": 5},
-      "i1790": {"rank": 2},
-      "i1799": {"rank": 1},
-      "i1798": {"rank": 3},
-      "i1801": {"rank": 1},
-      "i1802": {"rank": 2},
-      "i1803": {"rank": 3},
-      "i1804": {"rank": 4},
-      "i1800": {"rank": 4},
-      "i1806": {"rank": 1},
-      "i1807": {"rank": 2},
-      "i1805": {"rank": 5},
-      "i1809": {"rank": 1},
-      "i1810": {"rank": 2},
-      "i1808": {"rank": 6},
-      "i1783": {"rank": 2},
-      "i1813": {"rank": 1},
-      "i1814": {"rank": 2},
-      "i1812": {"rank": 1},
-      "i1816": {"rank": 1},
-      "i1815": {"rank": 2},
-      "i1818": {"rank": 1},
-      "i1817": {"rank": 3},
-      "i1820": {"rank": 1},
-      "i1821": {"rank": 2},
-      "i1819": {"rank": 4},
-      "i1823": {"rank": 1},
-      "i1822": {"rank": 5},
-      "i1811": {"rank": 3},
-      "i1825": {"rank": 1},
-      "i1826": {"rank": 2},
-      "i1827": {"rank": 3},
-      "i1828": {"rank": 4},
-      "i1829": {"rank": 5},
-      "i1830": {"rank": 6},
-      "i1831": {"rank": 7},
-      "i1824": {"rank": 4},
-      "i1778": {"rank": 1},
-      "i1835": {"rank": 1},
-      "i1836": {"rank": 2},
-      "i1837": {"rank": 3},
-      "i1838": {"rank": 4},
-      "i1839": {"rank": 5},
-      "i1834": {"rank": 1},
-      "i1841": {"rank": 1},
-      "i1842": {"rank": 2},
-      "i1843": {"rank": 3},
-      "i1844": {"rank": 4},
-      "i1840": {"rank": 2},
-      "i1846": {"rank": 1},
-      "i1847": {"rank": 2},
-      "i1845": {"rank": 3},
-      "i1833": {"rank": 1},
-      "i1850": {"rank": 1},
-      "i1851": {"rank": 2},
-      "i1852": {"rank": 3},
-      "i1849": {"rank": 1},
-      "i1854": {"rank": 1},
-      "i1855": {"rank": 2},
-      "i1853": {"rank": 2},
-      "i1857": {"rank": 1},
-      "i1858": {"rank": 2},
-      "i1856": {"rank": 3},
-      "i1860": {"rank": 1},
-      "i1861": {"rank": 2},
-      "i1859": {"rank": 4},
-      "i1848": {"rank": 2},
-      "i1863": {"rank": 1},
-      "i1864": {"rank": 2},
-      "i1865": {"rank": 3},
-      "i1866": {"rank": 4},
-      "i1867": {"rank": 5},
-      "i1868": {"rank": 6},
-      "i1862": {"rank": 3},
-      "i1832": {"rank": 2},
-      "i1871": {"rank": 1},
-      "i1872": {"rank": 2},
-      "i1873": {"rank": 3},
-      "i1874": {"rank": 4},
-      "i1875": {"rank": 5},
-      "i1876": {"rank": 6},
-      "i1870": {"rank": 1},
-      "i1878": {"rank": 1},
-      "i1879": {"rank": 2},
-      "i1880": {"rank": 3},
-      "i1877": {"rank": 2},
-      "i1882": {"rank": 1},
-      "i1883": {"rank": 2},
-      "i1884": {"rank": 3},
-      "i1881": {"rank": 3},
-      "i1886": {"rank": 1},
-      "i1887": {"rank": 2},
-      "i1888": {"rank": 3},
-      "i1885": {"rank": 4},
-      "i1869": {"rank": 3},
-      "i1891": {"rank": 1},
-      "i1892": {"rank": 2},
-      "i1893": {"rank": 3},
-      "i1890": {"rank": 1},
-      "i1895": {"rank": 1},
-      "i1896": {"rank": 2},
-      "i1897": {"rank": 3},
-      "i1894": {"rank": 2},
-      "i1899": {"rank": 1},
-      "i1900": {"rank": 2},
-      "i1901": {"rank": 3},
-      "i1898": {"rank": 3},
-      "i1889": {"rank": 4},
-      "i1902": {"rank": 5},
-      "p81": {"rank": 6},
-      "p144": {"rank": 7},
-      "p108": {}
-    };
-    var layout = {
-
-
-
-
-    "i29165":{"rank":"1"},
-    "i31468":{"rank":"2"},
-    "i5672":{"rank":"-8"},
-    "i22466":{"rank":"1"},
-    "i22467":{"rank":"2"},
-    "i22471":{"rank":"3"},
-    "i22498":{"rank":"4"},
-    "i22499":{"rank":"1"},
-    "i22755":{"rank":"2"},
-    "i23301":{"rank":"3"},
-    "i25078":{"rank":"4"},
-    "i26106":{"rank":"5"},
-    "i26324":{"rank":"1"},
-    "i28175":{"rank":"2"},
-    "i26323":{"rank":"6"},
-    "i26699":{"rank":"7"},
-    "i26735":{"rank":"8"},
-    "i26830":{"rank":"9"},
-    "i26927":{"rank":"10"},
-    "i27825":{"rank":"11"},
-    "i27854":{"rank":"12"},
-    "i28165":{"rank":"13"},
-    "i23300":{"rank":"5"},
-    "i22497":{"rank":"1"},
-    "i25071":{"rank":"2"},
-    "i25072":{"rank":"3"},
-    "i25912":{"rank":"4"},
-    "i25993":{"rank":"5"},
-    "i23348":{"rank":"6"},
-    "i25066":{"rank":"1"},
-    "i25067":{"rank":"2"},
-    "i26292":{"rank":"3"},
-    "i23349":{"rank":"7"},
-    "i25127":{"rank":"1"},
-    "i25146":{"rank":"2"},
-    "i26700":{"rank":"3"},
-    "i23350":{"rank":"8"},
-    "i25068":{"rank":"1"},
-    "i25165":{"rank":"2"},
-    "i25910":{"rank":"3"},
-    "i25932":{"rank":"4"},
-    "i26364":{"rank":"5"},
-    "i26366":{"rank":"6"},
-    "i26371":{"rank":"7"},
-    "i27760":{"rank":"8"},
-    "i27795":{"rank":"9"},
-    "i27796":{"rank":"10"},
-    "i28158":{"rank":"11"},
-    "i28917":{"rank":"12"},
-    "i29009":{"rank":"13"},
-    "i29073":{"rank":"14"},
-    "i23351":{"rank":"9"},
-    "i23367":{"rank":"1"},
-    "i23366":{"rank":"10"},
-    "i23615":{"rank":"1"},
-    "i23620":{"rank":"2"},
-    "i23621":{"rank":"3"},
-    "i23622":{"rank":"4"},
-    "i25166":{"rank":"5"},
-    "i25196":{"rank":"6"},
-    "i25281":{"rank":"7"},
-    "i26734":{"rank":"8"},
-    "i27108":{"rank":"9"},
-    "i27797":{"rank":"10"},
-    "i28912":{"rank":"11"},
-    "i23614":{"rank":"11"},
-    "i23623":{"rank":"1"},
-    "i23624":{"rank":"2"},
-    "i25700":{"rank":"3"},
-    "i23616":{"rank":"12"},
-    "i25161":{"rank":"1"},
-    "i25162":{"rank":"2"},
-    "i25163":{"rank":"3"},
-    "i25164":{"rank":"4"},
-    "i25167":{"rank":"5"},
-    "i25169":{"rank":"6"},
-    "i25973":{"rank":"7"},
-    "i25992":{"rank":"8"},
-    "i26351":{"rank":"9"},
-    "i26738":{"rank":"10"},
-    "i23617":{"rank":"13"},
-    "i26331":{"rank":"1"},
-    "i28176":{"rank":"2"},
-    "i23618":{"rank":"14"},
-    "i25154":{"rank":"1"},
-    "i25155":{"rank":"2"},
-    "i25156":{"rank":"3"},
-    "i25157":{"rank":"4"},
-    "i25158":{"rank":"5"},
-    "i25918":{"rank":"6"},
-    "i25971":{"rank":"7"},
-    "i26828":{"rank":"8"},
-    "i23619":{"rank":"15"},
-    "i23828":{"rank":"1"},
-    "i24957":{"rank":"2"},
-    "i25170":{"rank":"3"},
-    "i25390":{"rank":"4"},
-    "i25402":{"rank":"5"},
-    "i25942":{"rank":"6"},
-    "i25984":{"rank":"7"},
-    "i26291":{"rank":"8"},
-    "i26322":{"rank":"9"},
-    "i26325":{"rank":"10"},
-    "i26330":{"rank":"11"},
-    "i27886":{"rank":"12"},
-    "i28157":{"rank":"13"},
-    "i28875":{"rank":"14"},
-    "i23827":{"rank":"16"},
-    "i23876":{"rank":"1"},
-    "i25032":{"rank":"2"},
-    "i27793":{"rank":"3"},
-    "i23866":{"rank":"1"},
-    "i23878":{"rank":"1"},
-    "i24245":{"rank":"2"},
-    "i26220":{"rank":"3"},
-    "i26221":{"rank":"4"},
-    "i27792":{"rank":"5"},
-    "i27826":{"rank":"6"},
-    "i27950":{"rank":"7"},
-    "i28350":{"rank":"8"},
-    "i28896":{"rank":"9"},
-    "i23867":{"rank":"2"},
-    "i23869":{"rank":"3"},
-    "i27895":{"rank":"1"},
-    "i23870":{"rank":"4"},
-    "i23888":{"rank":"5"},
-    "i25120":{"rank":"6"},
-    "i27893":{"rank":"1"},
-    "i27894":{"rank":"2"},
-    "i25313":{"rank":"7"},
-    "i25349":{"rank":"8"},
-    "i27794":{"rank":"9"},
-    "i28159":{"rank":"10"},
-    "i23865":{"rank":"17"},
-    "i23877":{"rank":"18"},
-    "i25168":{"rank":"1"},
-    "i25233":{"rank":"2"},
-    "i25235":{"rank":"3"},
-    "i24976":{"rank":"19"},
-    "i25160":{"rank":"1"},
-    "i25972":{"rank":"2"},
-    "i24977":{"rank":"20"},
-    "i25258":{"rank":"1"},
-    "i26736":{"rank":"2"},
-    "i24978":{"rank":"21"},
-    "i25159":{"rank":"1"},
-    "i24979":{"rank":"22"},
-    "i25034":{"rank":"1"},
-    "i25036":{"rank":"2"},
-    "i25038":{"rank":"3"},
-    "i25039":{"rank":"4"},
-    "i25040":{"rank":"5"},
-    "i25041":{"rank":"6"},
-    "i25042":{"rank":"7"},
-    "i25043":{"rank":"8"},
-    "i25044":{"rank":"9"},
-    "i25045":{"rank":"10"},
-    "i25046":{"rank":"11"},
-    "i25047":{"rank":"12"},
-    "i25048":{"rank":"13"},
-    "i25049":{"rank":"14"},
-    "i25050":{"rank":"15"},
-    "i25052":{"rank":"16"},
-    "i25053":{"rank":"17"},
-    "i25054":{"rank":"18"},
-    "i25056":{"rank":"19"},
-    "i25058":{"rank":"20"},
-    "i25059":{"rank":"21"},
-    "i25282":{"rank":"22"},
-    "i25312":{"rank":"23"},
-    "i26147":{"rank":"24"},
-    "i26224":{"rank":"25"},
-    "i26225":{"rank":"26"},
-    "i27887":{"rank":"27"},
-    "i28895":{"rank":"28"},
-    "i28949":{"rank":"29"},
-    "i25033":{"rank":"23"},
-    "i25035":{"rank":"1"},
-    "i25037":{"rank":"2"},
-    "i25057":{"rank":"3"},
-    "i25060":{"rank":"4"},
-    "i25061":{"rank":"5"},
-    "i25062":{"rank":"6"},
-    "i25063":{"rank":"7"},
-    "i25064":{"rank":"8"},
-    "i25065":{"rank":"9"},
-    "i25051":{"rank":"24"},
-    "i25070":{"rank":"1"},
-    "i25069":{"rank":"25"},
-    "i25915":{"rank":"1"},
-    "i25914":{"rank":"26"},
-    "i25917":{"rank":"1"},
-    "i25916":{"rank":"27"},
-    "i28943":{"rank":"28"},
-    "i28944":{"rank":"29"},
-    "i28950":{"rank":"30"},
-    "i29044":{"rank":"1"},
-    "i29051":{"rank":"2"},
-    "i29052":{"rank":"3"},
-    "i29050":{"rank":"31"},
-    "i29298":{"rank":"32"},
-    "i30050":{"rank":"1"},
-    "i30051":{"rank":"2"},
-    "i30052":{"rank":"3"},
-    "i30053":{"rank":"4"},
-    "i30054":{"rank":"5"},
-    "i30049":{"rank":"33"},
-    "p583":{"rank":"1"},
-    "i19926":{"rank":"1"},
-    "i19937":{"rank":"1"},
-    "i19927":{"rank":"1"},
-    "i19928":{"rank":"2"},
-    "i19929":{"rank":"3"},
-    "i19930":{"rank":"4"},
-    "i19931":{"rank":"5"},
-    "i19932":{"rank":"6"},
-    "i19933":{"rank":"7"},
-    "i19934":{"rank":"8"},
-    "i19935":{"rank":"9"},
-    "i19936":{"rank":"10"},
-    "i19938":{"rank":"2"},
-    "i19925":{"rank":"1"},
-    "p554":{"rank":"2"},
-    "i26414":{"rank":"1"},
-    "i26487":{"rank":"1"},
-    "i26488":{"rank":"2"},
-    "i26489":{"rank":"3"},
-    "i26580":{"rank":"4"},
-    "i26415":{"rank":"2"},
-    "i26416":{"rank":"3"},
-    "i26417":{"rank":"4"},
-    "i26398":{"rank":"1"},
-    "i26401":{"rank":"1"},
-    "i26402":{"rank":"2"},
-    "i26404":{"rank":"3"},
-    "i26407":{"rank":"4"},
-    "i26411":{"rank":"5"},
-    "i26412":{"rank":"6"},
-    "i26422":{"rank":"7"},
-    "i26832":{"rank":"1"},
-    "i26833":{"rank":"2"},
-    "i26525":{"rank":"8"},
-    "i26528":{"rank":"9"},
-    "i26556":{"rank":"10"},
-    "i26399":{"rank":"2"},
-    "i26585":{"rank":"1"},
-    "i26586":{"rank":"2"},
-    "i27746":{"rank":"3"},
-    "i28064":{"rank":"4"},
-    "i26400":{"rank":"3"},
-    "i26503":{"rank":"1"},
-    "i26557":{"rank":"2"},
-    "i26449":{"rank":"4"},
-    "i27726":{"rank":"1"},
-    "i27728":{"rank":"2"},
-    "i27730":{"rank":"3"},
-    "i27725":{"rank":"5"},
-    "p615":{"rank":"3"},
-    "i18940":{"rank":"1"},
-    "i19177":{"rank":"1"},
-    "i19175":{"rank":"1"},
-    "i19172":{"rank":"2"},
-    "i24714":{"rank":"1"},
-    "i24715":{"rank":"2"},
-    "i24716":{"rank":"3"},
-    "i24717":{"rank":"4"},
-    "i24718":{"rank":"5"},
-    "i24719":{"rank":"6"},
-    "i24720":{"rank":"7"},
-    "i24721":{"rank":"8"},
-    "i24722":{"rank":"9"},
-    "i24723":{"rank":"10"},
-    "i24724":{"rank":"11"},
-    "i24725":{"rank":"12"},
-    "i24726":{"rank":"13"},
-    "i24727":{"rank":"14"},
-    "i24728":{"rank":"15"},
-    "i24729":{"rank":"16"},
-    "i24730":{"rank":"17"},
-    "i24731":{"rank":"18"},
-    "i24732":{"rank":"19"},
-    "i24733":{"rank":"20"},
-    "i24734":{"rank":"21"},
-    "i24735":{"rank":"22"},
-    "i24736":{"rank":"23"},
-    "i24737":{"rank":"24"},
-    "i24738":{"rank":"25"},
-    "i24739":{"rank":"26"},
-    "i24740":{"rank":"27"},
-    "i24713":{"rank":"1"},
-    "i24712":{"rank":"3"},
-    "i24741":{"rank":"4"},
-    "i24745":{"rank":"1"},
-    "i24746":{"rank":"2"},
-    "i24747":{"rank":"3"},
-    "i24744":{"rank":"1"},
-    "i24749":{"rank":"1"},
-    "i24750":{"rank":"2"},
-    "i24751":{"rank":"3"},
-    "i24748":{"rank":"2"},
-    "i24753":{"rank":"1"},
-    "i24754":{"rank":"2"},
-    "i24755":{"rank":"3"},
-    "i24752":{"rank":"3"},
-    "i24743":{"rank":"1"},
-    "i24762":{"rank":"1"},
-    "i24763":{"rank":"2"},
-    "i24764":{"rank":"3"},
-    "i24761":{"rank":"1"},
-    "i24766":{"rank":"1"},
-    "i24767":{"rank":"2"},
-    "i24768":{"rank":"3"},
-    "i24765":{"rank":"2"},
-    "i24770":{"rank":"1"},
-    "i24771":{"rank":"2"},
-    "i24772":{"rank":"3"},
-    "i24773":{"rank":"4"},
-    "i24769":{"rank":"3"},
-    "i24776":{"rank":"1"},
-    "i24777":{"rank":"2"},
-    "i24778":{"rank":"3"},
-    "i24775":{"rank":"1"},
-    "i24779":{"rank":"2"},
-    "i24774":{"rank":"4"},
-    "i24760":{"rank":"2"},
-    "i24784":{"rank":"1"},
-    "i24785":{"rank":"2"},
-    "i24783":{"rank":"1"},
-    "i24782":{"rank":"1"},
-    "i24790":{"rank":"1"},
-    "i24791":{"rank":"2"},
-    "i24792":{"rank":"3"},
-    "i24789":{"rank":"2"},
-    "i24794":{"rank":"1"},
-    "i24795":{"rank":"2"},
-    "i24796":{"rank":"3"},
-    "i24793":{"rank":"3"},
-    "i24798":{"rank":"1"},
-    "i24799":{"rank":"2"},
-    "i24800":{"rank":"3"},
-    "i24797":{"rank":"4"},
-    "i24802":{"rank":"1"},
-    "i24803":{"rank":"2"},
-    "i24804":{"rank":"3"},
-    "i24801":{"rank":"5"},
-    "i24806":{"rank":"1"},
-    "i24807":{"rank":"2"},
-    "i24808":{"rank":"3"},
-    "i24805":{"rank":"6"},
-    "i24810":{"rank":"1"},
-    "i24811":{"rank":"2"},
-    "i24812":{"rank":"3"},
-    "i24809":{"rank":"7"},
-    "i24818":{"rank":"1"},
-    "i24819":{"rank":"2"},
-    "i24820":{"rank":"3"},
-    "i24817":{"rank":"8"},
-    "i24826":{"rank":"1"},
-    "i24827":{"rank":"2"},
-    "i24828":{"rank":"3"},
-    "i24825":{"rank":"9"},
-    "i24830":{"rank":"1"},
-    "i24831":{"rank":"2"},
-    "i24832":{"rank":"3"},
-    "i24829":{"rank":"10"},
-    "i24834":{"rank":"1"},
-    "i24835":{"rank":"2"},
-    "i24836":{"rank":"3"},
-    "i24833":{"rank":"11"},
-    "i24781":{"rank":"1"},
-    "i24844":{"rank":"1"},
-    "i24843":{"rank":"1"},
-    "i24846":{"rank":"1"},
-    "i24848":{"rank":"1"},
-    "i24849":{"rank":"2"},
-    "i24850":{"rank":"3"},
-    "i24847":{"rank":"2"},
-    "i24845":{"rank":"2"},
-    "i24852":{"rank":"1"},
-    "i24851":{"rank":"3"},
-    "i24855":{"rank":"4"},
-    "i24864":{"rank":"1"},
-    "i24865":{"rank":"2"},
-    "i24863":{"rank":"5"},
-    "i24869":{"rank":"1"},
-    "i24868":{"rank":"6"},
-    "i24871":{"rank":"1"},
-    "i24870":{"rank":"7"},
-    "i24873":{"rank":"1"},
-    "i24874":{"rank":"2"},
-    "i24875":{"rank":"3"},
-    "i24872":{"rank":"8"},
-    "i24842":{"rank":"2"},
-    "i24780":{"rank":"3"},
-    "i24877":{"rank":"1"},
-    "i24876":{"rank":"4"},
-    "i24890":{"rank":"1"},
-    "i24891":{"rank":"2"},
-    "i24889":{"rank":"1"},
-    "i24893":{"rank":"1"},
-    "i24892":{"rank":"2"},
-    "i24888":{"rank":"1"},
-    "i24878":{"rank":"5"},
-    "i24903":{"rank":"1"},
-    "i26214":{"rank":"1"},
-    "i26216":{"rank":"2"},
-    "i26217":{"rank":"3"},
-    "i26219":{"rank":"1"},
-    "i26218":{"rank":"4"},
-    "i24904":{"rank":"2"},
-    "i24906":{"rank":"1"},
-    "i24907":{"rank":"2"},
-    "i24908":{"rank":"3"},
-    "i24909":{"rank":"4"},
-    "i24910":{"rank":"5"},
-    "i24912":{"rank":"6"},
-    "i24905":{"rank":"3"},
-    "i24913":{"rank":"4"},
-    "i24902":{"rank":"6"},
-    "i24742":{"rank":"5"},
-    "i24914":{"rank":"6"},
-    "i24917":{"rank":"1"},
-    "i24918":{"rank":"2"},
-    "i24916":{"rank":"1"},
-    "i24919":{"rank":"2"},
-    "i24921":{"rank":"1"},
-    "i24922":{"rank":"2"},
-    "i24923":{"rank":"3"},
-    "i24924":{"rank":"4"},
-    "i24920":{"rank":"3"},
-    "i24926":{"rank":"1"},
-    "i24927":{"rank":"2"},
-    "i24925":{"rank":"4"},
-    "i24929":{"rank":"1"},
-    "i24930":{"rank":"2"},
-    "i24928":{"rank":"5"},
-    "i24915":{"rank":"7"},
-    "i24933":{"rank":"1"},
-    "i24932":{"rank":"1"},
-    "i24935":{"rank":"1"},
-    "i24936":{"rank":"2"},
-    "i24934":{"rank":"2"},
-    "i24941":{"rank":"3"},
-    "i24942":{"rank":"4"},
-    "i24931":{"rank":"8"},
-    "i24944":{"rank":"1"},
-    "i24945":{"rank":"2"},
-    "i24946":{"rank":"3"},
-    "i24943":{"rank":"9"},
-    "i24948":{"rank":"1"},
-    "i24949":{"rank":"2"},
-    "i24950":{"rank":"3"},
-    "i24951":{"rank":"4"},
-    "i24952":{"rank":"5"},
-    "i24953":{"rank":"6"},
-    "i24947":{"rank":"10"},
-    "i24954":{"rank":"11"},
-    "i24955":{"rank":"12"},
-    "i24956":{"rank":"13"},
-    "p468":{"rank":"4"},
-    "i29924":{"rank":"1"},
-    "i29925":{"rank":"2"},
-    "i29926":{"rank":"3"},
-    "i29921":{"rank":"1"},
-    "i29928":{"rank":"1"},
-    "i29929":{"rank":"2"},
-    "i29930":{"rank":"3"},
-    "i29931":{"rank":"4"},
-    "i29932":{"rank":"5"},
-    "i29933":{"rank":"6"},
-    "i29934":{"rank":"7"},
-    "i29935":{"rank":"8"},
-    "i29927":{"rank":"2"},
-    "i29937":{"rank":"1"},
-    "i29938":{"rank":"2"},
-    "i29936":{"rank":"3"},
-    "i29940":{"rank":"1"},
-    "i29961":{"rank":"2"},
-    "i29939":{"rank":"4"},
-    "p655":{"rank":"5"},
-    "i23242":{"rank":"1"},
-    "i23243":{"rank":"2"},
-    "i23244":{"rank":"3"},
-    "i23245":{"rank":"4"},
-    "i23246":{"rank":"5"},
-    "i23247":{"rank":"6"},
-    "i23248":{"rank":"7"},
-    "i23249":{"rank":"8"},
-    "i23250":{"rank":"9"},
-    "i23251":{"rank":"10"},
-    "i23252":{"rank":"11"},
-    "i23253":{"rank":"12"},
-    "i23254":{"rank":"13"},
-    "i23255":{"rank":"14"},
-    "i23256":{"rank":"15"},
-    "i23257":{"rank":"16"},
-    "i23258":{"rank":"17"},
-    "i23259":{"rank":"18"},
-    "i23260":{"rank":"19"},
-    "i23261":{"rank":"20"},
-    "i23262":{"rank":"21"},
-    "i23263":{"rank":"22"},
-    "i23264":{"rank":"23"},
-    "i23265":{"rank":"24"},
-    "i23266":{"rank":"25"},
-    "i23267":{"rank":"26"},
-    "i23268":{"rank":"27"},
-    "i23241":{"rank":"1"},
-    "i22186":{"rank":"1"},
-    "i22187":{"rank":"2"},
-    "i22407":{"rank":"1"},
-    "i22408":{"rank":"2"},
-    "i22409":{"rank":"3"},
-    "i22217":{"rank":"1"},
-    "i22411":{"rank":"1"},
-    "i22412":{"rank":"2"},
-    "i22413":{"rank":"3"},
-    "i22218":{"rank":"2"},
-    "i22415":{"rank":"1"},
-    "i22416":{"rank":"2"},
-    "i22417":{"rank":"3"},
-    "i22219":{"rank":"3"},
-    "i22418":{"rank":"1"},
-    "i22419":{"rank":"2"},
-    "i22420":{"rank":"3"},
-    "i22220":{"rank":"4"},
-    "i22216":{"rank":"1"},
-    "i22223":{"rank":"1"},
-    "i22421":{"rank":"2"},
-    "i22422":{"rank":"3"},
-    "i22222":{"rank":"1"},
-    "i22423":{"rank":"1"},
-    "i22424":{"rank":"2"},
-    "i22425":{"rank":"3"},
-    "i22224":{"rank":"2"},
-    "i22426":{"rank":"1"},
-    "i22427":{"rank":"2"},
-    "i22428":{"rank":"3"},
-    "i22429":{"rank":"4"},
-    "i22225":{"rank":"3"},
-    "i22430":{"rank":"1"},
-    "i22431":{"rank":"2"},
-    "i22432":{"rank":"3"},
-    "i22227":{"rank":"1"},
-    "i22565":{"rank":"2"},
-    "i22226":{"rank":"4"},
-    "i22221":{"rank":"2"},
-    "i22611":{"rank":"1"},
-    "i22612":{"rank":"2"},
-    "i22610":{"rank":"1"},
-    "i22614":{"rank":"1"},
-    "i22615":{"rank":"2"},
-    "i22613":{"rank":"2"},
-    "i22579":{"rank":"1"},
-    "i22616":{"rank":"1"},
-    "i22617":{"rank":"2"},
-    "i22618":{"rank":"3"},
-    "i22581":{"rank":"2"},
-    "i22619":{"rank":"1"},
-    "i22620":{"rank":"2"},
-    "i22621":{"rank":"3"},
-    "i22583":{"rank":"3"},
-    "i22624":{"rank":"1"},
-    "i22625":{"rank":"2"},
-    "i22626":{"rank":"3"},
-    "i22584":{"rank":"4"},
-    "i22629":{"rank":"1"},
-    "i22630":{"rank":"2"},
-    "i22631":{"rank":"3"},
-    "i22586":{"rank":"5"},
-    "i22636":{"rank":"1"},
-    "i22637":{"rank":"2"},
-    "i22638":{"rank":"3"},
-    "i22587":{"rank":"6"},
-    "i22643":{"rank":"1"},
-    "i22644":{"rank":"2"},
-    "i22645":{"rank":"3"},
-    "i22588":{"rank":"7"},
-    "i22646":{"rank":"1"},
-    "i22647":{"rank":"2"},
-    "i22648":{"rank":"3"},
-    "i22589":{"rank":"8"},
-    "i22649":{"rank":"1"},
-    "i22650":{"rank":"2"},
-    "i22651":{"rank":"3"},
-    "i22590":{"rank":"9"},
-    "i22652":{"rank":"1"},
-    "i22653":{"rank":"2"},
-    "i22654":{"rank":"3"},
-    "i22591":{"rank":"10"},
-    "i22657":{"rank":"1"},
-    "i22658":{"rank":"2"},
-    "i22659":{"rank":"3"},
-    "i22592":{"rank":"11"},
-    "i22671":{"rank":"1"},
-    "i22672":{"rank":"2"},
-    "i22673":{"rank":"3"},
-    "i22593":{"rank":"12"},
-    "i22661":{"rank":"1"},
-    "i22662":{"rank":"2"},
-    "i22663":{"rank":"3"},
-    "i22660":{"rank":"13"},
-    "i22675":{"rank":"1"},
-    "i22676":{"rank":"2"},
-    "i22677":{"rank":"3"},
-    "i22678":{"rank":"4"},
-    "i22674":{"rank":"14"},
-    "i22577":{"rank":"1"},
-    "i22594":{"rank":"1"},
-    "i22189":{"rank":"1"},
-    "i22595":{"rank":"1"},
-    "i22633":{"rank":"1"},
-    "i22634":{"rank":"2"},
-    "i22635":{"rank":"3"},
-    "i22632":{"rank":"2"},
-    "i22231":{"rank":"2"},
-    "i22596":{"rank":"1"},
-    "i22567":{"rank":"3"},
-    "i22597":{"rank":"1"},
-    "i22568":{"rank":"4"},
-    "i22569":{"rank":"5"},
-    "i22598":{"rank":"1"},
-    "i22599":{"rank":"2"},
-    "i22570":{"rank":"6"},
-    "i22602":{"rank":"1"},
-    "i22571":{"rank":"7"},
-    "i22603":{"rank":"1"},
-    "i22572":{"rank":"8"},
-    "i22605":{"rank":"1"},
-    "i22606":{"rank":"2"},
-    "i22573":{"rank":"9"},
-    "i22607":{"rank":"1"},
-    "i22574":{"rank":"10"},
-    "i22608":{"rank":"1"},
-    "i22575":{"rank":"11"},
-    "i22609":{"rank":"1"},
-    "i22576":{"rank":"12"},
-    "i22640":{"rank":"1"},
-    "i22641":{"rank":"2"},
-    "i22642":{"rank":"3"},
-    "i22639":{"rank":"13"},
-    "i22578":{"rank":"2"},
-    "i22229":{"rank":"3"},
-    "i22688":{"rank":"1"},
-    "i22414":{"rank":"4"},
-    "i22505":{"rank":"1"},
-    "i22506":{"rank":"2"},
-    "i22507":{"rank":"3"},
-    "i22516":{"rank":"4"},
-    "i22228":{"rank":"1"},
-    "i22510":{"rank":"1"},
-    "i22511":{"rank":"2"},
-    "i22518":{"rank":"3"},
-    "i22509":{"rank":"2"},
-    "i22523":{"rank":"1"},
-    "i22524":{"rank":"2"},
-    "i22520":{"rank":"1"},
-    "i22543":{"rank":"1"},
-    "i22544":{"rank":"2"},
-    "i22521":{"rank":"2"},
-    "i22539":{"rank":"1"},
-    "i22540":{"rank":"2"},
-    "i22522":{"rank":"3"},
-    "i22541":{"rank":"1"},
-    "i22542":{"rank":"2"},
-    "i22525":{"rank":"4"},
-    "i22527":{"rank":"5"},
-    "i22519":{"rank":"3"},
-    "i22508":{"rank":"5"},
-    "i22547":{"rank":"1"},
-    "i22548":{"rank":"2"},
-    "i22551":{"rank":"1"},
-    "i22553":{"rank":"2"},
-    "i22554":{"rank":"3"},
-    "i22559":{"rank":"4"},
-    "i22560":{"rank":"5"},
-    "i22561":{"rank":"6"},
-    "i22562":{"rank":"7"},
-    "i22550":{"rank":"3"},
-    "i22564":{"rank":"4"},
-    "i22546":{"rank":"6"},
-    "i22188":{"rank":"3"},
-    "i22549":{"rank":"4"},
-    "i22722":{"rank":"1"},
-    "i22724":{"rank":"2"},
-    "i22696":{"rank":"1"},
-    "i22697":{"rank":"2"},
-    "i22725":{"rank":"1"},
-    "i22726":{"rank":"2"},
-    "i22727":{"rank":"3"},
-    "i22747":{"rank":"4"},
-    "i22698":{"rank":"3"},
-    "i22748":{"rank":"1"},
-    "i22757":{"rank":"2"},
-    "i22719":{"rank":"4"},
-    "i22759":{"rank":"1"},
-    "i22760":{"rank":"2"},
-    "i22758":{"rank":"5"},
-    "i22694":{"rank":"5"},
-    "i22765":{"rank":"1"},
-    "i22764":{"rank":"1"},
-    "i22767":{"rank":"1"},
-    "i22768":{"rank":"2"},
-    "i22766":{"rank":"2"},
-    "i22771":{"rank":"1"},
-    "i22772":{"rank":"2"},
-    "i22773":{"rank":"3"},
-    "i22770":{"rank":"3"},
-    "i22774":{"rank":"4"},
-    "i22775":{"rank":"5"},
-    "i22762":{"rank":"6"},
-    "i22780":{"rank":"1"},
-    "i22781":{"rank":"2"},
-    "i22782":{"rank":"3"},
-    "i27785":{"rank":"4"},
-    "i22779":{"rank":"7"},
-    "i22784":{"rank":"1"},
-    "i22785":{"rank":"2"},
-    "i22786":{"rank":"3"},
-    "i22787":{"rank":"4"},
-    "i22788":{"rank":"5"},
-    "i22789":{"rank":"6"},
-    "i22783":{"rank":"8"},
-    "i22790":{"rank":"9"},
-    "i22791":{"rank":"10"},
-    "i22792":{"rank":"11"},
-    "p576":{"rank":"6"},
-    "i3585":{"rank":"1"},
-    "i3587":{"rank":"2"},
-    "i3589":{"rank":"3"},
-    "i3590":{"rank":"4"},
-    "i3591":{"rank":"5"},
-    "i3593":{"rank":"6"},
-    "i3594":{"rank":"7"},
-    "i3595":{"rank":"8"},
-    "i9547":{"rank":"9"},
-    "i18173":{"rank":"10"},
-    "i3584":{"rank":"1"},
-    "i3597":{"rank":"1"},
-    "i3598":{"rank":"2"},
-    "i3599":{"rank":"3"},
-    "i3596":{"rank":"2"},
-    "i3602":{"rank":"1"},
-    "i3603":{"rank":"2"},
-    "i3604":{"rank":"3"},
-    "i3601":{"rank":"3"},
-    "i3606":{"rank":"1"},
-    "i3607":{"rank":"2"},
-    "i3608":{"rank":"3"},
-    "i3609":{"rank":"4"},
-    "i3605":{"rank":"4"},
-    "i3611":{"rank":"1"},
-    "i3613":{"rank":"2"},
-    "i3618":{"rank":"3"},
-    "i3645":{"rank":"4"},
-    "i3648":{"rank":"5"},
-    "i3653":{"rank":"6"},
-    "i3654":{"rank":"7"},
-    "i3655":{"rank":"8"},
-    "i11635":{"rank":"9"},
-    "i11826":{"rank":"10"},
-    "i11827":{"rank":"11"},
-    "i11828":{"rank":"12"},
-    "i11829":{"rank":"13"},
-    "i11830":{"rank":"14"},
-    "i11831":{"rank":"15"},
-    "i11832":{"rank":"16"},
-    "i11833":{"rank":"17"},
-    "i11834":{"rank":"18"},
-    "i11835":{"rank":"19"},
-    "i11836":{"rank":"20"},
-    "i11837":{"rank":"21"},
-    "i11838":{"rank":"22"},
-    "i11839":{"rank":"23"},
-    "i11840":{"rank":"24"},
-    "i11841":{"rank":"25"},
-    "i11842":{"rank":"26"},
-    "i11843":{"rank":"27"},
-    "i11844":{"rank":"28"},
-    "i11845":{"rank":"29"},
-    "i11846":{"rank":"30"},
-    "i11847":{"rank":"31"},
-    "i11848":{"rank":"32"},
-    "i11849":{"rank":"33"},
-    "i11850":{"rank":"34"},
-    "i11851":{"rank":"35"},
-    "i11852":{"rank":"36"},
-    "i11853":{"rank":"37"},
-    "i11854":{"rank":"38"},
-    "i11855":{"rank":"39"},
-    "i11856":{"rank":"40"},
-    "i11857":{"rank":"41"},
-    "i11858":{"rank":"42"},
-    "i11859":{"rank":"43"},
-    "i11860":{"rank":"44"},
-    "i11861":{"rank":"45"},
-    "i11862":{"rank":"46"},
-    "i19793":{"rank":"47"},
-    "i19794":{"rank":"48"},
-    "i3610":{"rank":"5"},
-    "i3658":{"rank":"1"},
-    "i3659":{"rank":"2"},
-    "i3660":{"rank":"3"},
-    "i3661":{"rank":"4"},
-    "i3662":{"rank":"5"},
-    "i3663":{"rank":"6"},
-    "i4096":{"rank":"7"},
-    "i4749":{"rank":"8"},
-    "i4750":{"rank":"9"},
-    "i4751":{"rank":"10"},
-    "i4752":{"rank":"11"},
-    "i3657":{"rank":"6"},
-    "i3666":{"rank":"1"},
-    "i3669":{"rank":"2"},
-    "i3671":{"rank":"3"},
-    "i3672":{"rank":"4"},
-    "i3674":{"rank":"5"},
-    "i3675":{"rank":"6"},
-    "i3676":{"rank":"7"},
-    "i3677":{"rank":"8"},
-    "i3679":{"rank":"9"},
-    "i3680":{"rank":"10"},
-    "i3681":{"rank":"11"},
-    "i3682":{"rank":"12"},
-    "i3684":{"rank":"13"},
-    "i3685":{"rank":"14"},
-    "i3686":{"rank":"15"},
-    "i3687":{"rank":"16"},
-    "i3688":{"rank":"17"},
-    "i3689":{"rank":"18"},
-    "i3691":{"rank":"19"},
-    "i3693":{"rank":"20"},
-    "i3694":{"rank":"21"},
-    "i3695":{"rank":"22"},
-    "i3696":{"rank":"23"},
-    "i3697":{"rank":"24"},
-    "i3698":{"rank":"25"},
-    "i3700":{"rank":"26"},
-    "i3701":{"rank":"27"},
-    "i3702":{"rank":"28"},
-    "i3703":{"rank":"29"},
-    "i3704":{"rank":"30"},
-    "i3705":{"rank":"31"},
-    "i3706":{"rank":"32"},
-    "i3707":{"rank":"33"},
-    "i3710":{"rank":"34"},
-    "i3711":{"rank":"35"},
-    "i3712":{"rank":"36"},
-    "i3713":{"rank":"37"},
-    "i3714":{"rank":"38"},
-    "i3715":{"rank":"39"},
-    "i3716":{"rank":"40"},
-    "i3718":{"rank":"41"},
-    "i3719":{"rank":"42"},
-    "i3724":{"rank":"43"},
-    "i3726":{"rank":"44"},
-    "i3728":{"rank":"45"},
-    "i3729":{"rank":"46"},
-    "i3730":{"rank":"47"},
-    "i3731":{"rank":"48"},
-    "i3732":{"rank":"49"},
-    "i3733":{"rank":"50"},
-    "i3734":{"rank":"51"},
-    "i3735":{"rank":"52"},
-    "i3736":{"rank":"53"},
-    "i3737":{"rank":"54"},
-    "i3739":{"rank":"55"},
-    "i3740":{"rank":"56"},
-    "i3742":{"rank":"57"},
-    "i3744":{"rank":"58"},
-    "i3746":{"rank":"59"},
-    "i3747":{"rank":"60"},
-    "i3748":{"rank":"61"},
-    "i3750":{"rank":"62"},
-    "i3752":{"rank":"63"},
-    "i3753":{"rank":"64"},
-    "i3754":{"rank":"65"},
-    "i3756":{"rank":"66"},
-    "i3757":{"rank":"67"},
-    "i3758":{"rank":"68"},
-    "i3759":{"rank":"69"},
-    "i3760":{"rank":"70"},
-    "i3762":{"rank":"71"},
-    "i3764":{"rank":"72"},
-    "i3765":{"rank":"73"},
-    "i3766":{"rank":"74"},
-    "i3767":{"rank":"75"},
-    "i3768":{"rank":"76"},
-    "i3769":{"rank":"77"},
-    "i3771":{"rank":"78"},
-    "i3772":{"rank":"79"},
-    "i3773":{"rank":"80"},
-    "i3774":{"rank":"81"},
-    "i3775":{"rank":"82"},
-    "i3776":{"rank":"83"},
-    "i3777":{"rank":"84"},
-    "i3778":{"rank":"85"},
-    "i3780":{"rank":"86"},
-    "i3782":{"rank":"87"},
-    "i3783":{"rank":"88"},
-    "i3784":{"rank":"89"},
-    "i3785":{"rank":"90"},
-    "i3788":{"rank":"91"},
-    "i3789":{"rank":"92"},
-    "i3790":{"rank":"93"},
-    "i3792":{"rank":"94"},
-    "i3793":{"rank":"95"},
-    "i9997":{"rank":"96"},
-    "i10016":{"rank":"97"},
-    "i21223":{"rank":"98"},
-    "i23354":{"rank":"99"},
-    "i26298":{"rank":"100"},
-    "i3664":{"rank":"7"},
-    "i9998":{"rank":"1"},
-    "i9999":{"rank":"2"},
-    "i10000":{"rank":"3"},
-    "i10001":{"rank":"4"},
-    "i10002":{"rank":"5"},
-    "i10003":{"rank":"6"},
-    "i10004":{"rank":"7"},
-    "i10005":{"rank":"8"},
-    "i10006":{"rank":"9"},
-    "i10007":{"rank":"10"},
-    "i10008":{"rank":"11"},
-    "i10009":{"rank":"12"},
-    "i10010":{"rank":"13"},
-    "i10011":{"rank":"14"},
-    "i10012":{"rank":"15"},
-    "i10013":{"rank":"16"},
-    "i10014":{"rank":"17"},
-    "i10015":{"rank":"18"},
-    "i10017":{"rank":"19"},
-    "i10018":{"rank":"20"},
-    "i10019":{"rank":"21"},
-    "i10020":{"rank":"22"},
-    "i10021":{"rank":"23"},
-    "i10022":{"rank":"24"},
-    "i10023":{"rank":"25"},
-    "i10024":{"rank":"26"},
-    "i10025":{"rank":"27"},
-    "i10026":{"rank":"28"},
-    "i10027":{"rank":"29"},
-    "i10028":{"rank":"30"},
-    "i14166":{"rank":"31"},
-    "i14167":{"rank":"32"},
-    "i15672":{"rank":"33"},
-    "i9996":{"rank":"8"},
-    "p321":{"rank":"7"},
-    "i20253":{"rank":"1"},
-    "i20252":{"rank":"8"},
-    "p428":{"rank":"-7"},
-    "i21368":{"rank":"1"},
-    "i21369":{"rank":"1"},
-    "i31897":{"rank":"2"},
-    "i31905":{"rank":"1"},
-    "i31904":{"rank":"3"},
-    "i32316":{"rank":"4"},
-    "p570":{"rank":"1"},
-    "i21834":{"rank":"1"},
-    "i21832":{"rank":"1"},
-    "i32307":{"rank":"1"},
-    "i32309":{"rank":"2"},
-    "i33457":{"rank":"3"},
-    "i32123":{"rank":"2"},
-    "i15004":{"rank":"1"},
-    "i33466":{"rank":"1"},
-    "i18101":{"rank":"2"},
-    "p482":{"rank":"2"},
-    "p520":{"rank":"1"},
-    "i26973":{"rank":"1"},
-    "i13839":{"rank":"1"},
-    "i31032":{"rank":"1"},
-    "i29171":{"rank":"1"},
-    "i31839":{"rank":"1"},
-    "i31108":{"rank":"2"},
-    "i32085":{"rank":"3"},
-    "i32358":{"rank":"4"},
-    "i22212":{"rank":"2"},
-    "i30932":{"rank":"1"},
-    "i25149":{"rank":"3"},
-    "i27930":{"rank":"1"},
-    "i29322":{"rank":"1"},
-    "i29949":{"rank":"2"},
-    "i32147":{"rank":"3"},
-    "i29008":{"rank":"2"},
-    "i31031":{"rank":"3"},
-    "i31450":{"rank":"4"},
-    "i32097":{"rank":"5"},
-    "i32098":{"rank":"6"},
-    "i32099":{"rank":"7"},
-    "i32100":{"rank":"8"},
-    "i32101":{"rank":"9"},
-    "i32306":{"rank":"10"},
-    "i32334":{"rank":"11"},
-    "i26957":{"rank":"4"},
-    "i31399":{"rank":"1"},
-    "i31953":{"rank":"2"},
-    "i31949":{"rank":"5"},
-    "i14221":{"rank":"2"},
-    "p432":{"rank":"3"},
-    "i17788":{"rank":"1"},
-    "i16682":{"rank":"1"},
-    "i17865":{"rank":"2"},
-    "i14986":{"rank":"1"},
-    "i14988":{"rank":"2"},
-    "p479":{"rank":"4"},
-    "i30734":{"rank":"1"},
-    "i31849":{"rank":"1"},
-    "i31850":{"rank":"2"},
-    "i31851":{"rank":"3"},
-    "i31852":{"rank":"4"},
-    "i31842":{"rank":"1"},
-    "i32418":{"rank":"2"},
-    "i31536":{"rank":"2"},
-    "i31537":{"rank":"3"},
-    "p658":{"rank":"5"},
-    "p452":{"rank":"6"},
-    "i23364":{"rank":"1"},
-    "i23700":{"rank":"1"},
-    "i23691":{"rank":"1"},
-    "i23693":{"rank":"2"},
-    "i23763":{"rank":"1"},
-    "i30916":{"rank":"1"},
-    "i30917":{"rank":"2"},
-    "i30919":{"rank":"3"},
-    "i29354":{"rank":"1"},
-    "i31790":{"rank":"2"},
-    "i23766":{"rank":"2"},
-    "i32421":{"rank":"3"},
-    "i32422":{"rank":"4"},
-    "i32423":{"rank":"5"},
-    "i32424":{"rank":"6"},
-    "i32426":{"rank":"1"},
-    "i32427":{"rank":"2"},
-    "i32428":{"rank":"3"},
-    "i32429":{"rank":"4"},
-    "i32430":{"rank":"5"},
-    "i32431":{"rank":"6"},
-    "i32425":{"rank":"7"},
-    "i23694":{"rank":"3"},
-    "i23697":{"rank":"4"},
-    "i30735":{"rank":"5"},
-    "p587":{"rank":"7"},
-    "p592":{"rank":"8"},
-    "i30925":{"rank":"1"},
-    "i30926":{"rank":"2"},
-    "i30927":{"rank":"1"},
-    "i30928":{"rank":"2"},
-    "i32324":{"rank":"1"},
-    "i32323":{"rank":"3"},
-    "i32326":{"rank":"1"},
-    "i32325":{"rank":"4"},
-    "i30922":{"rank":"1"},
-    "i30923":{"rank":"2"},
-    "i30929":{"rank":"1"},
-    "i32072":{"rank":"2"},
-    "i32321":{"rank":"3"},
-    "i32322":{"rank":"4"},
-    "i32395":{"rank":"5"},
-    "i30924":{"rank":"3"},
-    "p668":{"rank":"9"},
-    "i29053":{"rank":"1"},
-    "i29058":{"rank":"1"},
-    "i29059":{"rank":"2"},
-    "i29054":{"rank":"2"},
-    "i29082":{"rank":"1"},
-    "i31948":{"rank":"2"},
-    "i29055":{"rank":"3"},
-    "i29131":{"rank":"1"},
-    "i29056":{"rank":"4"},
-    "i29345":{"rank":"1"},
-    "i29348":{"rank":"1"},
-    "i29350":{"rank":"2"},
-    "i32339":{"rank":"3"},
-    "i29347":{"rank":"2"},
-    "i30005":{"rank":"3"},
-    "i30006":{"rank":"4"},
-    "i29339":{"rank":"5"},
-    "i30736":{"rank":"6"},
-    "p644":{"rank":"10"},
-    "p391":{"rank":"-6"},
-    "i30978":{"rank":"1"},
-    "i30979":{"rank":"2"},
-    "i30976":{"rank":"1"},
-    "i30982":{"rank":"2"},
-    "i30974":{"rank":"1"},
-    "i30984":{"rank":"1"},
-    "i30985":{"rank":"2"},
-    "i30986":{"rank":"3"},
-    "i30987":{"rank":"4"},
-    "i30988":{"rank":"5"},
-    "i30989":{"rank":"6"},
-    "i30990":{"rank":"7"},
-    "i30992":{"rank":"1"},
-    "i30993":{"rank":"2"},
-    "i30991":{"rank":"8"},
-    "i30994":{"rank":"9"},
-    "i30995":{"rank":"10"},
-    "i30983":{"rank":"2"},
-    "i30997":{"rank":"1"},
-    "i30998":{"rank":"2"},
-    "i30999":{"rank":"3"},
-    "i31000":{"rank":"4"},
-    "i30996":{"rank":"3"},
-    "i31005":{"rank":"1"},
-    "i31006":{"rank":"2"},
-    "i31007":{"rank":"3"},
-    "i31004":{"rank":"4"},
-    "p602":{"rank":"1"},
-    "p674":{"rank":"-5"},
-    "i32168":{"rank":"1"},
-    "i32169":{"rank":"2"},
-    "i32165":{"rank":"1"},
-    "i32171":{"rank":"2"},
-    "i32163":{"rank":"1"},
-    "i32173":{"rank":"1"},
-    "i32174":{"rank":"2"},
-    "i32176":{"rank":"3"},
-    "i32177":{"rank":"4"},
-    "i32178":{"rank":"5"},
-    "i32179":{"rank":"6"},
-    "i32181":{"rank":"1"},
-    "i32182":{"rank":"2"},
-    "i32180":{"rank":"7"},
-    "i32184":{"rank":"8"},
-    "i32172":{"rank":"2"},
-    "i32186":{"rank":"1"},
-    "i32187":{"rank":"2"},
-    "i32188":{"rank":"3"},
-    "i32189":{"rank":"4"},
-    "i32185":{"rank":"3"},
-    "i32191":{"rank":"1"},
-    "i32192":{"rank":"2"},
-    "i32190":{"rank":"4"},
-    "i32194":{"rank":"1"},
-    "i32195":{"rank":"2"},
-    "i32196":{"rank":"3"},
-    "i32193":{"rank":"5"},
-    "p663":{"rank":"1"},
-    "p619":{"rank":"2"},
-    "p694":{"rank":"3"},
-    "i28502":{"rank":"1"},
-    "i28503":{"rank":"2"},
-    "i28504":{"rank":"3"},
-    "i28505":{"rank":"4"},
-    "i28501":{"rank":"1"},
-    "i28510":{"rank":"1"},
-    "i28511":{"rank":"2"},
-    "i28512":{"rank":"3"},
-    "i28509":{"rank":"2"},
-    "p620":{"rank":"4"},
-    "i30344":{"rank":"1"},
-    "i30345":{"rank":"2"},
-    "i30346":{"rank":"3"},
-    "i30347":{"rank":"4"},
-    "i30348":{"rank":"5"},
-    "i30349":{"rank":"6"},
-    "i30350":{"rank":"7"},
-    "i30352":{"rank":"1"},
-    "i30353":{"rank":"2"},
-    "i30351":{"rank":"8"},
-    "i30354":{"rank":"9"},
-    "i30355":{"rank":"10"},
-    "i30343":{"rank":"1"},
-    "i30357":{"rank":"1"},
-    "i30358":{"rank":"2"},
-    "i30359":{"rank":"3"},
-    "i30360":{"rank":"4"},
-    "i30356":{"rank":"2"},
-    "i30362":{"rank":"1"},
-    "i30363":{"rank":"2"},
-    "i30361":{"rank":"3"},
-    "i30365":{"rank":"1"},
-    "i30366":{"rank":"2"},
-    "i30367":{"rank":"3"},
-    "i30364":{"rank":"4"},
-    "i30335":{"rank":"1"},
-    "i30337":{"rank":"1"},
-    "i30338":{"rank":"2"},
-    "i30339":{"rank":"3"},
-    "i30340":{"rank":"4"},
-    "i30336":{"rank":"2"},
-    "i30341":{"rank":"3"},
-    "i30342":{"rank":"4"},
-    "i30334":{"rank":"5"},
-    "p614":{"rank":"5"},
-    "p693":{"rank":"6"},
-    "p695":{"rank":"7"},
-    "p609":{"rank":"8"},
-    "p696":{"rank":"9"},
-    "p698":{"rank":"10"},
-    "p691":{"rank":"11"},
-    "i31964":{"rank":"1"},
-    "i31965":{"rank":"2"},
-    "i31967":{"rank":"3"},
-    "i31968":{"rank":"4"},
-    "i31969":{"rank":"5"},
-    "i31970":{"rank":"6"},
-    "i31972":{"rank":"1"},
-    "i31973":{"rank":"2"},
-    "i31971":{"rank":"7"},
-    "i31975":{"rank":"8"},
-    "i31963":{"rank":"1"},
-    "i31977":{"rank":"1"},
-    "i31978":{"rank":"2"},
-    "i31979":{"rank":"3"},
-    "i31980":{"rank":"4"},
-    "i31976":{"rank":"2"},
-    "i31982":{"rank":"1"},
-    "i31983":{"rank":"2"},
-    "i31981":{"rank":"3"},
-    "i31985":{"rank":"1"},
-    "i31986":{"rank":"2"},
-    "i31987":{"rank":"3"},
-    "i31984":{"rank":"4"},
-    "p662":{"rank":"12"},
-    "p697":{"rank":"13"},
-    "i31423":{"rank":"1"},
-    "i31424":{"rank":"2"},
-    "i31425":{"rank":"3"},
-    "i31426":{"rank":"4"},
-    "i31427":{"rank":"5"},
-    "i31428":{"rank":"6"},
-    "i31429":{"rank":"7"},
-    "i31431":{"rank":"1"},
-    "i31432":{"rank":"2"},
-    "i31430":{"rank":"8"},
-    "i31433":{"rank":"9"},
-    "i31434":{"rank":"10"},
-    "i31422":{"rank":"1"},
-    "i31436":{"rank":"1"},
-    "i31437":{"rank":"2"},
-    "i31438":{"rank":"3"},
-    "i31439":{"rank":"4"},
-    "i31435":{"rank":"2"},
-    "i31444":{"rank":"1"},
-    "i31445":{"rank":"2"},
-    "i31446":{"rank":"3"},
-    "i31443":{"rank":"3"},
-    "p664":{"rank":"14"},
-    "p692":{"rank":"15"},
-    "i27375":{"rank":"1"},
-    "i29215":{"rank":"2"},
-    "i29216":{"rank":"3"},
-    "i27353":{"rank":"1"},
-    "i27346":{"rank":"1"},
-    "i28588":{"rank":"1"},
-    "i30828":{"rank":"2"},
-    "i28586":{"rank":"2"},
-    "p632":{"rank":"16"},
-    "i32212":{"rank":"1"},
-    "i32215":{"rank":"1"},
-    "i32216":{"rank":"2"},
-    "i32214":{"rank":"1"},
-    "i32217":{"rank":"2"},
-    "i32219":{"rank":"1"},
-    "i32220":{"rank":"2"},
-    "i32221":{"rank":"3"},
-    "i32218":{"rank":"3"},
-    "i32223":{"rank":"1"},
-    "i32224":{"rank":"2"},
-    "i32225":{"rank":"3"},
-    "i32226":{"rank":"4"},
-    "i32227":{"rank":"5"},
-    "i32228":{"rank":"6"},
-    "i32229":{"rank":"7"},
-    "i32230":{"rank":"8"},
-    "i32231":{"rank":"9"},
-    "i32222":{"rank":"4"},
-    "i32213":{"rank":"2"},
-    "i32232":{"rank":"3"},
-    "i32233":{"rank":"4"},
-    "i32211":{"rank":"1"},
-    "i32235":{"rank":"1"},
-    "i32236":{"rank":"2"},
-    "i32237":{"rank":"3"},
-    "i32238":{"rank":"4"},
-    "i32239":{"rank":"5"},
-    "i32240":{"rank":"6"},
-    "i32241":{"rank":"7"},
-    "i32243":{"rank":"1"},
-    "i32244":{"rank":"2"},
-    "i32242":{"rank":"8"},
-    "i32245":{"rank":"9"},
-    "i32246":{"rank":"10"},
-    "i32234":{"rank":"2"},
-    "i32248":{"rank":"1"},
-    "i32251":{"rank":"1"},
-    "i32253":{"rank":"2"},
-    "i32250":{"rank":"1"},
-    "i32254":{"rank":"2"},
-    "i32256":{"rank":"1"},
-    "i32257":{"rank":"2"},
-    "i32258":{"rank":"3"},
-    "i32255":{"rank":"3"},
-    "i32260":{"rank":"1"},
-    "i32261":{"rank":"2"},
-    "i32262":{"rank":"3"},
-    "i32263":{"rank":"4"},
-    "i32264":{"rank":"5"},
-    "i32265":{"rank":"6"},
-    "i32266":{"rank":"7"},
-    "i32267":{"rank":"8"},
-    "i32268":{"rank":"9"},
-    "i32301":{"rank":"10"},
-    "i32302":{"rank":"11"},
-    "i32259":{"rank":"4"},
-    "i32249":{"rank":"2"},
-    "i32269":{"rank":"3"},
-    "i32270":{"rank":"4"},
-    "i32247":{"rank":"3"},
-    "i32271":{"rank":"1"},
-    "i32272":{"rank":"2"},
-    "i32273":{"rank":"3"},
-    "i32274":{"rank":"4"},
-    "i32252":{"rank":"4"},
-    "i32277":{"rank":"1"},
-    "i32278":{"rank":"2"},
-    "i32279":{"rank":"3"},
-    "i32280":{"rank":"4"},
-    "i32281":{"rank":"5"},
-    "i32282":{"rank":"6"},
-    "i32283":{"rank":"7"},
-    "i32285":{"rank":"1"},
-    "i32286":{"rank":"2"},
-    "i32284":{"rank":"8"},
-    "i32287":{"rank":"9"},
-    "i32288":{"rank":"10"},
-    "i32276":{"rank":"5"},
-    "i32290":{"rank":"1"},
-    "i32291":{"rank":"2"},
-    "i32292":{"rank":"3"},
-    "i32293":{"rank":"4"},
-    "i32289":{"rank":"6"},
-    "i32295":{"rank":"1"},
-    "i32296":{"rank":"2"},
-    "i32294":{"rank":"7"},
-    "i32298":{"rank":"1"},
-    "i32299":{"rank":"2"},
-    "i32300":{"rank":"3"},
-    "i32297":{"rank":"8"},
-    "p682":{"rank":"17"},
-    "p618":{"rank":"-4"},
-    "p666":{"rank":"1"},
-    "p665":{"rank":"1"},
-    "p667":{"rank":"2"},
-    "i6349":{"rank":"3"},
-    "i6352":{"rank":"4"},
-    "i6353":{"rank":"5"},
-    "i6354":{"rank":"6"},
-    "i7295":{"rank":"7"},
-    "i7753":{"rank":"8"},
-    "p366":{"rank":"1"},
-    "i31043":{"rank":"1"},
-    "i31484":{"rank":"2"},
-    "i31660":{"rank":"1"},
-    "i31485":{"rank":"3"},
-    "i31486":{"rank":"4"},
-    "i31487":{"rank":"5"},
-    "i31488":{"rank":"6"},
-    "i31489":{"rank":"7"},
-    "i31543":{"rank":"8"},
-    "i31663":{"rank":"1"},
-    "i31664":{"rank":"2"},
-    "i31665":{"rank":"3"},
-    "i31666":{"rank":"4"},
-    "i31662":{"rank":"9"},
-    "i31885":{"rank":"10"},
-    "p640":{"rank":"2"},
-    "i15863":{"rank":"1"},
-    "i32373":{"rank":"2"},
-    "i32374":{"rank":"3"},
-    "i32387":{"rank":"4"},
-    "i32453":{"rank":"5"},
-    "i32554":{"rank":"6"},
-    "p442":{"rank":"3"},
-    "p673":{"rank":"1"},
-    "i32506":{"rank":"1"},
-    "i33512":{"rank":"2"},
-    "i29237":{"rank":"2"},
-    "i29280":{"rank":"3"},
-    "i29279":{"rank":"1"},
-    "i29294":{"rank":"2"},
-    "i29292":{"rank":"4"},
-    "i31866":{"rank":"1"},
-    "i32088":{"rank":"2"},
-    "i30941":{"rank":"5"},
-    "i31907":{"rank":"1"},
-    "i31753":{"rank":"6"},
-    "i31814":{"rank":"7"},
-    "i31899":{"rank":"1"},
-    "i31901":{"rank":"2"},
-    "i31902":{"rank":"3"},
-    "i31900":{"rank":"8"},
-    "i31903":{"rank":"9"},
-    "i31989":{"rank":"1"},
-    "i31988":{"rank":"10"},
-    "i31992":{"rank":"1"},
-    "i31991":{"rank":"11"},
-    "i32118":{"rank":"1"},
-    "i32119":{"rank":"2"},
-    "i32120":{"rank":"3"},
-    "i32117":{"rank":"12"},
-    "i32124":{"rank":"13"},
-    "i33458":{"rank":"14"},
-    "p635":{"rank":"4"},
-    "i16844":{"rank":"1"},
-    "i32159":{"rank":"2"},
-    "p501":{"rank":"5"},
-    "i9617":{"rank":"1"},
-    "i9620":{"rank":"2"},
-    "i9621":{"rank":"3"},
-    "i9622":{"rank":"4"},
-    "i9819":{"rank":"1"},
-    "i9822":{"rank":"2"},
-    "i9824":{"rank":"3"},
-    "i9827":{"rank":"4"},
-    "i9828":{"rank":"5"},
-    "i9829":{"rank":"6"},
-    "i9830":{"rank":"7"},
-    "i9831":{"rank":"8"},
-    "i9832":{"rank":"9"},
-    "i9833":{"rank":"10"},
-    "i9834":{"rank":"11"},
-    "i9835":{"rank":"12"},
-    "i9836":{"rank":"13"},
-    "i9837":{"rank":"14"},
-    "i9838":{"rank":"15"},
-    "i9839":{"rank":"16"},
-    "i9840":{"rank":"17"},
-    "i9841":{"rank":"18"},
-    "i9842":{"rank":"19"},
-    "i9843":{"rank":"20"},
-    "i9844":{"rank":"21"},
-    "i9845":{"rank":"22"},
-    "i32468":{"rank":"1"},
-    "i9846":{"rank":"23"},
-    "i9847":{"rank":"24"},
-    "i32452":{"rank":"1"},
-    "i9848":{"rank":"25"},
-    "i9849":{"rank":"26"},
-    "i9850":{"rank":"27"},
-    "i9851":{"rank":"28"},
-    "i9852":{"rank":"29"},
-    "i9853":{"rank":"30"},
-    "i9854":{"rank":"31"},
-    "i9855":{"rank":"32"},
-    "i9856":{"rank":"33"},
-    "i9857":{"rank":"34"},
-    "i9858":{"rank":"35"},
-    "i9859":{"rank":"36"},
-    "i9860":{"rank":"37"},
-    "i9861":{"rank":"38"},
-    "i9862":{"rank":"39"},
-    "i9863":{"rank":"40"},
-    "i9864":{"rank":"41"},
-    "i9865":{"rank":"42"},
-    "i9866":{"rank":"43"},
-    "i9867":{"rank":"44"},
-    "i9868":{"rank":"45"},
-    "i9869":{"rank":"46"},
-    "i9870":{"rank":"47"},
-    "i9871":{"rank":"48"},
-    "i9872":{"rank":"49"},
-    "i9873":{"rank":"50"},
-    "i9874":{"rank":"51"},
-    "i9875":{"rank":"52"},
-    "i9876":{"rank":"53"},
-    "i9877":{"rank":"54"},
-    "i9878":{"rank":"55"},
-    "i9879":{"rank":"56"},
-    "i9880":{"rank":"57"},
-    "i9881":{"rank":"58"},
-    "i9882":{"rank":"59"},
-    "i9883":{"rank":"60"},
-    "i9884":{"rank":"61"},
-    "i9885":{"rank":"62"},
-    "i9886":{"rank":"63"},
-    "i9887":{"rank":"64"},
-    "i9888":{"rank":"65"},
-    "i9889":{"rank":"66"},
-    "i9890":{"rank":"67"},
-    "i9891":{"rank":"68"},
-    "i9892":{"rank":"69"},
-    "i9893":{"rank":"70"},
-    "i17567":{"rank":"1"},
-    "i9894":{"rank":"71"},
-    "i9895":{"rank":"72"},
-    "i9896":{"rank":"73"},
-    "i9897":{"rank":"74"},
-    "i9898":{"rank":"75"},
-    "i9899":{"rank":"76"},
-    "i9900":{"rank":"77"},
-    "i9901":{"rank":"78"},
-    "i9902":{"rank":"79"},
-    "i9903":{"rank":"80"},
-    "i9904":{"rank":"81"},
-    "i9905":{"rank":"82"},
-    "i9906":{"rank":"83"},
-    "i16497":{"rank":"1"},
-    "i9907":{"rank":"84"},
-    "i9908":{"rank":"85"},
-    "i9909":{"rank":"86"},
-    "i9910":{"rank":"87"},
-    "i9911":{"rank":"88"},
-    "i9912":{"rank":"89"},
-    "i9913":{"rank":"90"},
-    "i9914":{"rank":"91"},
-    "i9915":{"rank":"92"},
-    "i9916":{"rank":"93"},
-    "i9917":{"rank":"94"},
-    "i9918":{"rank":"95"},
-    "i9919":{"rank":"96"},
-    "i9920":{"rank":"97"},
-    "i9921":{"rank":"98"},
-    "i9922":{"rank":"99"},
-    "i9923":{"rank":"100"},
-    "i9924":{"rank":"101"},
-    "i9925":{"rank":"102"},
-    "i9926":{"rank":"103"},
-    "i9927":{"rank":"104"},
-    "i9928":{"rank":"105"},
-    "i9929":{"rank":"106"},
-    "i9930":{"rank":"107"},
-    "i9931":{"rank":"108"},
-    "i18541":{"rank":"1"},
-    "i19723":{"rank":"2"},
-    "i19725":{"rank":"3"},
-    "i19727":{"rank":"4"},
-    "i9932":{"rank":"109"},
-    "i9933":{"rank":"110"},
-    "i9934":{"rank":"111"},
-    "i9935":{"rank":"112"},
-    "i9936":{"rank":"113"},
-    "i9937":{"rank":"114"},
-    "i9938":{"rank":"115"},
-    "i9939":{"rank":"116"},
-    "i9940":{"rank":"117"},
-    "i9941":{"rank":"118"},
-    "i9942":{"rank":"119"},
-    "i9943":{"rank":"120"},
-    "i9944":{"rank":"121"},
-    "i9945":{"rank":"122"},
-    "i9946":{"rank":"123"},
-    "i9947":{"rank":"124"},
-    "i13091":{"rank":"125"},
-    "i9818":{"rank":"5"},
-    "i10699":{"rank":"1"},
-    "i10310":{"rank":"6"},
-    "i11864":{"rank":"1"},
-    "i11865":{"rank":"2"},
-    "i11866":{"rank":"3"},
-    "i11867":{"rank":"4"},
-    "i11868":{"rank":"5"},
-    "i11869":{"rank":"6"},
-    "i11870":{"rank":"7"},
-    "i11871":{"rank":"8"},
-    "i11872":{"rank":"9"},
-    "i11873":{"rank":"10"},
-    "i11874":{"rank":"11"},
-    "i11875":{"rank":"12"},
-    "i11876":{"rank":"13"},
-    "i11877":{"rank":"14"},
-    "i11878":{"rank":"15"},
-    "i11879":{"rank":"16"},
-    "i11880":{"rank":"17"},
-    "i11881":{"rank":"18"},
-    "i11882":{"rank":"19"},
-    "i11883":{"rank":"20"},
-    "i11884":{"rank":"21"},
-    "i11885":{"rank":"22"},
-    "i11886":{"rank":"23"},
-    "i11887":{"rank":"24"},
-    "i11888":{"rank":"25"},
-    "i11889":{"rank":"26"},
-    "i11890":{"rank":"27"},
-    "i11891":{"rank":"28"},
-    "i11892":{"rank":"29"},
-    "i11893":{"rank":"30"},
-    "i11895":{"rank":"31"},
-    "i11896":{"rank":"32"},
-    "i11897":{"rank":"33"},
-    "i11898":{"rank":"34"},
-    "i11899":{"rank":"35"},
-    "i11900":{"rank":"36"},
-    "i11901":{"rank":"37"},
-    "i11902":{"rank":"38"},
-    "i11903":{"rank":"39"},
-    "i11904":{"rank":"40"},
-    "i11905":{"rank":"41"},
-    "i11906":{"rank":"42"},
-    "i11907":{"rank":"43"},
-    "i11908":{"rank":"44"},
-    "i11909":{"rank":"45"},
-    "i12355":{"rank":"1"},
-    "i11894":{"rank":"46"},
-    "i11863":{"rank":"7"},
-    "i11924":{"rank":"1"},
-    "i11925":{"rank":"2"},
-    "i11926":{"rank":"3"},
-    "i11927":{"rank":"4"},
-    "i11923":{"rank":"8"},
-    "i11929":{"rank":"1"},
-    "i11930":{"rank":"2"},
-    "i11931":{"rank":"3"},
-    "i11928":{"rank":"9"},
-    "i11933":{"rank":"1"},
-    "i11934":{"rank":"2"},
-    "i11935":{"rank":"3"},
-    "i11932":{"rank":"10"},
-    "i11937":{"rank":"1"},
-    "i11938":{"rank":"2"},
-    "i11939":{"rank":"3"},
-    "i11940":{"rank":"4"},
-    "i11941":{"rank":"5"},
-    "i11942":{"rank":"6"},
-    "i11943":{"rank":"7"},
-    "i11944":{"rank":"8"},
-    "i11945":{"rank":"9"},
-    "i12764":{"rank":"10"},
-    "i11936":{"rank":"11"},
-    "i15032":{"rank":"1"},
-    "i15031":{"rank":"12"},
-    "i21266":{"rank":"1"},
-    "i15493":{"rank":"13"},
-    "i22309":{"rank":"1"},
-    "i22308":{"rank":"14"},
-    "i32444":{"rank":"15"},
-    "i32445":{"rank":"16"},
-    "i32450":{"rank":"17"},
-    "i33401":{"rank":"1"},
-    "i33402":{"rank":"2"},
-    "i33454":{"rank":"3"},
-    "i33459":{"rank":"4"},
-    "i33460":{"rank":"5"},
-    "i33465":{"rank":"6"},
-    "i33510":{"rank":"7"},
-    "i33400":{"rank":"18"},
-    "i33456":{"rank":"19"},
-    "i9624":{"rank":"1"},
-    "i32432":{"rank":"1"},
-    "i32433":{"rank":"2"},
-    "i9626":{"rank":"2"},
-    "i9627":{"rank":"3"},
-    "i9618":{"rank":"20"},
-    "p399":{"rank":"6"},
-    "i31081":{"rank":"1"},
-    "i31088":{"rank":"2"},
-    "i31089":{"rank":"3"},
-    "i31090":{"rank":"4"},
-    "i31091":{"rank":"5"},
-    "i31110":{"rank":"6"},
-    "i31111":{"rank":"7"},
-    "i31073":{"rank":"1"},
-    "i31125":{"rank":"1"},
-    "i31126":{"rank":"2"},
-    "i31127":{"rank":"3"},
-    "i31128":{"rank":"4"},
-    "i31129":{"rank":"5"},
-    "i31130":{"rank":"6"},
-    "i31131":{"rank":"7"},
-    "i31118":{"rank":"1"},
-    "i31132":{"rank":"1"},
-    "i31133":{"rank":"2"},
-    "i31134":{"rank":"3"},
-    "i31135":{"rank":"4"},
-    "i31136":{"rank":"5"},
-    "i31137":{"rank":"6"},
-    "i31138":{"rank":"7"},
-    "i31631":{"rank":"8"},
-    "i31119":{"rank":"2"},
-    "i31139":{"rank":"1"},
-    "i31140":{"rank":"2"},
-    "i31141":{"rank":"3"},
-    "i31142":{"rank":"4"},
-    "i31143":{"rank":"5"},
-    "i31144":{"rank":"6"},
-    "i31145":{"rank":"7"},
-    "i31632":{"rank":"8"},
-    "i31120":{"rank":"3"},
-    "i31146":{"rank":"1"},
-    "i31633":{"rank":"2"},
-    "i31634":{"rank":"3"},
-    "i31636":{"rank":"4"},
-    "i31121":{"rank":"4"},
-    "i31116":{"rank":"2"},
-    "i31122":{"rank":"1"},
-    "i31123":{"rank":"2"},
-    "i31124":{"rank":"3"},
-    "i31117":{"rank":"3"},
-    "i31345":{"rank":"1"},
-    "i31343":{"rank":"4"},
-    "i33376":{"rank":"5"},
-    "p642":{"rank":"7"},
-    "p393":{"rank":"-3"},
-    "i21927":{"rank":"1"},
-    "i22736":{"rank":"2"},
-    "i21712":{"rank":"1"},
-    "i21711":{"rank":"1"},
-    "i22750":{"rank":"2"},
-    "i28992":{"rank":"3"},
-    "i22769":{"rank":"1"},
-    "i21715":{"rank":"1"},
-    "i21714":{"rank":"4"},
-    "p573":{"rank":"1"},
-    "i30292":{"rank":"1"},
-    "i30295":{"rank":"2"},
-    "i30330":{"rank":"3"},
-    "i30381":{"rank":"4"},
-    "i30421":{"rank":"5"},
-    "i30457":{"rank":"6"},
-    "i31245":{"rank":"1"},
-    "i30458":{"rank":"7"},
-    "i30459":{"rank":"8"},
-    "i30460":{"rank":"9"},
-    "p659":{"rank":"2"},
-    "i31562":{"rank":"1"},
-    "i31563":{"rank":"2"},
-    "i31576":{"rank":"3"},
-    "i31577":{"rank":"4"},
-    "i28862":{"rank":"1"},
-    "i28888":{"rank":"2"},
-    "i30840":{"rank":"1"},
-    "i30839":{"rank":"1"},
-    "i28916":{"rank":"1"},
-    "i31567":{"rank":"2"},
-    "i31568":{"rank":"3"},
-    "i31569":{"rank":"4"},
-    "i31570":{"rank":"5"},
-    "i31571":{"rank":"6"},
-    "i31572":{"rank":"7"},
-    "i31573":{"rank":"8"},
-    "i31574":{"rank":"9"},
-    "i32476":{"rank":"10"},
-    "i31566":{"rank":"2"},
-    "i28890":{"rank":"3"},
-    "i28332":{"rank":"1"},
-    "i28891":{"rank":"4"},
-    "i28909":{"rank":"1"},
-    "i28892":{"rank":"5"},
-    "i30835":{"rank":"1"},
-    "i31148":{"rank":"2"},
-    "i31198":{"rank":"3"},
-    "i28893":{"rank":"6"},
-    "i28894":{"rank":"7"},
-    "i33387":{"rank":"1"},
-    "i28897":{"rank":"8"},
-    "i29997":{"rank":"1"},
-    "i30370":{"rank":"2"},
-    "i31607":{"rank":"3"},
-    "i32507":{"rank":"4"},
-    "i29334":{"rank":"9"},
-    "i30742":{"rank":"1"},
-    "i30743":{"rank":"2"},
-    "i30744":{"rank":"3"},
-    "i30745":{"rank":"4"},
-    "i31203":{"rank":"5"},
-    "i30739":{"rank":"1"},
-    "i30740":{"rank":"2"},
-    "i30741":{"rank":"3"},
-    "i30738":{"rank":"10"},
-    "i30760":{"rank":"1"},
-    "i30759":{"rank":"11"},
-    "i30830":{"rank":"1"},
-    "i30829":{"rank":"12"},
-    "i29950":{"rank":"1"},
-    "i28889":{"rank":"1"},
-    "i30833":{"rank":"2"},
-    "i30832":{"rank":"13"},
-    "i31335":{"rank":"14"},
-    "i32356":{"rank":"15"},
-    "p636":{"rank":"3"},
-    "i29075":{"rank":"1"},
-    "p420":{"rank":"4"},
-    "i32416":{"rank":"1"},
-    "i30864":{"rank":"1"},
-    "i29089":{"rank":"1"},
-    "i29090":{"rank":"2"},
-    "i29091":{"rank":"3"},
-    "i29321":{"rank":"1"},
-    "i29092":{"rank":"4"},
-    "i32018":{"rank":"1"},
-    "i32467":{"rank":"2"},
-    "i29093":{"rank":"5"},
-    "i29094":{"rank":"6"},
-    "i29095":{"rank":"7"},
-    "i31812":{"rank":"1"},
-    "i32475":{"rank":"2"},
-    "i29096":{"rank":"8"},
-    "i29097":{"rank":"9"},
-    "i29098":{"rank":"10"},
-    "i29297":{"rank":"1"},
-    "i29099":{"rank":"11"},
-    "i29100":{"rank":"12"},
-    "i29101":{"rank":"13"},
-    "i29102":{"rank":"14"},
-    "i29105":{"rank":"15"},
-    "i29181":{"rank":"16"},
-    "i29187":{"rank":"17"},
-    "i30392":{"rank":"18"},
-    "i31411":{"rank":"1"},
-    "i32474":{"rank":"2"},
-    "i30815":{"rank":"19"},
-    "i30854":{"rank":"20"},
-    "i30869":{"rank":"21"},
-    "i31310":{"rank":"22"},
-    "i31822":{"rank":"1"},
-    "i31552":{"rank":"23"},
-    "p645":{"rank":"5"},
-    "i31323":{"rank":"1"},
-    "i31348":{"rank":"2"},
-    "i31385":{"rank":"3"},
-    "i31398":{"rank":"4"},
-    "i31319":{"rank":"1"},
-    "i31362":{"rank":"1"},
-    "i31590":{"rank":"2"},
-    "i31608":{"rank":"3"},
-    "i31609":{"rank":"4"},
-    "i31637":{"rank":"5"},
-    "i31678":{"rank":"6"},
-    "i31320":{"rank":"2"},
-    "i31520":{"rank":"1"},
-    "i31321":{"rank":"3"},
-    "i31525":{"rank":"1"},
-    "i31324":{"rank":"1"},
-    "i31528":{"rank":"1"},
-    "i31325":{"rank":"2"},
-    "i31529":{"rank":"1"},
-    "i31326":{"rank":"3"},
-    "i31350":{"rank":"1"},
-    "i31526":{"rank":"2"},
-    "i31542":{"rank":"3"},
-    "i31327":{"rank":"4"},
-    "i31527":{"rank":"1"},
-    "i31541":{"rank":"2"},
-    "i31328":{"rank":"5"},
-    "i31524":{"rank":"1"},
-    "i31329":{"rank":"6"},
-    "i31349":{"rank":"1"},
-    "i31330":{"rank":"7"},
-    "i31331":{"rank":"8"},
-    "i31322":{"rank":"4"},
-    "i31351":{"rank":"5"},
-    "p671":{"rank":"6"},
-    "i22119":{"rank":"1"},
-    "i28288":{"rank":"2"},
-    "i21295":{"rank":"1"},
-    "i21296":{"rank":"2"},
-    "i21556":{"rank":"1"},
-    "i21969":{"rank":"2"},
-    "i21555":{"rank":"3"},
-    "i21294":{"rank":"1"},
-    "i27104":{"rank":"1"},
-    "i21302":{"rank":"1"},
-    "i21305":{"rank":"2"},
-    "i21301":{"rank":"2"},
-    "i22405":{"rank":"1"},
-    "i21313":{"rank":"1"},
-    "i21315":{"rank":"2"},
-    "i28918":{"rank":"3"},
-    "i21312":{"rank":"3"},
-    "i21814":{"rank":"1"},
-    "i21342":{"rank":"1"},
-    "i21549":{"rank":"2"},
-    "i21552":{"rank":"3"},
-    "i21544":{"rank":"4"},
-    "i28351":{"rank":"5"},
-    "i28353":{"rank":"6"},
-    "i29130":{"rank":"7"},
-    "i30368":{"rank":"8"},
-    "i30819":{"rank":"9"},
-    "p568":{"rank":"7"},
-    "i31594":{"rank":"1"},
-    "i32415":{"rank":"2"},
-    "i32466":{"rank":"3"},
-    "i32469":{"rank":"4"},
-    "i32470":{"rank":"5"},
-    "i30953":{"rank":"1"},
-    "i33452":{"rank":"1"},
-    "i30954":{"rank":"2"},
-    "i33399":{"rank":"1"},
-    "i30955":{"rank":"3"},
-    "i30956":{"rank":"4"},
-    "i31819":{"rank":"1"},
-    "i32107":{"rank":"2"},
-    "i30957":{"rank":"5"},
-    "i32073":{"rank":"1"},
-    "i32074":{"rank":"2"},
-    "i30958":{"rank":"6"},
-    "i30959":{"rank":"7"},
-    "i30965":{"rank":"1"},
-    "i30961":{"rank":"1"},
-    "i30960":{"rank":"8"},
-    "i30966":{"rank":"9"},
-    "i31591":{"rank":"10"},
-    "i31829":{"rank":"11"},
-    "i32477":{"rank":"12"},
-    "p670":{"rank":"8"},
-    "p657":{"rank":"1"},
-    "i28266":{"rank":"1"},
-    "i28342":{"rank":"1"},
-    "i28341":{"rank":"2"},
-    "i28901":{"rank":"1"},
-    "i28903":{"rank":"2"},
-    "i28902":{"rank":"3"},
-    "i28946":{"rank":"4"},
-    "i29308":{"rank":"1"},
-    "i29316":{"rank":"2"},
-    "i30320":{"rank":"3"},
-    "i29302":{"rank":"5"},
-    "i30004":{"rank":"6"},
-    "i30045":{"rank":"1"},
-    "i30046":{"rank":"2"},
-    "i30047":{"rank":"3"},
-    "i30044":{"rank":"1"},
-    "i30043":{"rank":"7"},
-    "i30285":{"rank":"8"},
-    "i30319":{"rank":"9"},
-    "i29047":{"rank":"1"},
-    "i30726":{"rank":"10"},
-    "i27415":{"rank":"1"},
-    "i30818":{"rank":"2"},
-    "i30817":{"rank":"11"},
-    "i30935":{"rank":"12"},
-    "p608":{"rank":"2"},
-    "i26066":{"rank":"1"},
-    "i26067":{"rank":"2"},
-    "i26070":{"rank":"3"},
-    "i26071":{"rank":"4"},
-    "i25997":{"rank":"1"},
-    "i26029":{"rank":"1"},
-    "i26009":{"rank":"1"},
-    "i26030":{"rank":"1"},
-    "i26010":{"rank":"2"},
-    "i26011":{"rank":"3"},
-    "i26012":{"rank":"4"},
-    "i26031":{"rank":"1"},
-    "i26013":{"rank":"5"},
-    "i26032":{"rank":"1"},
-    "i26014":{"rank":"6"},
-    "i26016":{"rank":"7"},
-    "i26069":{"rank":"1"},
-    "i26068":{"rank":"8"},
-    "i25998":{"rank":"2"},
-    "i26033":{"rank":"1"},
-    "i26034":{"rank":"2"},
-    "i26035":{"rank":"3"},
-    "i25999":{"rank":"3"},
-    "i26036":{"rank":"1"},
-    "i26037":{"rank":"2"},
-    "i26038":{"rank":"3"},
-    "i26088":{"rank":"4"},
-    "i26089":{"rank":"5"},
-    "i26000":{"rank":"4"},
-    "i26039":{"rank":"1"},
-    "i26040":{"rank":"2"},
-    "i26041":{"rank":"3"},
-    "i26087":{"rank":"4"},
-    "i26001":{"rank":"5"},
-    "i26042":{"rank":"1"},
-    "i26002":{"rank":"6"},
-    "i26043":{"rank":"1"},
-    "i26003":{"rank":"7"},
-    "i26044":{"rank":"1"},
-    "i26045":{"rank":"2"},
-    "i26004":{"rank":"8"},
-    "i26055":{"rank":"1"},
-    "i26056":{"rank":"2"},
-    "i26046":{"rank":"1"},
-    "i26057":{"rank":"1"},
-    "i26047":{"rank":"2"},
-    "i26005":{"rank":"9"},
-    "i26048":{"rank":"1"},
-    "i26049":{"rank":"2"},
-    "i26006":{"rank":"10"},
-    "i26050":{"rank":"1"},
-    "i26007":{"rank":"11"},
-    "i26051":{"rank":"1"},
-    "i26052":{"rank":"2"},
-    "i26053":{"rank":"3"},
-    "i26093":{"rank":"4"},
-    "i26008":{"rank":"12"},
-    "i26054":{"rank":"1"},
-    "i26015":{"rank":"13"},
-    "i26017":{"rank":"14"},
-    "i26018":{"rank":"15"},
-    "i26058":{"rank":"1"},
-    "i26059":{"rank":"2"},
-    "i26060":{"rank":"3"},
-    "i26075":{"rank":"4"},
-    "i26076":{"rank":"5"},
-    "i26077":{"rank":"6"},
-    "i26078":{"rank":"7"},
-    "i26079":{"rank":"8"},
-    "i26019":{"rank":"16"},
-    "i26020":{"rank":"17"},
-    "i26021":{"rank":"18"},
-    "i26072":{"rank":"1"},
-    "i26073":{"rank":"2"},
-    "i26074":{"rank":"3"},
-    "i26022":{"rank":"19"},
-    "i26080":{"rank":"1"},
-    "i26023":{"rank":"20"},
-    "i26061":{"rank":"1"},
-    "i26062":{"rank":"2"},
-    "i26063":{"rank":"3"},
-    "i26064":{"rank":"4"},
-    "i26065":{"rank":"5"},
-    "i26085":{"rank":"6"},
-    "i26086":{"rank":"7"},
-    "i26024":{"rank":"21"},
-    "i26081":{"rank":"1"},
-    "i26082":{"rank":"2"},
-    "i26083":{"rank":"3"},
-    "i26084":{"rank":"4"},
-    "i26025":{"rank":"22"},
-    "i26092":{"rank":"1"},
-    "i26026":{"rank":"23"},
-    "i26090":{"rank":"1"},
-    "i26027":{"rank":"24"},
-    "i26091":{"rank":"1"},
-    "i26028":{"rank":"25"},
-    "p607":{"rank":"3"},
-    "i27050":{"rank":"1"},
-    "i27051":{"rank":"2"},
-    "i27052":{"rank":"3"},
-    "i27053":{"rank":"4"},
-    "i27049":{"rank":"4"},
-    "i27055":{"rank":"1"},
-    "i27054":{"rank":"5"},
-    "i30048":{"rank":"6"},
-    "p439":{"rank":"9"},
-    "i11703":{"rank":"10"},
-    "p419":{"rank":"-2"},
-    "i30314":{"rank":"1"},
-    "p649":{"rank":"1"},
-    "i18178":{"rank":"1"},
-    "i22193":{"rank":"1"},
-    "i22192":{"rank":"1"},
-    "i21198":{"rank":"2"},
-    "p371":{"rank":"2"},
-    "i31994":{"rank":"1"},
-    "i28224":{"rank":"1"},
-    "i28215":{"rank":"1"},
-    "i24456":{"rank":"2"},
-    "i13676":{"rank":"1"},
-    "p478":{"rank":"3"},
-    "p546":{"rank":"4"},
-    "i25413":{"rank":"1"},
-    "i25442":{"rank":"2"},
-    "i25456":{"rank":"1"},
-    "i25453":{"rank":"1"},
-    "i25444":{"rank":"1"},
-    "i25546":{"rank":"1"},
-    "i25543":{"rank":"1"},
-    "i25481":{"rank":"2"},
-    "i25578":{"rank":"1"},
-    "i25577":{"rank":"3"},
-    "i25443":{"rank":"3"},
-    "i25615":{"rank":"4"},
-    "i25622":{"rank":"1"},
-    "i25623":{"rank":"2"},
-    "i25624":{"rank":"3"},
-    "i25625":{"rank":"4"},
-    "i25621":{"rank":"1"},
-    "i25616":{"rank":"5"},
-    "i25644":{"rank":"6"},
-    "i25649":{"rank":"1"},
-    "i25650":{"rank":"2"},
-    "i25651":{"rank":"3"},
-    "i25652":{"rank":"4"},
-    "i25653":{"rank":"5"},
-    "i25654":{"rank":"6"},
-    "i25648":{"rank":"7"},
-    "i25655":{"rank":"8"},
-    "i25656":{"rank":"9"},
-    "i25657":{"rank":"10"},
-    "i25641":{"rank":"1"},
-    "i25638":{"rank":"1"},
-    "i25632":{"rank":"11"},
-    "p599":{"rank":"5"},
-    "i15869":{"rank":"1"},
-    "i18410":{"rank":"2"},
-    "i15866":{"rank":"1"},
-    "i16286":{"rank":"1"},
-    "i15874":{"rank":"2"},
-    "i15875":{"rank":"3"},
-    "i16086":{"rank":"4"},
-    "i16519":{"rank":"5"},
-    "i17496":{"rank":"6"},
-    "i22388":{"rank":"1"},
-    "i17940":{"rank":"7"},
-    "i18411":{"rank":"8"},
-    "i28346":{"rank":"1"},
-    "i28956":{"rank":"2"},
-    "i18412":{"rank":"9"},
-    "i18559":{"rank":"10"},
-    "i16084":{"rank":"1"},
-    "i23365":{"rank":"1"},
-    "i23370":{"rank":"1"},
-    "i23371":{"rank":"2"},
-    "i23372":{"rank":"3"},
-    "i23373":{"rank":"4"},
-    "i23374":{"rank":"5"},
-    "i23375":{"rank":"6"},
-    "i23376":{"rank":"7"},
-    "i23377":{"rank":"8"},
-    "i23378":{"rank":"9"},
-    "i23379":{"rank":"10"},
-    "i23380":{"rank":"11"},
-    "i23381":{"rank":"12"},
-    "i23382":{"rank":"13"},
-    "i23383":{"rank":"14"},
-    "i23384":{"rank":"15"},
-    "i23385":{"rank":"16"},
-    "i23386":{"rank":"17"},
-    "i23387":{"rank":"18"},
-    "i23388":{"rank":"19"},
-    "i23389":{"rank":"20"},
-    "i23390":{"rank":"21"},
-    "i23391":{"rank":"22"},
-    "i23392":{"rank":"23"},
-    "i23393":{"rank":"24"},
-    "i23394":{"rank":"25"},
-    "i23395":{"rank":"26"},
-    "i23396":{"rank":"27"},
-    "i23369":{"rank":"1"},
-    "i23368":{"rank":"2"},
-    "i23397":{"rank":"3"},
-    "i23401":{"rank":"1"},
-    "i23402":{"rank":"2"},
-    "i23403":{"rank":"3"},
-    "i23400":{"rank":"1"},
-    "i15867":{"rank":"1"},
-    "i23405":{"rank":"2"},
-    "i23406":{"rank":"3"},
-    "i23407":{"rank":"4"},
-    "i23404":{"rank":"2"},
-    "i15879":{"rank":"1"},
-    "i23409":{"rank":"2"},
-    "i23410":{"rank":"3"},
-    "i23411":{"rank":"4"},
-    "i23408":{"rank":"3"},
-    "i15873":{"rank":"1"},
-    "i23413":{"rank":"2"},
-    "i23414":{"rank":"3"},
-    "i23415":{"rank":"4"},
-    "i23412":{"rank":"4"},
-    "i23399":{"rank":"1"},
-    "i23418":{"rank":"1"},
-    "i23419":{"rank":"2"},
-    "i23420":{"rank":"3"},
-    "i23417":{"rank":"1"},
-    "i23422":{"rank":"1"},
-    "i23423":{"rank":"2"},
-    "i23424":{"rank":"3"},
-    "i23421":{"rank":"2"},
-    "i23426":{"rank":"1"},
-    "i23427":{"rank":"2"},
-    "i23428":{"rank":"3"},
-    "i23429":{"rank":"4"},
-    "i23425":{"rank":"3"},
-    "i23432":{"rank":"1"},
-    "i23433":{"rank":"2"},
-    "i23434":{"rank":"3"},
-    "i23431":{"rank":"1"},
-    "i23435":{"rank":"2"},
-    "i23430":{"rank":"4"},
-    "i23416":{"rank":"2"},
-    "i23440":{"rank":"1"},
-    "i23441":{"rank":"2"},
-    "i23439":{"rank":"1"},
-    "i23443":{"rank":"1"},
-    "i23444":{"rank":"2"},
-    "i23442":{"rank":"2"},
-    "i23438":{"rank":"1"},
-    "i23446":{"rank":"1"},
-    "i23447":{"rank":"2"},
-    "i23448":{"rank":"3"},
-    "i23445":{"rank":"2"},
-    "i23450":{"rank":"1"},
-    "i23451":{"rank":"2"},
-    "i23452":{"rank":"3"},
-    "i23449":{"rank":"3"},
-    "i23454":{"rank":"1"},
-    "i23455":{"rank":"2"},
-    "i23456":{"rank":"3"},
-    "i23453":{"rank":"4"},
-    "i23458":{"rank":"1"},
-    "i23459":{"rank":"2"},
-    "i23460":{"rank":"3"},
-    "i23457":{"rank":"5"},
-    "i23462":{"rank":"1"},
-    "i23463":{"rank":"2"},
-    "i23464":{"rank":"3"},
-    "i23461":{"rank":"6"},
-    "i23466":{"rank":"1"},
-    "i23467":{"rank":"2"},
-    "i23468":{"rank":"3"},
-    "i23465":{"rank":"7"},
-    "i23470":{"rank":"1"},
-    "i23471":{"rank":"2"},
-    "i23472":{"rank":"3"},
-    "i23469":{"rank":"8"},
-    "i23474":{"rank":"1"},
-    "i23475":{"rank":"2"},
-    "i23476":{"rank":"3"},
-    "i23473":{"rank":"9"},
-    "i23478":{"rank":"1"},
-    "i23479":{"rank":"2"},
-    "i23480":{"rank":"3"},
-    "i23477":{"rank":"10"},
-    "i23482":{"rank":"1"},
-    "i23483":{"rank":"2"},
-    "i23484":{"rank":"3"},
-    "i23481":{"rank":"11"},
-    "i23486":{"rank":"1"},
-    "i23487":{"rank":"2"},
-    "i23488":{"rank":"3"},
-    "i23485":{"rank":"12"},
-    "i23490":{"rank":"1"},
-    "i23491":{"rank":"2"},
-    "i23492":{"rank":"3"},
-    "i23489":{"rank":"13"},
-    "i23494":{"rank":"1"},
-    "i23495":{"rank":"2"},
-    "i23496":{"rank":"3"},
-    "i23497":{"rank":"4"},
-    "i23493":{"rank":"14"},
-    "i23437":{"rank":"1"},
-    "i21333":{"rank":"1"},
-    "i22387":{"rank":"2"},
-    "i15871":{"rank":"1"},
-    "i23500":{"rank":"1"},
-    "i23499":{"rank":"2"},
-    "i15872":{"rank":"1"},
-    "i23502":{"rank":"2"},
-    "i23504":{"rank":"1"},
-    "i23505":{"rank":"2"},
-    "i23506":{"rank":"3"},
-    "i23503":{"rank":"3"},
-    "i23501":{"rank":"3"},
-    "i23508":{"rank":"1"},
-    "i23507":{"rank":"4"},
-    "i23510":{"rank":"1"},
-    "i23509":{"rank":"5"},
-    "i23511":{"rank":"6"},
-    "i23513":{"rank":"1"},
-    "i23514":{"rank":"2"},
-    "i23512":{"rank":"7"},
-    "i23516":{"rank":"1"},
-    "i23515":{"rank":"8"},
-    "i23518":{"rank":"1"},
-    "i23517":{"rank":"9"},
-    "i23520":{"rank":"1"},
-    "i23521":{"rank":"2"},
-    "i23519":{"rank":"10"},
-    "i23523":{"rank":"1"},
-    "i23522":{"rank":"11"},
-    "i23525":{"rank":"1"},
-    "i23524":{"rank":"12"},
-    "i23527":{"rank":"1"},
-    "i23526":{"rank":"13"},
-    "i23529":{"rank":"1"},
-    "i23530":{"rank":"2"},
-    "i23531":{"rank":"3"},
-    "i23528":{"rank":"14"},
-    "i23498":{"rank":"2"},
-    "i23436":{"rank":"3"},
-    "i16062":{"rank":"1"},
-    "i23533":{"rank":"1"},
-    "i23532":{"rank":"4"},
-    "i23537":{"rank":"1"},
-    "i23538":{"rank":"2"},
-    "i23539":{"rank":"3"},
-    "i23535":{"rank":"1"},
-    "i23541":{"rank":"1"},
-    "i23542":{"rank":"2"},
-    "i23543":{"rank":"3"},
-    "i23540":{"rank":"2"},
-    "i23546":{"rank":"1"},
-    "i23547":{"rank":"2"},
-    "i23545":{"rank":"1"},
-    "i23549":{"rank":"1"},
-    "i23550":{"rank":"2"},
-    "i23548":{"rank":"2"},
-    "i23552":{"rank":"1"},
-    "i23553":{"rank":"2"},
-    "i23551":{"rank":"3"},
-    "i23555":{"rank":"1"},
-    "i23556":{"rank":"2"},
-    "i23554":{"rank":"4"},
-    "i23557":{"rank":"5"},
-    "i23544":{"rank":"3"},
-    "i23534":{"rank":"5"},
-    "i23559":{"rank":"1"},
-    "i23560":{"rank":"2"},
-    "i23562":{"rank":"1"},
-    "i23563":{"rank":"2"},
-    "i23564":{"rank":"3"},
-    "i23565":{"rank":"4"},
-    "i23566":{"rank":"5"},
-    "i23567":{"rank":"6"},
-    "i15870":{"rank":"1"},
-    "i23568":{"rank":"7"},
-    "i23561":{"rank":"3"},
-    "i23569":{"rank":"4"},
-    "i23558":{"rank":"6"},
-    "i23398":{"rank":"4"},
-    "i23570":{"rank":"5"},
-    "i23574":{"rank":"1"},
-    "i23572":{"rank":"1"},
-    "i23575":{"rank":"2"},
-    "i23577":{"rank":"1"},
-    "i23578":{"rank":"2"},
-    "i23579":{"rank":"3"},
-    "i23580":{"rank":"4"},
-    "i23576":{"rank":"3"},
-    "i23582":{"rank":"1"},
-    "i23583":{"rank":"2"},
-    "i23581":{"rank":"4"},
-    "i23585":{"rank":"1"},
-    "i23586":{"rank":"2"},
-    "i23584":{"rank":"5"},
-    "i23571":{"rank":"6"},
-    "i23589":{"rank":"1"},
-    "i23588":{"rank":"1"},
-    "i23591":{"rank":"1"},
-    "i23592":{"rank":"2"},
-    "i23590":{"rank":"2"},
-    "i23594":{"rank":"1"},
-    "i23595":{"rank":"2"},
-    "i23596":{"rank":"3"},
-    "i23593":{"rank":"3"},
-    "i23597":{"rank":"4"},
-    "i23598":{"rank":"5"},
-    "i23587":{"rank":"7"},
-    "i23600":{"rank":"1"},
-    "i23601":{"rank":"2"},
-    "i23602":{"rank":"3"},
-    "i23599":{"rank":"8"},
-    "i23604":{"rank":"1"},
-    "i23605":{"rank":"2"},
-    "i23606":{"rank":"3"},
-    "i23607":{"rank":"4"},
-    "i23608":{"rank":"5"},
-    "i23609":{"rank":"6"},
-    "i23603":{"rank":"9"},
-    "i23610":{"rank":"10"},
-    "i23611":{"rank":"11"},
-    "i23612":{"rank":"12"},
-    "i27819":{"rank":"1"},
-    "i27821":{"rank":"2"},
-    "i27822":{"rank":"3"},
-    "i27823":{"rank":"4"},
-    "i27824":{"rank":"5"},
-    "i27818":{"rank":"13"},
-    "p494":{"rank":"6"},
-    "i27410":{"rank":"1"},
-    "i22667":{"rank":"1"},
-    "i23758":{"rank":"1"},
-    "i23759":{"rank":"2"},
-    "i27413":{"rank":"3"},
-    "p590":{"rank":"7"},
-    "i27857":{"rank":"1"},
-    "i27956":{"rank":"2"},
-    "i27958":{"rank":"1"},
-    "i27959":{"rank":"2"},
-    "i27957":{"rank":"3"},
-    "i26954":{"rank":"1"},
-    "i27981":{"rank":"1"},
-    "i26955":{"rank":"2"},
-    "i27974":{"rank":"1"},
-    "i27976":{"rank":"2"},
-    "i27977":{"rank":"3"},
-    "i27978":{"rank":"4"},
-    "i27807":{"rank":"3"},
-    "i27827":{"rank":"4"},
-    "i28045":{"rank":"1"},
-    "i28046":{"rank":"2"},
-    "i28047":{"rank":"3"},
-    "i28048":{"rank":"4"},
-    "i28049":{"rank":"5"},
-    "i28050":{"rank":"6"},
-    "i28051":{"rank":"7"},
-    "i28052":{"rank":"8"},
-    "i28053":{"rank":"9"},
-    "i28054":{"rank":"10"},
-    "i28055":{"rank":"11"},
-    "i28056":{"rank":"12"},
-    "i27984":{"rank":"1"},
-    "i27985":{"rank":"2"},
-    "i28058":{"rank":"1"},
-    "i28059":{"rank":"2"},
-    "i28060":{"rank":"3"},
-    "i27987":{"rank":"3"},
-    "i28061":{"rank":"1"},
-    "i27992":{"rank":"4"},
-    "i27993":{"rank":"5"},
-    "i27994":{"rank":"6"},
-    "i28062":{"rank":"1"},
-    "i27995":{"rank":"7"},
-    "i27850":{"rank":"1"},
-    "i27936":{"rank":"1"},
-    "i29268":{"rank":"2"},
-    "i29269":{"rank":"3"},
-    "i27851":{"rank":"2"},
-    "i28036":{"rank":"1"},
-    "i28071":{"rank":"2"},
-    "i28166":{"rank":"3"},
-    "i28349":{"rank":"4"},
-    "i27979":{"rank":"3"},
-    "i28044":{"rank":"4"},
-    "i27849":{"rank":"5"},
-    "i27968":{"rank":"1"},
-    "i27973":{"rank":"2"},
-    "i27806":{"rank":"1"},
-    "i27955":{"rank":"6"},
-    "i27980":{"rank":"7"},
-    "i29258":{"rank":"1"},
-    "i29257":{"rank":"8"},
-    "p621":{"rank":"8"},
-    "i27451":{"rank":"1"},
-    "i27453":{"rank":"2"},
-    "i27455":{"rank":"3"},
-    "i27456":{"rank":"4"},
-    "i27457":{"rank":"5"},
-    "i27458":{"rank":"6"},
-    "i27459":{"rank":"7"},
-    "i27462":{"rank":"8"},
-    "i27463":{"rank":"9"},
-    "i27464":{"rank":"10"},
-    "i27465":{"rank":"11"},
-    "i27466":{"rank":"12"},
-    "i27467":{"rank":"13"},
-    "i27469":{"rank":"14"},
-    "i27471":{"rank":"15"},
-    "i27472":{"rank":"16"},
-    "i27473":{"rank":"17"},
-    "i27475":{"rank":"18"},
-    "i27450":{"rank":"1"},
-    "i27449":{"rank":"1"},
-    "i27478":{"rank":"2"},
-    "i27486":{"rank":"1"},
-    "i27488":{"rank":"2"},
-    "i27485":{"rank":"1"},
-    "i27492":{"rank":"1"},
-    "i27489":{"rank":"2"},
-    "i27493":{"rank":"3"},
-    "i27480":{"rank":"1"},
-    "i27497":{"rank":"2"},
-    "i27578":{"rank":"1"},
-    "i27574":{"rank":"1"},
-    "i27518":{"rank":"1"},
-    "i27601":{"rank":"1"},
-    "i27602":{"rank":"2"},
-    "i27600":{"rank":"1"},
-    "i27610":{"rank":"1"},
-    "i27611":{"rank":"2"},
-    "i27612":{"rank":"3"},
-    "i27609":{"rank":"2"},
-    "i27579":{"rank":"2"},
-    "i27517":{"rank":"3"},
-    "i27614":{"rank":"1"},
-    "i27613":{"rank":"4"},
-    "i27479":{"rank":"3"},
-    "i27651":{"rank":"4"},
-    "i27786":{"rank":"1"},
-    "i27680":{"rank":"5"},
-    "i27685":{"rank":"1"},
-    "i27686":{"rank":"2"},
-    "i27687":{"rank":"3"},
-    "i27688":{"rank":"4"},
-    "i27689":{"rank":"5"},
-    "i27684":{"rank":"6"},
-    "i27691":{"rank":"7"},
-    "i27692":{"rank":"8"},
-    "i27693":{"rank":"9"},
-    "i30267":{"rank":"1"},
-    "i30268":{"rank":"2"},
-    "i30265":{"rank":"10"},
-    "p633":{"rank":"9"},
-    "i32834":{"rank":"1"},
-    "i32835":{"rank":"2"},
-    "i32836":{"rank":"3"},
-    "i32837":{"rank":"4"},
-    "i32838":{"rank":"5"},
-    "i32839":{"rank":"6"},
-    "i32840":{"rank":"7"},
-    "i32841":{"rank":"8"},
-    "i32842":{"rank":"9"},
-    "i32843":{"rank":"10"},
-    "i32844":{"rank":"11"},
-    "i32845":{"rank":"12"},
-    "i32846":{"rank":"13"},
-    "i32847":{"rank":"14"},
-    "i32848":{"rank":"15"},
-    "i32849":{"rank":"16"},
-    "i32850":{"rank":"17"},
-    "i32851":{"rank":"18"},
-    "i32852":{"rank":"19"},
-    "i32853":{"rank":"20"},
-    "i32854":{"rank":"21"},
-    "i32855":{"rank":"22"},
-    "i32856":{"rank":"23"},
-    "i32857":{"rank":"24"},
-    "i32858":{"rank":"25"},
-    "i32859":{"rank":"26"},
-    "i32860":{"rank":"27"},
-    "i32833":{"rank":"1"},
-    "i32832":{"rank":"1"},
-    "i32861":{"rank":"2"},
-    "i32865":{"rank":"1"},
-    "i32866":{"rank":"2"},
-    "i32867":{"rank":"3"},
-    "i32864":{"rank":"1"},
-    "i32869":{"rank":"1"},
-    "i32870":{"rank":"2"},
-    "i32871":{"rank":"3"},
-    "i32868":{"rank":"2"},
-    "i32873":{"rank":"1"},
-    "i32874":{"rank":"2"},
-    "i32875":{"rank":"3"},
-    "i32872":{"rank":"3"},
-    "i32877":{"rank":"1"},
-    "i32878":{"rank":"2"},
-    "i32879":{"rank":"3"},
-    "i32876":{"rank":"4"},
-    "i32863":{"rank":"1"},
-    "i32882":{"rank":"1"},
-    "i32883":{"rank":"2"},
-    "i32884":{"rank":"3"},
-    "i32881":{"rank":"1"},
-    "i32886":{"rank":"1"},
-    "i32887":{"rank":"2"},
-    "i32888":{"rank":"3"},
-    "i32885":{"rank":"2"},
-    "i32890":{"rank":"1"},
-    "i32891":{"rank":"2"},
-    "i32892":{"rank":"3"},
-    "i32893":{"rank":"4"},
-    "i32889":{"rank":"3"},
-    "i32896":{"rank":"1"},
-    "i32897":{"rank":"2"},
-    "i32898":{"rank":"3"},
-    "i32895":{"rank":"1"},
-    "i32899":{"rank":"2"},
-    "i32894":{"rank":"4"},
-    "i32880":{"rank":"2"},
-    "i32904":{"rank":"1"},
-    "i32905":{"rank":"2"},
-    "i32903":{"rank":"1"},
-    "i32907":{"rank":"1"},
-    "i32908":{"rank":"2"},
-    "i32906":{"rank":"2"},
-    "i32902":{"rank":"1"},
-    "i32911":{"rank":"1"},
-    "i32912":{"rank":"2"},
-    "i32909":{"rank":"2"},
-    "i32914":{"rank":"1"},
-    "i32915":{"rank":"2"},
-    "i32916":{"rank":"3"},
-    "i32913":{"rank":"3"},
-    "i32918":{"rank":"1"},
-    "i32919":{"rank":"2"},
-    "i32920":{"rank":"3"},
-    "i32917":{"rank":"4"},
-    "i32922":{"rank":"1"},
-    "i32923":{"rank":"2"},
-    "i32924":{"rank":"3"},
-    "i32921":{"rank":"5"},
-    "i32926":{"rank":"1"},
-    "i32927":{"rank":"2"},
-    "i32928":{"rank":"3"},
-    "i32925":{"rank":"6"},
-    "i32930":{"rank":"1"},
-    "i32931":{"rank":"2"},
-    "i32932":{"rank":"3"},
-    "i32929":{"rank":"7"},
-    "i32934":{"rank":"1"},
-    "i32935":{"rank":"2"},
-    "i32936":{"rank":"3"},
-    "i32933":{"rank":"8"},
-    "i32938":{"rank":"1"},
-    "i32939":{"rank":"2"},
-    "i32940":{"rank":"3"},
-    "i32937":{"rank":"9"},
-    "i32942":{"rank":"1"},
-    "i32943":{"rank":"2"},
-    "i32944":{"rank":"3"},
-    "i32941":{"rank":"10"},
-    "i32946":{"rank":"1"},
-    "i32947":{"rank":"2"},
-    "i32948":{"rank":"3"},
-    "i32945":{"rank":"11"},
-    "i32950":{"rank":"1"},
-    "i32951":{"rank":"2"},
-    "i32952":{"rank":"3"},
-    "i32949":{"rank":"12"},
-    "i32954":{"rank":"1"},
-    "i32955":{"rank":"2"},
-    "i32956":{"rank":"3"},
-    "i32953":{"rank":"13"},
-    "i32958":{"rank":"1"},
-    "i32959":{"rank":"2"},
-    "i32960":{"rank":"3"},
-    "i32961":{"rank":"4"},
-    "i32957":{"rank":"14"},
-    "i32901":{"rank":"1"},
-    "i32964":{"rank":"1"},
-    "i32963":{"rank":"1"},
-    "i32966":{"rank":"1"},
-    "i32968":{"rank":"1"},
-    "i32969":{"rank":"2"},
-    "i32970":{"rank":"3"},
-    "i32967":{"rank":"2"},
-    "i32965":{"rank":"2"},
-    "i32972":{"rank":"1"},
-    "i32971":{"rank":"3"},
-    "i32974":{"rank":"1"},
-    "i32973":{"rank":"4"},
-    "i32975":{"rank":"5"},
-    "i32977":{"rank":"1"},
-    "i32978":{"rank":"2"},
-    "i32976":{"rank":"6"},
-    "i32980":{"rank":"1"},
-    "i32979":{"rank":"7"},
-    "i32982":{"rank":"1"},
-    "i32981":{"rank":"8"},
-    "i32984":{"rank":"1"},
-    "i32985":{"rank":"2"},
-    "i32983":{"rank":"9"},
-    "i32987":{"rank":"1"},
-    "i32986":{"rank":"10"},
-    "i32989":{"rank":"1"},
-    "i32988":{"rank":"11"},
-    "i32991":{"rank":"1"},
-    "i32990":{"rank":"12"},
-    "i32993":{"rank":"1"},
-    "i32994":{"rank":"2"},
-    "i32995":{"rank":"3"},
-    "i32992":{"rank":"13"},
-    "i32962":{"rank":"2"},
-    "i32900":{"rank":"3"},
-    "i32997":{"rank":"1"},
-    "i32996":{"rank":"4"},
-    "i33000":{"rank":"1"},
-    "i33001":{"rank":"2"},
-    "i33002":{"rank":"3"},
-    "i33003":{"rank":"4"},
-    "i32999":{"rank":"1"},
-    "i33005":{"rank":"1"},
-    "i33006":{"rank":"2"},
-    "i33007":{"rank":"3"},
-    "i33004":{"rank":"2"},
-    "i33010":{"rank":"1"},
-    "i33011":{"rank":"2"},
-    "i33009":{"rank":"1"},
-    "i33013":{"rank":"1"},
-    "i33014":{"rank":"2"},
-    "i33012":{"rank":"2"},
-    "i33016":{"rank":"1"},
-    "i33017":{"rank":"2"},
-    "i33015":{"rank":"3"},
-    "i33019":{"rank":"1"},
-    "i33020":{"rank":"2"},
-    "i33018":{"rank":"4"},
-    "i33021":{"rank":"5"},
-    "i33008":{"rank":"3"},
-    "i32998":{"rank":"5"},
-    "i33023":{"rank":"1"},
-    "i33024":{"rank":"2"},
-    "i33026":{"rank":"1"},
-    "i33027":{"rank":"2"},
-    "i33028":{"rank":"3"},
-    "i33029":{"rank":"4"},
-    "i33030":{"rank":"5"},
-    "i33031":{"rank":"6"},
-    "i33032":{"rank":"7"},
-    "i33025":{"rank":"3"},
-    "i33033":{"rank":"4"},
-    "i33022":{"rank":"6"},
-    "i32862":{"rank":"3"},
-    "i33034":{"rank":"4"},
-    "i33037":{"rank":"1"},
-    "i33038":{"rank":"2"},
-    "i33036":{"rank":"1"},
-    "i33039":{"rank":"2"},
-    "i33041":{"rank":"1"},
-    "i33042":{"rank":"2"},
-    "i33043":{"rank":"3"},
-    "i33044":{"rank":"4"},
-    "i33040":{"rank":"3"},
-    "i33046":{"rank":"1"},
-    "i33047":{"rank":"2"},
-    "i33045":{"rank":"4"},
-    "i33049":{"rank":"1"},
-    "i33050":{"rank":"2"},
-    "i33048":{"rank":"5"},
-    "i33035":{"rank":"5"},
-    "i33053":{"rank":"1"},
-    "i33052":{"rank":"1"},
-    "i33055":{"rank":"1"},
-    "i33056":{"rank":"2"},
-    "i33054":{"rank":"2"},
-    "i33058":{"rank":"1"},
-    "i33059":{"rank":"2"},
-    "i33060":{"rank":"3"},
-    "i33057":{"rank":"3"},
-    "i33061":{"rank":"4"},
-    "i33062":{"rank":"5"},
-    "i33051":{"rank":"6"},
-    "i33064":{"rank":"1"},
-    "i33065":{"rank":"2"},
-    "i33066":{"rank":"3"},
-    "i33067":{"rank":"4"},
-    "i33063":{"rank":"7"},
-    "i33069":{"rank":"1"},
-    "i33070":{"rank":"2"},
-    "i33071":{"rank":"3"},
-    "i33072":{"rank":"4"},
-    "i33073":{"rank":"5"},
-    "i33074":{"rank":"6"},
-    "i33068":{"rank":"8"},
-    "i33075":{"rank":"9"},
-    "i33076":{"rank":"10"},
-    "i33077":{"rank":"11"},
-    "p688":{"rank":"10"},
-    "i32588":{"rank":"1"},
-    "i32589":{"rank":"2"},
-    "i32590":{"rank":"3"},
-    "i32591":{"rank":"4"},
-    "i32592":{"rank":"5"},
-    "i32593":{"rank":"6"},
-    "i32594":{"rank":"7"},
-    "i32595":{"rank":"8"},
-    "i32596":{"rank":"9"},
-    "i32597":{"rank":"10"},
-    "i32598":{"rank":"11"},
-    "i32599":{"rank":"12"},
-    "i32600":{"rank":"13"},
-    "i32601":{"rank":"14"},
-    "i32602":{"rank":"15"},
-    "i32603":{"rank":"16"},
-    "i32604":{"rank":"17"},
-    "i32605":{"rank":"18"},
-    "i32606":{"rank":"19"},
-    "i32607":{"rank":"20"},
-    "i32608":{"rank":"21"},
-    "i32609":{"rank":"22"},
-    "i32610":{"rank":"23"},
-    "i32611":{"rank":"24"},
-    "i32612":{"rank":"25"},
-    "i32613":{"rank":"26"},
-    "i32614":{"rank":"27"},
-    "i32587":{"rank":"1"},
-    "i32586":{"rank":"1"},
-    "i32615":{"rank":"2"},
-    "i32619":{"rank":"1"},
-    "i32620":{"rank":"2"},
-    "i32621":{"rank":"3"},
-    "i32618":{"rank":"1"},
-    "i32623":{"rank":"1"},
-    "i32624":{"rank":"2"},
-    "i32625":{"rank":"3"},
-    "i32622":{"rank":"2"},
-    "i32627":{"rank":"1"},
-    "i32628":{"rank":"2"},
-    "i32629":{"rank":"3"},
-    "i32626":{"rank":"3"},
-    "i32631":{"rank":"1"},
-    "i32632":{"rank":"2"},
-    "i32633":{"rank":"3"},
-    "i32630":{"rank":"4"},
-    "i32617":{"rank":"1"},
-    "i32636":{"rank":"1"},
-    "i32637":{"rank":"2"},
-    "i32638":{"rank":"3"},
-    "i32635":{"rank":"1"},
-    "i32640":{"rank":"1"},
-    "i32641":{"rank":"2"},
-    "i32642":{"rank":"3"},
-    "i32639":{"rank":"2"},
-    "i32644":{"rank":"1"},
-    "i32645":{"rank":"2"},
-    "i32646":{"rank":"3"},
-    "i32647":{"rank":"4"},
-    "i32643":{"rank":"3"},
-    "i32650":{"rank":"1"},
-    "i32651":{"rank":"2"},
-    "i32652":{"rank":"3"},
-    "i32649":{"rank":"1"},
-    "i32653":{"rank":"2"},
-    "i32648":{"rank":"4"},
-    "i32634":{"rank":"2"},
-    "i32658":{"rank":"1"},
-    "i32659":{"rank":"2"},
-    "i32657":{"rank":"1"},
-    "i32661":{"rank":"1"},
-    "i32662":{"rank":"2"},
-    "i32660":{"rank":"2"},
-    "i32656":{"rank":"1"},
-    "i32665":{"rank":"1"},
-    "i32666":{"rank":"2"},
-    "i32663":{"rank":"2"},
-    "i32668":{"rank":"1"},
-    "i32669":{"rank":"2"},
-    "i32670":{"rank":"3"},
-    "i32667":{"rank":"3"},
-    "i32672":{"rank":"1"},
-    "i32673":{"rank":"2"},
-    "i32674":{"rank":"3"},
-    "i32671":{"rank":"4"},
-    "i32676":{"rank":"1"},
-    "i32677":{"rank":"2"},
-    "i32678":{"rank":"3"},
-    "i32675":{"rank":"5"},
-    "i32680":{"rank":"1"},
-    "i32681":{"rank":"2"},
-    "i32682":{"rank":"3"},
-    "i32679":{"rank":"6"},
-    "i32684":{"rank":"1"},
-    "i32685":{"rank":"2"},
-    "i32686":{"rank":"3"},
-    "i32683":{"rank":"7"},
-    "i32688":{"rank":"1"},
-    "i32689":{"rank":"2"},
-    "i32690":{"rank":"3"},
-    "i32687":{"rank":"8"},
-    "i32692":{"rank":"1"},
-    "i32693":{"rank":"2"},
-    "i32694":{"rank":"3"},
-    "i32691":{"rank":"9"},
-    "i32696":{"rank":"1"},
-    "i32697":{"rank":"2"},
-    "i32698":{"rank":"3"},
-    "i32695":{"rank":"10"},
-    "i32700":{"rank":"1"},
-    "i32701":{"rank":"2"},
-    "i32702":{"rank":"3"},
-    "i32699":{"rank":"11"},
-    "i32704":{"rank":"1"},
-    "i32705":{"rank":"2"},
-    "i32706":{"rank":"3"},
-    "i32703":{"rank":"12"},
-    "i32708":{"rank":"1"},
-    "i32709":{"rank":"2"},
-    "i32710":{"rank":"3"},
-    "i32707":{"rank":"13"},
-    "i32712":{"rank":"1"},
-    "i32713":{"rank":"2"},
-    "i32714":{"rank":"3"},
-    "i32715":{"rank":"4"},
-    "i32711":{"rank":"14"},
-    "i32655":{"rank":"1"},
-    "i32718":{"rank":"1"},
-    "i32717":{"rank":"1"},
-    "i32720":{"rank":"1"},
-    "i32722":{"rank":"1"},
-    "i32723":{"rank":"2"},
-    "i32724":{"rank":"3"},
-    "i32721":{"rank":"2"},
-    "i32719":{"rank":"2"},
-    "i32726":{"rank":"1"},
-    "i32725":{"rank":"3"},
-    "i32728":{"rank":"1"},
-    "i32727":{"rank":"4"},
-    "i32729":{"rank":"5"},
-    "i32731":{"rank":"1"},
-    "i32732":{"rank":"2"},
-    "i32730":{"rank":"6"},
-    "i32734":{"rank":"1"},
-    "i32733":{"rank":"7"},
-    "i32736":{"rank":"1"},
-    "i32735":{"rank":"8"},
-    "i32738":{"rank":"1"},
-    "i32739":{"rank":"2"},
-    "i32737":{"rank":"9"},
-    "i32741":{"rank":"1"},
-    "i32740":{"rank":"10"},
-    "i32743":{"rank":"1"},
-    "i32742":{"rank":"11"},
-    "i32745":{"rank":"1"},
-    "i32744":{"rank":"12"},
-    "i32747":{"rank":"1"},
-    "i32748":{"rank":"2"},
-    "i32749":{"rank":"3"},
-    "i32746":{"rank":"13"},
-    "i32716":{"rank":"2"},
-    "i32654":{"rank":"3"},
-    "i32751":{"rank":"1"},
-    "i32750":{"rank":"4"},
-    "i32754":{"rank":"1"},
-    "i32755":{"rank":"2"},
-    "i32756":{"rank":"3"},
-    "i32757":{"rank":"4"},
-    "i32753":{"rank":"1"},
-    "i32759":{"rank":"1"},
-    "i32760":{"rank":"2"},
-    "i32761":{"rank":"3"},
-    "i32758":{"rank":"2"},
-    "i32764":{"rank":"1"},
-    "i32765":{"rank":"2"},
-    "i32763":{"rank":"1"},
-    "i32767":{"rank":"1"},
-    "i32768":{"rank":"2"},
-    "i32766":{"rank":"2"},
-    "i32770":{"rank":"1"},
-    "i32771":{"rank":"2"},
-    "i32769":{"rank":"3"},
-    "i32773":{"rank":"1"},
-    "i32774":{"rank":"2"},
-    "i32772":{"rank":"4"},
-    "i32775":{"rank":"5"},
-    "i32762":{"rank":"3"},
-    "i32752":{"rank":"5"},
-    "i32777":{"rank":"1"},
-    "i32778":{"rank":"2"},
-    "i32780":{"rank":"1"},
-    "i32781":{"rank":"2"},
-    "i32782":{"rank":"3"},
-    "i32783":{"rank":"4"},
-    "i32784":{"rank":"5"},
-    "i32785":{"rank":"6"},
-    "i32786":{"rank":"7"},
-    "i32779":{"rank":"3"},
-    "i32787":{"rank":"4"},
-    "i32776":{"rank":"6"},
-    "i32616":{"rank":"3"},
-    "i32788":{"rank":"4"},
-    "i32791":{"rank":"1"},
-    "i32792":{"rank":"2"},
-    "i32790":{"rank":"1"},
-    "i32793":{"rank":"2"},
-    "i32795":{"rank":"1"},
-    "i32796":{"rank":"2"},
-    "i32797":{"rank":"3"},
-    "i32798":{"rank":"4"},
-    "i32794":{"rank":"3"},
-    "i32800":{"rank":"1"},
-    "i32801":{"rank":"2"},
-    "i32799":{"rank":"4"},
-    "i32803":{"rank":"1"},
-    "i32804":{"rank":"2"},
-    "i32802":{"rank":"5"},
-    "i32789":{"rank":"5"},
-    "i32807":{"rank":"1"},
-    "i32806":{"rank":"1"},
-    "i32809":{"rank":"1"},
-    "i32810":{"rank":"2"},
-    "i32808":{"rank":"2"},
-    "i32812":{"rank":"1"},
-    "i32813":{"rank":"2"},
-    "i32814":{"rank":"3"},
-    "i32811":{"rank":"3"},
-    "i32815":{"rank":"4"},
-    "i32816":{"rank":"5"},
-    "i32805":{"rank":"6"},
-    "i32818":{"rank":"1"},
-    "i32819":{"rank":"2"},
-    "i32820":{"rank":"3"},
-    "i32821":{"rank":"4"},
-    "i32817":{"rank":"7"},
-    "i32823":{"rank":"1"},
-    "i32824":{"rank":"2"},
-    "i32825":{"rank":"3"},
-    "i32826":{"rank":"4"},
-    "i32827":{"rank":"5"},
-    "i32828":{"rank":"6"},
-    "i32822":{"rank":"8"},
-    "i32829":{"rank":"9"},
-    "i32830":{"rank":"10"},
-    "i32831":{"rank":"11"},
-    "p689":{"rank":"11"},
-    "i33080":{"rank":"1"},
-    "i33081":{"rank":"2"},
-    "i33082":{"rank":"3"},
-    "i33083":{"rank":"4"},
-    "i33084":{"rank":"5"},
-    "i33085":{"rank":"6"},
-    "i33086":{"rank":"7"},
-    "i33087":{"rank":"8"},
-    "i33088":{"rank":"9"},
-    "i33089":{"rank":"10"},
-    "i33090":{"rank":"11"},
-    "i33091":{"rank":"12"},
-    "i33092":{"rank":"13"},
-    "i33093":{"rank":"14"},
-    "i33094":{"rank":"15"},
-    "i33095":{"rank":"16"},
-    "i33096":{"rank":"17"},
-    "i33097":{"rank":"18"},
-    "i33098":{"rank":"19"},
-    "i33099":{"rank":"20"},
-    "i33100":{"rank":"21"},
-    "i33101":{"rank":"22"},
-    "i33102":{"rank":"23"},
-    "i33103":{"rank":"24"},
-    "i33104":{"rank":"25"},
-    "i33105":{"rank":"26"},
-    "i33106":{"rank":"27"},
-    "i33079":{"rank":"1"},
-    "i33078":{"rank":"1"},
-    "i33107":{"rank":"2"},
-    "i33111":{"rank":"1"},
-    "i33112":{"rank":"2"},
-    "i33113":{"rank":"3"},
-    "i33110":{"rank":"1"},
-    "i33115":{"rank":"1"},
-    "i33116":{"rank":"2"},
-    "i33117":{"rank":"3"},
-    "i33114":{"rank":"2"},
-    "i33119":{"rank":"1"},
-    "i33120":{"rank":"2"},
-    "i33121":{"rank":"3"},
-    "i33118":{"rank":"3"},
-    "i33123":{"rank":"1"},
-    "i33124":{"rank":"2"},
-    "i33125":{"rank":"3"},
-    "i33122":{"rank":"4"},
-    "i33109":{"rank":"1"},
-    "i33128":{"rank":"1"},
-    "i33129":{"rank":"2"},
-    "i33130":{"rank":"3"},
-    "i33127":{"rank":"1"},
-    "i33132":{"rank":"1"},
-    "i33133":{"rank":"2"},
-    "i33134":{"rank":"3"},
-    "i33131":{"rank":"2"},
-    "i33136":{"rank":"1"},
-    "i33137":{"rank":"2"},
-    "i33138":{"rank":"3"},
-    "i33139":{"rank":"4"},
-    "i33135":{"rank":"3"},
-    "i33142":{"rank":"1"},
-    "i33143":{"rank":"2"},
-    "i33144":{"rank":"3"},
-    "i33141":{"rank":"1"},
-    "i33145":{"rank":"2"},
-    "i33140":{"rank":"4"},
-    "i33126":{"rank":"2"},
-    "i33150":{"rank":"1"},
-    "i33151":{"rank":"2"},
-    "i33149":{"rank":"1"},
-    "i33153":{"rank":"1"},
-    "i33154":{"rank":"2"},
-    "i33152":{"rank":"2"},
-    "i33148":{"rank":"1"},
-    "i33157":{"rank":"1"},
-    "i33158":{"rank":"2"},
-    "i33155":{"rank":"2"},
-    "i33160":{"rank":"1"},
-    "i33161":{"rank":"2"},
-    "i33162":{"rank":"3"},
-    "i33159":{"rank":"3"},
-    "i33164":{"rank":"1"},
-    "i33165":{"rank":"2"},
-    "i33166":{"rank":"3"},
-    "i33163":{"rank":"4"},
-    "i33168":{"rank":"1"},
-    "i33169":{"rank":"2"},
-    "i33170":{"rank":"3"},
-    "i33167":{"rank":"5"},
-    "i33172":{"rank":"1"},
-    "i33173":{"rank":"2"},
-    "i33174":{"rank":"3"},
-    "i33171":{"rank":"6"},
-    "i33176":{"rank":"1"},
-    "i33177":{"rank":"2"},
-    "i33178":{"rank":"3"},
-    "i33175":{"rank":"7"},
-    "i33180":{"rank":"1"},
-    "i33181":{"rank":"2"},
-    "i33182":{"rank":"3"},
-    "i33179":{"rank":"8"},
-    "i33184":{"rank":"1"},
-    "i33185":{"rank":"2"},
-    "i33186":{"rank":"3"},
-    "i33183":{"rank":"9"},
-    "i33188":{"rank":"1"},
-    "i33189":{"rank":"2"},
-    "i33190":{"rank":"3"},
-    "i33187":{"rank":"10"},
-    "i33192":{"rank":"1"},
-    "i33193":{"rank":"2"},
-    "i33194":{"rank":"3"},
-    "i33191":{"rank":"11"},
-    "i33196":{"rank":"1"},
-    "i33197":{"rank":"2"},
-    "i33198":{"rank":"3"},
-    "i33195":{"rank":"12"},
-    "i33200":{"rank":"1"},
-    "i33201":{"rank":"2"},
-    "i33202":{"rank":"3"},
-    "i33199":{"rank":"13"},
-    "i33204":{"rank":"1"},
-    "i33205":{"rank":"2"},
-    "i33206":{"rank":"3"},
-    "i33207":{"rank":"4"},
-    "i33203":{"rank":"14"},
-    "i33147":{"rank":"1"},
-    "i33210":{"rank":"1"},
-    "i33209":{"rank":"1"},
-    "i33212":{"rank":"1"},
-    "i33214":{"rank":"1"},
-    "i33215":{"rank":"2"},
-    "i33216":{"rank":"3"},
-    "i33213":{"rank":"2"},
-    "i33211":{"rank":"2"},
-    "i33218":{"rank":"1"},
-    "i33217":{"rank":"3"},
-    "i33220":{"rank":"1"},
-    "i33219":{"rank":"4"},
-    "i33221":{"rank":"5"},
-    "i33223":{"rank":"1"},
-    "i33224":{"rank":"2"},
-    "i33222":{"rank":"6"},
-    "i33226":{"rank":"1"},
-    "i33225":{"rank":"7"},
-    "i33228":{"rank":"1"},
-    "i33227":{"rank":"8"},
-    "i33230":{"rank":"1"},
-    "i33231":{"rank":"2"},
-    "i33229":{"rank":"9"},
-    "i33233":{"rank":"1"},
-    "i33232":{"rank":"10"},
-    "i33235":{"rank":"1"},
-    "i33234":{"rank":"11"},
-    "i33237":{"rank":"1"},
-    "i33236":{"rank":"12"},
-    "i33239":{"rank":"1"},
-    "i33240":{"rank":"2"},
-    "i33241":{"rank":"3"},
-    "i33238":{"rank":"13"},
-    "i33208":{"rank":"2"},
-    "i33146":{"rank":"3"},
-    "i33243":{"rank":"1"},
-    "i33242":{"rank":"4"},
-    "i33246":{"rank":"1"},
-    "i33247":{"rank":"2"},
-    "i33248":{"rank":"3"},
-    "i33249":{"rank":"4"},
-    "i33245":{"rank":"1"},
-    "i33251":{"rank":"1"},
-    "i33252":{"rank":"2"},
-    "i33253":{"rank":"3"},
-    "i33250":{"rank":"2"},
-    "i33256":{"rank":"1"},
-    "i33257":{"rank":"2"},
-    "i33255":{"rank":"1"},
-    "i33259":{"rank":"1"},
-    "i33260":{"rank":"2"},
-    "i33258":{"rank":"2"},
-    "i33262":{"rank":"1"},
-    "i33263":{"rank":"2"},
-    "i33261":{"rank":"3"},
-    "i33265":{"rank":"1"},
-    "i33266":{"rank":"2"},
-    "i33264":{"rank":"4"},
-    "i33267":{"rank":"5"},
-    "i33254":{"rank":"3"},
-    "i33244":{"rank":"5"},
-    "i33269":{"rank":"1"},
-    "i33270":{"rank":"2"},
-    "i33272":{"rank":"1"},
-    "i33273":{"rank":"2"},
-    "i33274":{"rank":"3"},
-    "i33275":{"rank":"4"},
-    "i33276":{"rank":"5"},
-    "i33277":{"rank":"6"},
-    "i33278":{"rank":"7"},
-    "i33271":{"rank":"3"},
-    "i33279":{"rank":"4"},
-    "i33268":{"rank":"6"},
-    "i33108":{"rank":"3"},
-    "i33280":{"rank":"4"},
-    "i33283":{"rank":"1"},
-    "i33284":{"rank":"2"},
-    "i33282":{"rank":"1"},
-    "i33285":{"rank":"2"},
-    "i33287":{"rank":"1"},
-    "i33288":{"rank":"2"},
-    "i33289":{"rank":"3"},
-    "i33290":{"rank":"4"},
-    "i33286":{"rank":"3"},
-    "i33292":{"rank":"1"},
-    "i33293":{"rank":"2"},
-    "i33291":{"rank":"4"},
-    "i33295":{"rank":"1"},
-    "i33296":{"rank":"2"},
-    "i33294":{"rank":"5"},
-    "i33281":{"rank":"5"},
-    "i33299":{"rank":"1"},
-    "i33298":{"rank":"1"},
-    "i33301":{"rank":"1"},
-    "i33302":{"rank":"2"},
-    "i33300":{"rank":"2"},
-    "i33304":{"rank":"1"},
-    "i33305":{"rank":"2"},
-    "i33306":{"rank":"3"},
-    "i33303":{"rank":"3"},
-    "i33307":{"rank":"4"},
-    "i33308":{"rank":"5"},
-    "i33297":{"rank":"6"},
-    "i33310":{"rank":"1"},
-    "i33311":{"rank":"2"},
-    "i33312":{"rank":"3"},
-    "i33313":{"rank":"4"},
-    "i33309":{"rank":"7"},
-    "i33315":{"rank":"1"},
-    "i33316":{"rank":"2"},
-    "i33317":{"rank":"3"},
-    "i33318":{"rank":"4"},
-    "i33319":{"rank":"5"},
-    "i33320":{"rank":"6"},
-    "i33314":{"rank":"8"},
-    "i33321":{"rank":"9"},
-    "i33322":{"rank":"10"},
-    "i33323":{"rank":"11"},
-    "p687":{"rank":"12"},
-    "i17511":{"rank":"1"},
-    "i28408":{"rank":"2"},
-    "p519":{"rank":"13"},
-    "p581":{"rank":"14"},
-    "i24225":{"rank":"1"},
-    "i24233":{"rank":"2"},
-    "i24234":{"rank":"3"},
-    "p593":{"rank":"15"},
-    "i30468":{"rank":"1"},
-    "i30469":{"rank":"2"},
-    "i30470":{"rank":"3"},
-    "i30471":{"rank":"4"},
-    "i30472":{"rank":"5"},
-    "i30473":{"rank":"6"},
-    "i30474":{"rank":"7"},
-    "i30475":{"rank":"8"},
-    "i30476":{"rank":"9"},
-    "i30477":{"rank":"10"},
-    "i30478":{"rank":"11"},
-    "i30479":{"rank":"12"},
-    "i30480":{"rank":"13"},
-    "i30481":{"rank":"14"},
-    "i30482":{"rank":"15"},
-    "i30483":{"rank":"16"},
-    "i30484":{"rank":"17"},
-    "i30485":{"rank":"18"},
-    "i30486":{"rank":"19"},
-    "i30487":{"rank":"20"},
-    "i30488":{"rank":"21"},
-    "i30489":{"rank":"22"},
-    "i30490":{"rank":"23"},
-    "i30491":{"rank":"24"},
-    "i30492":{"rank":"25"},
-    "i30493":{"rank":"26"},
-    "i30494":{"rank":"27"},
-    "i30467":{"rank":"1"},
-    "i30466":{"rank":"1"},
-    "i30495":{"rank":"2"},
-    "i30499":{"rank":"1"},
-    "i30500":{"rank":"2"},
-    "i30501":{"rank":"3"},
-    "i30498":{"rank":"1"},
-    "i30503":{"rank":"1"},
-    "i30504":{"rank":"2"},
-    "i30505":{"rank":"3"},
-    "i30502":{"rank":"2"},
-    "i30509":{"rank":"1"},
-    "i30506":{"rank":"3"},
-    "i30511":{"rank":"1"},
-    "i30512":{"rank":"2"},
-    "i30513":{"rank":"3"},
-    "i30510":{"rank":"4"},
-    "i30497":{"rank":"1"},
-    "i30516":{"rank":"1"},
-    "i30517":{"rank":"2"},
-    "i30518":{"rank":"3"},
-    "i30515":{"rank":"1"},
-    "i30520":{"rank":"1"},
-    "i30521":{"rank":"2"},
-    "i30522":{"rank":"3"},
-    "i30519":{"rank":"2"},
-    "i30524":{"rank":"1"},
-    "i30525":{"rank":"2"},
-    "i30526":{"rank":"3"},
-    "i30527":{"rank":"4"},
-    "i30523":{"rank":"3"},
-    "i30530":{"rank":"1"},
-    "i30531":{"rank":"2"},
-    "i30532":{"rank":"3"},
-    "i30529":{"rank":"1"},
-    "i30533":{"rank":"2"},
-    "i30528":{"rank":"4"},
-    "i30514":{"rank":"2"},
-    "i30538":{"rank":"1"},
-    "i30539":{"rank":"2"},
-    "i30537":{"rank":"1"},
-    "i30541":{"rank":"1"},
-    "i30542":{"rank":"2"},
-    "i30540":{"rank":"2"},
-    "i30536":{"rank":"1"},
-    "i30544":{"rank":"1"},
-    "i30545":{"rank":"2"},
-    "i30546":{"rank":"3"},
-    "i30543":{"rank":"2"},
-    "i30548":{"rank":"1"},
-    "i30549":{"rank":"2"},
-    "i30550":{"rank":"3"},
-    "i30547":{"rank":"3"},
-    "i30552":{"rank":"1"},
-    "i30553":{"rank":"2"},
-    "i30554":{"rank":"3"},
-    "i30551":{"rank":"4"},
-    "i30556":{"rank":"1"},
-    "i30557":{"rank":"2"},
-    "i30558":{"rank":"3"},
-    "i30555":{"rank":"5"},
-    "i30560":{"rank":"1"},
-    "i30561":{"rank":"2"},
-    "i30562":{"rank":"3"},
-    "i30559":{"rank":"6"},
-    "i30564":{"rank":"1"},
-    "i30565":{"rank":"2"},
-    "i30566":{"rank":"3"},
-    "i30563":{"rank":"7"},
-    "i30569":{"rank":"1"},
-    "i30570":{"rank":"2"},
-    "i30567":{"rank":"8"},
-    "i30572":{"rank":"1"},
-    "i30573":{"rank":"2"},
-    "i30574":{"rank":"3"},
-    "i30571":{"rank":"9"},
-    "i30576":{"rank":"1"},
-    "i30577":{"rank":"2"},
-    "i30578":{"rank":"3"},
-    "i30575":{"rank":"10"},
-    "i30580":{"rank":"1"},
-    "i30581":{"rank":"2"},
-    "i30582":{"rank":"3"},
-    "i30579":{"rank":"11"},
-    "i30584":{"rank":"1"},
-    "i30585":{"rank":"2"},
-    "i30586":{"rank":"3"},
-    "i30583":{"rank":"12"},
-    "i30588":{"rank":"1"},
-    "i30589":{"rank":"2"},
-    "i30590":{"rank":"3"},
-    "i30587":{"rank":"13"},
-    "i30592":{"rank":"1"},
-    "i30593":{"rank":"2"},
-    "i30594":{"rank":"3"},
-    "i30595":{"rank":"4"},
-    "i30591":{"rank":"14"},
-    "i30535":{"rank":"1"},
-    "i30598":{"rank":"1"},
-    "i30597":{"rank":"1"},
-    "i30600":{"rank":"1"},
-    "i30602":{"rank":"1"},
-    "i30603":{"rank":"2"},
-    "i30604":{"rank":"3"},
-    "i30601":{"rank":"2"},
-    "i30599":{"rank":"2"},
-    "i30606":{"rank":"1"},
-    "i30605":{"rank":"3"},
-    "i30608":{"rank":"1"},
-    "i30607":{"rank":"4"},
-    "i30609":{"rank":"5"},
-    "i30611":{"rank":"1"},
-    "i30612":{"rank":"2"},
-    "i30610":{"rank":"6"},
-    "i30614":{"rank":"1"},
-    "i30613":{"rank":"7"},
-    "i30616":{"rank":"1"},
-    "i30615":{"rank":"8"},
-    "i30618":{"rank":"1"},
-    "i30619":{"rank":"2"},
-    "i30617":{"rank":"9"},
-    "i30621":{"rank":"1"},
-    "i30620":{"rank":"10"},
-    "i30623":{"rank":"1"},
-    "i30622":{"rank":"11"},
-    "i30625":{"rank":"1"},
-    "i30624":{"rank":"12"},
-    "i30627":{"rank":"1"},
-    "i30628":{"rank":"2"},
-    "i30629":{"rank":"3"},
-    "i30626":{"rank":"13"},
-    "i30596":{"rank":"2"},
-    "i30534":{"rank":"3"},
-    "i30631":{"rank":"1"},
-    "i30630":{"rank":"4"},
-    "i30634":{"rank":"1"},
-    "i30635":{"rank":"2"},
-    "i30636":{"rank":"3"},
-    "i30637":{"rank":"4"},
-    "i30633":{"rank":"1"},
-    "i30639":{"rank":"1"},
-    "i30640":{"rank":"2"},
-    "i30641":{"rank":"3"},
-    "i30638":{"rank":"2"},
-    "i30644":{"rank":"1"},
-    "i30645":{"rank":"2"},
-    "i30643":{"rank":"1"},
-    "i30647":{"rank":"1"},
-    "i30648":{"rank":"2"},
-    "i30646":{"rank":"2"},
-    "i30650":{"rank":"1"},
-    "i30651":{"rank":"2"},
-    "i30649":{"rank":"3"},
-    "i30653":{"rank":"1"},
-    "i30654":{"rank":"2"},
-    "i30652":{"rank":"4"},
-    "i30655":{"rank":"5"},
-    "i30642":{"rank":"3"},
-    "i30632":{"rank":"5"},
-    "i30657":{"rank":"1"},
-    "i30658":{"rank":"2"},
-    "i30660":{"rank":"1"},
-    "i30661":{"rank":"2"},
-    "i30662":{"rank":"3"},
-    "i30663":{"rank":"4"},
-    "i30665":{"rank":"5"},
-    "i30659":{"rank":"3"},
-    "i30667":{"rank":"4"},
-    "i30656":{"rank":"6"},
-    "i30496":{"rank":"3"},
-    "i30668":{"rank":"4"},
-    "i30671":{"rank":"1"},
-    "i30672":{"rank":"2"},
-    "i30670":{"rank":"1"},
-    "i30673":{"rank":"2"},
-    "i30675":{"rank":"1"},
-    "i30676":{"rank":"2"},
-    "i30677":{"rank":"3"},
-    "i30678":{"rank":"4"},
-    "i30674":{"rank":"3"},
-    "i30680":{"rank":"1"},
-    "i30681":{"rank":"2"},
-    "i30679":{"rank":"4"},
-    "i30683":{"rank":"1"},
-    "i30684":{"rank":"2"},
-    "i30682":{"rank":"5"},
-    "i30669":{"rank":"5"},
-    "i30687":{"rank":"1"},
-    "i30686":{"rank":"1"},
-    "i30689":{"rank":"1"},
-    "i30690":{"rank":"2"},
-    "i30688":{"rank":"2"},
-    "i30692":{"rank":"1"},
-    "i30693":{"rank":"2"},
-    "i30694":{"rank":"3"},
-    "i30691":{"rank":"3"},
-    "i30695":{"rank":"4"},
-    "i30696":{"rank":"5"},
-    "i30685":{"rank":"6"},
-    "i30698":{"rank":"1"},
-    "i30699":{"rank":"2"},
-    "i30700":{"rank":"3"},
-    "i30701":{"rank":"4"},
-    "i30697":{"rank":"7"},
-    "i30703":{"rank":"1"},
-    "i30704":{"rank":"2"},
-    "i30705":{"rank":"3"},
-    "i30706":{"rank":"4"},
-    "i30707":{"rank":"5"},
-    "i30708":{"rank":"6"},
-    "i30702":{"rank":"8"},
-    "i30709":{"rank":"9"},
-    "i30710":{"rank":"10"},
-    "i30711":{"rank":"11"},
-    "p648":{"rank":"16"},
-    "i28618":{"rank":"1"},
-    "i28619":{"rank":"2"},
-    "i28620":{"rank":"3"},
-    "i28621":{"rank":"4"},
-    "i28622":{"rank":"5"},
-    "i28623":{"rank":"6"},
-    "i28624":{"rank":"7"},
-    "i28625":{"rank":"8"},
-    "i28626":{"rank":"9"},
-    "i28627":{"rank":"10"},
-    "i28628":{"rank":"11"},
-    "i28629":{"rank":"12"},
-    "i28630":{"rank":"13"},
-    "i28631":{"rank":"14"},
-    "i28632":{"rank":"15"},
-    "i28633":{"rank":"16"},
-    "i28634":{"rank":"17"},
-    "i28635":{"rank":"18"},
-    "i28636":{"rank":"19"},
-    "i28637":{"rank":"20"},
-    "i28638":{"rank":"21"},
-    "i28639":{"rank":"22"},
-    "i28640":{"rank":"23"},
-    "i28641":{"rank":"24"},
-    "i28642":{"rank":"25"},
-    "i28643":{"rank":"26"},
-    "i28644":{"rank":"27"},
-    "i28617":{"rank":"1"},
-    "i28616":{"rank":"1"},
-    "i28645":{"rank":"2"},
-    "i28649":{"rank":"1"},
-    "i28650":{"rank":"2"},
-    "i28651":{"rank":"3"},
-    "i28648":{"rank":"1"},
-    "i28653":{"rank":"1"},
-    "i28654":{"rank":"2"},
-    "i28655":{"rank":"3"},
-    "i28652":{"rank":"2"},
-    "i28657":{"rank":"1"},
-    "i28658":{"rank":"2"},
-    "i28659":{"rank":"3"},
-    "i28656":{"rank":"3"},
-    "i28661":{"rank":"1"},
-    "i28662":{"rank":"2"},
-    "i28663":{"rank":"3"},
-    "i28660":{"rank":"4"},
-    "i28647":{"rank":"1"},
-    "i28666":{"rank":"1"},
-    "i28667":{"rank":"2"},
-    "i28668":{"rank":"3"},
-    "i28665":{"rank":"1"},
-    "i28670":{"rank":"1"},
-    "i28671":{"rank":"2"},
-    "i28672":{"rank":"3"},
-    "i28669":{"rank":"2"},
-    "i28674":{"rank":"1"},
-    "i28675":{"rank":"2"},
-    "i28676":{"rank":"3"},
-    "i28677":{"rank":"4"},
-    "i28673":{"rank":"3"},
-    "i28680":{"rank":"1"},
-    "i28681":{"rank":"2"},
-    "i28682":{"rank":"3"},
-    "i28679":{"rank":"1"},
-    "i28683":{"rank":"2"},
-    "i28678":{"rank":"4"},
-    "i28664":{"rank":"2"},
-    "i28688":{"rank":"1"},
-    "i28689":{"rank":"2"},
-    "i28687":{"rank":"1"},
-    "i28691":{"rank":"1"},
-    "i28692":{"rank":"2"},
-    "i28690":{"rank":"2"},
-    "i28686":{"rank":"1"},
-    "i28694":{"rank":"1"},
-    "i28695":{"rank":"2"},
-    "i28696":{"rank":"3"},
-    "i28693":{"rank":"2"},
-    "i28698":{"rank":"1"},
-    "i28699":{"rank":"2"},
-    "i28700":{"rank":"3"},
-    "i28697":{"rank":"3"},
-    "i28702":{"rank":"1"},
-    "i28703":{"rank":"2"},
-    "i28704":{"rank":"3"},
-    "i28701":{"rank":"4"},
-    "i28706":{"rank":"1"},
-    "i28707":{"rank":"2"},
-    "i28708":{"rank":"3"},
-    "i28705":{"rank":"5"},
-    "i28710":{"rank":"1"},
-    "i28711":{"rank":"2"},
-    "i28712":{"rank":"3"},
-    "i28709":{"rank":"6"},
-    "i28714":{"rank":"1"},
-    "i28715":{"rank":"2"},
-    "i28716":{"rank":"3"},
-    "i28713":{"rank":"7"},
-    "i28719":{"rank":"1"},
-    "i28720":{"rank":"2"},
-    "i28717":{"rank":"8"},
-    "i28722":{"rank":"1"},
-    "i28723":{"rank":"2"},
-    "i28724":{"rank":"3"},
-    "i28721":{"rank":"9"},
-    "i28726":{"rank":"1"},
-    "i28727":{"rank":"2"},
-    "i28728":{"rank":"3"},
-    "i28725":{"rank":"10"},
-    "i28730":{"rank":"1"},
-    "i28731":{"rank":"2"},
-    "i28732":{"rank":"3"},
-    "i28729":{"rank":"11"},
-    "i28734":{"rank":"1"},
-    "i28735":{"rank":"2"},
-    "i28736":{"rank":"3"},
-    "i28733":{"rank":"12"},
-    "i28738":{"rank":"1"},
-    "i28739":{"rank":"2"},
-    "i28740":{"rank":"3"},
-    "i28737":{"rank":"13"},
-    "i28742":{"rank":"1"},
-    "i28743":{"rank":"2"},
-    "i28744":{"rank":"3"},
-    "i28745":{"rank":"4"},
-    "i28741":{"rank":"14"},
-    "i28685":{"rank":"1"},
-    "i28748":{"rank":"1"},
-    "i28747":{"rank":"1"},
-    "i28750":{"rank":"1"},
-    "i28752":{"rank":"1"},
-    "i28753":{"rank":"2"},
-    "i28754":{"rank":"3"},
-    "i28751":{"rank":"2"},
-    "i28749":{"rank":"2"},
-    "i28756":{"rank":"1"},
-    "i28755":{"rank":"3"},
-    "i28758":{"rank":"1"},
-    "i28757":{"rank":"4"},
-    "i28759":{"rank":"5"},
-    "i28761":{"rank":"1"},
-    "i28762":{"rank":"2"},
-    "i28760":{"rank":"6"},
-    "i28764":{"rank":"1"},
-    "i28763":{"rank":"7"},
-    "i28766":{"rank":"1"},
-    "i28765":{"rank":"8"},
-    "i28768":{"rank":"1"},
-    "i28769":{"rank":"2"},
-    "i28767":{"rank":"9"},
-    "i28771":{"rank":"1"},
-    "i28770":{"rank":"10"},
-    "i28773":{"rank":"1"},
-    "i28772":{"rank":"11"},
-    "i28775":{"rank":"1"},
-    "i28774":{"rank":"12"},
-    "i28777":{"rank":"1"},
-    "i28778":{"rank":"2"},
-    "i28779":{"rank":"3"},
-    "i28776":{"rank":"13"},
-    "i28746":{"rank":"2"},
-    "i28684":{"rank":"3"},
-    "i28781":{"rank":"1"},
-    "i28780":{"rank":"4"},
-    "i28784":{"rank":"1"},
-    "i28785":{"rank":"2"},
-    "i28786":{"rank":"3"},
-    "i28787":{"rank":"4"},
-    "i28783":{"rank":"1"},
-    "i28789":{"rank":"1"},
-    "i28790":{"rank":"2"},
-    "i28791":{"rank":"3"},
-    "i28788":{"rank":"2"},
-    "i28794":{"rank":"1"},
-    "i28795":{"rank":"2"},
-    "i28793":{"rank":"1"},
-    "i28797":{"rank":"1"},
-    "i28798":{"rank":"2"},
-    "i28796":{"rank":"2"},
-    "i28800":{"rank":"1"},
-    "i28801":{"rank":"2"},
-    "i28799":{"rank":"3"},
-    "i28803":{"rank":"1"},
-    "i28804":{"rank":"2"},
-    "i28802":{"rank":"4"},
-    "i28805":{"rank":"5"},
-    "i28792":{"rank":"3"},
-    "i28782":{"rank":"5"},
-    "i28807":{"rank":"1"},
-    "i28808":{"rank":"2"},
-    "i28810":{"rank":"1"},
-    "i28812":{"rank":"2"},
-    "i28813":{"rank":"3"},
-    "i28815":{"rank":"4"},
-    "i28809":{"rank":"3"},
-    "i28817":{"rank":"4"},
-    "i28806":{"rank":"6"},
-    "i28646":{"rank":"3"},
-    "i28818":{"rank":"4"},
-    "i28821":{"rank":"1"},
-    "i28822":{"rank":"2"},
-    "i28820":{"rank":"1"},
-    "i28823":{"rank":"2"},
-    "i28825":{"rank":"1"},
-    "i28826":{"rank":"2"},
-    "i28827":{"rank":"3"},
-    "i28828":{"rank":"4"},
-    "i28824":{"rank":"3"},
-    "i28830":{"rank":"1"},
-    "i28831":{"rank":"2"},
-    "i28829":{"rank":"4"},
-    "i28833":{"rank":"1"},
-    "i28834":{"rank":"2"},
-    "i28832":{"rank":"5"},
-    "i28819":{"rank":"5"},
-    "i28837":{"rank":"1"},
-    "i28836":{"rank":"1"},
-    "i28839":{"rank":"1"},
-    "i28840":{"rank":"2"},
-    "i28838":{"rank":"2"},
-    "i28842":{"rank":"1"},
-    "i28843":{"rank":"2"},
-    "i28844":{"rank":"3"},
-    "i28841":{"rank":"3"},
-    "i28845":{"rank":"4"},
-    "i28846":{"rank":"5"},
-    "i28835":{"rank":"6"},
-    "i28848":{"rank":"1"},
-    "i28849":{"rank":"2"},
-    "i28850":{"rank":"3"},
-    "i28851":{"rank":"4"},
-    "i28847":{"rank":"7"},
-    "i28853":{"rank":"1"},
-    "i28854":{"rank":"2"},
-    "i28855":{"rank":"3"},
-    "i28856":{"rank":"4"},
-    "i28857":{"rank":"5"},
-    "i28858":{"rank":"6"},
-    "i28852":{"rank":"8"},
-    "i28859":{"rank":"9"},
-    "i28860":{"rank":"10"},
-    "i28861":{"rank":"11"},
-    "p639":{"rank":"17"},
-    "p129":{"rank":"-1"},
-    "p610":{"rank":"1"},
-    "i26848":{"rank":"1"},
-    "i13767":{"rank":"1"},
-    "i14909":{"rank":"2"},
-    "i19995":{"rank":"1"},
-    "i16921":{"rank":"3"},
-    "i17047":{"rank":"4"},
-    "i12332":{"rank":"1"},
-    "i18444":{"rank":"2"},
-    "i17048":{"rank":"5"},
-    "i17052":{"rank":"6"},
-    "p333":{"rank":"1"},
-    "p325":{"rank":"2"},
-    "i13491":{"rank":"1"},
-    "i13534":{"rank":"2"},
-    "i10233":{"rank":"1"},
-    "i13489":{"rank":"1"},
-    "i13535":{"rank":"2"},
-    "i10234":{"rank":"2"},
-    "i22063":{"rank":"1"},
-    "i13487":{"rank":"1"},
-    "i13537":{"rank":"2"},
-    "i10235":{"rank":"3"},
-    "i13485":{"rank":"1"},
-    "i13538":{"rank":"2"},
-    "i10236":{"rank":"4"},
-    "i10232":{"rank":"1"},
-    "i13473":{"rank":"1"},
-    "i13539":{"rank":"2"},
-    "i10248":{"rank":"1"},
-    "i13475":{"rank":"1"},
-    "i13540":{"rank":"2"},
-    "i10249":{"rank":"2"},
-    "i13477":{"rank":"1"},
-    "i13541":{"rank":"2"},
-    "i10250":{"rank":"3"},
-    "i13479":{"rank":"1"},
-    "i13542":{"rank":"2"},
-    "i10251":{"rank":"4"},
-    "i22064":{"rank":"1"},
-    "i13481":{"rank":"1"},
-    "i13543":{"rank":"2"},
-    "i10252":{"rank":"5"},
-    "i22065":{"rank":"1"},
-    "i13483":{"rank":"1"},
-    "i13544":{"rank":"2"},
-    "i10253":{"rank":"6"},
-    "i20749":{"rank":"1"},
-    "i20751":{"rank":"2"},
-    "i21083":{"rank":"3"},
-    "i20767":{"rank":"7"},
-    "i10237":{"rank":"2"},
-    "i10231":{"rank":"1"},
-    "i12674":{"rank":"1"},
-    "i13515":{"rank":"2"},
-    "i14388":{"rank":"3"},
-    "i10247":{"rank":"1"},
-    "i13549":{"rank":"1"},
-    "i13550":{"rank":"2"},
-    "i14389":{"rank":"3"},
-    "i10398":{"rank":"2"},
-    "i10238":{"rank":"2"},
-    "i14456":{"rank":"3"},
-    "i20810":{"rank":"1"},
-    "i20811":{"rank":"2"},
-    "i20812":{"rank":"3"},
-    "i20813":{"rank":"4"},
-    "i20816":{"rank":"5"},
-    "i20817":{"rank":"6"},
-    "i20808":{"rank":"4"},
-    "i20824":{"rank":"1"},
-    "i20825":{"rank":"2"},
-    "i20826":{"rank":"3"},
-    "i20827":{"rank":"4"},
-    "i20828":{"rank":"5"},
-    "i20829":{"rank":"6"},
-    "i20818":{"rank":"5"},
-    "i20830":{"rank":"6"},
-    "i30029":{"rank":"7"},
-    "i30033":{"rank":"8"},
-    "p417":{"rank":"3"},
-    "i29168":{"rank":"1"},
-    "i29170":{"rank":"2"},
-    "i17074":{"rank":"1"},
-    "i17069":{"rank":"1"},
-    "i17748":{"rank":"2"},
-    "i17065":{"rank":"1"},
-    "i28294":{"rank":"2"},
-    "p511":{"rank":"4"},
-    "i19700":{"rank":"1"},
-    "i19701":{"rank":"2"},
-    "i19709":{"rank":"1"},
-    "i19710":{"rank":"2"},
-    "i19711":{"rank":"3"},
-    "i19712":{"rank":"4"},
-    "i19713":{"rank":"5"},
-    "i19714":{"rank":"6"},
-    "i19708":{"rank":"1"},
-    "i19704":{"rank":"3"},
-    "i19716":{"rank":"1"},
-    "i19715":{"rank":"1"},
-    "i19705":{"rank":"4"},
-    "i19918":{"rank":"5"},
-    "i33498":{"rank":"6"},
-    "i33513":{"rank":"7"},
-    "i33514":{"rank":"8"},
-    "i33515":{"rank":"9"},
-    "i33517":{"rank":"1"},
-    "i33518":{"rank":"2"},
-    "i33520":{"rank":"3"},
-    "i33516":{"rank":"10"},
-    "p543":{"rank":"5"},
-    "i24963":{"rank":"1"},
-    "i23324":{"rank":"1"},
-    "i25927":{"rank":"2"},
-    "i30913":{"rank":"3"},
-    "i16842":{"rank":"1"},
-    "i19900":{"rank":"2"},
-    "i25128":{"rank":"1"},
-    "i30912":{"rank":"2"},
-    "i23706":{"rank":"3"},
-    "i24970":{"rank":"2"},
-    "p507":{"rank":"6"},
-    "i24460":{"rank":"1"},
-    "i29080":{"rank":"1"},
-    "i24461":{"rank":"2"},
-    "p594":{"rank":"7"},
-    "i29034":{"rank":"1"},
-    "i29083":{"rank":"1"},
-    "i29084":{"rank":"2"},
-    "i29085":{"rank":"3"},
-    "i29086":{"rank":"4"},
-    "i29087":{"rank":"5"},
-    "i29337":{"rank":"6"},
-    "i29035":{"rank":"2"},
-    "i31521":{"rank":"1"},
-    "i31360":{"rank":"1"},
-    "i31359":{"rank":"3"},
-    "p643":{"rank":"8"},
-    "p539":{"rank":"9"},
-    "i28550":{"rank":"1"},
-    "i28566":{"rank":"1"},
-    "i28560":{"rank":"1"},
-    "i28569":{"rank":"1"},
-    "i28570":{"rank":"2"},
-    "i28571":{"rank":"3"},
-    "i31879":{"rank":"4"},
-    "i28561":{"rank":"2"},
-    "i28573":{"rank":"1"},
-    "i28574":{"rank":"2"},
-    "i28562":{"rank":"3"},
-    "i28572":{"rank":"1"},
-    "i28563":{"rank":"4"},
-    "i28609":{"rank":"5"},
-    "i28551":{"rank":"2"},
-    "i31597":{"rank":"1"},
-    "i31598":{"rank":"2"},
-    "i31596":{"rank":"1"},
-    "i31602":{"rank":"1"},
-    "i31599":{"rank":"2"},
-    "i31603":{"rank":"1"},
-    "i31605":{"rank":"1"},
-    "i31604":{"rank":"2"},
-    "i31600":{"rank":"3"},
-    "i31595":{"rank":"3"},
-    "i31606":{"rank":"4"},
-    "p637":{"rank":"10"},
-    "i31037":{"rank":"1"},
-    "i18050":{"rank":"1"},
-    "i20648":{"rank":"1"},
-    "i28536":{"rank":"1"},
-    "i28537":{"rank":"2"},
-    "i31316":{"rank":"3"},
-    "i31378":{"rank":"4"},
-    "i28534":{"rank":"2"},
-    "i18051":{"rank":"2"},
-    "i26156":{"rank":"3"},
-    "p537":{"rank":"11"},
-    "i23336":{"rank":"1"},
-    "i29243":{"rank":"2"},
-    "i29244":{"rank":"3"},
-    "i29245":{"rank":"4"},
-    "i29241":{"rank":"1"},
-    "i31168":{"rank":"1"},
-    "i28041":{"rank":"1"},
-    "i25108":{"rank":"1"},
-    "i25107":{"rank":"1"},
-    "i25114":{"rank":"1"},
-    "i25113":{"rank":"2"},
-    "i23337":{"rank":"1"},
-    "i29246":{"rank":"2"},
-    "i29242":{"rank":"2"},
-    "i32089":{"rank":"1"},
-    "i32090":{"rank":"2"},
-    "i31047":{"rank":"1"},
-    "i31048":{"rank":"2"},
-    "i31049":{"rank":"3"},
-    "i29249":{"rank":"3"},
-    "i29252":{"rank":"1"},
-    "i29253":{"rank":"2"},
-    "i29254":{"rank":"3"},
-    "i29250":{"rank":"4"},
-    "i29255":{"rank":"1"},
-    "i29251":{"rank":"5"},
-    "p541":{"rank":"12"},
-    "p447":{"rank":"2"},
-    "i32142":{"rank":"1"},
-    "i32143":{"rank":"2"},
-    "i32145":{"rank":"3"},
-    "i32407":{"rank":"4"},
-    "i32408":{"rank":"5"},
-    "i32131":{"rank":"1"},
-    "i32133":{"rank":"2"},
-    "i32134":{"rank":"3"},
-    "i32132":{"rank":"1"},
-    "i32138":{"rank":"1"},
-    "i32139":{"rank":"2"},
-    "i32137":{"rank":"2"},
-    "i32141":{"rank":"1"},
-    "i32140":{"rank":"3"},
-    "p680":{"rank":"1"},
-    "i30382":{"rank":"1"},
-    "i30384":{"rank":"2"},
-    "i30385":{"rank":"3"},
-    "i20396":{"rank":"1"},
-    "i18111":{"rank":"1"},
-    "i29964":{"rank":"1"},
-    "i18115":{"rank":"2"},
-    "i20388":{"rank":"1"},
-    "i18118":{"rank":"3"},
-    "i31534":{"rank":"1"},
-    "i20390":{"rank":"1"},
-    "i18119":{"rank":"4"},
-    "i19866":{"rank":"5"},
-    "i32413":{"rank":"1"},
-    "i30270":{"rank":"6"},
-    "i31174":{"rank":"1"},
-    "i31177":{"rank":"2"},
-    "i31173":{"rank":"1"},
-    "i31172":{"rank":"7"},
-    "i17971":{"rank":"1"},
-    "p535":{"rank":"2"},
-    "p681":{"rank":"3"},
-    "i31238":{"rank":"1"},
-    "i31239":{"rank":"2"},
-    "i31240":{"rank":"3"},
-    "i31237":{"rank":"1"},
-    "i31232":{"rank":"1"},
-    "i31234":{"rank":"2"},
-    "i31158":{"rank":"1"},
-    "i31159":{"rank":"2"},
-    "i31236":{"rank":"3"},
-    "i31241":{"rank":"2"},
-    "i31945":{"rank":"1"},
-    "i31242":{"rank":"3"},
-    "i31243":{"rank":"4"},
-    "i31872":{"rank":"1"},
-    "i31873":{"rank":"2"},
-    "i31874":{"rank":"3"},
-    "i31871":{"rank":"1"},
-    "i31519":{"rank":"1"},
-    "i31875":{"rank":"2"},
-    "i31515":{"rank":"5"},
-    "i31195":{"rank":"1"},
-    "i31209":{"rank":"1"},
-    "i31210":{"rank":"2"},
-    "i31196":{"rank":"2"},
-    "i31211":{"rank":"1"},
-    "i31197":{"rank":"3"},
-    "p672":{"rank":"4"},
-    "i31384":{"rank":"1"},
-    "i32063":{"rank":"2"},
-    "i31295":{"rank":"1"},
-    "i31305":{"rank":"1"},
-    "i31304":{"rank":"2"},
-    "i31308":{"rank":"1"},
-    "i31309":{"rank":"2"},
-    "i31307":{"rank":"3"},
-    "p676":{"rank":"5"},
-    "i32388":{"rank":"1"},
-    "i32389":{"rank":"2"},
-    "i32390":{"rank":"3"},
-    "i26346":{"rank":"1"},
-    "p606":{"rank":"6"},
-    "i33438":{"rank":"1"},
-    "i33439":{"rank":"2"},
-    "i33440":{"rank":"3"},
-    "i33441":{"rank":"4"},
-    "i33442":{"rank":"5"},
-    "i33443":{"rank":"6"},
-    "i33444":{"rank":"7"},
-    "i33445":{"rank":"8"},
-    "i33437":{"rank":"1"},
-    "i33447":{"rank":"1"},
-    "i33448":{"rank":"2"},
-    "i33446":{"rank":"2"},
-    "i33450":{"rank":"1"},
-    "i33451":{"rank":"2"},
-    "i33449":{"rank":"3"},
-    "p719":{"rank":"7"},
-    "i30279":{"rank":"1"},
-    "i32363":{"rank":"2"},
-    "i30280":{"rank":"1"},
-    "i32362":{"rank":"1"},
-    "i32365":{"rank":"2"},
-    "i32366":{"rank":"3"},
-    "i32434":{"rank":"4"},
-    "i32435":{"rank":"5"},
-    "i32436":{"rank":"6"},
-    "i32364":{"rank":"2"},
-    "i30278":{"rank":"1"},
-    "i32368":{"rank":"2"},
-    "i32369":{"rank":"3"},
-    "i32367":{"rank":"3"},
-    "i30274":{"rank":"1"},
-    "i23760":{"rank":"1"},
-    "i30036":{"rank":"1"},
-    "i30037":{"rank":"2"},
-    "i30034":{"rank":"2"},
-    "i30038":{"rank":"1"},
-    "i30039":{"rank":"2"},
-    "i30040":{"rank":"3"},
-    "i30041":{"rank":"4"},
-    "i30042":{"rank":"5"},
-    "i30035":{"rank":"3"},
-    "p591":{"rank":"8"},
-    "i32158":{"rank":"1"},
-    "i32335":{"rank":"2"},
-    "i32336":{"rank":"3"},
-    "i32337":{"rank":"4"},
-    "i32338":{"rank":"5"},
-    "p701":{"rank":"1"},
-    "i31709":{"rank":"1"},
-    "i31711":{"rank":"2"},
-    "i31695":{"rank":"1"},
-    "i31707":{"rank":"1"},
-    "i31708":{"rank":"2"},
-    "i31696":{"rank":"2"},
-    "i31697":{"rank":"1"},
-    "i31688":{"rank":"1"},
-    "i31700":{"rank":"1"},
-    "i31702":{"rank":"2"},
-    "i31703":{"rank":"3"},
-    "i32121":{"rank":"4"},
-    "i32157":{"rank":"5"},
-    "i31699":{"rank":"1"},
-    "i31704":{"rank":"1"},
-    "i31705":{"rank":"2"},
-    "i31706":{"rank":"3"},
-    "i31701":{"rank":"2"},
-    "i31689":{"rank":"2"},
-    "i31736":{"rank":"1"},
-    "i31737":{"rank":"2"},
-    "i31738":{"rank":"3"},
-    "i31739":{"rank":"4"},
-    "i31740":{"rank":"5"},
-    "i31718":{"rank":"1"},
-    "i31741":{"rank":"1"},
-    "i31742":{"rank":"2"},
-    "i31719":{"rank":"2"},
-    "i31690":{"rank":"3"},
-    "i31713":{"rank":"1"},
-    "i31714":{"rank":"2"},
-    "i31715":{"rank":"3"},
-    "i31716":{"rank":"4"},
-    "i31717":{"rank":"5"},
-    "i31712":{"rank":"1"},
-    "i31691":{"rank":"4"},
-    "i31721":{"rank":"1"},
-    "i31722":{"rank":"2"},
-    "i31723":{"rank":"3"},
-    "i31724":{"rank":"4"},
-    "i31725":{"rank":"5"},
-    "i31726":{"rank":"6"},
-    "i31720":{"rank":"1"},
-    "i31692":{"rank":"5"},
-    "i31728":{"rank":"1"},
-    "i31729":{"rank":"2"},
-    "i31730":{"rank":"3"},
-    "i31727":{"rank":"1"},
-    "i31733":{"rank":"1"},
-    "i31734":{"rank":"2"},
-    "i31735":{"rank":"3"},
-    "i31731":{"rank":"2"},
-    "i31693":{"rank":"6"},
-    "i31744":{"rank":"1"},
-    "i31745":{"rank":"2"},
-    "i31746":{"rank":"3"},
-    "i31747":{"rank":"4"},
-    "i31743":{"rank":"1"},
-    "i31694":{"rank":"7"},
-    "i31698":{"rank":"2"},
-    "p677":{"rank":"9"},
-    "i30786":{"rank":"1"},
-    "i30787":{"rank":"2"},
-    "i30776":{"rank":"1"},
-    "i30791":{"rank":"1"},
-    "i30777":{"rank":"2"},
-    "i30770":{"rank":"1"},
-    "i30792":{"rank":"1"},
-    "i30848":{"rank":"2"},
-    "i30779":{"rank":"3"},
-    "i30771":{"rank":"1"},
-    "i30805":{"rank":"2"},
-    "i30845":{"rank":"3"},
-    "i30780":{"rank":"4"},
-    "i30806":{"rank":"1"},
-    "i30849":{"rank":"2"},
-    "i30781":{"rank":"5"},
-    "i30807":{"rank":"1"},
-    "i30846":{"rank":"2"},
-    "i30782":{"rank":"6"},
-    "i30808":{"rank":"1"},
-    "i30850":{"rank":"2"},
-    "i30783":{"rank":"7"},
-    "i30809":{"rank":"1"},
-    "i30847":{"rank":"2"},
-    "i30784":{"rank":"8"},
-    "i30810":{"rank":"1"},
-    "i30851":{"rank":"2"},
-    "i30785":{"rank":"9"},
-    "p661":{"rank":"10"},
-    "p550":{"rank":"3"},
-    "i31915":{"rank":"1"},
-    "i31916":{"rank":"2"},
-    "i31918":{"rank":"1"},
-    "i31924":{"rank":"2"},
-    "i31925":{"rank":"3"},
-    "i31917":{"rank":"3"},
-    "i31920":{"rank":"1"},
-    "i31921":{"rank":"2"},
-    "i31929":{"rank":"3"},
-    "i31930":{"rank":"4"},
-    "i31919":{"rank":"4"},
-    "i31923":{"rank":"5"},
-    "i31934":{"rank":"1"},
-    "i31935":{"rank":"2"},
-    "i31936":{"rank":"3"},
-    "i31937":{"rank":"4"},
-    "i31938":{"rank":"5"},
-    "i31927":{"rank":"1"},
-    "i31926":{"rank":"6"},
-    "i31931":{"rank":"7"},
-    "i31993":{"rank":"1"},
-    "i31932":{"rank":"8"},
-    "i31940":{"rank":"1"},
-    "i31941":{"rank":"2"},
-    "i31942":{"rank":"3"},
-    "i31939":{"rank":"9"},
-    "i31943":{"rank":"10"},
-    "i32013":{"rank":"11"},
-    "p683":{"rank":"1"},
-    "i31996":{"rank":"1"},
-    "i31999":{"rank":"1"},
-    "i32000":{"rank":"2"},
-    "i32001":{"rank":"3"},
-    "i31998":{"rank":"2"},
-    "i32002":{"rank":"3"},
-    "i32081":{"rank":"1"},
-    "i32004":{"rank":"4"},
-    "i32082":{"rank":"1"},
-    "i32005":{"rank":"5"},
-    "i32009":{"rank":"6"},
-    "i32012":{"rank":"7"},
-    "i32014":{"rank":"8"},
-    "p686":{"rank":"2"},
-    "i31776":{"rank":"1"},
-    "i31777":{"rank":"2"},
-    "i31778":{"rank":"3"},
-    "i31779":{"rank":"4"},
-    "i31780":{"rank":"5"},
-    "i31781":{"rank":"6"},
-    "i31782":{"rank":"7"},
-    "i31783":{"rank":"8"},
-    "i31787":{"rank":"1"},
-    "i31765":{"rank":"9"},
-    "p679":{"rank":"3"},
-    "p678":{"rank":"4"},
-    "i32496":{"rank":"1"},
-    "i32497":{"rank":"2"},
-    "i32498":{"rank":"3"},
-    "i32585":{"rank":"4"},
-    "i32495":{"rank":"1"},
-    "p705":{"rank":"1"},
-    "i28203":{"rank":"1"},
-    "i28204":{"rank":"2"},
-    "i32500":{"rank":"1"},
-    "i32501":{"rank":"2"},
-    "i32502":{"rank":"3"},
-    "i32499":{"rank":"3"},
-    "i28193":{"rank":"1"},
-    "i28205":{"rank":"1"},
-    "i28206":{"rank":"2"},
-    "i28194":{"rank":"2"},
-    "i28207":{"rank":"1"},
-    "i28195":{"rank":"3"},
-    "p540":{"rank":"2"},
-    "i29326":{"rank":"1"},
-    "i31224":{"rank":"2"},
-    "i27140":{"rank":"1"},
-    "i25115":{"rank":"1"},
-    "i27816":{"rank":"1"},
-    "i28970":{"rank":"2"},
-    "i27815":{"rank":"2"},
-    "i25200":{"rank":"1"},
-    "i31550":{"rank":"1"},
-    "i31549":{"rank":"1"},
-    "i25201":{"rank":"2"},
-    "i31217":{"rank":"1"},
-    "i31218":{"rank":"2"},
-    "i32020":{"rank":"3"},
-    "i32061":{"rank":"4"},
-    "i31215":{"rank":"3"},
-    "p596":{"rank":"3"},
-    "p714":{"rank":"5"},
-    "p641":{"rank":"6"},
-    "i27947":{"rank":"1"},
-    "i30327":{"rank":"1"},
-    "i28293":{"rank":"2"},
-    "i31188":{"rank":"1"},
-    "i31189":{"rank":"2"},
-    "i31191":{"rank":"3"},
-    "i31187":{"rank":"3"},
-    "i27411":{"rank":"1"},
-    "p631":{"rank":"1"},
-    "i32028":{"rank":"1"},
-    "i32030":{"rank":"2"},
-    "i32031":{"rank":"3"},
-    "i32032":{"rank":"4"},
-    "i32033":{"rank":"5"},
-    "i32060":{"rank":"6"},
-    "i32029":{"rank":"1"},
-    "i32035":{"rank":"1"},
-    "i32036":{"rank":"2"},
-    "i32037":{"rank":"3"},
-    "i32038":{"rank":"4"},
-    "i32034":{"rank":"2"},
-    "i32041":{"rank":"1"},
-    "i32040":{"rank":"1"},
-    "i32039":{"rank":"3"},
-    "i32043":{"rank":"1"},
-    "i32044":{"rank":"2"},
-    "i32045":{"rank":"3"},
-    "i32058":{"rank":"4"},
-    "i32042":{"rank":"4"},
-    "i32047":{"rank":"1"},
-    "i32048":{"rank":"2"},
-    "i32046":{"rank":"5"},
-    "i32049":{"rank":"6"},
-    "i32051":{"rank":"7"},
-    "i32052":{"rank":"8"},
-    "i32054":{"rank":"1"},
-    "i32055":{"rank":"2"},
-    "i32053":{"rank":"9"},
-    "i32056":{"rank":"10"},
-    "i32330":{"rank":"1"},
-    "i32508":{"rank":"2"},
-    "i32329":{"rank":"11"},
-    "i32411":{"rank":"1"},
-    "i32410":{"rank":"12"},
-    "i32027":{"rank":"1"},
-    "i32400":{"rank":"1"},
-    "i32401":{"rank":"2"},
-    "i32402":{"rank":"3"},
-    "i32403":{"rank":"4"},
-    "i32397":{"rank":"2"},
-    "p690":{"rank":"2"},
-    "i11522":{"rank":"1"},
-    "i11520":{"rank":"1"},
-    "i11576":{"rank":"2"},
-    "i11600":{"rank":"3"},
-    "i12263":{"rank":"4"},
-    "i18134":{"rank":"5"},
-    "i11599":{"rank":"2"},
-    "i14056":{"rank":"3"},
-    "i28190":{"rank":"1"},
-    "i8033":{"rank":"1"},
-    "p376":{"rank":"3"},
-    "i23838":{"rank":"1"},
-    "i23839":{"rank":"2"},
-    "i23840":{"rank":"3"},
-    "i23837":{"rank":"1"},
-    "i29166":{"rank":"2"},
-    "i23755":{"rank":"1"},
-    "p589":{"rank":"4"},
-    "p122":{"rank":"7"},
-    "i9120":{"rank":"8"},
-    "i26702":{"rank":"1"},
-    "i22742":{"rank":"9"}};
-    var encoded = easyTests.ysyInstance.storage.extra._encodeLayout(layout);
-    var decoded = easyTests.ysyInstance.storage.extra._decodeLayout(encoded);
-    var keys = Object.getOwnPropertyNames(layout);
-    for(var i=0;i<keys.length;i++){
-      var key = keys[i];
-      if (!_.isEqual(layout[key], decoded[key])) {
-        console.log(key);
-        console.log(layout[key]);
-        console.log(decoded[key]);
-        throw "different";
-      }
-    }
-  },
-  encodeSmallLayout: function () {
-    var layout = {"p25":{},"i2280": {"rank": 1}, "i3027": {"rank": 5, "position": [808.5, 111.25, 1]}};
-    var encoded = easyTests.ysyInstance.storage.extra._encodeLayout(layout);
-    var decoded = easyTests.ysyInstance.storage.extra._decodeLayout(encoded);
-    if (!_.isEqual(layout, decoded)) {
-      console.log(JSON.stringify(layout));
-      console.log(JSON.stringify(decoded));
-      throw "different";
-    }
-  }
-});
\ No newline at end of file
diff --git a/plugins/easy_wbs/assets/javascripts/wbs_main.js b/plugins/easy_wbs/assets/javascripts/wbs_main.js
index a531949..545e533 100644
--- a/plugins/easy_wbs/assets/javascripts/wbs_main.js
+++ b/plugins/easy_wbs/assets/javascripts/wbs_main.js
@@ -15,9 +15,7 @@
     this.patch();
     this.init();
     // this.settings.noSave = true;
-    if (window.easyTests) {
-      easyTests.ysyInstance = this;
-    }
+    easyTests.ysyInstance = this;
   }
 
   classes.extendClass(WbsMain, classes.MindMup);
diff --git a/plugins/easy_wbs/assets/javascripts/wbs_modals.js b/plugins/easy_wbs/assets/javascripts/wbs_modals.js
index 9be08d4..74a694c 100644
--- a/plugins/easy_wbs/assets/javascripts/wbs_modals.js
+++ b/plugins/easy_wbs/assets/javascripts/wbs_modals.js
@@ -41,7 +41,7 @@
     sendPack.updateNodeData();
     var startsWith = this.ysy.util.startsWith;
     var preFill = _.omit(sendPack.node.attr.data, function (value, key) {
-      return _.isFunction(value) || startsWith(key, "_") || key === "journals";
+      return _.isFunction(value) || startsWith(key, "_");
     });
     if(preFill.custom_fields){
       var customValues={};
diff --git a/plugins/easy_wbs/assets/stylesheets/generated/easy_wbs.css b/plugins/easy_wbs/assets/stylesheets/generated/easy_wbs.css
deleted file mode 100644
index 2217480..0000000
--- a/plugins/easy_wbs/assets/stylesheets/generated/easy_wbs.css
+++ /dev/null
@@ -1 +0,0 @@
-@charset "UTF-8";#logo-img,.logo{background-image:url(//d1g6a398qq2djm.cloudfront.net/img/logo_32.png);height:40px}#floating-toolbar,.ideaInput{z-index:999;position:absolute}#gplus-share,#logo-img,.logo{background-position:left center;background-repeat:no-repeat}#gplus-share,#toolbarSocial,.menulink{margin-right:20px}#modalDownload .modal-body,.center,.ideaInput{text-align:center}#splittable,.ios-wkwebview{-webkit-tap-highlight-color:transparent}body,html{height:100%}#container,#floating-toolbar,#topbar,.dropdown-menu,.floating{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.logo{width:0;padding-left:40px;padding-top:5px;padding-bottom:5px}#logo-img{width:40px;padding:0;margin-left:50px}@media (max-width:979px){.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;margin-bottom:0}}@media (max-width:767px){.navbar{padding-left:20px;padding-right:20px}body{padding:0}}.span-toolbar{width:120px;cursor:move}.dropdown-menu li a,.share{cursor:pointer}@media (max-width:1024px){.hidden-tablet-landscape{display:none!important}}@media (max-width:768px){.hidden-tablet-portrait{display:none!important}}@media (max-width:320px){.hidden-phone-portrait{display:none!important}}@media (max-height:320px){.form-horizontal .control-group{margin-bottom:5px}.hidden-phone-landscape{display:none}}@media (max-width:400px){.map-changed .changed-phone-hidden{display:none!important}}@media (max-width:1024px){.map-changed .changed-tablet-hidden{display:none!important}}@media screen{#topbar .nav>li>a{font-weight:700}}#floating-toolbar{top:100px;right:10px;border:1px solid #08c;border-radius:9px}.android .visible-touch,.ios .visible-touch{display:block!important}.android .hidden-touch,.ios .hidden-touch,.map-changed .hidden-map-changed,.map-unchanged .visible-map-changed{display:none!important}div.topbar-color-picker{width:127px}.topbar-color-picker .colorPicker-swatch{width:15px;height:15px}.android .colorPicker-swatch,.ios .colorPicker-swatch{width:30px;height:30px}.android .colorPicker-palette,.ios .colorPicker-palette{width:217px;top:55px;left:5px}#toolbarEdit .btn-group{margin-left:0}#toolbarEdit button{margin-top:5px;width:40px;height:32px}#toolbarEdit button.two{width:80px}#toolbarEdit button.three{width:120px}.ideaInput{background-color:#5FBF5F;color:#FFF;font-family:Helvetica,"Arial Unicode MS",sans-serif;font-weight:700;font-size:13px;line-height:13px;padding:0}.ideaInput:focus{outline:0}.menulink{margin-top:10px}#gplus-share{border:0;height:16px;background-image:url(//ssl.gstatic.com/images/icons/gplus-16.png);margin-left:6px;margin-top:2px;padding-right:0}.btn-inline,a.repo{margin-left:5px}#modalDownload .modal-body p{text-align:left}#modalDownload img{max-height:300px;max-width:450px}.share{width:32px;height:32px;display:inline-block;margin:5px}.btn-inline{border:0;background:0 0;margin-right:5px}.colorPicker_hexWrap{display:none}div.colorPicker-picker{display:inline;float:left;background:0 0}.repo,.repo-a{background-image:url(//d1g6a398qq2djm.cloudfront.net/img/logo_16.png)}#modalImport input[type=file]{opacity:0;position:absolute;width:0;height:0}.repo{background-repeat:no-repeat;background-position:left center}.repo-b{background-image:url(//d1g6a398qq2djm.cloudfront.net/img/logo_gold_16.png)}.repo-p{background-image:url(//d1g6a398qq2djm.cloudfront.net/img/logo_gold_private_16.png)}.repo-g{background-image:url(//d1g6a398qq2djm.cloudfront.net/img/google_drive.png)}.repo-menuitem{background-position:14px center;padding-left:35px!important}span.repo-a,span.repo-b,span.repo-g,span.repo-p{padding-left:16px}.visible-collapsed-toolbar{display:none}.collapsed-toolbar .visible-collapsed-toolbar{display:block!important}.collapsed-toolbar span.visible-collapsed-toolbar{display:inline!important}.collapsed-toolbar .hidden-collapsed-toolbar{display:none!important}.visible-row-split{display:none}.row-split .visible-row-split{display:block!important}.row-split span.visible-row-split{display:inline!important}.row-split .hidden-row-split{display:none!important}.visible-column-split{display:none}.column-split .visible-column-split{display:block!important}.column-split span.visible-column-split{display:inline!important}.column-split .hidden-column-split{display:none!important}#listBookmarks a .btn-inline{margin-left:15px}.gecko #listBookmarks a .btn-inline{position:relative;top:-17px;left:15px}.hidden-topbar #topbar .navbar-inner{visibility:hidden}.column-split.hidden-topbar #topbar .navbar-inner,.hidden-topbar #topbar:hover .navbar-inner{visibility:visible}.topbar-autohide{display:inline}.hidden-topbar .topbar-autohide,span.topbar-fixed{display:none}.hidden-topbar span.topbar-fixed{display:inline}body.ipad{margin:1px}body.ios-wkwebview{margin:0;padding:0;width:100%}.image-render-visible{display:none}.image-render .image-render-visible{display:block}.image-render .image-render-hidden,.offline-disabled-hidden{display:none}body.offline-enabled .offline-disabled-hidden{display:block}#attachEditArea{overflow:scroll;outline:0;border:1px solid #ccc;padding:4px;margin:10px;box-sizing:content-box;-webkit-box-shadow:rgba(0,0,0,.0745098) 0 1px 1px 0 inset;box-shadow:rgba(0,0,0,.0745098) 0 1px 1px 0 inset;border-radius:3px;background-color:#fff;display:block;clear:both}.alert-error input,input.noglow{outline:0;box-shadow:none!important;cursor:pointer}#modalAttachmentEditor .editor-topbar{padding-right:10px;padding-left:10px;display:none}#modalAttachmentEditor.mm-editable .editor-topbar{display:block}#modalAttachmentEditor .viewer-topbar{margin-top:5px;margin-bottom:5px;padding-right:10px;padding-left:10px;height:30px}#modalAttachmentEditor.mm-editable .viewer-topbar{display:none}#modalAttachmentEditor button.close{margin-left:5px}#modalAttachmentEditor .editor-topbar>.btn-toolbar{display:inline-block;background-color:#eee;width:100%}#modalAttachmentEditor{position:absolute;height:80%;width:80%;left:10%;top:10%;padding:5px;z-index:9999;background-color:#eee;-moz-border-radius:6px;border-radius:6px;margin:0}@media (max-width:1200px){#modalAttachmentEditor{width:90%;left:5%;top:5%}}#modalAttachmentEditor .dropdown-menu .btn-toolbar{display:block}.show-active,.visible-map-source-a,.visible-map-source-b,.visible-map-source-g,.visible-map-source-n,.visible-map-source-p{display:none}#modalAttachmentEditor .dropdown-menu .btn-toolbar .btn-group{margin-top:5px;margin-left:5px;margin-right:5px}.android #modalAttachmentEditor .btn-toolbar,.ios #modalAttachmentEditor .btn-toolbar{margin-top:5px;margin-bottom:5px;padding-bottom:10px}.android #modalAttachmentEditor,.ios #modalAttachmentEditor{top:0;height:60%}@media (max-height:768px){.android #modalAttachmentEditor,.ios #modalAttachmentEditor{height:37%}}.non-group .btn{margin-left:5px}#linkEditWidget{display:none;z-index:999;position:absolute;padding:10px}.alert a{text-decoration:underline;cursor:pointer}.alert a.btn{text-decoration:none}input.noglow{border:none!important}.map-source-a .visible-map-source-a,.map-source-b .visible-map-source-b,.map-source-g .visible-map-source-g,.map-source-n .visible-map-source-n,.map-source-p .visible-map-source-p{display:inherit}@media (max-width:1200px){.hidden-narrow-screen{display:none}}@media (max-width:1600px){#optionalPane .hidden-narrow-screen{display:none}}.btn-xlarge{padding:18px 28px;font-size:40px;line-height:normal;-webkit-border-radius:8px;-moz-border-radius:8px;border-radius:8px;width:250px;height:180px;margin-left:10px;margin-bottom:10px}@media (max-height:320px){.btn-xlarge{padding:9px 14px;font-size:20px;line-height:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;width:230px;height:70px;margin-left:5px;margin-bottom:5px}}@media (max-width:320px){.btn-xlarge{padding:9px 14px;font-size:20px;line-height:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;width:230px;height:70px;margin-left:5px;margin-bottom:5px}#mainMenu>li>a{padding-left:0;padding-right:15px}#logo-img{margin:0;padding-left:5px;padding-right:0}}@media (min-height:640px){#modalKeyActions .modal-body{min-height:400px}}#modalKeyActions .item a span{padding-right:10px}#modalKeyActions a{cursor:pointer;color:#000}#modalKeyActions div.item>a{text-decoration:underline}.social{display:inline-block!important;padding-left:2px!important;padding-right:2px!important;margin-right:2px!important;cursor:pointer}#modalGoldLicense textarea{width:95%;height:200px}#modalIconEdit .file-drop-zone{width:150px;height:150px;border:1px dashed #000;color:gray;font-size:7px;text-align:center}#modalIconEdit .file-drop-zone img{max-width:150px;max-height:150px}#modalIconEdit{z-index:9999}.btn .export{max-width:24px}.btn .landscape{transform:rotate(90deg);-moz-transform:rotate(90deg);-webkit-transform:rotate(90deg)}#modalPDFExport form{margin-top:20px}.alert-error input{background:0 0;margin:0 0 2px;color:#b94a48;border:none!important;width:100%}body .modal.huge{width:90%;height:80%;left:5%;margin-left:auto;margin-right:auto}#measuresSheet table{background-color:#fff;border:1px solid #d4d4d4}#measuresSheet td{text-align:right;width:20%}#measuresSheet th+th{text-align:right}#measuresSheet table thead tr th{border-bottom:3px gray solid!important}#measuresSheet table tfoot tr th{border-top:3px gray solid!important}#measuresSheet table tbody th{width:30%}#measuresSheet form{margin:0!important}#measuresSheet td:focus{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #7ab5d3;outline:#5b9dd9 auto}.measures-editor.error{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px red!important;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px red!important;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px red!important;outline:red auto thin!important}#measuresSheet .modal-footer a{margin-right:20px;margin-left:20px;line-height:30px}#optionalPane .input-append.navbar-form button{margin-top:5px!important}#measuresSheet{margin:5px;border-left:1px;user-select:none;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.table-container{margin-right:5px}.column-split .table-container{margin-bottom:45px}.mm-active .show-active{display:initial}.black{color:#000!important}.activated-scene{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #7ab5d3;-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #7ab5d3;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #7ab5d3;outline:#5b9dd9 auto}#storyboard{user-select:none;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.storyboard-container{padding-left:10px;padding-right:10px}.storyboard-scene{display:inline-block;max-height:120px;max-width:160px;min-height:120px;min-width:160px;overflow:hidden;background-color:#fff;border:1px solid silver;border-radius:4px;color:#000;margin:10px;text-align:center;cursor:move}.ios-icon-label,.ios-modal-title{color:#22AAE0;line-height:1.2;font-family:Helvetica,"Arial Unicode MS",sans-serif;text-align:center}.storyboard-scene-title{display:table-cell;vertical-align:middle;white-space:pre-wrap;font-weight:700}.drag-shadow{opacity:.5;box-shadow:none;outline:0;cursor:move;z-index:10;position:relative}.collaborator-list-container td,.mapjs-node{cursor:pointer}.potential-drop-left{margin-left:0;margin-right:20px}.potential-drop-right{margin-left:20px;margin-right:0}#splittable{width:100%;height:calc(100% - 41px);margin-top:41px}.hidden-topbar:not(.column-split)>#splittable{height:100%;margin-top:0}.ios-wkwebview>#splittable{width:100%;height:calc(100%);margin-top:0}.splittable-optional .navbar{width:100%;margin-bottom:0}.splittable-optional .content{overflow:scroll;height:calc(100% - 41px)}.ios-wkwebview .splittable-optional .content{overflow:scroll;height:100%}.splittable-optional{display:none;outline:#d4d4d4 solid 1px}.column-split .splittable-primary{float:left;height:100%;overflow:scroll;width:50%}.column-split .splittable-optional{float:right;height:100%;width:50%;display:block}.splittable-primary{outline:#d4d4d4 solid 1px;height:100%;width:100%}.row-split .splittable-primary{height:50%;overflow:scroll;width:100%}.row-split .splittable-optional{height:50%;width:100%;display:block}#optionalPane{background-color:#f5f5f5;-webkit-box-shadow:0 1px 10px rgba(0,0,0,.1);-moz-box-shadow:0 1px 10px rgba(0,0,0,.1);box-shadow:0 1px 10px rgba(0,0,0,.1)}.splittable-optional .content-container{height:100%}.pull-middle{display:table;height:100%}.pull-middle>div{display:table-cell;vertical-align:middle;padding-left:20%;padding-right:20%}.ios-menu-toggle-container{position:absolute;height:90px;width:75px;top:calc(100% - 90px);left:0;overflow:hidden;z-index:999}.ios-corner-icon-container{position:absolute;height:100px;width:75px;top:5;left:-11px;border:1px solid #22AAE0;background-color:#FFF;border-radius:5px;box-shadow:2px 2px 1px rgba(104,104,104,.8)}.ios-modal,.ios-toolbar{background-color:rgba(255,255,255,.85)}.ios-menu-toggle{position:absolute;top:5;left:12;height:73px;width:65px}.ios-toolbar{position:absolute;box-shadow:0 -1px 0 #22AAE0;top:calc(100% - 80px);left:65px;width:calc(100% - 65px);height:80px;z-index:998;overflow:scroll}.ios-icon-label{position:absolute;font-size:8pt;font-weight:400;top:calc(100% - 22px);width:50px;left:5px;height:22px}.ios-toolbar-item{display:inline-block;position:relative;top:0;height:75px;width:60px}.ios-toolbar-icon{position:absolute;left:10px;top:8px;height:40px;width:40px}.ios-modal{z-index:1090;position:absolute;overflow:hidden;left:0;top:0;height:100%;width:100%}.ios-modal-close{position:absolute;top:20;left:calc(100% - 45px);height:40px;width:40px}.ios-modal-title{position:absolute;left:45px;top:30px;height:40px;width:calc(100% - 90px);font-size:12pt;font-weight:700}.ios-modal-content{z-index:1095;position:absolute;top:80px;height:calc(100% - 75px);overflow-x:hidden;overflow-y:auto;background-color:#FFF;border:1px solid #22AAE0;border-radius:5px;box-shadow:2px 2px 1px rgba(104,104,104,.8)}.ios-color-selector{display:inline-block;box-shadow:1px 1px 3px rgba(104,104,104,.7)}.ios-color-palette{display:block;text-align:center}@media (min-width:601px){.ios-modal-content{left:calc(50% - 300px);width:600px}.ios-color-palette{margin:20px 50px}.ios-color-selector{margin:20px;height:80px;width:80px;border-radius:40px}}@media (max-width:600px){.ios-modal-content{left:calc(50% - 150px);width:300px}.ios-color-palette{margin:10px 20px}.ios-color-selector{margin:10px;height:40px;width:40px;border-radius:20px}}.collaborator-list-container{max-height:200px;overflow-y:scroll;padding-top:10px}.mm-icon-gdrive,.mm-icon-gmail{padding:15px 9px;background-position:center center;background-repeat:no-repeat}.collaborator-list-container table{margin-bottom:0}.mm-collaborator{position:absolute;width:32px;height:32px;border:3px solid transparent;border-radius:20px;transform:translate(5px,5px);min-width:30px}.collab-name{overflow-x:hidden;max-width:50px;white-space:nowrap}.collab-name a{line-height:30px}.collab-photo{width:30px}.collab-photo img{width:30px;height:30px;float:left;margin-right:10px;border:2px solid transparent;border-radius:20px}#floating-collaborators{position:absolute;top:100px;left:10px;z-index:999;border:1px solid #08c;border-radius:9px}.visible-collaboration-toolbar{display:none}.map-source-c.collaboration-toolbar .visible-collaboration-toolbar{display:initial!important}.collaboration-toolbar .hidden-collaboration-toolbar{display:none!important}.mm-has-collaborators .hidden-has-collaborators,.visible-has-collaborators{display:none}.mm-has-collaborators .visible-has-collaborators{display:block}.visible-collaboration-mute-speech{display:none}.collaboration-mute-speech .visible-collaboration-mute-speech{display:initial!important}.collaboration-mute-speech .hidden-collaboration-mute-speech{display:none!important}.btn-share{margin-left:10px;margin-right:10px}.mm-icon-gmail{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgNDU1NTUgMzMwOTciPg0KIDxnPg0KICA8cGF0aCBmaWxsPSIjRTc1QTREIiBkPSJNNDE2MDggMjUwYzIwMzAsMCAzNjkxLDE2NjEgMzY5MSwzNjkxbDAgMTA3MCAtNTEyOCAzNjYzIC0xNzQ1OCAxMjAyNiAtMTc0NTggLTEyMTQ4IDAgMjQyOTUgLTEzMTQgMGMtMjAzMCwwIC0zNjkxLC0xNjYxIC0zNjkxLC0zNjkxbDAgLTI1MjE0YzAsLTIwMzAgMTY2MSwtMzY5MSAzNjkxLC0zNjkxbDE4NzcyIDEzOTE4IDE4ODk0IC0xMzkxOHoiLz4NCiAgPHBvbHlnb24gZmlsbD0iI0U3RTRENyIgcG9pbnRzPSI1MjU1LDg1NTIgNTI1NSwzMjg0NyA0MDE3MiwzMjg0NyA0MDE3Miw4Njc0IDIyNzEzLDIwNzAwICIvPg0KICA8cG9seWdvbiBmaWxsPSIjQjhCN0FFIiBwb2ludHM9IjUyNTUsMzI4NDcgMjI3MTMsMjA3MDAgMjI2MzQsMjA2NDQgNTI1NSwzMjU4MCAiLz4NCiAgPHBvbHlnb24gZmlsbD0iI0I3QjZBRCIgcG9pbnRzPSI0MDE3Miw4Njc0IDQwMTgyLDMyODQ3IDIyNzEzLDIwNzAwICIvPg0KICA8cGF0aCBmaWxsPSIjQjIzOTJGIiBkPSJNNDUyOTkgNTAxMWw2IDI0MTQ4Yy04OCwyNjg0IC0xMjUxLDM2NDIgLTUxMjMsMzY4N2wtMTAgLTI0MTczIDUxMjggLTM2NjN6Ii8+DQogIDxwb2x5Z29uIGZpbGw9IiNGN0Y1RUQiIHBvaW50cz0iMzk0MSwyNTAgMjI3MTMsMTQxNjggNDE2MDgsMjUwICIvPg0KIDwvZz4NCjwvc3ZnPg0K)}.mm-icon-gdrive{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWw6c3BhY2U9InByZXNlcnZlIiB3aWR0aD0iNTA5LjA4bW0iIGhlaWdodD0iNDQwLjk3bW0iIHZlcnNpb249IjEuMSIgc3R5bGU9InNoYXBlLXJlbmRlcmluZzpnZW9tZXRyaWNQcmVjaXNpb247IHRleHQtcmVuZGVyaW5nOmdlb21ldHJpY1ByZWNpc2lvbjsgaW1hZ2UtcmVuZGVyaW5nOm9wdGltaXplUXVhbGl0eTsgZmlsbC1ydWxlOmV2ZW5vZGQ7IGNsaXAtcnVsZTpldmVub2RkIg0Kdmlld0JveD0iMCAwIDEzMzE1NiAxMTUzNDEiDQogeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPg0KIDxkZWZzPg0KICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KICAgPCFbQ0RBVEFbDQogICAgLmZpbDIge2ZpbGw6IzExQTg2MX0NCiAgICAuZmlsMCB7ZmlsbDojMzc3N0UzfQ0KICAgIC5maWwxIHtmaWxsOiNGRkNGNjN9DQogICBdXT4NCiAgPC9zdHlsZT4NCiA8L2RlZnM+DQogPGcgaWQ9IkxpdmVsbG9feDAwMjBfMSI+DQogIDxtZXRhZGF0YSBpZD0iQ29yZWxDb3JwSURfMENvcmVsLUxheWVyIi8+DQogIDxwb2x5Z29uIGNsYXNzPSJmaWwwIiBwb2ludHM9IjIyMTk0LDExNTM0MSA0NDM4NSw3Njg5NCAxMzMxNTYsNzY4OTQgMTEwOTYzLDExNTM0MSAiLz4NCiAgPHBvbHlnb24gY2xhc3M9ImZpbDEiIHBvaW50cz0iODg3NzIsNzY4OTQgMTMzMTU2LDc2ODk0IDg4NzcyLDAgNDQzODUsMCAiLz4NCiAgPHBvbHlnb24gY2xhc3M9ImZpbDIiIHBvaW50cz0iMCw3Njg5NCAyMjE5NCwxMTUzNDEgNjY1NzgsMzg0NDcgNDQzODUsMCAiLz4NCiA8L2c+DQo8L3N2Zz4=);background-size:100% 100%}.mapjs-attachment,.mapjs-hyperlink{background-repeat:no-repeat no-repeat;position:absolute}#collaboratorSpeechBubble{position:absolute;z-index:2;top:100px;left:20px}#collaboratorSpeechBubble img{width:60px;height:60px;border-radius:60px;border:2px solid transparent;box-shadow:0 5px 10px rgba(0,0,0,.2);cursor:pointer}#collaboratorSpeechBubble img:hover{box-shadow:0 5px 10px rgba(0,0,0,.5)}.speech-bubble-inner{max-width:300px;max-height:100px;min-width:150px;overflow:hidden}.speech-bubble-title{max-width:300px;white-space:nowrap;overflow:hidden}.icon-volume-off{margin-left:3px;margin-right:5px}.hidden-topbar #mainMenu .dropdown-menu{margin-top:0}.mapjs-node.activated,.mapjs-node.droppable{margin:-2px;outline:0}.mapjs-node{z-index:3;user-select:none;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;font-family:Helvetica,"Arial Unicode MS",sans-serif;font-weight:700;font-size:12px}.mapjs-add-link{cursor:crosshair}.mapjs-add-link .mapjs-node{cursor:alias}.mapjs-node span{white-space:pre-wrap;text-align:center;line-height:150%;display:block;max-width:146px;min-height:1.5em;min-width:1em;outline:0;cursor:pointer}.mapjs-node span[contenteditable=true]{user-select:text;-moz-user-select:text;-webkit-user-select:text;-ms-user-select:text;cursor:auto}#container,.mindmup-container,.mindmup-noselect{-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.mapjs-node:focus{outline:0}.mapjs-node.selected{outline:0;z-index:4}.mapjs-node.dragging{opacity:.4;z-index:5}.mapjs-node-light{color:#4F4F4F}.mapjs-node-dark{color:#EEE}.mapjs-node-white{color:#000}.mapjs-label{left:-.75em;position:absolute;bottom:-1em;opacity:.8;background-color:#f13333;padding:1px 2px;border:1px solid #fff;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;display:inline-block;font-size:10px;font-weight:700;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25);white-space:nowrap;vertical-align:baseline}.mapjs-hyperlink{right:-.75em;bottom:-.75em;background-image:url(data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiDQoNCgkgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQoNCjxwYXRoIHN0eWxlPSJmaWxsOmJsYWNrIiBkPSJNMTU2LjIyNiwxOTkuNjc5YzcuNTQxLTcuNTQsMTUuOTAyLTEzLjc1NywyNC43OTQtMTguNjU5YzQ5LjU1Ni0yNy4zMTgsMTEzLjExNy0xMi43ODgsMTQ0Ljk3LDM1LjUxOA0KDQoJbC0zOC41NDcsMzguNTQ3Yy0xMS4wNTktMjUuMjI3LTM4LjUtMzkuNTY1LTY1LjgxMy0zMy40NTZjLTEwLjI4MiwyLjMtMjAuMDU0LDcuNDI3LTI4LjAzOSwxNS40MTNsLTczLjg5OCw3My44OTYNCg0KCWMtMjIuNDMzLDIyLjQzMy0yMi40MzIsNTguOTM2LDAuMDAyLDgxLjM2OWMyMi40MzMsMjIuNDMzLDU4LjkzNSwyMi40MzMsODEuMzY4LDBsMjIuNzgtMjIuNzc5DQoNCgljMjAuNzEsOC4yMTcsNDIuOTM4LDExLjUwOCw2NC44NjIsOS44NjNsLTUwLjI3OCw1MC4yNzhjLTQzLjEwNSw0My4xMDUtMTEyLjk5MSw0My4xMDUtMTU2LjA5NiwwDQoNCgljLTQzLjEwNS00My4xMDQtNDMuMTA2LTExMi45OTEtMC4wMDEtMTU2LjA5NkwxNTYuMjI2LDE5OS42Nzl6IE0yNzMuNTc0LDgyLjMzbC01MC4yNzgsNTAuMjc4DQoNCgljMjEuOTI4LTEuNjQzLDQ0LjE1MiwxLjY0OCw2NC44NjMsOS44NjVsMjIuNzc5LTIyLjc4YzIyLjQzNC0yMi40MzQsNTguOTM2LTIyLjQzNCw4MS4zNywwYzIyLjQzNCwyMi40MzQsMjIuNDM0LDU4LjkzNiwwLDgxLjM3DQoNCglsLTczLjg5Nyw3My44OTVjLTIyLjUwMSwyMi41MDEtNTkuMDYxLDIyLjMxMS04MS4zNjgsMGMtNS4yMDItNS4yMDEtOS42OTQtMTEuNjc4LTEyLjQ4NC0xOC4wNGwtMzguNTQ2LDM4LjU0Ng0KDQoJYzQuMDQ5LDYuMTQyLDguMjYxLDExLjQ1MywxMy42NjYsMTYuODU4YzEzLjk0OSwxMy45NSwzMS42OTgsMjQuMzM5LDUyLjExNywyOS4yNTFjMjYuNDY2LDYuMzcsNTQuODIzLDIuODM5LDc5LjE4NS0xMC41OTINCg0KCWM4Ljg5Mi00LjkwMywxNy4yNTQtMTEuMTE5LDI0Ljc5NC0xOC42NTlsNzMuODk2LTczLjg5NWM0My4xMDUtNDMuMTA1LDQzLjEwNS0xMTIuOTkxLDAuMDAxLTE1Ni4wOTcNCg0KCUMzODYuNTY2LDM5LjIyNSwzMTYuNjgsMzkuMjI1LDI3My41NzQsODIuMzN6Ii8+DQo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjAsLTIwKSIgc3R5bGU9ImZpbGw6IzAwOUFDRCI+DQo8cGF0aCAgZD0iTTE1Ni4yMjYsMTk5LjY3OWM3LjU0MS03LjU0LDE1LjkwMi0xMy43NTcsMjQuNzk0LTE4LjY1OWM0OS41NTYtMjcuMzE4LDExMy4xMTctMTIuNzg4LDE0NC45NywzNS41MTgNCg0KCWwtMzguNTQ3LDM4LjU0N2MtMTEuMDU5LTI1LjIyNy0zOC41LTM5LjU2NS02NS44MTMtMzMuNDU2Yy0xMC4yODIsMi4zLTIwLjA1NCw3LjQyNy0yOC4wMzksMTUuNDEzbC03My44OTgsNzMuODk2DQoNCgljLTIyLjQzMywyMi40MzMtMjIuNDMyLDU4LjkzNiwwLjAwMiw4MS4zNjljMjIuNDMzLDIyLjQzMyw1OC45MzUsMjIuNDMzLDgxLjM2OCwwbDIyLjc4LTIyLjc3OQ0KDQoJYzIwLjcxLDguMjE3LDQyLjkzOCwxMS41MDgsNjQuODYyLDkuODYzbC01MC4yNzgsNTAuMjc4Yy00My4xMDUsNDMuMTA1LTExMi45OTEsNDMuMTA1LTE1Ni4wOTYsMA0KDQoJYy00My4xMDUtNDMuMTA0LTQzLjEwNi0xMTIuOTkxLTAuMDAxLTE1Ni4wOTZMMTU2LjIyNiwxOTkuNjc5eiBNMjczLjU3NCw4Mi4zM2wtNTAuMjc4LDUwLjI3OA0KDQoJYzIxLjkyOC0xLjY0Myw0NC4xNTIsMS42NDgsNjQuODYzLDkuODY1bDIyLjc3OS0yMi43OGMyMi40MzQtMjIuNDM0LDU4LjkzNi0yMi40MzQsODEuMzcsMGMyMi40MzQsMjIuNDM0LDIyLjQzNCw1OC45MzYsMCw4MS4zNw0KDQoJbC03My44OTcsNzMuODk1Yy0yMi41MDEsMjIuNTAxLTU5LjA2MSwyMi4zMTEtODEuMzY4LDBjLTUuMjAyLTUuMjAxLTkuNjk0LTExLjY3OC0xMi40ODQtMTguMDRsLTM4LjU0NiwzOC41NDYNCg0KCWM0LjA0OSw2LjE0Miw4LjI2MSwxMS40NTMsMTMuNjY2LDE2Ljg1OGMxMy45NDksMTMuOTUsMzEuNjk4LDI0LjMzOSw1Mi4xMTcsMjkuMjUxYzI2LjQ2Niw2LjM3LDU0LjgyMywyLjgzOSw3OS4xODUtMTAuNTkyDQoNCgljOC44OTItNC45MDMsMTcuMjU0LTExLjExOSwyNC43OTQtMTguNjU5bDczLjg5Ni03My44OTVjNDMuMTA1LTQzLjEwNSw0My4xMDUtMTEyLjk5MSwwLjAwMS0xNTYuMDk3DQoNCglDMzg2LjU2NiwzOS4yMjUsMzE2LjY4LDM5LjIyNSwyNzMuNTc0LDgyLjMzeiIvPg0KPC9nPg0KDQo8L3N2Zz4NCg0K);width:2em;height:2em;background-size:2em}.mapjs-hyperlink:hover{background-image:url(data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiDQoNCgkgd2lkdGg9IjUxMnB4IiBoZWlnaHQ9IjUxMnB4IiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQoNCjxwYXRoIHN0eWxlPSJmaWxsOmJsYWNrIiBkPSJNMTU2LjIyNiwxOTkuNjc5YzcuNTQxLTcuNTQsMTUuOTAyLTEzLjc1NywyNC43OTQtMTguNjU5YzQ5LjU1Ni0yNy4zMTgsMTEzLjExNy0xMi43ODgsMTQ0Ljk3LDM1LjUxOA0KDQoJbC0zOC41NDcsMzguNTQ3Yy0xMS4wNTktMjUuMjI3LTM4LjUtMzkuNTY1LTY1LjgxMy0zMy40NTZjLTEwLjI4MiwyLjMtMjAuMDU0LDcuNDI3LTI4LjAzOSwxNS40MTNsLTczLjg5OCw3My44OTYNCg0KCWMtMjIuNDMzLDIyLjQzMy0yMi40MzIsNTguOTM2LDAuMDAyLDgxLjM2OWMyMi40MzMsMjIuNDMzLDU4LjkzNSwyMi40MzMsODEuMzY4LDBsMjIuNzgtMjIuNzc5DQoNCgljMjAuNzEsOC4yMTcsNDIuOTM4LDExLjUwOCw2NC44NjIsOS44NjNsLTUwLjI3OCw1MC4yNzhjLTQzLjEwNSw0My4xMDUtMTEyLjk5MSw0My4xMDUtMTU2LjA5NiwwDQoNCgljLTQzLjEwNS00My4xMDQtNDMuMTA2LTExMi45OTEtMC4wMDEtMTU2LjA5NkwxNTYuMjI2LDE5OS42Nzl6IE0yNzMuNTc0LDgyLjMzbC01MC4yNzgsNTAuMjc4DQoNCgljMjEuOTI4LTEuNjQzLDQ0LjE1MiwxLjY0OCw2NC44NjMsOS44NjVsMjIuNzc5LTIyLjc4YzIyLjQzNC0yMi40MzQsNTguOTM2LTIyLjQzNCw4MS4zNywwYzIyLjQzNCwyMi40MzQsMjIuNDM0LDU4LjkzNiwwLDgxLjM3DQoNCglsLTczLjg5Nyw3My44OTVjLTIyLjUwMSwyMi41MDEtNTkuMDYxLDIyLjMxMS04MS4zNjgsMGMtNS4yMDItNS4yMDEtOS42OTQtMTEuNjc4LTEyLjQ4NC0xOC4wNGwtMzguNTQ2LDM4LjU0Ng0KDQoJYzQuMDQ5LDYuMTQyLDguMjYxLDExLjQ1MywxMy42NjYsMTYuODU4YzEzLjk0OSwxMy45NSwzMS42OTgsMjQuMzM5LDUyLjExNywyOS4yNTFjMjYuNDY2LDYuMzcsNTQuODIzLDIuODM5LDc5LjE4NS0xMC41OTINCg0KCWM4Ljg5Mi00LjkwMywxNy4yNTQtMTEuMTE5LDI0Ljc5NC0xOC42NTlsNzMuODk2LTczLjg5NWM0My4xMDUtNDMuMTA1LDQzLjEwNS0xMTIuOTkxLDAuMDAxLTE1Ni4wOTcNCg0KCUMzODYuNTY2LDM5LjIyNSwzMTYuNjgsMzkuMjI1LDI3My41NzQsODIuMzN6Ii8+DQo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjAsLTIwKSIgc3R5bGU9ImZpbGw6IzAwQkZGRiI+DQo8cGF0aCAgZD0iTTE1Ni4yMjYsMTk5LjY3OWM3LjU0MS03LjU0LDE1LjkwMi0xMy43NTcsMjQuNzk0LTE4LjY1OWM0OS41NTYtMjcuMzE4LDExMy4xMTctMTIuNzg4LDE0NC45NywzNS41MTgNCg0KCWwtMzguNTQ3LDM4LjU0N2MtMTEuMDU5LTI1LjIyNy0zOC41LTM5LjU2NS02NS44MTMtMzMuNDU2Yy0xMC4yODIsMi4zLTIwLjA1NCw3LjQyNy0yOC4wMzksMTUuNDEzbC03My44OTgsNzMuODk2DQoNCgljLTIyLjQzMywyMi40MzMtMjIuNDMyLDU4LjkzNiwwLjAwMiw4MS4zNjljMjIuNDMzLDIyLjQzMyw1OC45MzUsMjIuNDMzLDgxLjM2OCwwbDIyLjc4LTIyLjc3OQ0KDQoJYzIwLjcxLDguMjE3LDQyLjkzOCwxMS41MDgsNjQuODYyLDkuODYzbC01MC4yNzgsNTAuMjc4Yy00My4xMDUsNDMuMTA1LTExMi45OTEsNDMuMTA1LTE1Ni4wOTYsMA0KDQoJYy00My4xMDUtNDMuMTA0LTQzLjEwNi0xMTIuOTkxLTAuMDAxLTE1Ni4wOTZMMTU2LjIyNiwxOTkuNjc5eiBNMjczLjU3NCw4Mi4zM2wtNTAuMjc4LDUwLjI3OA0KDQoJYzIxLjkyOC0xLjY0Myw0NC4xNTIsMS42NDgsNjQuODYzLDkuODY1bDIyLjc3OS0yMi43OGMyMi40MzQtMjIuNDM0LDU4LjkzNi0yMi40MzQsODEuMzcsMGMyMi40MzQsMjIuNDM0LDIyLjQzNCw1OC45MzYsMCw4MS4zNw0KDQoJbC03My44OTcsNzMuODk1Yy0yMi41MDEsMjIuNTAxLTU5LjA2MSwyMi4zMTEtODEuMzY4LDBjLTUuMjAyLTUuMjAxLTkuNjk0LTExLjY3OC0xMi40ODQtMTguMDRsLTM4LjU0NiwzOC41NDYNCg0KCWM0LjA0OSw2LjE0Miw4LjI2MSwxMS40NTMsMTMuNjY2LDE2Ljg1OGMxMy45NDksMTMuOTUsMzEuNjk4LDI0LjMzOSw1Mi4xMTcsMjkuMjUxYzI2LjQ2Niw2LjM3LDU0LjgyMywyLjgzOSw3OS4xODUtMTAuNTkyDQoNCgljOC44OTItNC45MDMsMTcuMjU0LTExLjExOSwyNC43OTQtMTguNjU5bDczLjg5Ni03My44OTVjNDMuMTA1LTQzLjEwNSw0My4xMDUtMTEyLjk5MSwwLjAwMS0xNTYuMDk3DQoNCglDMzg2LjU2NiwzOS4yMjUsMzE2LjY4LDM5LjIyNSwyNzMuNTc0LDgyLjMzeiIvPg0KPC9nPg0KDQo8L3N2Zz4NCg==)}.mapjs-attachment{right:-5px;top:-15px;background-image:url(data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiDQoJIHdpZHRoPSIxNnB4IiBoZWlnaHQ9IjMycHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHN0eWxlPSJ0cmFuc2Zvcm06cm90YXRlKDEwKSI+DQogPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMiwtMikgcm90YXRlKDEwKSIgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2Utd2lkdGg6MiI+DQogICA8cGF0aCBkPSJNNCwxNiBMNCwxMSBDNCwxIDE1LDEgMTUsMTEgTDE1LDI2IEMxNSwzMSA4LDMxIDgsMjUgTDgsMTAiDQogICAgIHN0eWxlPSJzdHJva2U6YmxhY2siLz4NCiAgIDxwYXRoIGQ9Ik0zLDE2IEwzLDEwIEMzLDAgMTQsMCAxNCwxMCBMMTQsMjUgQzE0LDMwIDcsMzAgNywyNSBMNywxMCINCiAgICAgc3R5bGU9InN0cm9rZTojMDA5QUNEIi8+DQogIDwvZz4NCjwvc3ZnPg0K);width:16px;height:32px;background-size:16px 32px}.mapjs-attachment:hover{background-image:url(data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiDQoJIHdpZHRoPSIxNnB4IiBoZWlnaHQ9IjMycHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHN0eWxlPSJ0cmFuc2Zvcm06cm90YXRlKDEwKSI+DQogPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMiwtMikgcm90YXRlKDEwKSIgc3R5bGU9ImZpbGw6bm9uZTtzdHJva2Utd2lkdGg6MiI+DQogICA8cGF0aCBkPSJNNCwxNiBMNCwxMSBDNCwxIDE1LDEgMTUsMTEgTDE1LDI2IEMxNSwzMSA4LDMxIDgsMjUgTDgsMTAiDQogICAgIHN0eWxlPSJzdHJva2U6YmxhY2siLz4NCiAgIDxwYXRoIGQ9Ik0zLDE2IEwzLDEwIEMzLDAgMTQsMCAxNCwxMCBMMTQsMjUgQzE0LDMwIDcsMzAgNywyNSBMNywxMCINCiAgICAgc3R5bGU9InN0cm9rZTojMDBCRkZGIi8+DQogIDwvZz4NCjwvc3ZnPg0K)}.mapjs-draw-container{position:absolute;margin:0;padding:0}.mapjs-draw-container[data-mapjs-role=connector]{z-index:1}.mapjs-draw-container[data-mapjs-role=link]{z-index:2}.mapjs-connector{stroke-width:1px;fill:none}.mapjs-link{stroke-width:1.5px;fill:none}.mapjs-link-hit{stroke:transparent;stroke-width:15;cursor:crosshair}#container{background-color:#FFF;margin:0;padding:0}.mapjs-reorder-bounds{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4NCjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgd2lkdGg9IjExcHgiIGhlaWdodD0iMjAuNjM0cHgiPg0KPHBhdGggZD0iTTAuMTk5LDEwLjc5bDkuNjQ2LDkuNjQ2YzAuMTMsMC4xMzMsMC4zMDIsMC4xOTcsMC40NzEsMC4xOTdjMC4xNzMsMCwwLjM0NC0wLjA2NCwwLjQ3My0wLjE5N2wwLjAxMy0wLjAxMQ0KCUMxMC45MjksMjAuMzA1LDExLDIwLjEzMiwxMSwxOS45NTJ2LTQuNjhjMC0wLjE4Mi0wLjA3LTAuMzQ3LTAuMTk4LTAuNDc1TDYuMzIzLDEwLjMybDQuNDc5LTQuNDgzQzEwLjkyOSw1LjcxNCwxMSw1LjU0MywxMSw1LjM2NA0KCVYwLjY4MmMwLTAuMTc2LTAuMDctMC4zNDUtMC4xOTgtMC40NzRsLTAuMDEzLTAuMDEyQzEwLjY2NywwLjA3MSwxMC40OTcsMCwxMC4zMTYsMEMxMC4xNDMsMCw5Ljk3LDAuMDcxLDkuODQ1LDAuMTk2TDAuMTk5LDkuODQ1DQoJQy0wLjA2NiwxMC4xMDQtMC4wNjYsMTAuNTMyLDAuMTk5LDEwLjc5eiIvPg0KPC9zdmc+);background-height:100%;background-width:100%;height:20px;width:11px;z-index:999;background-repeat:no-repeat}.mapjs-reorder-bounds[mapjs-edge=left]{background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4NCjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgd2lkdGg9IjExcHgiIGhlaWdodD0iMjAuNjM0cHgiPg0KICAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTEsIDApIHNjYWxlKC0xLCAxKSI+DQo8cGF0aCBkPSJNMC4xOTksMTAuNzlsOS42NDYsOS42NDZjMC4xMywwLjEzMywwLjMwMiwwLjE5NywwLjQ3MSwwLjE5N2MwLjE3MywwLDAuMzQ0LTAuMDY0LDAuNDczLTAuMTk3bDAuMDEzLTAuMDExDQoJQzEwLjkyOSwyMC4zMDUsMTEsMjAuMTMyLDExLDE5Ljk1MnYtNC42OGMwLTAuMTgyLTAuMDctMC4zNDctMC4xOTgtMC40NzVMNi4zMjMsMTAuMzJsNC40NzktNC40ODNDMTAuOTI5LDUuNzE0LDExLDUuNTQzLDExLDUuMzY0DQoJVjAuNjgyYzAtMC4xNzYtMC4wNy0wLjM0NS0wLjE5OC0wLjQ3NGwtMC4wMTMtMC4wMTJDMTAuNjY3LDAuMDcxLDEwLjQ5NywwLDEwLjMxNiwwQzEwLjE0MywwLDkuOTcsMC4wNzEsOS44NDUsMC4xOTZMMC4xOTksOS44NDUNCglDLTAuMDY2LDEwLjEwNC0wLjA2NiwxMC41MzIsMC4xOTksMTAuNzl6Ii8+DQogIDwvZz4NCjwvc3ZnPg==)}.mindmup_context_menu_item{cursor:pointer}.mindmup-legend-item-cont .gravatar,.mindmup-node-icon-avatar{height:24px;width:24px}.mindmup-legend-item-cont .avatar-container,.mindmup-node-icon-avatar .avatar-container{margin:0}.mindmup-legend-item-cont .gravatar:hover,.mindmup-node-icon-avatar .gravatar:hover{transform:scale(2);position:relative;z-index:5}#context-menu li:not(.folder)>ul{display:none}.controller-easy_wbs .easy-content-page{transform:none!important}.link-edit-widget{display:none;z-index:999;position:absolute;padding:10px;background-color:#fff;border:1px solid #dd3e3a;cursor:default}mindmup-sidebar{display:block}.date-picker__date-wrap .input-append{width:auto;float:right}.date-picker__date-wrap{margin-bottom:5px;overflow:hidden}easy-lookup .easy-lookup-values-wrapper{height:auto}#sidebar{z-index:3}.mindmup-sidebar__closed_button:before{font-family:"Material Icons",sans-serif}.mindmup-sidebar-comments .journal{margin:6px 0 0}.mindmup_hover_menu{-webkit-border-radius:.24rem;-moz-border-radius:.24rem;border-radius:.24rem}.mapjs-collapsor,.mindmup-legend-color-box{-webkit-border-radius:5000px!important;-moz-border-radius:5000px!important;border-radius:5000px!important}.easy .mindmup__menu-group--tooltiped li>.menu-children,.easy .mindmup__menu-group--tooltiped ul,.mapjs-node{-webkit-border-radius:.12rem;-moz-border-radius:.12rem;border-radius:.12rem}.mindmup-node-icon-milestone-shell{-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.mindmup-node-icon-progress{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.mindmup__legend-body,.mindmup_hover_menu{-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box}.mindmup__menu{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.mindmup__legend-header,.mindmup__menu,.mindmup__node-control{display:-webkit-box;display:-moz-box;display:box;display:-webkit-flex;display:-moz-flex;display:-ms-flexbox;display:flex}.mindmup__menu,.mindmup__node-control{-webkit-justify-content:space-between;-ms-justify-content:space-between;justify-content:space-between}.mindmup__legend-header>label,.mindmup__menu-group--sizing{-webkit-flex-grow:1;-ms-flex-grow:1;flex-grow:1}.easy .mindmup__menu-group--tooltiped li{font-family:Ubuntu,"Open Sans",sans-serif;font-size:15px;font-weight:400;color:#324164}@media only screen and (max-width:1400px){.easy .mindmup__menu-group--tooltiped li{font-size:12px}}@media only screen and (max-width:1400px) and (min-resolution:100dpi){.easy .mindmup__menu-group--tooltiped li{font-size:15px}}@-webkit-keyframes icon-blink{from{opacity:.25;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}to{opacity:1;-webkit-transform:scale(1.25);-moz-transform:scale(1.25);-ms-transform:scale(1.25);-o-transform:scale(1.25);transform:scale(1.25)}}@-moz-keyframes icon-blink{from{opacity:.25;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}to{opacity:1;-webkit-transform:scale(1.25);-moz-transform:scale(1.25);-ms-transform:scale(1.25);-o-transform:scale(1.25);transform:scale(1.25)}}@-ms-keyframes icon-blink{from{opacity:.25;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}to{opacity:1;-webkit-transform:scale(1.25);-moz-transform:scale(1.25);-ms-transform:scale(1.25);-o-transform:scale(1.25);transform:scale(1.25)}}@keyframes icon-blink{from{opacity:.25;-webkit-transform:scale(1);-moz-transform:scale(1);-ms-transform:scale(1);-o-transform:scale(1);transform:scale(1)}to{opacity:1;-webkit-transform:scale(1.25);-moz-transform:scale(1.25);-ms-transform:scale(1.25);-o-transform:scale(1.25);transform:scale(1.25)}}@-webkit-keyframes spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}@-moz-keyframes spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}@-ms-keyframes spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}@-webkit-keyframes spin-inverse{from{transform:rotate(0)}to{transform:rotate(-360deg)}}@-moz-keyframes spin-inverse{from{transform:rotate(0)}to{transform:rotate(-360deg)}}@-ms-keyframes spin-inverse{from{transform:rotate(0)}to{transform:rotate(-360deg)}}@keyframes spin-inverse{from{transform:rotate(0)}to{transform:rotate(-360deg)}}@-webkit-keyframes translate-x{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@-moz-keyframes translate-x{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@-ms-keyframes translate-x{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@keyframes translate-x{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes translate-y{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@-moz-keyframes translate-y{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@-ms-keyframes translate-y{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@keyframes translate-y{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes translate-menu{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@-moz-keyframes translate-menu{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@-ms-keyframes translate-menu{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@keyframes translate-menu{100%{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}}@-webkit-keyframes load7{0%,100%,80%{box-shadow:0 2.5em 0 -1.3em}40%{box-shadow:0 2.5em 0 0}}@-moz-keyframes load7{0%,100%,80%{box-shadow:0 2.5em 0 -1.3em}40%{box-shadow:0 2.5em 0 0}}@-ms-keyframes load7{0%,100%,80%{box-shadow:0 2.5em 0 -1.3em}40%{box-shadow:0 2.5em 0 0}}@keyframes load7{0%,100%,80%{box-shadow:0 2.5em 0 -1.3em}40%{box-shadow:0 2.5em 0 0}}@-webkit-keyframes ripple{0%{-webkit-transform:scale(0);-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);transform:scale(0);opacity:.2}100%{-webkit-transform:scale(3);-moz-transform:scale(3);-ms-transform:scale(3);-o-transform:scale(3);transform:scale(3);opacity:0}}@-moz-keyframes ripple{0%{-webkit-transform:scale(0);-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);transform:scale(0);opacity:.2}100%{-webkit-transform:scale(3);-moz-transform:scale(3);-ms-transform:scale(3);-o-transform:scale(3);transform:scale(3);opacity:0}}@-ms-keyframes ripple{0%{-webkit-transform:scale(0);-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);transform:scale(0);opacity:.2}100%{-webkit-transform:scale(3);-moz-transform:scale(3);-ms-transform:scale(3);-o-transform:scale(3);transform:scale(3);opacity:0}}@keyframes ripple{0%{-webkit-transform:scale(0);-moz-transform:scale(0);-ms-transform:scale(0);-o-transform:scale(0);transform:scale(0);opacity:.2}100%{-webkit-transform:scale(3);-moz-transform:scale(3);-ms-transform:scale(3);-o-transform:scale(3);transform:scale(3);opacity:0}}.easy .mindmup__menu-group--tooltiped li a.submenu:after,.easy-mindmup__icon:before,.mapjs-collapsor,.mindmup__legend-toggler.active a:before{speak:none;font-weight:400;font-style:normal;display:inline-block;width:auto;height:auto;background-position:0 0;background-repeat:repeat;background-image:none;vertical-align:baseline;text-decoration:none!important;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga';font-family:'Material Icons',EasyIcons}.easy-mindmup__icon:before,.mapjs-collapsor,.material-icons{font-family:"Material Icons",sans-serif}.easy-mindmup__icon,.mindmup__legend-toggler.active a{position:relative;background-repeat:no-repeat;background-image:none!important}.mindmup-container{background-image:linear-gradient(rgba(217,217,217,.2) 1px,transparent 1px),linear-gradient(90deg,rgba(217,217,217,.2) 1px,transparent 1px);background-size:.8rem .8rem,.8rem .8rem;background-position:-1px -1px,-1px -1px}.easy .mindmup__menu-group--tooltiped li>.menu-children,.easy .mindmup__menu-group--tooltiped ul,.mindmup-sidebar__input__name .baseline,.mindmup__legend-container{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.1),0 1px 1px rgba(0,0,0,.1);-moz-box-shadow:0 1px 2px rgba(0,0,0,.1),0 1px 1px rgba(0,0,0,.1);box-shadow:0 1px 2px rgba(0,0,0,.1),0 1px 1px rgba(0,0,0,.1)}.easy .mindmup__menu-group--tooltiped ul,.mapjs-node.activated,.mapjs-node.droppable,.mapjs-node.selected{-webkit-box-shadow:0 2px 4px rgba(0,0,0,.1),0 2px 2px rgba(0,0,0,.1);-moz-box-shadow:0 2px 4px rgba(0,0,0,.1),0 2px 2px rgba(0,0,0,.1);box-shadow:0 2px 4px rgba(0,0,0,.1),0 2px 2px rgba(0,0,0,.1)}@-webkit-keyframes sidebar-buttons-slide-left{from{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}to{-webkit-transform:translate(-12.2666666667rem,0);-moz-transform:translate(-12.2666666667rem,0);-ms-transform:translate(-12.2666666667rem,0);-o-transform:translate(-12.2666666667rem,0);transform:translate(-12.2666666667rem,0)}}@-moz-keyframes sidebar-buttons-slide-left{from{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}to{-webkit-transform:translate(-12.2666666667rem,0);-moz-transform:translate(-12.2666666667rem,0);-ms-transform:translate(-12.2666666667rem,0);-o-transform:translate(-12.2666666667rem,0);transform:translate(-12.2666666667rem,0)}}@-ms-keyframes sidebar-buttons-slide-left{from{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}to{-webkit-transform:translate(-12.2666666667rem,0);-moz-transform:translate(-12.2666666667rem,0);-ms-transform:translate(-12.2666666667rem,0);-o-transform:translate(-12.2666666667rem,0);transform:translate(-12.2666666667rem,0)}}@keyframes sidebar-buttons-slide-left{from{-webkit-transform:translate(0,0);-moz-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}to{-webkit-transform:translate(-12.2666666667rem,0);-moz-transform:translate(-12.2666666667rem,0);-ms-transform:translate(-12.2666666667rem,0);-o-transform:translate(-12.2666666667rem,0);transform:translate(-12.2666666667rem,0)}}.easy-mindmup__icon.button:before{position:absolute;left:0;width:2.4rem;text-align:center;font-size:1.25em;line-height:1;color:inherit;top:50%;margin-top:-.5em}.easy .mindmup__menu-group--tooltiped li a.submenu:after{margin:0;padding:0;display:block;background:0 0;border:none;min-width:1.6rem;position:absolute;top:0;right:0;bottom:0}.easy .mindmup__menu-group--tooltiped li a.submenu:after span{display:none}.easy .mindmup__menu-group--tooltiped li>.menu-children,.easy .mindmup__menu-group--tooltiped ul{position:absolute;background-color:#fff;color:#324164;padding:.5em;font-size:.89em;line-height:1;margin-top:-.25em;white-space:pre}.easy .mindmup__menu-group--tooltiped li>.menu-children a,.easy .mindmup__menu-group--tooltiped ul a{color:#324164}.easy .mindmup__menu-group--tooltiped .input-append li>.menu-children,.easy .mindmup__menu-group--tooltiped .input-append ul,.input-append .easy .mindmup__menu-group--tooltiped li>.menu-children,.input-append .easy .mindmup__menu-group--tooltiped ul{font-weight:400;margin-top:1px}.easy .mindmup__menu-group--tooltiped li{padding:0 0 0 2.4rem;border:1px solid transparent;border-left:none;border-right:none;position:relative;line-height:1.25}.easy .mindmup__menu-group--tooltiped li:hover{border-color:#f0f8ff!important;background:#f0f8ff!important;z-index:1}.easy .mindmup__menu-group--tooltiped li a{-ms-word-break:break-all;word-break:break-word;-webkit-hyphens:auto;-moz-hyphens:auto;-ms-hyphens:auto;hyphens:auto;padding:.4rem .8rem .4rem 3.2rem;margin-left:-2.4rem;display:block;text-decoration:none}.easy .mindmup__menu-group--tooltiped li a.active{color:#fe7d99;background:0 0;border:none}.easy .mindmup__menu-group--tooltiped li a.active:before{color:#fe7d99}.easy .mindmup__menu-group--tooltiped li a:before{position:absolute;left:0;width:2.4rem;text-align:center;padding-top:1px;color:#324164}.easy .mindmup__menu-group--tooltiped li a.submenu{padding-right:0!important;background:0 0!important;position:relative}.easy .mindmup__menu-group--tooltiped li a.submenu:after{content:"\005d"!important;left:auto;font-size:.6666666667rem;text-align:left;line-height:1.9}.easy .mindmup__menu-group--tooltiped li a~span{font-size:.89em}.easy .mindmup__menu-group--tooltiped{position:relative}.easy .mindmup__menu-group--tooltiped ul{z-index:1500;margin:0;list-style:none;font-size:1.125em;min-width:200px;padding:.4rem 0;white-space:normal!important}.easy .mindmup__menu-group--tooltiped ul:after{content:'';position:absolute;top:0;bottom:0;right:auto;left:2.4rem;border-left:1px solid #e5e5e5;z-index:0}.easy .mindmup__menu-group--tooltiped li>.menu-children{display:none;white-space:normal;top:6px;left:99%;width:150px}.easy .mindmup__menu-group--tooltiped li>.menu-children>li+li{border-top:1px dashed #e5e5e5}.easy .mindmup__menu-group--tooltiped li>.menu-children>li a{text-decoration:none;display:block;padding:.5em}.easy .mindmup__menu-group--tooltiped li:hover>.menu-children,.mindmup__menu-item{display:inline-block}.easy .mindmup__menu-group--tooltiped li>.menu-children>li a:hover{text-decoration:underline}.easy .mindmup__menu-group--tooltiped li>.menu-children>li a:before{color:#01c8a9;text-decoration:none;margin-right:.4rem}.mindmup-cont{margin:0 -1.2rem .4rem;width:auto;overflow:hidden}@media only screen and (max-width:32rem){.mindmup-cont{margin-top:-1.6rem}}.mindmup-container{position:relative;cursor:all-scroll;border-top:none;box-sizing:border-box;background-color:#f9f9f9;margin:0;padding:0;outline:0;overflow-y:hidden!important}.mindmup__menu{user-select:none;position:relative;z-index:1;background-color:#fff;border-bottom:1px solid #e5e5e5;padding:1.6rem}.mindmup__menu_addons{position:absolute;right:0;top:5.6rem;z-index:5}.mindmup__menu-item{text-align:left}.mindmup__menu-item a.active,.mindmup__menu-item a.active:before{color:#d94838!important}@media only screen and (max-width:960px){.mindmup__menu-item>a{padding-right:0}.mindmup__menu-item>a>span{display:none}}@media only screen and (max-width:1400px){.mindmup__menu-item>a.easy-mindmup__icon--display,.mindmup__menu-item>a.easy-mindmup__icon--settings{padding-right:0}.mindmup__menu-item>a.easy-mindmup__icon--display>span,.mindmup__menu-item>a.easy-mindmup__icon--settings>span{display:none}}.mindmup__menu .right-menu{float:right}.mindmup__menu-group ul{margin:0}.mindmup__menu-group--tooltiped>ul{display:none}.mindmup__menu-group--tooltiped:hover>ul{display:block}.mindmup__menu-group--sizing{text-align:center;font-size:1.5em;position:absolute;top:1.6rem;left:-7.6rem;line-height:1.6rem}@media only screen and (max-width:32rem){.mindmup__menu-group--sizing{display:none}}.mindmup__menu-group--sizing a{color:rgba(50,65,100,.5);text-decoration:none}.mindmup__menu-group--sizing li{list-style:none;display:inline-block}@media only screen and (min-width:33rem){.mindmup__menu-group-display{margin-left:18.1333333333rem}}.mindmup__legend-container--hidden+.mindmup__menu-group-display{margin-left:0}.mindmup_hover_menu{display:block;position:absolute;z-index:99;background-color:#fff;min-width:160px;padding:5px 0;margin:2px 0 0;border:1px solid rgba(0,0,0,.2);-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.mindmup-reload-modal-errors li{color:red}.mindmup-last-modal-diffs li{color:#fa9b3c}.mindmup__button--disabled{opacity:.4}.mindmup_modal__flash_close{position:absolute;right:.8rem;font-size:1.6rem;line-height:1.6rem}.easy-mindmup__icon--add:before,.easy-mindmup__icon--add_sibling:before{content:"\e3ba"}.easy-mindmup__icon--insert_between:before{content:"\e3bb"}.easy-mindmup__icon--remove:before{content:"\e92b"}.easy-mindmup__icon--rename:before{content:"\e22b"}.easy-mindmup__icon--edit_data:before{content:"\e880"}.easy-mindmup__icon--edit:before{content:"\e3c9"}.easy-mindmup__icon--follow_url:before{content:"\e0e2"}.easy-mindmup__icon--zoom_in:before{content:"\e8ff"}.easy-mindmup__icon--zoom_out:before{content:"\e900"}.easy-mindmup__icon--refresh_view:before{content:"\e881"}.easy-mindmup__icon--filter:before{content:"\e152"}.easy-mindmup__icon--cancel:before,.easy-mindmup__icon--cancel_filter:before{content:"\e5c9"}.easy-mindmup__icon--close:before{content:"\e5cd"}.easy-mindmup__icon--display:before{content:"\e417"}.easy-mindmup__icon--links:before{content:"\e157"}.easy-mindmup__icon--icons:before{content:"\e24e"}.easy-mindmup__icon--collapse:before{content:"\e909"}.easy-mindmup__icon--expand:before{content:"\e146"}.easy-mindmup__icon--one_side:before{content:"\e86d"}.easy-mindmup__icon--settings:before{content:"\e8b8"}.easy-mindmup__icon--undo:before{content:"\e166"}.easy-mindmup__icon--redo:before{content:"\e15a"}.easy-mindmup__icon--print:before{content:"\e16b"}.easy-mindmup__icon--cut:before{content:"\e14e"}.easy-mindmup__icon--copy:before{content:"\e14d"}.easy-mindmup__icon--paste:before{content:"\e14f"}.easy-mindmup__icon--save:before{content:"\e161"}.easy-mindmup__icon--legend:before{content:"\e5d2"}.easy-mindmup__icon--legend_hide:before{content:"\e06d"}.easy-mindmup__icon.button{padding-left:2.4rem}.material-icons{font-style:normal}.redmine .button.easy-mindmup__icon:before,.redmine.mindmup__context_menu .easy-mindmup__icon:before{position:absolute;left:0;text-align:center;font-size:1.2em;line-height:1;color:inherit;top:50%;margin-top:-.5em}.redmine .mindmup-cont{margin:0 -10px -10px}.redmine .mindmup-menu{padding:5px 1px 0}.redmine .mindmup-menu .menu-item{padding:8px 10px}.redmine .mindmup__menu{background-color:#FFF;padding:14px}.redmine .mindmup__menu-group--tooltiped>a{padding-top:10px;padding-bottom:10px}.redmine .mindmup__menu-group--tooltiped>ul{position:absolute;background-color:#fff;border:1px solid #dfccaf;color:#42321a;padding:5px 5px 5px 0;margin-top:10px;margin-left:-10px;min-width:150px}.redmine .mindmup__menu-group--tooltiped>ul li{display:block;padding:3px}.redmine .mindmup__menu-group--tooltiped>ul li a{display:block;padding-left:20px}.mindmup__legend-container--hidden,.mindmup__legend-toggler .tip,.redmine .mindmup__menu-group--tooltiped:after{display:none}.redmine .mindmup__menu-group--tooltiped>ul li a:before{position:absolute;left:0;width:20px}.redmine .mindmup__menu-group--tooltiped .icon{background-image:none}.redmine .mindmup__menu-group--sizing a{padding:0}.redmine .mindmup__menu-item{padding-left:15px;padding-right:15px}.redmine .mindmup__menu-save a{padding-left:30px!important}.redmine .mindmup__menu-save a:before{text-align:center;width:36px;position:absolute}.redmine .mindmup__menu_addons{top:25px}.redmine .mindmup__menu_addons ul{padding:0}.redmine .mindmup-legend{margin-top:1px}.redmine .mindmup-legend__filter_cancel_icon{position:absolute}.redmine .mindmup__legend-container{top:0}.redmine .mindmup__legend-toggler{height:25px}.redmine .mindmup__legend-toggler a{line-height:1.5em}.redmine .mindmup__legend-header{padding:8px}.redmine a.button-positive{background-color:#4ebf67;border-radius:2px;border:1.3px solid #3aa051;color:#fff;padding:8px 16px}.redmine .menu-item.active{background-color:#9DB9D5}.redmine .menu-item.active a.button{color:#fff}.redmine .gravatar{max-width:100%;height:auto;border-radius:5000px;overflow:hidden;box-sizing:border-box}.redmine .button-2.active{background-color:transparent!important}.redmine .mindmup_modal__flash_close{color:#800;right:15px;line-height:18px}.mindmup-progress-modal{position:fixed;z-index:10000;width:40%;height:150px;top:150px;left:30%;right:30%;padding-top:30px;background-color:#fff;border:1px solid #d9d9d9;box-shadow:6px 6px 42px 7px rgba(217,217,217,.65)}.mindmup-progress-modal h3{text-align:center}.mindmup-progress-cont{height:8px;width:80%;margin-left:10%;margin-right:10%;border:1px solid #d9d9d9}.mindmup-progress-bar{width:50%;height:100%;background-color:green}.mindmup__legend-container{box-shadow:0 1px 4px rgba(0,0,0,.14),0 2px 2px rgba(0,0,0,.05);background-color:#fff;position:absolute;left:.8rem;top:.8rem;width:17.3333333333rem}@media only screen and (max-width:32rem){.mindmup__legend-container{top:110%}}.mindmup__legend-header{background:#f9f9f9;padding:.8rem}.mindmup__legend-toggler a{color:rgba(50,65,100,.5);font-size:1.5em;line-height:2.4rem}.mindmup__legend-toggler.active a:before{content:"\e5ce"!important}.mindmup__legend-body{overflow-y:auto;padding:.8rem 1.6rem;overflow-x:hidden}.mindmup-legend-color-box{background-color:#E0E0E0;border-color:#E0E0E0;width:1.6rem;height:1.6rem;display:inline-block;border-width:1px;border-style:solid;vertical-align:middle;margin-right:5px}.mindmup-legend-used{margin-top:10px;display:inline-block;color:rgba(66,50,26,.5)}.mindmup-legend-used-toggle{float:right;margin:5px 0}.mindmup-legend-used-all--used,.mindmup-legend-used-used--all{display:none}.mindmup-legend-item-cont{cursor:pointer;margin-top:.4rem}.mindmup-legend-item-cont .avatar-container{float:none}.mindmup-legend .hotkey_link{margin:.8rem -.8rem 0;padding-top:.8rem;border-top:1px solid #e5e5e5}.easy .mindmup-legend .hotkey_link a{color:rgba(50,65,100,.5)}.mindmup-legend__filter_cont{position:absolute;right:.8rem}.mindmup-legend__filter_icon{color:rgba(50,65,100,.4);font-size:2em;padding:5px}.mindmup-legend__filter_cancel_icon{position:absolute;display:none;color:#f33;font-size:2em;padding:0 5px 5px}.mindmup-legend__filter_cont:hover .mindmup-legend__filter_cancel_icon{display:inline}.mindmup-legend-drag-overlay{position:fixed;top:0;left:0;z-index:10000;cursor:move}.mindmup-legend-drag-avatar{position:absolute;z-index:5;height:20px;width:20px;margin-left:-12px;margin-top:-12px;border-width:1px;border-style:solid}.mapjs-connector{stroke:#ccc}.mapjs-draw-container{pointer-events:none;overflow:visible}.mapjs-link-hit{pointer-events:all;fill:none}.mapjs-exclamation{right:-.9em;position:absolute;top:-.9em;background-image:url(data:image/png;base64,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);width:2em;height:2em;background-size:2em;background-repeat:no-repeat no-repeat}.mapjs-link{stroke:#4ebf67!important}.mapjs-link[stroke-dasharray*='8']{stroke:#628DB6!important}.mapjs-arrow{fill:#4ebf67!important}.mapjs-node{border:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;background-color:#e5e5e5;color:#324164;margin:0;padding:0}.mapjs-node.activated{background-color:#ccc;border:1px solid transparent}.mapjs-node.activated,.mapjs-node.droppable,.mapjs-node.selected{-webkit-box-shadow:0 3px 3px 0 rgba(0,0,0,.3);-moz-box-shadow:0 3px 3px 0 rgba(0,0,0,.3);box-shadow:0 3px 3px 0 rgba(0,0,0,.3);margin-left:-2px}.mapjs-node.droppable{border:1px solid #fa9b3c}.mapjs-node span{margin:.6rem 1.2rem;border:1px solid transparent}.mapjs-collapsor{position:absolute;left:auto;top:50%;right:-.9333333333rem;margin-top:-.8rem;background-color:#fff;color:red;line-height:1;font-size:1.6rem;font-weight:400}.mapjs-collapsor:before{content:"î…ś"}.collapsed .mapjs-collapsor{color:#4ebf67}.collapsed .mapjs-collapsor:before{content:"î…‡"}.mindmup-node-left .mapjs-collapsor{right:auto;left:-.9333333333rem}.mapjs-node:hover .mapjs-collapsor{font-size:2rem;margin-top:-.9333333333rem;right:-1.1333333333rem}.mindmup-node-left:hover .mapjs-collapsor{left:-1.1333333333rem;right:auto}.mindmup__node--filtered_out{opacity:.2}.mindmup-node-icon{height:1.3333333333rem;display:inline-block;margin-right:.1333333333rem;vertical-align:middle}.mindmup-node-icons{position:absolute;left:-.6666666667rem;top:-1.2rem;white-space:nowrap}.mindmup-node-icons-all{display:inline-block}.mindmup-node-icon-progress{width:.2666666667rem;background-color:#d9d9d9;margin:0 .8rem}.mindmup-node-icon-progress-bar{background-color:#4ebf67;position:relative}.mindmup-node-icon-milestone-shell{vertical-align:middle;display:inline-block}.mindmup-node-icon-milestone-diamond{border-width:1px;border-style:solid;width:8px;height:8px}.mindmup-node-icon-status{font-weight:400;opacity:.5;max-width:2rem;overflow:hidden;font-size:.89em}.mindmup-node-icon-avatar .gravatar{vertical-align:sub}.mindmup-node-icon-is_edited{margin-right:1px;margin-left:-10px;vertical-align:middle;border-radius:5000px;width:1.3333333333rem;height:1.3333333333rem;color:#fff;background-color:#e50026;line-height:1.2;font-size:1.2rem;text-align:center;display:inline-block}.mindmup-node--renaming .mindmup__node-control,.mindmup__print-area .mindmup-node-add-button{display:none}.mindmup-node-icon-is_edited:before{content:"!";font-family:inherit}.mindmup-node-filtered{opacity:.2}.mindmup-node-avatar{position:absolute;top:50%;margin-top:-.7333333333rem}.mindmup-node-left .mindmup-node-avatar{left:auto;right:-2.0666666667rem}.mindmup__node-control{color:#628DB6;background-color:transparent;position:absolute;bottom:100%;height:1rem;width:100%;line-height:1rem;margin-bottom:-.3333333333rem;-webkit-box-shadow:0 3px 3px 0 rgba(0,0,0,.1);-moz-box-shadow:0 3px 3px 0 rgba(0,0,0,.1);box-shadow:0 3px 3px 0 rgba(0,0,0,.1)}.mindmup__node-control .easy-mindmup__icon{width:50%;text-align:center;background-color:#f2f2f2}.mindmup__node-control .easy-mindmup__icon--edit{border-right:solid 1px #e5e5e5}.mindmup__node-control .easy-mindmup__icon--add{border-left:solid 1px #e5e5e5}.mindmup__node-control .easy-mindmup__icon:hover{background-color:#e5e5e5}.mindmup-scheme-project{background-color:#628DB6;color:#fff}.mindmup-scheme-project.activated{background-color:#49739c}.scheme-by-priority .scheme-priority-1{background-color:#fad1d1}.scheme-by-priority .scheme-priority-1.activated,.scheme-by-priority .scheme-priority-2{background-color:#f5a3a3}.scheme-by-priority .scheme-priority-2.activated{background-color:#f07575}.scheme-by-priority .scheme-priority-3{background-color:#d1fada}.scheme-by-priority .scheme-priority-3.activated,.scheme-by-priority .scheme-priority-4{background-color:#a3f5b5}.scheme-by-priority .scheme-priority-4.activated{background-color:#75f090}.scheme-by-priority .scheme-priority-5{background-color:#d1e6fa}.scheme-by-priority .scheme-priority-5.activated,.scheme-by-priority .scheme-priority-6{background-color:#a3cdf5}.scheme-by-priority .scheme-priority-6.activated{background-color:#75b4f0}.scheme-by-priority .scheme-priority-7{background-color:#fae6d1}.scheme-by-priority .scheme-priority-7.activated,.scheme-by-priority .scheme-priority-8{background-color:#f5cca3}.scheme-by-priority .scheme-priority-8.activated{background-color:#f0b375}.scheme-by-priority .scheme-priority-9{background-color:#add7f3}.scheme-by-priority .scheme-priority-9.activated{background-color:#81c1ec}.scheme-by-priority .scheme-priority-10{background-color:#e5e5e5}.scheme-by-priority .scheme-priority-10.activated{background-color:#ccc}.scheme-by-priority .scheme-priority-11{background-color:#fcc}.scheme-by-priority .scheme-priority-11.activated{background-color:#f99}.scheme-by-priority .scheme-priority-12{background-color:#ffb4b4}.scheme-by-priority .scheme-priority-12.activated{background-color:#ff8181}.scheme-by-status .scheme-status-1{background-color:#f9f6a8}.scheme-by-status .scheme-status-1.activated{background-color:#f6f178}.scheme-by-status .scheme-status-2{background-color:#fbe3bd}.scheme-by-status .scheme-status-2.activated{background-color:#f8cf8d}.scheme-by-status .scheme-status-3{background-color:#f3cccf}.scheme-by-status .scheme-status-3.activated{background-color:#e9a3a8}.scheme-by-status .scheme-status-4{background-color:#eabdec}.scheme-by-status .scheme-status-4.activated{background-color:#dd95e1}.scheme-by-status .scheme-status-5{background-color:#d0b8ec}.scheme-by-status .scheme-status-5.activated{background-color:#b590e1}.scheme-by-status .scheme-status-6{background-color:#c9c9f9}.scheme-by-status .scheme-status-6.activated{background-color:#9b9bf4}.scheme-by-status .scheme-status-7{background-color:#c9dcff}.scheme-by-status .scheme-status-7.activated{background-color:#96bbff}.scheme-by-status .scheme-status-8{background-color:#d2f4f0}.scheme-by-status .scheme-status-8.activated{background-color:#a9eae2}.scheme-by-status .scheme-status-9{background-color:#d2ecc9}.scheme-by-status .scheme-status-9.activated{background-color:#b3dfa3}.scheme-by-status .scheme-status-10{background-color:#e1ee9e}.scheme-by-status .scheme-status-10.activated{background-color:#d4e673}.scheme-by-status .scheme-status-11{background-color:#dadace}.scheme-by-status .scheme-status-11.activated{background-color:#c4c4b1}.scheme-by-status .scheme-status-12{background-color:#dbd1c7}.scheme-by-status .scheme-status-12.activated{background-color:#c7b8a8}.scheme-by-tracker .scheme-tracker-1{background-color:#f9f6a8}.scheme-by-tracker .scheme-tracker-1.activated{background-color:#f6f178}.scheme-by-tracker .scheme-tracker-2{background-color:#fbe3bd}.scheme-by-tracker .scheme-tracker-2.activated{background-color:#f8cf8d}.scheme-by-tracker .scheme-tracker-3{background-color:#f3cccf}.scheme-by-tracker .scheme-tracker-3.activated{background-color:#e9a3a8}.scheme-by-tracker .scheme-tracker-4{background-color:#eabdec}.scheme-by-tracker .scheme-tracker-4.activated{background-color:#dd95e1}.scheme-by-tracker .scheme-tracker-5{background-color:#d0b8ec}.scheme-by-tracker .scheme-tracker-5.activated{background-color:#b590e1}.scheme-by-tracker .scheme-tracker-6{background-color:#c9c9f9}.scheme-by-tracker .scheme-tracker-6.activated{background-color:#9b9bf4}.scheme-by-tracker .scheme-tracker-7{background-color:#c9dcff}.scheme-by-tracker .scheme-tracker-7.activated{background-color:#96bbff}.scheme-by-tracker .scheme-tracker-8{background-color:#d2f4f0}.scheme-by-tracker .scheme-tracker-8.activated{background-color:#a9eae2}.scheme-by-tracker .scheme-tracker-9{background-color:#d2ecc9}.scheme-by-tracker .scheme-tracker-9.activated{background-color:#b3dfa3}.scheme-by-tracker .scheme-tracker-10{background-color:#e1ee9e}.scheme-by-tracker .scheme-tracker-10.activated{background-color:#d4e673}.scheme-by-tracker .scheme-tracker-11{background-color:#dadace}.scheme-by-tracker .scheme-tracker-11.activated{background-color:#c4c4b1}.scheme-by-tracker .scheme-tracker-12{background-color:#dbd1c7}.scheme-by-tracker .scheme-tracker-12.activated{background-color:#c7b8a8}.scheme-by-assignee .scheme-assignee-1{background-color:#f9f6a8}.scheme-by-assignee .scheme-assignee-1.activated{background-color:#f6f178}.scheme-by-assignee .scheme-assignee-2{background-color:#fbe3bd}.scheme-by-assignee .scheme-assignee-2.activated{background-color:#f8cf8d}.scheme-by-assignee .scheme-assignee-3{background-color:#f3cccf}.scheme-by-assignee .scheme-assignee-3.activated{background-color:#e9a3a8}.scheme-by-assignee .scheme-assignee-4{background-color:#eabdec}.scheme-by-assignee .scheme-assignee-4.activated{background-color:#dd95e1}.scheme-by-assignee .scheme-assignee-5{background-color:#d0b8ec}.scheme-by-assignee .scheme-assignee-5.activated{background-color:#b590e1}.scheme-by-assignee .scheme-assignee-6{background-color:#c9c9f9}.scheme-by-assignee .scheme-assignee-6.activated{background-color:#9b9bf4}.scheme-by-assignee .scheme-assignee-7{background-color:#c9dcff}.scheme-by-assignee .scheme-assignee-7.activated{background-color:#96bbff}.scheme-by-assignee .scheme-assignee-8{background-color:#d2f4f0}.scheme-by-assignee .scheme-assignee-8.activated{background-color:#a9eae2}.scheme-by-assignee .scheme-assignee-9{background-color:#d2ecc9}.scheme-by-assignee .scheme-assignee-9.activated{background-color:#b3dfa3}.scheme-by-assignee .scheme-assignee-10{background-color:#e1ee9e}.scheme-by-assignee .scheme-assignee-10.activated{background-color:#d4e673}.scheme-by-assignee .scheme-assignee-11{background-color:#dadace}.scheme-by-assignee .scheme-assignee-11.activated{background-color:#c4c4b1}.scheme-by-assignee .scheme-assignee-12{background-color:#dbd1c7}.scheme-by-assignee .scheme-assignee-12.activated{background-color:#c7b8a8}.scheme-by-milestone .scheme-milestone-1{background-color:#f9f6a8}.scheme-by-milestone .scheme-milestone-1.activated{background-color:#f6f178}.scheme-by-milestone .scheme-milestone-2{background-color:#fbe3bd}.scheme-by-milestone .scheme-milestone-2.activated{background-color:#f8cf8d}.scheme-by-milestone .scheme-milestone-3{background-color:#f3cccf}.scheme-by-milestone .scheme-milestone-3.activated{background-color:#e9a3a8}.scheme-by-milestone .scheme-milestone-4{background-color:#eabdec}.scheme-by-milestone .scheme-milestone-4.activated{background-color:#dd95e1}.scheme-by-milestone .scheme-milestone-5{background-color:#d0b8ec}.scheme-by-milestone .scheme-milestone-5.activated{background-color:#b590e1}.scheme-by-milestone .scheme-milestone-6{background-color:#c9c9f9}.scheme-by-milestone .scheme-milestone-6.activated{background-color:#9b9bf4}.scheme-by-milestone .scheme-milestone-7{background-color:#c9dcff}.scheme-by-milestone .scheme-milestone-7.activated{background-color:#96bbff}.scheme-by-milestone .scheme-milestone-8{background-color:#d2f4f0}.scheme-by-milestone .scheme-milestone-8.activated{background-color:#a9eae2}.scheme-by-milestone .scheme-milestone-9{background-color:#d2ecc9}.scheme-by-milestone .scheme-milestone-9.activated{background-color:#b3dfa3}.scheme-by-milestone .scheme-milestone-10{background-color:#e1ee9e}.scheme-by-milestone .scheme-milestone-10.activated{background-color:#d4e673}.scheme-by-milestone .scheme-milestone-11{background-color:#dadace}.scheme-by-milestone .scheme-milestone-11.activated{background-color:#c4c4b1}.scheme-by-milestone .scheme-milestone-12{background-color:#dbd1c7}.scheme-by-milestone .scheme-milestone-12.activated{background-color:#c7b8a8}.scheme-by-progress .scheme-progress-1{background-color:#e5e5e5}.scheme-by-progress .scheme-progress-1.activated{background-color:#ccc}.scheme-by-progress .scheme-progress-2{background-color:#edf9f0}.scheme-by-progress .scheme-progress-2.activated{background-color:#c7ecd0}.scheme-by-progress .scheme-progress-3{background-color:#d3efd9}.scheme-by-progress .scheme-progress-3.activated{background-color:#aee1b9}.scheme-by-progress .scheme-progress-4{background-color:#afe2bb}.scheme-by-progress .scheme-progress-4.activated{background-color:#8ad49b}.scheme-by-progress .scheme-progress-5{background-color:#83d295}.scheme-by-progress .scheme-progress-5.activated{background-color:#5ec475}.scheme-by-progress .scheme-progress-6{background-color:#4ebf67}.scheme-by-progress .scheme-progress-6.activated{background-color:#3aa051}@media print{.gravatar{max-width:100%;height:auto;-webkit-border-radius:5000px;-moz-border-radius:5000px;border-radius:5000px}}.mindmup__print-area--compact{position:relative}.mindmup__print-area--stripped{border-left:2px solid #e6e6e6}.mindmup__print-area .mapjs-node{box-sizing:border-box}.mindmup__print-strip{position:relative;border:1px solid #e6e6e6;overflow:hidden;white-space:nowrap;break-inside:avoid;margin-left:-1px;display:inline-block;background-color:#fff;border-left:0}.mindmup-sidebar .mindmup-sidebar__coworkers--no_id,.mindmup-sidebar .mindmup-sidebar__empty-title{background-color:#ffa;border:1px solid #bfb23f;padding:5px;text-align:center}.mindmup-sidebar__empty-title{display:block;margin-top:10px;box-sizing:border-box}.mindmup-sidebar__resize{position:fixed;width:10px;text-align:center;cursor:col-resize;display:block;z-index:101;background-color:silver;box-sizing:border-box;padding-top:8px;font-size:20px;border-radius:5px}.mindmup-sidebar__root{display:block}.mindmup-sidebar__toggler{position:absolute;top:1px;right:1px;padding:0;z-index:1}.mindmup-sidebar__toggler.active{background:#d94838;color:#fff}.mindmup-sidebar__toggler.easy-mindmup__icon{padding-left:15px}.mindmup-sidebar__toggler.easy-mindmup__icon:before{width:auto}.mindmup-sidebar__container{border-left:1px solid #dfccaf;background:#f9f9f9;padding-left:8px;overflow-y:scroll}.mindmup-sidebar__long-text{position:relative;max-height:100px;overflow:hidden;min-height:30px;border:1px solid #dfccaf}.mindmup-sidebar__long-text__curtain{height:100%;position:absolute;width:100%;top:0;left:1px;border-bottom:1px solid #dfccaf;background:-moz-linear-gradient(top,rgba(255,255,255,.01) 0,rgba(255,255,255,.01) 65%,#fff 100%);background:-webkit-linear-gradient(top,rgba(255,255,255,.01) 0,rgba(255,255,255,.01) 65%,#fff 100%);background:linear-gradient(to bottom,rgba(255,255,255,.01) 0,rgba(255,255,255,.01) 65%,#fff 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#03ffffff', endColorstr='#ffffff', GradientType=0)}.mindmup-sidebar__long-text__content{padding:3px;min-height:25px;width:100%;box-sizing:border-box;max-height:100px}.mindmup-sidebar__long-text__textarea{height:200px}.mindmup-sidebar__long-text--hidden{display:none}.mindmup-sidebar__attribute{display:block;overflow:hidden;margin-top:3px;position:relative}.mindmup-sidebar__attribute .invalid{border:1px solid red;color:red}.mindmup-sidebar__attribute-form-field{max-width:200px!important;padding:1px 1px 1px 5px!important;line-height:normal;float:right;overflow-x:hidden!important}.mindmup-sidebar__attribute-value{max-width:197px!important;width:100%;padding:1px 1px 1px 5px!important;line-height:normal;float:right;margin-top:6px}.mindmup-sidebar__attribute-label-disabled{color:rgba(0,0,0,.38)}.mindmup-sidebar__attribute-label{display:inline-block;width:100px;margin-top:5px}.mindmup-sidebar__attribute .spaceholder{display:none}.mindmup-sidebar__attribute .top-section{margin-bottom:0}.mindmup-sidebar__attribute-full-screen-icon{display:block;float:right;cursor:pointer;margin-top:6px;position:absolute;top:0;right:0}.mindmup-sidebar__attribute .icon-edit{position:absolute;top:0;right:0;cursor:pointer}.mindmup-sidebar__closed_button{width:200px}.mindmup-sidebar__add-comment-button{margin:3px 0 12px}.mindmup-sidebar__add-comment-button a{width:100%;box-sizing:border-box}.mindmup-sidebar__journal-avatar-container{margin-top:6px}.mindmup-sidebar__journal-header{margin-top:-6px}.mindmup-sidebar__journal-notes{margin-top:0;background:#fff;border:1px solid #dfccaf;margin-bottom:0!important;box-sizing:border-box;padding:3px}.mindmup-sidebar__journal-notes p:last-child{margin-bottom:0}.mindmup-sidebar__journal-full-screen-icon{display:block;float:right;cursor:pointer;margin-top:25px;position:absolute;top:0;right:20px}.mindmup-sidebar__journal-timestamp{font-weight:700;font-size:smaller}.mindmup-sidebar__tab{margin-left:1px;margin-right:1px}.mindmup-sidebar__tab header{min-height:36px!important;padding:0 12px 8px!important}.mindmup-sidebar__tab header.open{min-height:44px}.mindmup-sidebar__tab header:not(:hover){color:inherit!important;background-color:unset!important}.mindmup-sidebar__input__name{color:#000;font-family:"Open Sans",sans-serif;width:100%!important}.mindmup-sidebar__input__name .input{max-width:none;text-align:center;font-weight:700;opacity:1;color:#000!important}.mindmup-sidebar__input__name .bottom-section{display:none!important}.mindmup-sidebar__input__name .baseline{background-color:#fff}.mindmup-sidebar .gravatar{max-height:32px;max-width:32px}.mindmup-sidebar .content-wrapper{margin:0 12px 8px!important}.mindmup-sidebar .material-tab{padding:0!important}@font-face{font-family:"Material Icons";src:local("Material Icons"),local("MaterialIcons-Regular"),url(../../../easy_mindmup/fonts/EasyMaterialIcons-Regular.eot);src:local("Material Icons"),local("MaterialIcons-Regular"),url(../../../easy_mindmup/fonts/EasyMaterialIcons-Regular.eot?#iefix) format("embedded-opentype"),url(../../../easy_mindmup/fonts/EasyMaterialIcons-Regular.woff) format("woff"),url(../../../easy_mindmup/fonts/EasyMaterialIcons-Regular.woff2) format("woff2"),url(../../../easy_mindmup/fonts/EasyMaterialIcons-Regular.ttf) format("truetype"),url(../../../easy_mindmup/fonts/EasyMaterialIcons-Regular.svg) format("svg");font-weight:400;font-style:normal}.mindmup-sidebar__attachments-thumbnails>img{display:none}.mindmup-sidebar__attachments-thumbnails{margin:0}
\ No newline at end of file
diff --git a/plugins/easy_wbs/config/locales/hu.yml b/plugins/easy_wbs/config/locales/hu.yml
index d0309d9..c5a661d 100644
--- a/plugins/easy_wbs/config/locales/hu.yml
+++ b/plugins/easy_wbs/config/locales/hu.yml
@@ -4,11 +4,10 @@ hu:
     name:
       easy_wbs_easy_issue_query: Easy WBS
   easy_wbs:
-    button_actions: Egyéb műveletek
-    button_add_comment: Új hozzászólás
+    button_actions: Más tevékenységek
     button_add_child: Alfeladat hozzáadása
     button_add_parent: Szülő feladat hozzáadása
-    button_add_sibling: Feladat hozzáadása
+    button_add_sibling: Testvérfeladat hozzáadása
     button_all_icons: Ikonok
     button_collapse: Összezár
     button_collapse_all: Összes összezárása
@@ -18,22 +17,21 @@ hu:
     button_expand: Kinyit
     button_expand_all: Összes kinyitása
     button_expand_collapse: Kinyit / Összezár
-    button_legend: Jelmagyarázat
+    button_legend: Magyarázat
     button_one_side: Egy oldal
     button_paste: Beillesztés
     button_project_menu: Easy WBS
     button_redo: Visszaállít
-    button_remove_comment: Hozzászólás eltávolítása
     button_remove_node: Ág eltávolítása
-    button_show_links: Kapcsolatok megjelenítése
+    button_show_links: Linkek megjelenítése
     button_undo: Visszavonás
     edit_issue: Feladat szerkesztése
-    error_create: létrehozás  sikertelen
+    error_create: nem lehet létrehozni
     error_delete: nem lehet törölni
     error_update: Frissítés nem sikerült
     errors:
-      not_subtaskable: 'Ez a feladat: "%{task_name}" nem lehet alfeladat a típus beállítása
-        miatt'
+      not_subtaskable: Feladat "%{task_name}" nem lehet alfeladat a beállítás típusa
+        miatt
     free:
       button_upgrade: Szerezze be a teljes verziĂłt
       button_upgrade_href: https://www.easyproject.hu/szoftver
@@ -41,31 +39,31 @@ hu:
       feature_context_menu: Ág tulajdonságának megváltoztatása
       feature_filtering: Ágak szűrése tulajdonság alapján
       header_not_available: Csak teljes verzióban érhető el
-      text_not_available: csak az Easy WBS teljes verziójában elérhető
+      text_not_available: az Easy WBS teljes verziója elérhető
     hotkeys:
       info_mac_metakey: Mac OSX esetén a Ctrl és Cmd billentyűkombináció használható
         olyan rövidítésekre melyeket Ctrl-er jelölünk- néhány böngésző nem engedélyezi
         ezeket a kombinációkat. Ezért például, ha a Cmd + Space nem működik a böngészőjében,
         akkor próbálja meg a Ctrl+ Space kombinációt
       keyboard:
-      - title: Elemek módosítása
+      - title: ág manipulálás
         hotkeys:
         - hotkey: Enter
-          info: Feladat hozzáadása
+          info: Testvér hozzáadása
         - hotkey: Shift+Enter
-          info: Feladat hozzáadása fölé  vagy sortöréssel (elem átnevezésekor)
+          info: Testvér hozzáadása fölé  vagy sortöréssel (ág átnevezésekor)
         - hotkey: Tab vagy Beillesztés
           info: alfeladat hozzáadása
         - hotkey: Shift+Tab
-          info: Szülőfeladat hozzáadása
+          info: Szülő beillesztése
         - hotkey: Space
-          info: Elem átnevezése
+          info: Ág átnevezése
         - hotkey: Shift+Space
-          info: Elem adatainak szerkesztése
+          info: Ág adat szerkesztése
         - hotkey: Backspace vagy Törlés
-          info: Elem eltávolítása
+          info: Ág eltávolítása
         - hotkey: Ctrl+Fel/Le
-          info: Elem elmozdítása fel/le
+          info: Ág elmozdítása fel/le
       - title: Szerkesztés
         hotkeys:
         - hotkey: Ctrl+S
@@ -77,42 +75,40 @@ hu:
         - hotkey: Ctrl+V vagy P
           info: Beillesztés
         - hotkey: U vagy Ctrl+Z
-          info: Visszaállítás
+          info: Visszaállít
         - hotkey: R vagy Ctrl+Y vagy Ctrl+Shift+Z
           info: Redo
       - title: Kiválasztás
         hotkeys:
-        - hotkey: Kurzor billentyűk
-          info: A jelenleg kiválaszott elemhez képest válassza ki a fel/le/bal/jobb
-            irányban lévőt
-        - hotkey: Shift + Kurzor billentyűk
-          info: Elem hozzáadása fel/le/bal/jobb irányban ( hasznos a feladat többszörös
-            kiválasztására)
+        - hotkey: Nyilak
+          info: A jelenleg kiválaszott ágból válassza ki a fel/le/bal/jobb
+        - hotkey: Shift + Nyilak
+          info: Ág hozzáadása fel/le/bel/jobb ( hasznos a testvér többszörös kiválasztása)
         - hotkey: "{"
-          info: Jelenlegi elem és az alatta lévő fastruktúra elemeinek kiválasztása
+          info: Többszörös kiválasztása a jelenlegi ágnak és a maradé alfának alatta
         - hotkey: "["
-          info: Jelenlegi elem alatt lévő fastruktúra elemeinek kiválasztása(maga
-            az elem nem kerül kijelölésre)
+          info: Többszörös kiválasztása az alfának amelyik a jelenlegi ág alatt van
+            (nem az ágat magát)
         - hotkey: "="
-          info: A jelenlegi elemmel egy szinten lévő elemek együttes kiválasztása
-            (amelyek azonos szülővel rendelkeznek)
+          info: A jelenlegi ág testvéreinek többszörös kiválasztása ( amelyek ugyanazza
+            a szülővel rendelkeznek)
         - hotkey: "."
-          info: Többszörös kiválasztás törlése és válassza ki a jelenlegi elemet ismét
+          info: Többszörös kiválasztás törélese és válassza ki a jelenlegi ágat ismét
         - hotkey: 1 - 9
-          info: 'Egy kiválasztott szinten az összes elem kiválasztása (pl. 1: az összes
-            első szintű elem kiválasztása)'
+          info: Egy kiválaszott szinten az összes ág kiválasztása (pl. 1 az összes
+            eslő szintes ágak kiválasztása)
       - title: Navigáció és  képernyő
         hotkeys:
         - hotkey: "/ vagy F"
-          info: Elem kibontása vagy összezárása (kibontja vagy bezárja az alfeladatokat
+          info: Ág kinyitása vagy összezárása (kinyitja vagy bezárja az alfeladatot
             )
         - hotkey: Ctrl + vagy Z
           info: Nagyítás
         - hotkey: Ctrl - vagy Shift Z
-          info: Kicsinyítés
+          info: Kizoomolás
         - hotkey: Esc, 0, Ctrl+0
-          info: Térkép nézet alaphelyzetbe állítása - Gyökérelem kiválasztása és a
-            képernyő közepére hozása
+          info: Térkép nézet újraindítása - Gyökérág kiválasztása és a képernyő közepére
+            hozása
       mouse:
       - action: Térkép áthelyezése
         gesture: kattintás és  behúzás a központi ágba, kattintás és behúzás a háttér
@@ -140,37 +136,32 @@ hu:
       - action: Nyitott feladatok vagy projekt különböző ablakban
         gesture: Alt+click
       title_key_shortcuts: Billentyű műveletek
-      title_mouse_shortcuts: Egér műveletek
-      title_shortcuts: Gyorsparancsok
+      title_mouse_shortcuts: Egér műveltek
+      title_shortcuts: Lerövidítések
     info_all_saved: Sikeresen mentve
     info_any_failed: Kérések egy része sikertelen
     info_no_permission: Nem rendelkezik a szükséges jogosultsággal a művelet végrehajtásához
-    label_additional_data: További adatok
-    label_all: Mind
-    label_basic_data: Alap adatok
-    label_color_by: Színkódolás szempontja
-    label_go_to: Ugrás feladathoz
+    label_color_by: SzĂ­nezve
+    label_go_to: Megy!
     label_or: vagy
     last_state_modal:
       label_differencies: Különbségek a szerveren
       message_changed: különböző attribútumokkal rendelkezik (böngésző => server ({{változások}})
       message_missing: hiányzik a szülő feladatból "{{from}}"
       message_moved: alfeladata "{{to}}", nem "{{from}}"
-      message_present: 'alfeladataként jelenik meg az alábbi feladatnak: "{{to}}"'
+      message_present: alfeladatként kelenik meg a "{{to}}"-nak
       text_reload_appeal: Szeretné újratölteni az állapotot a szerverről?
       title: A WBS utolsó kliens állapota különbözik a szerver állapottól
-    name_an_entity_first: Először adjon egy nevet
     reload_modal:
       label_errors: Hibák
       text_reload_appeal: Szeretné figyelmen kívül hagyni a nem mentett elemeket és
-        újratölteni a szerverről?
+        újratölteni a serverről?
       title: Nem sikerült megfelelően menteni a WBS-t
     stored_modal:
       text_load_appeal: Szeretné betölteni? A friss állapot helyett a szerverről?
       title: Nem mentett állapotot talált
-    text_issue_not_saved: Feladat még nincs mentve. Először  mentsen.
-    warning_delete_node: Biztosan törölni akarja a(z) {{name}}  elemet és annak alsóbb
-      szintű elemeit?
+    warning_delete_node: Biztosan törölni akarja a {{name}}  csomópontot és minden
+      származékát?
     warning_not_saved: nem megfelelő mentés
   heading_easy_wbs_issues: Easy WBS
   label_filter_group_easy_wbs_easy_issue_query: Feladatok
@@ -178,4 +169,3 @@ hu:
     easy_issue_query: Feladatok
   project_default_page:
     easy_wbs: Easy WBS
-  project_module_easy_wbs: Easy WBS
diff --git a/plugins/easy_wbs/config/locales/ja.yml b/plugins/easy_wbs/config/locales/ja.yml
index 82947d5..55af76a 100644
--- a/plugins/easy_wbs/config/locales/ja.yml
+++ b/plugins/easy_wbs/config/locales/ja.yml
@@ -4,35 +4,31 @@ ja:
     name:
       easy_wbs_easy_issue_query: Easy WBS
   easy_wbs:
-    button_actions: その他の操作
-    button_add_comment: コメントを追加する
-    button_add_child: 子ノードの追加
-    button_add_parent: 親ノードの追加
-    button_add_sibling: 兄弟ノードの追加
+    button_add_child: 子を追加
+    button_add_parent: 親を追加
+    button_add_sibling: 兄弟を追加
     button_all_icons: アイコン
     button_collapse: 折りたたみ
-    button_collapse_all: すべて折りたたみ
+    button_collapse_all: すべて展開
     button_cut: 切り取り
-    button_display: 画面の表示設定
-    button_edit_data: データの編集
+    button_edit_data: ノードの詳細を編集
     button_expand: 展開
     button_expand_all: すべて展開
     button_expand_collapse: 展開/折りたたみ
-    button_legend: 凡例
+    button_legend: 判例
     button_one_side: 片側
     button_paste: 貼り付け
-    button_project_menu: Easy WBS
+    button_project_menu: WBS
     button_redo: やり直し
-    button_remove_comment: コメントを削除する
     button_remove_node: ノードを削除
     button_show_links: リンクを表示
     button_undo: 元に戻す
-    edit_issue: タスクの編集
+    edit_issue: チケットを編集
     error_create: 作成できませんでした
     error_delete: 削除できませんでした
     error_update: 更新できませんでした
     errors:
-      not_subtaskable: トラッカーの設定のため、タスク "%{task_name}" サブタスクにすることはできません
+      not_subtaskable: "%{task_name} は子チケットにできません"
     free:
       button_upgrade: フルバージョンを入手
       button_upgrade_href: https://www.easyredmine.com/redmine-wbs-plugin
@@ -40,9 +36,9 @@ ja:
       feature_context_menu: ノードのプロパティを変更
       feature_filtering: プロパティ毎にノードをフィルタ
       header_not_available: フルバージョンでのみ入手可能
-      text_not_available: 'Easy WBSのフルバージョンでのみ利用可能'
+      text_not_available: 'WBSプラグインのフルバージョンで入手可能:'
     hotkeys:
-      info_mac_metakey: Mac OS Xの場合、ControlキーとCommandキーは、以下でCtrlと表示しているショートカットに使用可能です。しかしブラウザによっては、無効なキーの組み合わせがあります。そのため、例えばCommand+Spaceが効かない場合は、Control+Spaceを試してください。
+      info_mac_metakey: Mac OS Xの場合、ControlキーとCommandキーは、以下でCtrlと表示しているショートカットで使用可能です。しかしブラウザによっては、無効なキーの組み合わせがあります。そのため、例えばCommand+Spaceが効かない場合は、Control+Spaceを試してください。
       keyboard:
       - title: ノード操作
         hotkeys:
@@ -79,9 +75,9 @@ ja:
       - title: ノード選択
         hotkeys:
         - hotkey: 矢印キー
-          info: 現在選択中のノードの上下左右のノードを選択する
+          info: ノードの選択を、上下左右に移動
         - hotkey: Shift+矢印キー
-          info: 現在選択しているノードに上下左右のノードを追加(兄弟ノードを複数選択するときに便利です)
+          info: ノードの選択範囲を、上下左右に拡大(兄弟を複数選択するときに便利です)
         - hotkey: "{"
           info: 選択中ノードと、その子すべてを選択
         - hotkey: "["
@@ -92,34 +88,34 @@ ja:
           info: 複数選択を解除し、現在のノードだけを選択
         - hotkey: 1 - 9
           info: 指定された階層のノードをすべて選択(1の場合、第一階層のノードがすべて選択されます)
-      - title: ナビゲーションと画面
+      - title: 表示
         hotkeys:
         - hotkey: "/ または F"
-          info: ノードの展開/折りたたみ(子ノードをフォルダーに入れる/から出す)
+          info: ノードの展開/折りたたみ
         - hotkey: Ctrl+(+) または Z
-          info: ズームイン
+          info: 拡大
         - hotkey: Ctrl+(-) または Shift+Z
-          info: ズームアウト
+          info: 縮小
         - hotkey: Esc または 0 または Ctrl+0
-          info: マップ表示のリセット - ルートノードを画面中央に移動する
+          info: マップ表示を元に戻す - ルートノードを画面中央に異動します
       mouse:
       - action: マップを移動
-        gesture: 中央のノードをクリック、背景をクリック・アンド・ドラッグ、トラックパッド/タッチパッドをスクロールする
+        gesture: ルートノードを選択してドラッグ
       - action: ノード選択
-        gesture: ノードをクリック、またはタップする
+        gesture: ノードをクリック
       - action: ノードの複数選択
         gesture: Shift+クリック
       - action: ノードの並べ替え
-        gesture: 兄弟ノードを水平方向に並べ替え位置に近いところ迄ドラッグする。並べ替え中には黒い矢印が表示される
+        gesture: ノードをもっていきたいところまでドラッグして、黒い矢印が表示されたら離す
       - action: ノードの位置を手動で調整
-        gesture: 黒い矢印の表示が消えるところまで、ノードをドラッグしてください。並べ替えの黒い矢印が表示されている時に強制的に手動で位置を決めるには、Shiftを押しながらドラッグしてください。注意:第一階層のノードはどの方向にも自由に配置できますが、それより下位階層のノードは、ルートに対して親の向きにしか配置できません。
+        gesture: ノードをもっていきたいところまでドラッグして離す(黒い矢印が表示されていないことを確認してください)。黒い矢印が表示される場所に移動したい場合は、Shiftを押しながらドラッグしてください。注意:第一階層のノードはどの方向にも自由に配置できますが、それより下位階層のノードは、ルート⇒親の向きにしか配置できません。
       - action: ノード名の変更
-        gesture: ダブルクリックまたはダブルタップ
-      - action: 操作に関するコンテキスト・メニューを表示する
-        gesture: ノードを右クリックする(マウスで)、または背景をダブルタップする、または(タッチパネル上に)表示する
+        gesture: ダブルクリック
+      - action: コンテキストメニューを表示
+        gesture: ノードを右クリック
       - action: ノードの親を変更
-        gesture: ドラッグ&ドロップ(無限ループとなる関係にはできません)
-      - action: タスク/プロジェクトを別ウィンドウで表示
+        gesture: ドラッグ&ドロップ(循環リンクとなる関係にはできません)
+      - action: チケット/プロジェクトを別ウィンドウで表示
         gesture: Alt+クリック
       title_key_shortcuts: キーボード操作
       title_mouse_shortcuts: マウスとタッチ操作
@@ -127,9 +123,6 @@ ja:
     info_all_saved: すべて正しく保存されました
     info_any_failed: いくつかの要求に失敗しました:
     info_no_permission: その操作をする権限がありません
-    label_additional_data: 追加データ
-    label_all: すべて
-    label_basic_data: 基本データ
     label_color_by: 色の塗り分け
     label_go_to: '次へ:'
     label_or: または
@@ -140,22 +133,19 @@ ja:
       message_moved: は {from} ではなく {to} の子ノードです
       message_present: は {to} の子ノードです
       text_reload_appeal: サーバからステータスを再読込みしますか?
-      title: 最新のWBSクライアント・データ・ステータスがサーバー・でーた・ステータスと異なります
-    name_an_entity_first: まずはじめにエンティティーの名前をつける
+      title: WBSのクライアントのステータスがサーバのデータと異なります
     reload_modal:
       label_errors: エラー
       text_reload_appeal: 保存されていない変更を破棄して、サーバのデータを再読み込みしますか?
       title: WBSを正しく保存できませんでした
     stored_modal:
-      text_load_appeal: サーバーからの最新データの代わりにこれを読み込みますか?
+      text_load_appeal: サーバから最新のデータを読み込みますか?
       title: 保存されていないステータスが見つかりました
-    text_issue_not_saved: 懸案事項が保存されていません。まず保存してください。
     warning_delete_node: 本当に {{name}} とその子孫チケットを削除してもよろしいですか?
     warning_not_saved: は正しく保存されません
   heading_easy_wbs_issues: Easy WBS
   label_filter_group_easy_wbs_easy_issue_query: ă‚żă‚ąă‚Ż
   label_filter_group_easy_wbs:
-    easy_issue_query: ă‚żă‚ąă‚Ż
+    easy_issue_query: チケット
   project_default_page:
     easy_wbs: Easy WBS
-  project_module_easy_wbs: Easy WBS
diff --git a/plugins/easy_wbs/init.rb b/plugins/easy_wbs/init.rb
index a7a0195..2f8159a 100644
--- a/plugins/easy_wbs/init.rb
+++ b/plugins/easy_wbs/init.rb
@@ -2,11 +2,11 @@ Redmine::Plugin.register :easy_wbs do
   name 'Easy WBS plugin'
   author 'Easy Software Ltd'
   description 'new WBS tree hierarchy generator'
-  version '1.9'
+  version '1.5'
   url 'www.easyredmine.com'
   author_url 'www.easysoftware.cz'
 
-  requires_redmine_plugin :easy_mindmup, version_or_higher: '1.4'
+  requires_redmine_plugin :easy_mindmup, version_or_higher: '1.0'
 
   if Redmine::Plugin.installed?(:easy_extensions)
     depends_on [:easy_mindmup]
diff --git a/plugins/easy_wbs/lib/easy_wbs/redmine_patch/controllers/queries_controller_patch.rb b/plugins/easy_wbs/lib/easy_wbs/redmine_patch/controllers/queries_controller_patch.rb
index 6c75d86..ca8efa8 100644
--- a/plugins/easy_wbs/lib/easy_wbs/redmine_patch/controllers/queries_controller_patch.rb
+++ b/plugins/easy_wbs/lib/easy_wbs/redmine_patch/controllers/queries_controller_patch.rb
@@ -2,25 +2,19 @@ module EasyWbs
   module QueriesControllerPatch
 
     def self.included(base)
-      base.send(:include, InstanceMethods)
-
-      base.class_eval do
-        if method_defined?(:query_class) || private_method_defined?(:query_class)
-          alias_method_chain :query_class, :easy_wbs
-        end
-      end
+      base.prepend(InstanceMethods)
     end
 
     module InstanceMethods
 
       # Redmine return only direct sublasses but
       # Wbs query inherit from IssueQuery
-      def query_class_with_easy_wbs
+      def query_class
         case params[:type]
         when 'EasyWbs::IssueQuery'
           EasyWbs::IssueQuery
         else
-          query_class_without_easy_wbs
+          super
         end
       end
 
diff --git a/plugins/easy_wbs/lib/easy_wbs/redmine_patch/helpers/application_helper_patch.rb b/plugins/easy_wbs/lib/easy_wbs/redmine_patch/helpers/application_helper_patch.rb
index e0ab00f..6eb1ada 100644
--- a/plugins/easy_wbs/lib/easy_wbs/redmine_patch/helpers/application_helper_patch.rb
+++ b/plugins/easy_wbs/lib/easy_wbs/redmine_patch/helpers/application_helper_patch.rb
@@ -2,9 +2,6 @@ module EasyWbs
   module ApplicationHelperPatch
 
     def self.included(base)
-      base.extend(ClassMethods)
-      base.send(:include, InstanceMethods)
-
       base.class_eval do
 
         def link_to_project_with_easy_wbs(project, options = {})
@@ -14,12 +11,6 @@ module EasyWbs
       end
     end
 
-    module InstanceMethods
-    end
-
-    module ClassMethods
-    end
-
   end
 end
 
diff --git a/plugins/easy_wbs/lib/easy_wbs/redmine_patch/models/project_patch.rb b/plugins/easy_wbs/lib/easy_wbs/redmine_patch/models/project_patch.rb
index 86a82e6..67467aa 100644
--- a/plugins/easy_wbs/lib/easy_wbs/redmine_patch/models/project_patch.rb
+++ b/plugins/easy_wbs/lib/easy_wbs/redmine_patch/models/project_patch.rb
@@ -3,12 +3,6 @@ module EasyWbs
 
     def self.included(base)
       base.send(:include, InstanceMethods) unless base.respond_to?(:assignable_users_including_all_subprojects)
-
-      base.class_eval do
-
-
-      end
-
     end
 
     module InstanceMethods
diff --git a/plugins/easy_wbs/spec/features/jasmine_spec.rb b/plugins/easy_wbs/spec/features/jasmine_spec.rb
index 2ba6df8..6f26ae3 100644
--- a/plugins/easy_wbs/spec/features/jasmine_spec.rb
+++ b/plugins/easy_wbs/spec/features/jasmine_spec.rb
@@ -1,31 +1,22 @@
-require File.expand_path('../../../../easyproject/easy_plugins/easy_extensions/test/spec/spec_helper', __FILE__)
-
-RSpec.feature 'Jasmine', logged: :admin, js: true, js_wait: true do
-
-  let(:superproject) {
-    FactoryGirl.create(:project, add_modules: ['easy_wbs'], number_of_issues: 3)
-  }
-  let(:subproject) {
-    FactoryGirl.create(:project, parent_id: superproject.id, number_of_issues: 3)
-  }
-  let(:subissues) {
-    FactoryGirl.create_list(:issue, 3, parent_issue_id: superproject.issues[0].id, project_id: superproject.id)
-  }
-
-  around(:each) do |example|
-    with_settings(rest_api_enabled: 1) {
-      with_easy_settings(easy_wbs_no_sidebar: 1) { example.run }
-    }
-  end
-
-  describe 'WBS' do
-    it 'should not fail' do
-      visit project_easy_wbs_index_path(superproject, run_jasmine_tests: true)
-      wait_for_ajax
-      expect(page).to have_css('.jasmine-bar')
-      result = page.evaluate_script('jasmine.ysyInstance.tests.parseResult();')
-      expect(result).to eq('success')
-    end
-  end
-
-end
+# require File.expand_path('../../../../easyproject/easy_plugins/easy_extensions/test/spec/spec_helper', __FILE__)
+#
+# RSpec.feature 'Jasmine', logged: :admin, js: true, js_wait: true do
+#
+#   let(:superproject) {
+#     FactoryGirl.create(:project, add_modules: ['easy_wbs'], number_of_issues: 3)
+#   }
+#
+#   around(:each) do |example|
+#     with_settings(rest_api_enabled: 1) { example.run }
+#   end
+#
+#   describe 'WBS' do
+#     it 'should not fail' do
+#       visit project_easy_wbs_index_path(superproject, run_jasmine_tests: true)
+#       wait_for_ajax
+#       expect(page).to have_css('.jasmine-bar')
+#       result = page.evaluate_script('ysy.pro.test.parseResult();')
+#       expect(result).to eq('success')
+#     end
+#   end
+# end
diff --git a/plugins/redmine_agile/.drone.yml b/plugins/redmine_agile/.drone.yml
deleted file mode 100644
index 19242fc..0000000
--- a/plugins/redmine_agile/.drone.yml
+++ /dev/null
@@ -1,78 +0,0 @@
-pipeline:
-  tests:
-    image: redmineup/redmine_agile
-    pull: true
-    commands:
-      - service postgresql start && service mysql start && sleep 5
-      - export PATH=~/.rbenv/shims:$PATH
-      - export CODEPATH=`pwd`
-      - /root/run_for.sh redmine_agile+${LICENSE} ${RUBY_VER} ${DB} ${REDMINE} ${PLUGINS}
-matrix:
-  include:
-    - RUBY_VER: ruby-2.2.6
-      LICENSE: pro
-      DB: mysql
-      REDMINE: redmine-3.3
-    - RUBY_VER: ruby-2.2.6
-      LICENSE: light
-      DB: mysql
-      REDMINE: redmine-3.3
-    - RUBY_VER: ruby-2.2.6
-      LICENSE: pro
-      DB: pg
-      REDMINE: redmine-3.3
-    - RUBY_VER: ruby-2.2.6
-      LICENSE: light
-      DB: pg
-      REDMINE: redmine-3.3
-    - RUBY_VER: ruby-2.2.6
-      LICENSE: pro
-      DB: mysql
-      REDMINE: redmine-3.0
-    - RUBY_VER: ruby-2.4.1
-      DB: mysql
-      LICENSE: light
-      REDMINE: redmine-3.4
-    - RUBY_VER: ruby-2.4.1
-      DB: pg
-      LICENSE: pro
-      REDMINE: redmine-3.4
-    - RUBY_VER: ruby-2.4.1
-      DB: mysql
-      LICENSE: pro
-      REDMINE: redmine-3.4
-      PLUGINS: redmine_checklists+pro
-      PLUGIN: redmine_agile
-      COVERAGE_EXPORT: 1
-    - RUBY_VER: ruby-1.9.3
-      DB: pg
-      LICENSE: pro
-      REDMINE: redmine-2.6
-    - RUBY_VER: ruby-1.9.3
-      DB: pg
-      LICENSE: pro
-      REDMINE: redmine-3.3
-    - RUBY_VER: ruby-1.9.3
-      DB: mysql
-      LICENSE: pro
-      REDMINE: redmine-3.3
-    - RUBY_VER: ruby-1.9.3
-      DB: pg
-      LICENSE: light
-      REDMINE: redmine-3.3
-    - RUBY_VER: ruby-2.4.1
-      DB: mysql
-      LICENSE: pro
-      REDMINE: redmine-trunk
-    - RUBY_VER: ruby-2.4.1
-      DB: mysql
-      LICENSE: light
-      REDMINE: redmine-trunk
-    - RUBY_VER: ruby-2.4.1
-      DB: pg
-      LICENSE: pro
-      REDMINE: redmine-trunk
-    - RUBY_VER: ruby-2.2.6
-      DB: mysql
-      LICENSE: pro
-      REDMINE: redmine-trunk
\ No newline at end of file
diff --git a/plugins/redmine_agile/README.rdoc b/plugins/redmine_agile/README.rdoc
old mode 100755
new mode 100644
diff --git a/plugins/redmine_agile/app/controllers/agile_boards_controller.rb b/plugins/redmine_agile/app/controllers/agile_boards_controller.rb
old mode 100755
new mode 100644
index 8d39544..2f04dd2
--- a/plugins/redmine_agile/app/controllers/agile_boards_controller.rb
+++ b/plugins/redmine_agile/app/controllers/agile_boards_controller.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -22,8 +22,14 @@ class AgileBoardsController < ApplicationController
 
   menu_item :agile
 
-  before_action :find_issue, :only => [:update, :issue_tooltip, :inline_comment]
-  before_action :find_optional_project, :only => [:index, :create_issue]
+  before_action :find_issue, only: [:update, :issue_tooltip, :inline_comment, :edit_issue, :update_issue, :agile_data]
+  before_action :find_optional_project, only: [
+                                               :index,
+                                               :create_issue,
+                                              ]
+  before_action :authorize, except: [:index, :edit_issue, :update_issue]
+
+  accept_api_auth :agile_data
 
   helper :issues
   helper :journals
@@ -46,6 +52,7 @@ class AgileBoardsController < ApplicationController
   include IssuesHelper
   helper :timelog
   include RedmineAgile::AgileHelper
+  helper :checklists if RedmineAgile.use_checklist?
 
   def index
     retrieve_agile_query
@@ -53,7 +60,7 @@ class AgileBoardsController < ApplicationController
       @issues = @query.issues
       @issue_board = @query.issue_board
       @board_columns = @query.board_statuses
-      @swimlanes = @query.swimlanes
+      @allowed_statuses = statuses_allowed_for_create
 
       respond_to do |format|
         format.html { render :template => 'agile_boards/index', :layout => !request.xhr? }
@@ -76,6 +83,7 @@ class AgileBoardsController < ApplicationController
     @issue.init_journal(User.current)
     @issue.safe_attributes = auto_assign_on_move? ? params[:issue].merge(:assigned_to_id => User.current.id) : params[:issue]
     checking_params = params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params
+
     saved = checking_params['issue'] && checking_params['issue'].inject(true) do |total, attribute|
       if @issue.attributes.include?(attribute.first)
         total &&= @issue.attributes[attribute.first].to_i == attribute.last.to_i
@@ -83,7 +91,7 @@ class AgileBoardsController < ApplicationController
         total &&= true
       end
     end
-    call_hook(:controller_agile_boards_update_before_save, { :params => params, :issue => @issue})
+    call_hook(:controller_agile_boards_update_before_save, { params: params, issue: @issue})
     @update = true
     if saved && @issue.save
       call_hook(:controller_agile_boards_update_after_save, { :params => params, :issue => @issue})
@@ -95,16 +103,6 @@ class AgileBoardsController < ApplicationController
       end if params[:positions]
 
       @inline_adding = params[:issue][:notes] || nil
-      if Redmine::VERSION.to_s > '2.4'
-        if current_status = @query.board_statuses.detect{ |st| st == @issue.status }
-          @error_msg =  l(:lable_agile_wip_limit_exceeded) if current_status.over_wp_limit?
-          @wp_class = current_status.wp_class
-        end
-
-        if @old_status = @query.board_statuses.detect{ |st| st == old_status }
-          @wp_class_for_old_status = @old_status.wp_class
-        end
-      end
 
       respond_to do |format|
         format.html { render(:partial => 'issue_card', :locals => {:issue => @issue}, :status => :ok, :layout => nil) }
@@ -114,52 +112,7 @@ class AgileBoardsController < ApplicationController
         messages = @issue.errors.full_messages
         messages = [l(:text_agile_move_not_possible)] if messages.empty?
         format.html {
-          render :json => messages, :status => :fail, :layout => nil
-        }
-      end
-    end
-  end
-  def create_issue
-    if !User.current.allowed_to?(:add_issues, @project) || params[:subject].blank?
-      messages = [l(:notice_not_authorized)]
-      respond_to do |format|
-        format.html {
-          render :json => messages, :status => :fail, :layout => nil
-        }
-      end
-      return
-    end
-    retrieve_agile_query_from_session
-    @update = true
-    @issue = Issue.new(:subject => params[:subject].strip, :project => @project,
-      :tracker => @project.trackers.first, :author => User.current, :status_id => params[:status_id])
-    begin
-      if @issue.save(:validate => false)
-        if Redmine::VERSION.to_s > '2.4'
-          if current_status = @query.board_statuses.detect{ |st| st == @issue.status }
-            @error_msg =  l(:lable_agile_wip_limit_exceeded) if current_status.over_wp_limit?
-            @wp_class = current_status.wp_class
-          end
-        end
-        @not_in_scope = !@query.issues.include?(@issue)
-        respond_to do |format|
-          format.html { render(:partial => 'issue_card', :locals => {:issue => @issue}, :status => :ok, :layout => nil) }
-        end
-      else
-        respond_to do |format|
-          messages = @issue.errors.full_messages
-          messages = [l(:text_agile_move_not_possible)] if messages.empty?
-          format.html {
-            render :json => messages, :status => :fail, :layout => nil
-          }
-        end
-      end
-    rescue
-      respond_to do |format|
-        messages = @issue.errors.full_messages
-        messages = [l(:text_agile_create_issue_error)] if messages.empty?
-        format.html {
-          render :json => messages, :status => :fail, :layout => nil
+          render json: messages, status: :unprocessable_entity, layout: nil
         }
       end
     end
@@ -173,6 +126,16 @@ class AgileBoardsController < ApplicationController
     render 'inline_comment', :layout => nil
   end
 
+  def agile_data
+    @agile_data = @issue.agile_data
+    return render_404 unless @agile_data
+
+    respond_to do |format|
+      format.any { head :ok }
+      format.api { }
+    end
+  end
+
   private
 
   def auto_assign_on_move?
@@ -181,4 +144,15 @@ class AgileBoardsController < ApplicationController
       @issue.status_id != params[:issue]['status_id'].to_i
   end
 
+  def statuses_allowed_for_create
+    issue = Issue.new(project: @project)
+    issue.tracker = issue_tracker(issue)
+    issue.new_statuses_allowed_to
+  end
+
+  def issue_tracker(issue)
+    return issue.allowed_target_trackers.first if issue.respond_to?(:allowed_target_trackers)
+    return @project.trackers.first if @project
+    nil
+  end
 end
diff --git a/plugins/redmine_agile/app/controllers/agile_charts_controller.rb b/plugins/redmine_agile/app/controllers/agile_charts_controller.rb
index 563c9ae..5fd0ebe 100644
--- a/plugins/redmine_agile/app/controllers/agile_charts_controller.rb
+++ b/plugins/redmine_agile/app/controllers/agile_charts_controller.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -59,17 +59,22 @@ class AgileChartsController < ApplicationController
   def render_chart
     if @version
       @issues = @version.fixed_issues
-      options = {:date_from => @version.start_date,
-                 :date_to => [@version.due_date,
-                              @issues.maximum(:due_date),
-                              @issues.maximum(:updated_on)].compact.max,
-                 :due_date => @version.due_date || @issues.maximum(:due_date) || @issues.maximum(:updated_on)}
+      options = { date_from: @version.start_date,
+                  date_to: [@version.due_date,
+                            @issues.maximum(:due_date),
+                            @issues.maximum(:updated_on)].compact.max,
+                  due_date: @version.due_date || @issues.maximum(:due_date) || @issues.maximum(:updated_on),
+                  chart_unit: params[:chart_unit] }
       @chart = params[:chart]
     else
       retrieve_charts_query
-      @query.date_to ||= Date.today
-      @issues = Issue.visible.where(@query.statement)
-      options = {:date_from => @query.date_from, :date_to => @query.date_to}
+      @issues = Issue.visible
+      @issues = @issues.joins(:fixed_version) if @query.filters.keys.include?('version_status')
+      @issues = @issues.where(@query.statement)
+      options = { date_from: @query.date_from,
+                  date_to: @query.date_to,
+                  interval_size: @query.interval_size,
+                  chart_unit: @query.chart_unit }
     end
     render_data(options)
   end
@@ -80,31 +85,15 @@ class AgileChartsController < ApplicationController
   private
 
   def render_data(options = {})
-    case @chart
-    when 'lead_time'
-      data = RedmineAgile::LeadTimeChart.data(@issues, options)
-    when 'average_lead_time'
-      data = RedmineAgile::AverageLeadTimeChart.data(@issues, options)
-    when 'burnup'
-      data = RedmineAgile::BurnupChart.data(@issues, options)
-    when 'trackers_cumulative_flow'
-      data = RedmineAgile::TrackersCumulativeFlowChart.data(@issues, options)
-    when 'cumulative_flow'
-      data = RedmineAgile::CumulativeFlowChart.data(@issues, options)
-    when 'issues_velocity'
-      data = RedmineAgile::VelocityChart.data(@issues, options)
-    when 'work_burnup_hours'
-      data = RedmineAgile::WorkBurnupChart.data(@issues, options.merge(:estimated_unit => 'hours'))
-    when 'work_burnup_sp'
-      data = RedmineAgile::WorkBurnupChart.data(@issues, options.merge(:estimated_unit => 'story_points'))
-    when 'work_burndown_hours'
-      data = RedmineAgile::WorkBurndownChart.data(@issues, options.merge(:estimated_unit => 'hours'))
-    when 'work_burndown_sp'
-      data = RedmineAgile::WorkBurndownChart.data(@issues, options.merge(:estimated_unit => 'story_points'))
-    else
-      data = RedmineAgile::BurndownChart.data(@issues, options)
+    agile_chart = RedmineAgile::Charts::AGILE_CHARTS[@chart]
+    data = agile_chart[:class].data(@issues, options) if agile_chart
+
+    if data
+      data[:chart] = @chart
+      data[:chart_unit] = options[:chart_unit]
+      return render json: data
     end
-    return render :json => data if data
+
     raise ActiveRecord::RecordNotFound
   end
 
@@ -115,28 +104,37 @@ class AgileChartsController < ApplicationController
   end
 
   def retrieve_charts_query
-    if params[:set_filter] || session[:agile_charts_query].nil? || session[:agile_charts_query][:project_id] != (@project ? @project.id : nil)
+    if params[:query_id].present?
+      @query = AgileChartsQuery.find(params[:query_id])
+      raise ::Unauthorized unless @query.visible?
+      @query.project = @project
+    elsif params[:set_filter] || session[:agile_charts_query].nil? || session[:agile_charts_query][:project_id] != (@project ? @project.id : nil)
       # Give it a name, required to be valid
-      @query = AgileChartsQuery.new(:name => "_")
+      @query = AgileChartsQuery.new(:name => '_')
       @query.project = @project
       @query.build_from_params(params)
-      session[:agile_charts_query] = {:project_id => @query.project_id,
-                                      :filters => @query.filters,
-                                      :group_by => @query.group_by,
-                                      :column_names => @query.column_names,
-                                      :date_from => @query.date_from,
-                                      :date_to => @query.date_to}
+      session[:agile_charts_query] = { project_id: @query.project_id,
+                                       filters: @query.filters,
+                                       group_by: @query.group_by,
+                                       column_names: @query.column_names,
+                                       date_from: @query.date_from,
+                                       date_to: @query.date_to,
+                                       interval_size: @query.interval_size,
+                                       chart: @query.chart,
+                                       chart_unit: @query.chart_unit }
     else
       # retrieve from session
-      @query = AgileChartsQuery.new(:name => "_",
-        :filters => session[:agile_charts_query][:filters] || session[:agile_query][:filters],
-        :group_by => session[:agile_charts_query][:group_by],
-        :column_names => session[:agile_charts_query][:column_names],
-        :date_from => session[:agile_charts_query][:date_from],
-        :date_to => session[:agile_charts_query][:date_to]
-        )
+      @query = AgileChartsQuery.new(name: '_',
+                                    filters: session[:agile_charts_query][:filters] || session[:agile_query][:filters],
+                                    group_by: session[:agile_charts_query][:group_by],
+                                    column_names: session[:agile_charts_query][:column_names],
+                                    date_from: session[:agile_charts_query][:date_from],
+                                    date_to: session[:agile_charts_query][:date_to],
+                                    interval_size: session[:agile_charts_query][:interval_size],
+                                    chart: session[:agile_charts_query][:chart],
+                                    chart_unit: session[:agile_charts_query][:chart_unit])
       @query.project = @project
     end
-    @chart = params[:chart] || "issues_burndown"
+    @chart = params[:chart] || @query.chart
   end
 end
diff --git a/plugins/redmine_agile/app/controllers/agile_colors_controller.rb b/plugins/redmine_agile/app/controllers/agile_colors_controller.rb
deleted file mode 100644
index 0b6f5ff..0000000
--- a/plugins/redmine_agile/app/controllers/agile_colors_controller.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-class AgileColorsController < ApplicationController
-  unloadable
-
-  layout 'admin'
-
-  before_action :require_admin
-  before_action :find_coloreds, :only => [:index, :update]
-
-  def index
-  end
-
-  def update
-    @colored_class.transaction do
-      params[:coloreds].each do |colored|
-        @colored_class.update(colored[:id], :color => colored[:color])
-      end
-      flash[:notice] = l(:notice_successful_update)
-    end
-    redirect_to :action => :index, :object_type => params[:object_type]
-  end
-
-  private
-
-  def find_coloreds
-    klass = Object.const_get(params[:object_type].camelcase) rescue nil
-    @colored_class = klass
-    @coloreds = klass.sorted if klass && klass.new.respond_to?('color')
-    render_404 unless @coloreds.present?
-  end
-
-end
diff --git a/plugins/redmine_agile/app/controllers/agile_journal_details_controller.rb b/plugins/redmine_agile/app/controllers/agile_journal_details_controller.rb
index 8eaf878..3d2c6c0 100644
--- a/plugins/redmine_agile/app/controllers/agile_journal_details_controller.rb
+++ b/plugins/redmine_agile/app/controllers/agile_journal_details_controller.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -24,6 +24,7 @@ class AgileJournalDetailsController < ApplicationController
 
   helper :issues
   helper :agile_support
+  include AgileSupportHelper
 
   def done_ratio
     @done_ratios = @issue.journals.map(&:details).flatten.select {|detail| 'done_ratio' == detail.prop_key }.sort_by {|a| a.journal.created_on }
@@ -32,9 +33,13 @@ class AgileJournalDetailsController < ApplicationController
   end
 
   def status
-    @statuses = @issue.journals.map(&:details).flatten.select {|detail| 'status_id' == detail.prop_key }.sort_by {|a| a.journal.created_on }
-    @statuses.unshift(JournalDetail.new(:property => 'attr', :prop_key => 'status_id', :value => history_initial_value(@statuses) || @issue.status.id,
-                                        :journal => Journal.new(:user => @issue.author, :created_on => @issue.created_on)))
+    @statuses_collector = AgileStatusesCollector.new(@issue)
+    @group = params[:group_by] if params[:group_by].present?
+
+    respond_to do |format|
+      format.html
+      format.csv  { send_data(issue_statuses_to_csv(@statuses_collector), type: 'text/csv; header=present', filename: "issue_#{@issue.id}_statuses.csv") }
+    end
   end
 
   def assignee
diff --git a/plugins/redmine_agile/app/controllers/agile_queries_controller.rb b/plugins/redmine_agile/app/controllers/agile_queries_controller.rb
deleted file mode 100644
index ab63020..0000000
--- a/plugins/redmine_agile/app/controllers/agile_queries_controller.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-class AgileQueriesController < ApplicationController
-  unloadable
-
-  menu_item :agile
-
-  before_action :find_query, :except => [:new, :create, :index]
-  before_action :find_optional_project, :only => [:new, :create]
-
-  include QueriesHelper
-  helper :queries
-  helper :agile_boards
-
-  def index
-    @limit = per_page_option
-    @query_count = AgileQuery.visible.count
-    @query_pages = Paginator.new @query_count, @limit, params['page']
-    @queries = AgileQuery.visible.
-                    order("#{Query.table_name}.name").
-                    limit(@limit).
-                    offset(@offset).
-                    all
-  end
-
-  def new
-    @query = AgileQuery.new
-    @query.user = User.current
-    @query.project = @project
-    @query.visibility = AgileQuery::VISIBILITY_PRIVATE unless User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
-    @query.build_from_params(params)
-  end
-
-  def create
-    @query = AgileQuery.new
-    @query.user = User.current
-    @query.project = params[:query_is_for_all] ? nil : @project
-    @query.build_from_params(params)
-    @query.name = params[:query] && params[:query][:name]
-    if User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
-      @query.visibility = (params[:query] && params[:query][:visibility]) || AgileQuery::VISIBILITY_PRIVATE
-      @query.role_ids = params[:query] && params[:query][:role_ids] if Redmine::VERSION.to_s > '2.4'
-    else
-      @query.visibility = AgileQuery::VISIBILITY_PRIVATE
-    end
-    @query.column_names = nil if params[:default_columns]
-
-    if @query.save
-      flash[:notice] = l(:notice_successful_create)
-      redirect_to_agile_board(:query_id => @query)
-    else
-      render :action => 'new', :layout => !request.xhr?
-    end
-  end
-
-  def edit
-  end
-
-  def update
-    @query.project = nil if params[:query_is_for_all]
-    @query.build_from_params(params)
-    @query.name = params[:query] && params[:query][:name]
-    if User.current.allowed_to?(:manage_public_queries, @project) || User.current.admin?
-      @query.visibility = (params[:query] && params[:query][:visibility]) || AgileQuery::VISIBILITY_PRIVATE
-      @query.role_ids = params[:query] && params[:query][:role_ids] if Redmine::VERSION.to_s > '2.4'
-    else
-      @query.visibility = AgileQuery::VISIBILITY_PRIVATE
-    end
-    @query.column_names = nil if params[:default_columns]
-
-    if @query.save
-      flash[:notice] = l(:notice_successful_update)
-      redirect_to_agile_board(:query_id => @query)
-    else
-      render :action => 'edit'
-    end
-  end
-
-  def destroy
-    @query.destroy
-    redirect_to_agile_board(:set_filter => 1)
-  end
-
-private
-  def find_query
-    @query = AgileQuery.find(params[:id])
-    @project = @query.project
-    render_403 unless @query.editable_by?(User.current)
-  rescue ActiveRecord::RecordNotFound
-    render_404
-  end
-
-  def find_optional_project
-    @project = Project.find(params[:project_id]) if params[:project_id]
-    render_403 unless User.current.allowed_to?(:add_agile_queries, @project, :global => true)
-  rescue ActiveRecord::RecordNotFound
-    render_404
-  end
-
-  def redirect_to_agile_board(options)
-    if @project
-      redirect_to agile_board_path(options.merge(:project_id => @project))
-    else
-      redirect_to agile_board_path(options)
-    end
-  end
-end
diff --git a/plugins/redmine_agile/app/controllers/agile_versions_controller.rb b/plugins/redmine_agile/app/controllers/agile_versions_controller.rb
deleted file mode 100644
index 611b72f..0000000
--- a/plugins/redmine_agile/app/controllers/agile_versions_controller.rb
+++ /dev/null
@@ -1,85 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-class AgileVersionsController < ApplicationController
-  unloadable
-
-  menu_item :agile
-
-  before_action :find_project_by_project_id, :only => [:index, :autocomplete, :load]
-  before_action :find_version, :only => [:load]
-  before_action :authorize, :except => [:autocomplete, :load]
-  before_action :find_no_version_issues, :only => [:index, :autocomplete]
-
-  include QueriesHelper
-  helper :queries
-  include RedmineAgile::AgileHelper
-
-  def index
-    retrieve_versions_query
-    if @query.valid?
-      @backlog_version = @query.backlog_version
-      @backlog_version_issues =  @query.backlog_version_issues
-
-      @current_version = @query.current_version
-      @current_version_issues = @query.current_version_issues
-    end
-    respond_to do |format|
-      format.html
-      format.js
-    end
-  rescue ActiveRecord::RecordNotFound
-    render_404
-  end
-
-  def autocomplete
-    render :layout => false
-  end
-
-  def load
-    retrieve_versions_query
-    @version_issues = @query.version_issues(@version)
-    @version_type = params[:version_type]
-    @other_version_type = @version_type == "backlog" ? "current" : "backlog"
-    @other_version_id = params[:other_version_id]
-    respond_to do |format|
-      format.js
-    end
-  end
-
-  private
-
-  def find_version
-    @version = Version.visible.find(params[:version_id])
-    @project ||= @version.project
-  rescue ActiveRecord::RecordNotFound
-    render_404
-  end
-
-  def find_no_version_issues
-    retrieve_versions_query
-    if @query.valid?
-      scope = @query.no_version_issues(params)
-      @issue_count = scope.count
-      @issue_pages = Redmine::Pagination::Paginator.new @issue_count, 20, params['page']
-      @version_issues = scope.offset(@issue_pages.offset).limit(@issue_pages.per_page).all
-    end
-  end
-
-end
diff --git a/plugins/redmine_agile/app/helpers/agile_boards_helper.rb b/plugins/redmine_agile/app/helpers/agile_boards_helper.rb
index b05cf67..5a11b6f 100644
--- a/plugins/redmine_agile/app/helpers/agile_boards_helper.rb
+++ b/plugins/redmine_agile/app/helpers/agile_boards_helper.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -21,133 +21,41 @@
 
 module AgileBoardsHelper
   def agile_color_class(issue, options={})
-    if options[:color_base]
-      color = case options[:color_base]
-      when AgileColor::COLOR_GROUPS[:issue]
-        issue.color
-      when AgileColor::COLOR_GROUPS[:tracker]
-        issue.tracker.color
-      when AgileColor::COLOR_GROUPS[:priority]
-        issue.priority.color
-      when AgileColor::COLOR_GROUPS[:spent_time]
-        AgileColor.for_spent_time(issue.estimated_hours, issue.spent_hours)
-      when AgileColor::COLOR_GROUPS[:project]
-        issue.project.color
-      end
-    else
-      color = if RedmineAgile.tracker_colors?
-        issue.tracker.color
-      elsif RedmineAgile.issue_colors?
-        issue.color
-      elsif RedmineAgile.priority_colors?
-        issue.priority.color
-      elsif RedmineAgile.spent_time_colors?
-        AgileColor.for_spent_time(issue.estimated_hours, issue.spent_hours)
+    ''
       end
-    end
-    "#{RedmineAgile.color_prefix}-#{color}" if color && RedmineAgile.use_colors?
-          end
 
   def agile_user_color(user, options={})
-    return if Redmine::VERSION.to_s < '2.4'
-    user_color = user.color rescue nil
-    user_color ||= AgileColor.for_user(user.login)
-    if options[:color_base]
-      "border-left: 5px solid #{user_color}".html_safe if options[:color_base] ==  AgileColor::COLOR_GROUPS[:user]
-    elsif RedmineAgile.user_color?
-      "border-left: 5px solid #{user_color}".html_safe
-    end
   end
 
   def header_th(name, rowspan = 1, colspan = 1, leaf = nil)
     th_attributes = {}
     if leaf
       # th_attributes[:style] = ""
-      th_attributes[:style] = "border-bottom: 4px solid; border-bottom-color: #{color_by_name(leaf.name)};" if RedmineAgile.status_colors?
       th_attributes[:"data-column-id"] = leaf.id
       issue_count = leaf.instance_variable_get("@issue_count") || 0
-      if Redmine::VERSION.to_s > '2.4'
-        wp_count = leaf.instance_variable_get("@wp_max")
-        unless wp_count.blank?
-          th_attributes[:class] = leaf.wp_class
-          issue_count_tag = content_tag(:span, issue_count, :class => leaf.wp_class)
-          count_tag = " (#{content_tag(:span, issue_count_tag + "/#{wp_count}", :class => 'count')})".html_safe
-        else
-          count_tag = " (#{content_tag(:span, issue_count.to_i, :class => 'count')})".html_safe
-        end
-      else
-        count_tag = " (#{content_tag(:span, issue_count.to_i, :class => 'count')})".html_safe
-      end
-            
+      count_tag = " (#{content_tag(:span, issue_count.to_i, :class => 'count')})".html_safe
+      
 
       # estimated hours total
       story_points_count = leaf.instance_variable_get("@story_points") || 0
       hours_count = leaf.instance_variable_get("@estimated_hours_sum") || 0
-      if story_points_count > 0
-        hours_tag = " #{content_tag(:span, (story_points_count).to_s + 'sp',
-          :class => 'hours', :title => l(:field_estimated_hours))}".html_safe
-      else
-        hours_tag = " #{content_tag(:span, ("%.2fh" % hours_count.to_f).to_s,
-        :class => 'hours', :title => l(:field_estimated_hours))}".html_safe if hours_count > 0
+      values = []
+      values << '%.2fh' % hours_count.to_f if hours_count > 0
+      values << "#{story_points_count}sp" if story_points_count > 0
+      if values.present?
+        hours_tag = content_tag(:span, values.join('/').html_safe, class: 'hours', title: l(:field_estimated_hours))
       end
     end
-    th_attributes[:rowspan] = rowspan if rowspan > 1
-    th_attributes[:colspan] = colspan if colspan > 1
     content_tag :th, h(name) + count_tag + hours_tag, th_attributes
   end
 
   def render_board_headers(columns)
-    tree = HeaderTree.new
-
-    columns.map do |column|
-      path = column.name.split(':').map(&:strip)
-      tree.put path, column
-    end
-
-    # puts tree
-
-    maxdepth = tree.depth
-
-    ret = tree.render
-
-    ret[1..-1].map do |row|
-      row.map do |th_params|
-        header_th *th_params
+    "<tr>#{columns.map{|column| header_th(column.name, 1, 1, column)}.join}</tr>".html_safe
       end
-    end.map{|x| "<tr>#{x.join('')}</tr>" }.join.html_safe
-          end
 
   def color_by_name(name)
     "##{"%06x" % (name.unpack('H*').first.hex % 0xffffff)}"
   end
-  def format_swimlane_object(object, html=true)
-    case object.class.name
-    when 'Array'
-      object.map {|o| format_swimlane_object(o, html)}.join(', ').html_safe
-    when 'Time'
-      format_time(object)
-    when 'Date'
-      format_date(object)
-    when 'Fixnum'
-      object.to_s
-    when 'Float'
-      sprintf "%.2f", object
-    when 'User'
-      html ? link_to_user(object) : object.to_s
-    when 'Project'
-      html ? link_to_project(object) : object.to_s
-    when 'Version'
-      html ? link_to(object.name, version_path(object)) : object.to_s
-    when 'TrueClass'
-      l(:general_text_Yes)
-    when 'FalseClass'
-      l(:general_text_No)
-    when 'Issue'
-      object.visible? && html ? link_to_issue(object) : "##{object.id}"
-    else
-      html ? h(object) : object.to_s
-    end
-  end
 
   def render_board_fields_selection(query)
     query.available_inline_columns.reject(&:frozen?).reject{ |c| c.name == :story_points && !RedmineAgile.use_story_points? }.map do |column|
@@ -160,13 +68,9 @@ module AgileBoardsHelper
     current_statuses = query.options[:f_status] || IssueStatus.where(:is_closed => false).pluck(:id).map(&:to_s)
     wp = query.options[:wp] || {}
     status_tags = available_statuses.map do |status|
-      content_tag(:span,
-        label_tag('', check_box_tag('f_status[]', status.id, current_statuses.include?(status.id.to_s)
-        ) + content_tag(:span, status.to_s), :title => status.to_s) + text_field_tag("wp[#{status.id}]", wp[status.id.to_s],
-          :size => 5, :class => 'wp_input', :placeholder => "WIP",
-          :title => l(:label_agile_wip_limit)), :class => 'floating'
-      )
-                end.join(' ').html_safe
+      label_tag('', check_box_tag('f_status[]', status.id, current_statuses.include?(status.id.to_s)
+      ) + status.to_s, :class => 'floating')
+    end.join(' ').html_safe
     hidden_field_tag('f[]', 'status_id').html_safe +
       hidden_field_tag('op[status_id]', "=").html_safe +
       status_tags
@@ -177,7 +81,7 @@ module AgileBoardsHelper
     hours << "%.2f" % issue.total_spent_hours.to_f if query.has_column_name?(:spent_hours) && issue.total_spent_hours > 0
     hours << "%.2f" % issue.estimated_hours.to_f if query.has_column_name?(:estimated_hours) && issue.estimated_hours
     hours = [hours.join('/') + "h"] unless hours.blank?
-    hours << "#{issue.story_points}sp" if query.has_column_name?(:story_points) && issue.story_points
+    hours << "#{issue.story_points}sp" if RedmineAgile.use_story_points? && query.has_column_name?(:story_points) && issue.story_points
 
     content_tag(:span, "(#{hours.join('/')})", :class => 'hours') unless hours.blank?
   end
@@ -216,8 +120,8 @@ module AgileBoardsHelper
     "#{I18n.t('datetime.distance_in_words.x_days', :count => (hours/24).to_i)}"
   end
 
-  def class_for_closed_issue(issue)
-    return '' if !RedmineAgile.hide_closed_issues_data?
+  def class_for_closed_issue(issue, is_version_board)
+    return '' if !RedmineAgile.hide_closed_issues_data? && !is_version_board
     return 'closed-issue' if issue.closed?
     ''
   end
@@ -234,11 +138,22 @@ module AgileBoardsHelper
     javascript_tag(js_code)
   end
 
+  def estimated_value(issue)
+    return (issue.story_points || 0) if RedmineAgile.use_story_points?
+    issue.estimated_hours.to_f || 0
+  end
+
+  def estimated_time_value(query, issue)
+    issue.estimated_hours.to_f if query.has_column_name?(:estimated_hours)
+  end
+
+  def story_points_value(query, issue)
+    issue.story_points.to_f if query.has_column_name?(:story_points) && RedmineAgile.use_story_points?
+  end
+
   def show_checklist?(issue)
     RedmineAgile.use_checklist? && issue.checklists.any? && User.current.allowed_to?(:view_checklists, issue.project)
   rescue
     false
   end
-
-
 end
diff --git a/plugins/redmine_agile/app/helpers/agile_charts_helper.rb b/plugins/redmine_agile/app/helpers/agile_charts_helper.rb
index b813026..679997a 100644
--- a/plugins/redmine_agile/app/helpers/agile_charts_helper.rb
+++ b/plugins/redmine_agile/app/helpers/agile_charts_helper.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -22,16 +22,11 @@
 module AgileChartsHelper
   def render_agile_charts_breadcrumb
     links = []
-    links << link_to(l(:label_project_all), {:project_id => nil, :issue_id => nil})
-    links << link_to(h(@project), {:project_id => @project, :issue_id => nil}) if @project
+    links << link_to(l(:label_project_all), project_id: nil, issue_id: nil)
+    links << link_to(h(@project), project_id: @project, issue_id: nil) if @project
     if @version
-      if @version.visible?
-        links << link_to(@version.name, version_path(@version))
-      else
-        links << @version.name
-      end
+      links << @version.visible? ? link_to(@version.name, version_path(@version)) : @version.name
     end
     breadcrumb links
   end
-
 end
diff --git a/plugins/redmine_agile/app/helpers/agile_support_helper.rb b/plugins/redmine_agile/app/helpers/agile_support_helper.rb
index a18d86d..aba1c22 100644
--- a/plugins/redmine_agile/app/helpers/agile_support_helper.rb
+++ b/plugins/redmine_agile/app/helpers/agile_support_helper.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -20,6 +20,8 @@
 # along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
 
 module AgileSupportHelper
+  include ActionView::Helpers::DateHelper
+
   # Returns a h2 tag and sets the html title with the given arguments
   def title(*args)
     strings = args.map do |arg|
@@ -37,4 +39,32 @@ module AgileSupportHelper
     end_time = next_event ? next_event.journal.created_on : Time.now
     distance_of_time_in_words(end_time, event.journal.created_on).html_safe
   end
+
+  def issue_statuses_to_csv(collector)
+    decimal_separator = l(:general_csv_decimal_separator)
+    encoding = 'utf-8'
+    export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
+      headers = [ "#",
+                  l(:field_created_on, locale: :en),
+                  l(:field_status, locale: :en),
+                  l(:field_duration, locale: :en),
+                  l(:field_author, locale: :en),
+                  l(:field_assigned_to, locale: :en)
+                  ]
+      csv << headers.collect {|c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) }
+
+      collector.data.each_with_index do |data, index|
+        issue_status = IssueStatus.where(id: data.status_id).first
+        fields = [index + 1,
+                  format_time(data.journal.created_on),
+                  issue_status.name,
+                  distance_of_time_in_words(data.end_time, data.start_time),
+                  data.journal.user.name,
+                  Principal.where(id: data.assigned_to_id).first.try(:name)
+                  ]
+        csv << fields.collect { |c| Redmine::CodesetUtil.from_utf8(c.to_s, encoding) }
+      end
+    end
+    export
+  end
 end
diff --git a/plugins/redmine_agile/app/helpers/agile_versions_helper.rb b/plugins/redmine_agile/app/helpers/agile_versions_helper.rb
deleted file mode 100644
index 9b306e6..0000000
--- a/plugins/redmine_agile/app/helpers/agile_versions_helper.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-# encoding: utf-8
-#
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module AgileVersionsHelper
-  def version_select_tag(version, option={})
-    return "" if version.blank?
-    version_id =  version.is_a?(Version) && version.id || version
-    other_version_id = option[:other_version].is_a?(Version) && option[:other_version].id || option[:other_version]
-    select_tag('version_id',
-      options_for_select(versions_collection_for_select,
-          {:selected => version_id, :disabled => other_version_id}),
-      :data => {:remote => true,
-                :method => 'get',
-                :url => load_agile_versions_path(:version_type => option[:version_type],
-                                                 :other_version_id => other_version_id,
-                                                 :project_id => @project)}) +
-    content_tag(:span, '', :class => "hours header-hours #{option[:version_type]}-hours")
-  end
-
-  def versions_collection_for_select
-    @project.shared_versions.open.map{|version| [format_version_name(version), version.id.to_s]}
-  end
-
-  def estimated_hours(issue)
-    "%.2fh" % issue.estimated_hours.to_f
-  end
-
-  def estimated_value(issue)
-    return (issue.story_points || 0) if RedmineAgile.use_story_points?
-    issue.estimated_hours.to_f || 0
-  end
-
-  def estimated_unit
-    RedmineAgile.use_story_points? ? 'sp' : 'h'
-  end
-end
diff --git a/plugins/redmine_agile/app/models/agile_charts_query.rb b/plugins/redmine_agile/app/models/agile_charts_query.rb
index 85fce06..c38ffff 100644
--- a/plugins/redmine_agile/app/models/agile_charts_query.rb
+++ b/plugins/redmine_agile/app/models/agile_charts_query.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -22,136 +22,67 @@ class AgileChartsQuery < AgileQuery
 
   validate :validate_query_dates
 
-  def initialize(attributes=nil, *args)
+  attr_writer :date_from, :date_to
+
+  def initialize(attributes = nil, *args)
     super attributes
     self.filters.delete('status_id')
+    self.filters['chart_period'] = { operator: 'm', values: [''] } unless has_filter?('chart_period')
   end
 
-  self.operators_by_filter_type[:chart_period] = [ "><", "w", "lw", "l2w", "m", "lm", "y"]
+  self.operators_by_filter_type[:chart_period] = ['><', 'w', 'lw', 'l2w', 'm', 'lm', 'y']
 
   def initialize_available_filters
-    principals = []
-    subprojects = []
-    versions = []
-    categories = []
-    issue_custom_fields = []
-
-    # add_available_filter "chart_period", :type => :chart_period, :name => l(:label_agile_chart_period)
-
-    if project
-      principals += project.principals.sort
-      unless project.leaf?
-        subprojects = project.descendants.visible.all
-        principals += Principal.member_of(subprojects)
-      end
-      versions = project.shared_versions.all
-      categories = project.issue_categories.all
-      issue_custom_fields = project.all_issue_custom_fields
-    else
-      if all_projects.any?
-        principals += Principal.member_of(all_projects)
-      end
-      versions = Version.visible.where(:sharing => 'system').all
-      issue_custom_fields = IssueCustomField.where(:is_for_all => true)
-    end
-    principals.uniq!
-    principals.sort!
-    users = principals.select {|p| p.is_a?(User)}
-
-    if project.nil?
-      project_values = []
-      if User.current.logged? && User.current.memberships.any?
-        project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"]
-      end
-      project_values += all_projects_values
-      add_available_filter("project_id",
-        :type => :list, :values => project_values
-      ) unless project_values.empty?
-    end
-
-    add_available_filter "tracker_id",
-      :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] }
-    add_available_filter "priority_id",
-      :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
-
-    author_values = []
-    author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
-    author_values += users.collect{|s| [s.name, s.id.to_s] }
-    add_available_filter("author_id",
-      :type => :list, :values => author_values
-    ) unless author_values.empty?
-
-    assigned_to_values = []
-    assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
-    assigned_to_values += (Setting.issue_group_assignment? ?
-                              principals : users).collect{|s| [s.name, s.id.to_s] }
-    add_available_filter("assigned_to_id",
-      :type => :list_optional, :values => assigned_to_values
-    ) unless assigned_to_values.empty?
-
-    if versions.any?
-      add_available_filter "fixed_version_id",
-        :type => :list_optional,
-        :values => versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] }
-    end
-
-    if categories.any?
-      add_available_filter "category_id",
-        :type => :list_optional,
-        :values => categories.collect{|s| [s.name, s.id.to_s] }
-    end
-
-    add_available_filter "subject", :type => :text
-    add_available_filter "created_on", :type => :date_past
-    add_available_filter "updated_on", :type => :date_past
-    add_available_filter "closed_on", :type => :date_past
-    add_available_filter "start_date", :type => :date
-    add_available_filter "due_date", :type => :date
-    add_available_filter "estimated_hours", :type => :float
-    add_available_filter "done_ratio", :type => :integer
-
-    if subprojects.any?
-      add_available_filter "subproject_id",
-        :type => :list_subprojects,
-        :values => subprojects.collect{|s| [s.name, s.id.to_s] }
-    end
+    super
 
-    add_custom_fields_filters(issue_custom_fields)
+    add_available_filter 'chart_period', type: :date_past, name: l(:label_date)
+  end
 
-    add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
+  def sprint_values
+    return [] unless project
 
-    Tracker.disabled_core_fields(trackers).each {|field|
-      delete_available_filter field
-    }
+    project.shared_agile_sprints.available.map { |s| [s.to_s, s.id.to_s] }
   end
 
   def default_columns_names
     @default_columns_names = [:id, :subject, :estimated_hours, :spent_hours, :done_ratio, :assigned_to]
   end
 
-  def sql_for_chart_period_field(field, operator, value)
-    "1=1"
+  def sql_for_chart_period_field(_field, _operator, _value)
+    '1=1'
   end
 
-  def date_from
-    @date_from
+  def chart
+    @chart ||= RedmineAgile::Charts.valid_chart_name_by(options[:chart])
   end
 
-  def date_from=(arg)
-    @date_from = Date.parse(arg.to_s) rescue nil
+  def chart=(arg)
+    options[:chart] = arg
+  end
+
+  def date_from
+    @date_from ||= chart_period[:from]
   end
 
   def date_to
-    @date_to
+    @date_to ||= chart_period[:to]
+  end
+
+  def interval_size
+    if RedmineAgile::AgileChart::TIME_INTERVALS.include?(options[:interval_size])
+      options[:interval_size]
+    else
+      RedmineAgile::AgileChart::DAY_INTERVAL
+    end
   end
 
-  def date_to=(arg)
-    @date_to = Date.parse(arg.to_s) rescue nil
+  def interval_size=(value)
+    options[:interval_size] = value
   end
 
   def build_from_params(params)
     if params[:fields] || params[:f]
-      self.filters = {}
+      self.filters = {}.merge(chart_period_filter(params))
       add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v])
     else
       available_filters.keys.each do |field|
@@ -160,33 +91,77 @@ class AgileChartsQuery < AgileQuery
     end
     self.group_by = params[:group_by] || (params[:query] && params[:query][:group_by])
     self.column_names = params[:c] || (params[:query] && params[:query][:column_names])
-
     self.date_from = params[:date_from] || (params[:query] && params[:query][:date_from])
     self.date_to = params[:date_to] || (params[:query] && params[:query][:date_to])
+    self.chart = params[:chart] || (params[:query] && params[:query][:chart]) || params[:default_chart] || RedmineAgile.default_chart
+    self.interval_size = params[:interval_size] || (params[:query] && params[:query][:interval_size]) || RedmineAgile::AgileChart::DAY_INTERVAL
+    self.chart_unit = params[:chart_unit] || (params[:query] && params[:query][:chart_unit]) || RedmineAgile::Charts::UNIT_ISSUES
+
     self
   end
 
-private
+  def condition_for_status
+    '1=1'
+  end
+
+  private
 
-  def issue_scope
-    Issue.visible.
-      eager_load(:status,
-                 :project,
-                 :assigned_to,
-                 :tracker,
-                 :priority,
-                 :category,
-                 :fixed_version,
-                 :agile_data).
-      where(statement)
+  def chart_period_filter(params)
+    return {} if (params[:fields] || params[:f]).include?('chart_period')
+    { 'chart_period' => { operator: 'm', values: [''] } }
   end
 
   def validate_query_dates
-    if (self.date_from && self.date_to && self.date_from >= self.date_to) ||
-       (self.date_from && self.date_to.blank?)
-      m = l(:label_agile_chart_dates) + " " + l(:invalid, :scope => 'activerecord.errors.messages')
-      errors.add(:base, m)
+    if (self.date_from && self.date_to && self.date_from >= self.date_to)
+      errors.add(:base, l(:label_agile_chart_dates) + ' ' + l(:invalid, scope: 'activerecord.errors.messages'))
+    end
+  end
+
+  def db_timestamp_regex
+    /(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:.\d*))/
+  end
+
+  def chart_period
+    @chart_period ||= {
+      from: chart_period_statement.match("chart_period > '#{db_timestamp_regex}") { |m| Time.zone.parse(m[1]) },
+      to: chart_period_statement.match("chart_period <= '#{db_timestamp_regex}") { |m| Time.zone.parse(m[1]) }
+    }
+  end
+
+  def chart_period_statement
+    @chart_period_statement ||= build_chart_period_statement
+  end
+
+  def build_chart_period_statement
+    field = 'chart_period'
+    operator = filters[field][:operator]
+    values = filters[field][:values]
+    date = User.current.today
+
+    case operator
+    when 'w'
+      first_day_of_week = (Setting.start_of_week || l(:general_first_day_of_week)).to_i
+      day_of_week = date.cwday
+      days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
+      sql_for_field(field, '><t-', [days_ago], Issue.table_name, field)
+    when 'm'
+      days_ago = date - date.beginning_of_month
+      sql_for_field(field, '><t-', [days_ago], Issue.table_name, field)
+    when 'y'
+      days_ago = date - date.beginning_of_year
+      sql_for_field(field, '><t-', [days_ago], Issue.table_name, field)
+    when '><'
+      sql_for_field(field, '><', adjusted_values(values), Issue.table_name, field)
+    else
+      sql_for_field(field, operator, values, Issue.table_name, field)
     end
   end
 
+  def adjusted_values(values)
+    return values unless values.is_a?(Array)
+
+    from = values[0].present? ? Date.parse(values[0]) : Date.today
+    to = values[1].present? ? Date.parse(values[1]) : Date.today
+    [from.to_s, (to < from ? from : to).to_s]
+  end
 end
diff --git a/plugins/redmine_agile/app/models/agile_color.rb b/plugins/redmine_agile/app/models/agile_color.rb
deleted file mode 100644
index 1bfeb46..0000000
--- a/plugins/redmine_agile/app/models/agile_color.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-class AgileColor < ActiveRecord::Base
-  unloadable
-  include Redmine::SafeAttributes
-
-  COLOR_GROUPS = {
-    :issue => 'issue',
-    :priority => 'priority',
-    :tracker => 'tracker',
-    :spent_time => 'spent_time',
-    :user => 'user',
-    :project => 'project'
-  }
-
-  AGILE_COLORS = {
-    :green  => 'green',
-    :blue => 'blue',
-    :turquoise  => 'turquoise',
-    :light_green  => 'lightgreen',
-    :yellow => 'yellow',
-    :orange => 'orange',
-    :red  => 'red',
-    :purple => 'purple',
-    :gray => 'gray'
-  }
-
-  belongs_to :container, :polymorphic => true
-
-  attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
-  safe_attributes 'color'
-
-  def self.for_user(user_name)
-    '0xffffff'.html_safe if !user_name
-    "##{"%06x" % (user_name.unpack('H*').first.hex % 0xffffff)}".html_safe
-  end
-
-  def self.for_spent_time(est_time = nil, spent_time = nil)
-    return AGILE_COLORS[:gray] if !est_time || !spent_time || est_time.to_f.zero? || (spent_time.to_f.zero? && est_time.to_f.zero?)
-    percent = ((spent_time / est_time.to_f) * 100).to_i
-    if percent <= 80
-      return AGILE_COLORS[:green]
-    elsif percent > 80 && (spent_time < est_time)
-      return AGILE_COLORS[:yellow]
-    elsif (spent_time >= est_time) && (spent_time < est_time * 2)
-      return AGILE_COLORS[:red]
-    elsif spent_time >= est_time * 2
-      return AGILE_COLORS[:purple]
-    end
-  end
-end
diff --git a/plugins/redmine_agile/app/models/agile_data.rb b/plugins/redmine_agile/app/models/agile_data.rb
old mode 100755
new mode 100644
index f5685f4..87db483
--- a/plugins/redmine_agile/app/models/agile_data.rb
+++ b/plugins/redmine_agile/app/models/agile_data.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -20,5 +20,6 @@
 class AgileData < ActiveRecord::Base
   unloadable
   belongs_to :issue
+
   validates :story_points, :numericality => {:only_integer => true, :greater_than_or_equal_to => 0, :allow_nil => true, :message => :invalid}
 end
diff --git a/plugins/redmine_agile/app/models/agile_query.rb b/plugins/redmine_agile/app/models/agile_query.rb
index f465bc9..1cb03d1 100644
--- a/plugins/redmine_agile/app/models/agile_query.rb
+++ b/plugins/redmine_agile/app/models/agile_query.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -18,7 +18,6 @@
 # along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
 
 class AgileQuery < Query
-  unloadable
   include Redmine::SafeAttributes
 
   attr_reader :truncated
@@ -27,36 +26,26 @@ class AgileQuery < Query
   self.view_permission = :view_issues if Redmine::VERSION.to_s >= '3.4'
 
   self.available_columns = [
-    QueryColumn.new(:id, :sortable => "#{Issue.table_name}.id", :default_order => 'desc', :caption => :label_agile_issue_id),
-    QueryColumn.new(:project, :groupable => "#{Issue.table_name}.project_id", :sortable => "#{Project.table_name}.id"),
-    QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
-    QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
-    QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio"),
-    QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true),
-    QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement("users")}, :groupable => true),
-    QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => "#{Issue.table_name}.category_id"),
-    QueryColumn.new(:fixed_version, :sortable => lambda {Version.fields_for_order_statement}, :groupable => "#{Issue.table_name}.fixed_version_id"),
-    QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
-    QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
-    QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on"),
-    QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on"),
-    QueryColumn.new(:thumbnails, :caption => :label_agile_board_thumbnails),
-    QueryColumn.new(:description),
-    QueryColumn.new(:sub_issues, :caption => :label_agile_sub_issues),
-    QueryColumn.new(:day_in_state, :caption => :label_agile_day_in_state),
-    QueryColumn.new(:parent, :groupable => "#{Issue.table_name}.parent_id", :sortable => "#{AgileData.table_name}.position", :caption => :field_parent_issue),
-    QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => "#{Issue.table_name}.assigned_to_id"),
-    QueryColumn.new(:relations, :caption => :label_related_issues),
-    QueryColumn.new(:last_comment, :caption => :label_agile_last_comment),
-    QueryColumn.new(:story_points, :caption => :label_agile_story_points)
+    QueryColumn.new(:id, sortable: "#{Issue.table_name}.id", default_order: 'desc', caption: :label_agile_issue_id),
+    QueryColumn.new(:project, groupable: "#{Issue.table_name}.project_id", sortable: "#{Project.table_name}.id"),
+    QueryColumn.new(:tracker, sortable: "#{Tracker.table_name}.position", groupable: true),
+    QueryColumn.new(:estimated_hours, sortable: "#{Issue.table_name}.estimated_hours"),
+    QueryColumn.new(:done_ratio, sortable: "#{Issue.table_name}.done_ratio"),
+    QueryColumn.new(:day_in_state, caption: :label_agile_day_in_state),
+    QueryColumn.new(:parent, groupable: "#{Issue.table_name}.parent_id", sortable: "#{AgileData.table_name}.position", caption: :field_parent_issue),
+    QueryColumn.new(:assigned_to, sortable: lambda { User.fields_for_order_statement }, groupable: "#{Issue.table_name}.assigned_to_id"),
+    QueryColumn.new(:relations, caption: :label_related_issues),
+    QueryColumn.new(:last_comment, caption: :label_agile_last_comment),
+    QueryColumn.new(:story_points, caption: :label_agile_story_points)
   ]
 
-  if RedmineAgile.use_checklist?
-    self.available_columns << QueryColumn.new(:checklists, :caption => :label_checklist_plural)
+  self.available_columns << QueryColumn.new(:checklists, caption: :label_checklist_plural) if RedmineAgile.use_checklist?
+
+  def self.build_from_params(params, attributes = {})
+    new(attributes).build_from_params(params)
   end
-  before_save :set_default_when_appropriate
 
-  scope :visible, lambda {|*args|
+  scope :visible, lambda { |*args|
     user = args.shift || User.current
     base = Project.allowed_to_condition(user, :view_issues, *args)
     scope = eager_load(:project).where("#{table_name}.project_id IS NULL OR (#{base})")
@@ -80,17 +69,17 @@ class AgileQuery < Query
     end
   }
 
-  def initialize(attributes=nil, *args)
+  def initialize(attributes = nil, *args)
     super attributes
     unless Redmine::VERSION.to_s > '2.4'
-      self.filters ||= { 'status_id' => {:operator => "*", :values => [""]} }
+      self.filters ||= { 'status_id' => { operator: '*', values: [''] } }
     end
-    self.filters ||= { }
+    self.filters ||= {}
     @truncated = false
   end
 
   def card_columns
-    self.inline_columns.select{|c| !%w(day_in_state tracker thumbnails description assigned_to done_ratio spent_hours estimated_hours project id sub_issues checklists last_comment story_points).include?(c.name.to_s)}
+    self.inline_columns.select { |c| !%w(day_in_state tracker thumbnails description assigned_to done_ratio spent_hours estimated_hours project id sub_issues checklists last_comment story_points).include?(c.name.to_s) }
   end
 
   def visible?(user=User.current)
@@ -103,7 +92,7 @@ class AgileQuery < Query
       if project
         (user.roles_for_project(project) & roles).any?
       else
-        Member.where(:user_id => user.id).joins(:roles).where(:member_roles => {:role_id => roles.map(&:id)}).any?
+        Member.where(user_id: user.id).joins(:roles).where(member_roles: {role_id: roles.map(&:id)}).any?
       end
     else
       user == self.user
@@ -119,34 +108,35 @@ class AgileQuery < Query
   end
 
   def color_base
-    options[:color_base] || RedmineAgile.color_base
   end
 
   def color_base=(value)
-    options[:color_base] = value
   end
-  def is_default?
-    !!options[:is_default]
+
+  def default_chart
+  end
+
+  def default_chart=(value)
   end
 
-  def is_default=(value)
-    options[:is_default] = !!value
+  def chart_unit
+    @chart_unit ||= RedmineAgile::Charts.valid_chart_unit_by(options[:chart], options[:chart_unit])
   end
 
-  def set_as_default
-    AgileQuery.where(:project_id => self.project_id).where(:visibility => self.visibility).where("#{AgileQuery.table_name}.id <> ?", self.id).each do |query|
-      query.is_default = false
-      query.save
-    end if self.is_default?
+  def chart_unit=(value)
+    options[:chart_unit] = value
   end
 
-  def self.default_query(project=nil)
-    board_scope = AgileQuery.visible
-    board_scope = board_scope.where(:project_id => project)
-    default_query = board_scope.where("#{table_name}.visibility = ? AND #{table_name}.user_id = ?", VISIBILITY_PRIVATE, User.current).detect{|q| q.is_default?}
-    default_query ||= board_scope.eager_load(:user => :memberships).where("#{table_name}.visibility = ?", VISIBILITY_ROLES).detect{|q| q.is_default?}
-    default_query ||= board_scope.where("#{table_name}.visibility = ?", VISIBILITY_PUBLIC).detect{|q| q.is_default?}
-    default_query
+  def draw_relations
+    r = options[:draw_relations]
+    r.nil? || r == '1'
+  end
+
+  def draw_relations=(arg)
+    options[:draw_relations] = (arg == '0' ? '0' : nil)
+  end
+
+  def with_totals?
   end
 
   def build_from_params(params)
@@ -161,7 +151,6 @@ class AgileQuery < Query
     self.group_by = params[:group_by] || (params[:query] && params[:query][:group_by])
     self.column_names = params[:c] || (params[:query] && params[:query][:column_names])
     self.color_base = params[:color_base] || (params[:query] && params[:query][:color_base])
-    self.is_default = params[:is_default] || (params[:query] && params[:query][:is_default])
     self.draw_relations = params[:draw_relations] || (params[:query] && params[:query][:draw_relations])
     if params[:f_status] || params[:wp]
       self.options = options.merge({ :f_status => params[:f_status], :wp => params[:wp] })
@@ -169,11 +158,6 @@ class AgileQuery < Query
     self
   end
 
-  # Builds a new query from the given params and attributes
-  def self.build_from_params(params, attributes={})
-    new(attributes).build_from_params(params)
-  end
-
   def initialize_available_filters
     principals = []
     subprojects = []
@@ -194,16 +178,16 @@ class AgileQuery < Query
       if all_projects.any?
         principals += Principal.member_of(all_projects)
       end
-      versions = Version.visible.where(:sharing => 'system').all
-      issue_custom_fields = IssueCustomField.where(:is_for_all => true)
+      versions = Version.visible.where(sharing: 'system').all
+      issue_custom_fields = IssueCustomField.where(is_for_all: true)
     end
     principals.uniq!
     principals.sort!
-    users = principals.select {|p| p.is_a?(User)}
+    users = principals.select { |p| p.is_a?(User) }
 
     unless Redmine::VERSION.to_s > '2.4'
-      add_available_filter "status_id",
-        :type => :list_status, :values => IssueStatus.sorted.collect{|s| [s.name, s.id.to_s] }
+      add_available_filter 'status_id',
+        type: :list_status, values: IssueStatus.sorted.collect{|s| [s.name, s.id.to_s] }
     end
 
     if project.nil?
@@ -213,20 +197,20 @@ class AgileQuery < Query
       end
       project_values += all_projects_values
       add_available_filter("project_id",
-        :type => :list, :values => project_values
+        type: :list, values: project_values
       ) unless project_values.empty?
     end
 
     add_available_filter "tracker_id",
-      :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] }
+      type: :list, values: trackers.collect{|s| [s.name, s.id.to_s] }
     add_available_filter "priority_id",
-      :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
+      type: :list, values: IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
 
     author_values = []
     author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
     author_values += users.collect{|s| [s.name, s.id.to_s] }
     add_available_filter("author_id",
-      :type => :list, :values => author_values
+      type: :list, values: author_values
     ) unless author_values.empty?
 
     assigned_to_values = []
@@ -234,17 +218,17 @@ class AgileQuery < Query
     assigned_to_values += (Setting.issue_group_assignment? ?
                               principals : users).collect{|s| [s.name, s.id.to_s] }
     add_available_filter("assigned_to_id",
-      :type => :list_optional, :values => assigned_to_values
+      type: :list_optional, values: assigned_to_values
     ) unless assigned_to_values.empty?
 
-    group_values = Group.all.collect {|g| [g.name, g.id.to_s] }
+    group_values = Group.visible.all.collect {|g| [g.name, g.id.to_s] }
     add_available_filter("member_of_group",
-      :type => :list_optional, :values => group_values
+      type: :list_optional, values: group_values
     ) unless group_values.empty?
 
     role_values = Role.givable.collect {|r| [r.name, r.id.to_s] }
     add_available_filter("assigned_to_role",
-      :type => :list_optional, :values => role_values
+      type: :list_optional, values: role_values
     ) unless role_values.empty?
 
     if versions.any?
@@ -252,39 +236,39 @@ class AgileQuery < Query
       fixed_versions << ["<< #{l(:label_current_version)} >>", 'current_version']
       versions.sort.each{ |s| fixed_versions << ["#{s.project.name} - #{s.name}", s.id.to_s] }
       add_available_filter "fixed_version_id",
-        :type => :list_optional,
-        :values => fixed_versions
+        type: :list_optional,
+        values: fixed_versions
     end
 
     if categories.any?
       add_available_filter "category_id",
-        :type => :list_optional,
-        :values => categories.collect{|s| [s.name, s.id.to_s] }
-    end
-
-    add_available_filter "subject", :type => :text
-    add_available_filter "created_on", :type => :date_past
-    add_available_filter "updated_on", :type => :date_past
-    add_available_filter "closed_on", :type => :date_past
-    add_available_filter "start_date", :type => :date
-    add_available_filter "due_date", :type => :date
-    add_available_filter "estimated_hours", :type => :float
-    add_available_filter "done_ratio", :type => :integer
-    add_available_filter "parent_issue_id", :type => :relation
-    add_available_filter "has_sub_issues", :type => :list,
-      :values => [ l(:general_text_yes), l(:general_text_no)],
-      :label => :label_agile_has_sub_issues
-    add_available_filter "version_status", :type => :list,
-      :name => l("label_attribute_of_fixed_version", :name => 'status'),
-      :values => Version::VERSION_STATUSES.collect {|s| [l("version_status_#{s}"), s]}
-    add_available_filter "parent_issue_tracker_id", :type => :list,
-      :label => :label_agile_parent_issue_tracker_id,
-      :values => Tracker.pluck(:name)
+        type: :list_optional,
+        values: categories.collect{|s| [s.name, s.id.to_s] }
+    end
+
+    add_available_filter "subject", type: :text
+    add_available_filter "created_on", type: :date_past
+    add_available_filter "updated_on", type: :date_past
+    add_available_filter "closed_on", type: :date_past
+    add_available_filter "start_date", type: :date
+    add_available_filter "due_date", type: :date
+    add_available_filter "estimated_hours", type: :float
+    add_available_filter "done_ratio", type: :integer
+    add_available_filter "parent_issue_id", type: :relation, values: all_projects_values
+    add_available_filter "has_sub_issues", type: :list,
+      values: [l(:general_text_yes), l(:general_text_no)],
+      label: :label_agile_has_sub_issues
+    add_available_filter "version_status", type: :list,
+      name: l("label_attribute_of_fixed_version", name: 'status'),
+      values: Version::VERSION_STATUSES.collect {|s| [l("version_status_#{s}"), s]}
+    add_available_filter "parent_issue_tracker_id", type: :list,
+      label: :label_agile_parent_issue_tracker_id,
+      values: Tracker.pluck(:name)
 
     if subprojects.any?
       add_available_filter "subproject_id",
-        :type => :list_subprojects,
-        :values => subprojects.collect{|s| [s.name, s.id.to_s] }
+        type: :list_subprojects,
+        values: subprojects.collect{|s| [s.name, s.id.to_s] }
     end
 
 
@@ -293,24 +277,23 @@ class AgileQuery < Query
     add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
 
     IssueRelation::TYPES.each do |relation_type, options|
-      add_available_filter relation_type, :type => :relation, :label => options[:name]
+      add_available_filter relation_type, type: :relation, label: options[:name], values: all_projects_values
     end
 
-    Tracker.disabled_core_fields(trackers).each {|field|
+    Tracker.disabled_core_fields(trackers).each { |field|
       delete_available_filter field
     }
 
-    add_available_filter "issue_id", :type => :integer, :label => :label_issue
-    add_available_filter 'description', :type => :text
+    add_available_filter "issue_id", type: :integer, label: :label_issue
 
-    if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
-      User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
+    if User.current.allowed_to?(:set_issues_private, nil, global: true) ||
+      User.current.allowed_to?(:set_own_issues_private, nil, global: true)
       add_available_filter 'is_private', type: :list,
                            values: [[l(:general_text_yes), '1'], [l(:general_text_no), '0']]
     end
 
     if User.current.logged?
-      add_available_filter 'watcher_id', type: :list, values: [["<< #{l(:label_me)} >>", 'me']]
+      add_available_filter 'watcher_id', type: :list, values: author_values
     end
   end
 
@@ -319,29 +302,29 @@ class AgileQuery < Query
     @available_columns = self.class.available_columns.dup
     @available_columns += (project ? project.all_issue_custom_fields : IssueCustomField).visible.collect { |cf| QueryCustomFieldColumn.new(cf) }
 
-    if User.current.allowed_to?(:view_time_entries, project, :global => true)
+    if User.current.allowed_to?(:view_time_entries, project, global: true)
       index = nil
-      @available_columns.each_with_index {|column, i| index = i if column.name == :estimated_hours}
+      @available_columns.each_with_index { |column, i| index = i if column.name == :estimated_hours}
       index = (index ? index + 1 : -1)
       # insert the column after estimated_hours or at the end
       @available_columns.insert index, QueryColumn.new(:spent_hours,
-        :sortable => "COALESCE((SELECT SUM(hours) FROM #{TimeEntry.table_name} WHERE #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id), 0)",
-        :default_order => 'desc',
-        :caption => :label_spent_time
+        sortable: "COALESCE((SELECT SUM(hours) FROM #{TimeEntry.table_name} WHERE #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id), 0)",
+        default_order: 'desc',
+        caption: :label_spent_time
       )
     end
 
-    if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
-      User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
-      @available_columns << QueryColumn.new(:is_private, :sortable => "#{Issue.table_name}.is_private")
+    if User.current.allowed_to?(:set_issues_private, nil, global: true) ||
+      User.current.allowed_to?(:set_own_issues_private, nil, global: true)
+      @available_columns << QueryColumn.new(:is_private, sortable: "#{Issue.table_name}.is_private")
     end
 
-    disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')}
-    @available_columns.reject! {|column|
+    disabled_fields = Tracker.disabled_core_fields(trackers).map { |field| field.sub(/_id$/, '')}
+    @available_columns.reject! { |column|
       disabled_fields.include?(column.name.to_s)
     }
 
-    @available_columns.reject! {|column| column.name == :done_ratio} unless Issue.use_field_for_done_ratio?
+    @available_columns.reject! { |column| column.name == :done_ratio} unless Issue.use_field_for_done_ratio?
 
     @available_columns
   end
@@ -359,11 +342,12 @@ class AgileQuery < Query
   end
 
   def has_column_name?(name)
-    columns.detect{|c| c.name == name}
+    columns.detect { |c| c.name == name}
   end
 
   def groupable_columns
-    available_columns.select {|c| c.groupable && !c.is_a?(QueryCustomFieldColumn)}
+    groupable_method = Redmine::VERSION.to_s > '4.2' ? :groupable? : :groupable
+    available_columns.select { |c| c.public_send(groupable_method) && !c.is_a?(QueryCustomFieldColumn) }
   end
 
   def sql_for_issue_id_field(field, operator, value)
@@ -387,7 +371,7 @@ class AgileQuery < Query
   end
 
   def sql_for_version_status_field(field, operator, value)
-     sql_for_field(field, operator, value, Version.table_name, "status")
+    sql_for_field(field, operator, value, Version.table_name, "status")
   end
 
   def sql_for_has_sub_issues_field(field, operator, value)
@@ -398,22 +382,25 @@ class AgileQuery < Query
   end
 
   def sql_for_parent_issue_id_field(field, operator, value, options={})
-    value = value.first.split(",") if value.is_a? Array
-    value = value.split(",") if value.is_a? String
+    value = value.first.split(',') if value.is_a? Array
+    value = value.split(',') if value.is_a? String
     sql = case operator
-      when "*", "!*", "=", "!"
-        sql_for_field(field, operator, value, queried_table_name, "parent_id")
-      when "=p", "=!p", "!p"
-        op = (operator == "!p" ? 'NOT IN' : 'IN')
-        comp = (operator == "=!p" ? '<>' : '=')
-        "#{Issue.table_name}.parent_id #{op} (SELECT DISTINCT #{Issue.table_name}.id FROM #{Issue.table_name} WHERE #{Issue.table_name}.project_id #{comp} #{value.first.to_i})"
-      end
+          when '*', '!*', '=', '!'
+            sql_for_field(field, operator, value, queried_table_name, 'parent_id')
+          when '=p', '=!p', '!p'
+            op = (operator == '!p' ? 'NOT IN' : 'IN')
+            comp = (operator == '=!p' ? '<>' : '=')
+            "#{Issue.table_name}.parent_id #{op} (SELECT DISTINCT #{Issue.table_name}.id FROM #{Issue.table_name} WHERE #{Issue.table_name}.project_id #{comp} #{value.first.to_i})"
+          when '*o', '!o'
+            op = (operator == '!o' ? 'NOT IN' : 'IN')
+            "#{Issue.table_name}.parent_id #{op} (SELECT DISTINCT #{Issue.table_name}.id FROM #{Issue.table_name} WHERE #{Issue.table_name}.status_id IN (SELECT DISTINCT #{IssueStatus.table_name}.id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_false}))"
+          end
     "(#{sql})"
   end
 
   def sql_for_parent_issue_tracker_id_field(field, operator, value)
     cond = if operator == '=' then '' else 'NOT' end
-    selected_trackers_ids = Tracker.where(:name => value).pluck(:id).join(',')
+    selected_trackers_ids = Tracker.where(name: value).pluck(:id).join(',')
     "( EXISTS (SELECT * FROM #{Issue.table_name} AS parents WHERE parents.tracker_id #{cond} IN (#{selected_trackers_ids}) AND parents.id = issues.parent_id ) )"
   end
 
@@ -455,16 +442,58 @@ class AgileQuery < Query
     end
   end
 
+  def sql_for_relations(field, operator, value, options={})
+    relation_options = IssueRelation::TYPES[field]
+    return relation_options unless relation_options
+
+    relation_type = field
+    join_column, target_join_column = "issue_from_id", "issue_to_id"
+    if relation_options[:reverse] || options[:reverse]
+      relation_type = relation_options[:reverse] || relation_type
+      join_column, target_join_column = target_join_column, join_column
+    end
+
+    sql = case operator
+      when "*", "!*"
+        op = (operator == "*" ? 'IN' : 'NOT IN')
+        "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}')"
+      when "=", "!"
+        op = (operator == "=" ? 'IN' : 'NOT IN')
+        "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
+      when "=p", "=!p", "!p"
+        op = (operator == "!p" ? 'NOT IN' : 'IN')
+        comp = (operator == "=!p" ? '<>' : '=')
+        "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
+      when "*o", "!o"
+        op = (operator == "!o" ? 'NOT IN' : 'IN')
+        "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_false}))"
+      end
+
+    if relation_options[:sym] == field && !options[:reverse]
+      sqls = [sql, sql_for_relations(field, operator, value, :reverse => true)]
+      sql = sqls.join(["!", "!*", "!p"].include?(operator) ? " AND " : " OR ")
+    end
+    "(#{sql})"
+  end
+
+  IssueRelation::TYPES.keys.each do |relation_type|
+    alias_method "sql_for_#{relation_type}_field".to_sym, :sql_for_relations
+  end
+
   def condition_for_status
     if Redmine::VERSION.to_s > '2.4'
-      return {:status_id => options[:f_status] || IssueStatus.where(:is_closed => false)}
+      return { status_id: options[:f_status] || IssueStatus.where(is_closed: false) }
     end
     '1=1'
   end
 
   def issues(options={})
+    @issues_cache ||= {}
+    return @issues_cache[options.to_s] if @issues_cache.has_key?(options.to_s)
+
     order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?)
-    scope = issue_scope.
+    scope = options[:scope] ? options[:scope] : issue_scope
+    scope = scope.
       joins(:status).
       eager_load((options[:include] || []).uniq).
       where(options[:conditions]).
@@ -474,20 +503,7 @@ class AgileQuery < Query
       offset(options[:offset])
 
     scope = scope.preload(:custom_values)
-    if has_column?(:author)
-      scope = scope.preload(:author)
-    end
-    if color_base == AgileColor::COLOR_GROUPS[:issue]
-      scope = scope.preload(:agile_color)
-    end
-
-    if color_base == AgileColor::COLOR_GROUPS[:priority]
-      scope = scope.preload(:priority => :agile_color)
-    end
-
-    if color_base == AgileColor::COLOR_GROUPS[:tracker]
-      scope = scope.preload(:tracker => :agile_color)
-    end
+    scope = scope.preload(:author) if has_column?(:author)
 
     if has_column_name?(:checklists)
       scope = scope.preload(:checklists)
@@ -516,8 +532,7 @@ class AgileQuery < Query
         :journal_details => {:prop_key => 'status_id'}).order("created_on DESC")
     end
 
-
-    scope
+    @issues_cache[options.to_s] = scope
     rescue ::ActiveRecord::StatementInvalid => e
       raise StatementInvalid.new(e.message)
   end
@@ -526,6 +541,10 @@ class AgileQuery < Query
     @issues_ids ||= scope.map(&:id)
   end
 
+  def issues_paginator(issues, page = nil)
+    Redmine::Pagination::Paginator.new(issues.count, 20, page)
+  end
+
   def journals_for_state
     @journals_for_state
   end
@@ -542,62 +561,41 @@ class AgileQuery < Query
         if Redmine::VERSION.to_s >= '3.4' && project
           project.rolled_up_statuses
         else
-          IssueStatus.where(:id => Tracker.eager_load(:issues => [:status, :project, :fixed_version]).where(statement).map(&:issue_statuses).flatten.uniq.map(&:id))
+          IssueStatus.where(id: Tracker.eager_load(issues: [:status, :project, :fixed_version]).where(statement).map(&:issue_statuses).flatten.uniq.map(&:id))
         end
       status_filter_values = (options[:f_status] if options)
       if status_filter_values
-        result_statuses = statuses.where(:id => status_filter_values)
+        result_statuses = statuses.where(id: status_filter_values)
       else
-        result_statuses = statuses.where(:is_closed => false)
+        result_statuses = statuses.where(is_closed: false)
       end
       result_statuses.sorted.map do |s|
         s.instance_variable_set "@issue_count", self.issue_count_by_status[s.id].to_i
         if has_column_name?(:estimated_hours)
           s.instance_variable_set "@estimated_hours_sum", self.issue_count_by_estimated_hours[s.id].to_f
         end
-        if has_column_name?(:story_points)
+        if RedmineAgile.use_story_points? && has_column_name?(:story_points)
           s.instance_variable_set "@story_points", self.issue_count_by_story_points[s.id].to_i
         end
-        if options && options[:wp]
-          wp_string = options[:wp][s.id.to_s]
-          if /(\d+)-?(\d*)/i =~ wp_string
-            s.instance_variable_set("@wp_max", $2.blank? ? $1.to_i : $2.to_i)
-            s.instance_variable_set("@wp_min", $1.to_i) if  !$1.blank? && !$2.blank?
-          end
-        end
-
-        def s.over_wp_limit?
-          return false if @wp_max.blank?
-          @wp_max.to_i < @issue_count
-        end
-
-        def s.under_wp_limit?
-          return false if @wp_min.blank?
-          @wp_min.to_i > @issue_count
-        end
-
-        def s.wp_class
-          return 'over_wp_limit' if over_wp_limit?
-          'under_wp_limit' if under_wp_limit?
-        end
         s
       end
     else
       status_filter_operator = filters.fetch("status_id", {}).fetch(:operator, nil)
       status_filter_values = filters.fetch("status_id", {}).fetch(:values, [])
-      statuses = IssueStatus.where(:id => Tracker.eager_load(:issues => [:status, :project, :fixed_version]).where(statement).map(&:issue_statuses).flatten.uniq.map(&:id))
-      result_statuses = case status_filter_operator
-      when "o"
-        statuses.where(:is_closed => false).sorted
-      when "c"
-        statuses.where(:is_closed => true).sorted
-      when "="
-        statuses.where(:id => status_filter_values).sorted
-      when "!"
-        statuses.where("#{IssueStatus.table_name}.id NOT IN (" + status_filter_values.map{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + ")").sorted
-      else
-        statuses.sorted
-      end
+      statuses = IssueStatus.where(id: Tracker.eager_load(issues: [:status, :project, :fixed_version]).where(statement).map(&:issue_statuses).flatten.uniq.map(&:id))
+      result_statuses =
+        case status_filter_operator
+        when "o"
+          statuses.where(is_closed: false).sorted
+        when "c"
+          statuses.where(is_closed: true).sorted
+        when "="
+          statuses.where(id: status_filter_values).sorted
+        when "!"
+          statuses.where("#{IssueStatus.table_name}.id NOT IN (" + status_filter_values.map{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + ")").sorted
+        else
+          statuses.sorted
+        end
       result_statuses.map do |s|
         s.instance_variable_set "@issue_count", self.issue_count_by_status[s.id].to_i
         if has_column_name?(:estimated_hours)
@@ -608,19 +606,6 @@ class AgileQuery < Query
       s
     end
   end
-  def swimlanes
-    return [] unless self.grouped?
-    lane_ids = issue_scope.group(self.group_by_column.groupable).count.keys
-    lanes = Issue.reflect_on_association(self.group_by_column.name).klass.where(:id => lane_ids).reorder(group_by_sort_order)
-    lanes = lanes.eager_load(:agile_data) if self.group_by_column.name == :parent
-    lanes = lanes.to_a
-    lanes << nil if lane_ids.include?(nil)
-    lanes
-  end
-
-  def issue_count_by_swimlane
-    @issue_count_by_swimlane ||= issue_scope.group("#{Issue.table_name}.#{self.group_by_column.name}_id").count if self.grouped?
-  end
 
   def issue_count_by_status
     @issue_count_by_status ||= issue_scope.group("#{Issue.table_name}.status_id").count
@@ -634,68 +619,17 @@ class AgileQuery < Query
     @issue_count_by_story_points ||= issue_scope.group("#{Issue.table_name}.status_id").sum("#{AgileData.table_name}.story_points")
   end
 
-  def issue_board(options={})
+  def issue_board
     @truncated = RedmineAgile.board_items_limit <= issue_scope.count
     all_issues = self.issues.limit(RedmineAgile.board_items_limit).sorted_by_rank
-    grouped_issues = grouped? ? all_issues.group_by{|i| [i.status_id, i.send("#{self.group_by_column.name}_id")]} : all_issues.group_by{|i| [i.status_id]}
-    grouped_issues.values.each{|x|x.sort!{|a,b| a.position.to_i <=> b.position.to_i}} if grouped?
-    grouped_issues
-          end
-
-  # Function for filter with relations
-
-  def sql_for_relations(field, operator, value, options={})
-    relation_options = IssueRelation::TYPES[field]
-    return relation_options unless relation_options
-
-    relation_type = field
-    join_column, target_join_column = "issue_from_id", "issue_to_id"
-    if relation_options[:reverse] || options[:reverse]
-      relation_type = relation_options[:reverse] || relation_type
-      join_column, target_join_column = target_join_column, join_column
-    end
-
-    sql = case operator
-      when "*", "!*"
-        op = (operator == "*" ? 'IN' : 'NOT IN')
-        "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}')"
-      when "=", "!"
-        op = (operator == "=" ? 'IN' : 'NOT IN')
-        "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
-      when "=p", "=!p", "!p"
-        op = (operator == "!p" ? 'NOT IN' : 'IN')
-        comp = (operator == "=!p" ? '<>' : '=')
-        "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
-      when "*o", "!o"
-        op = (operator == "!o" ? 'NOT IN' : 'IN')
-        "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{self.class.connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{self.class.connection.quoted_false}))"
+    all_issues.group_by{|i| [i.status_id]}
       end
 
-    if relation_options[:sym] == field && !options[:reverse]
-      sqls = [sql, sql_for_relations(field, operator, value, :reverse => true)]
-      sql = sqls.join(["!", "!*", "!p"].include?(operator) ? " AND " : " OR ")
-    end
-    "(#{sql})"
-  end
-
-  IssueRelation::TYPES.keys.each do |relation_type|
-    alias_method "sql_for_#{relation_type}_field".to_sym, :sql_for_relations
-  end
-
-  def draw_relations
-    r = options[:draw_relations]
-    r.nil? || r == '1'
-  end
-
-  def draw_relations=(arg)
-    options[:draw_relations] = (arg == '0' ? '0' : nil)
-  end
-
   def statement
     if values_for('fixed_version_id') == ['current_version'] && project
       version = current_version
       # substitute id for current version
-      filters['fixed_version_id'][:values] = [version.id.to_s] if version
+      version ? filters['fixed_version_id'][:values] = [version.id.to_s] : filters.delete('fixed_version_id')
     end
     clauses = super
     if version
@@ -705,31 +639,41 @@ class AgileQuery < Query
     clauses
   end
 
-private
+  private
+
+  def base_agile_query_scope
+    Issue.visible
+         .eager_load(:status, :project, :assigned_to, :tracker, :priority, :category, :fixed_version, :agile_data)
+         .where(agile_projects)
+         .where(statement)
+         .where(condition_for_status)
+  end
+
+  def agile_projects
+    return '1=1' unless project
+
+    p_ids = [project.id]
+    p_ids += project.descendants.select { |sub| sub.module_enabled?('agile') }.map(&:id) if Setting.display_subprojects_issues?
+
+    "#{Project.table_name}.id IN (#{p_ids.join(',')})"
+  end
+
   def issue_scope
-    Issue.visible.
-      eager_load(:status,
-                 :project,
-                 :assigned_to,
-                 :tracker,
-                 :priority,
-                 :category,
-                 :fixed_version,
-                 :agile_data).
-      where(statement).
-      where(condition_for_status)
+    return @agile_scope if @agile_scope
+
+    @agile_scope = base_agile_query_scope
+    @agile_scope
+  end
+
+  def project_statement
+      return super
   end
 
   def current_version
     return @current_version if @current_version
+
     versions = project.shared_versions.open.where("LOWER(#{Version.table_name}.name) NOT LIKE LOWER(?)", 'backlog')
     versions -= versions.select(&:completed?).reverse
     @current_version = versions.to_a.uniq.sort.first
   end
-  def set_default_when_appropriate
-    if options[:is_default]
-      set_as_default
-    end
-  end
-
 end
diff --git a/plugins/redmine_agile/app/models/agile_versions_query.rb b/plugins/redmine_agile/app/models/agile_versions_query.rb
deleted file mode 100644
index 53c18b9..0000000
--- a/plugins/redmine_agile/app/models/agile_versions_query.rb
+++ /dev/null
@@ -1,150 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-
-class AgileVersionsQuery < Query
-  unloadable
-
-  self.queried_class = Issue
-  self.view_permission = :view_issues if Redmine::VERSION.to_s >= '3.4'
-
-  self.available_columns = [
-    QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
-    QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
-    QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true),
-    QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement("users")}, :groupable => true),
-    QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => "#{Issue.table_name}.category_id"),
-    QueryColumn.new(:status, :groupable => true, :caption => :field_invoice_status),
-    QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => "#{Issue.table_name}.assigned_to_id")  
-  ]
-
-  def initialize(attributes=nil, *args)
-    super attributes
-    self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
-  end
-
-  def initialize_available_filters
-    principals = []
-    categories = []
-    issue_custom_fields = []
-
-    if project
-      principals += project.principals.sort
-      unless project.leaf?
-        subprojects = project.descendants.visible.all
-        principals += Principal.member_of(subprojects)
-      end
-      categories = project.issue_categories.all
-      issue_custom_fields = project.all_issue_custom_fields
-    else
-      if all_projects.any?
-        principals += Principal.member_of(all_projects)
-      end
-      issue_custom_fields = IssueCustomField.where(:is_for_all => true)
-    end
-    principals.uniq!
-    principals.sort!
-    users = principals.select {|p| p.is_a?(User)}
-
-    add_available_filter "tracker_id",
-      :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] }
-    add_available_filter "priority_id",
-      :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
-
-    author_values = []
-    author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
-    author_values += users.collect{|s| [s.name, s.id.to_s] }
-    add_available_filter("author_id",
-      :type => :list, :values => author_values
-    ) unless author_values.empty?
-
-    assigned_to_values = []
-    assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
-    assigned_to_values += (Setting.issue_group_assignment? ?
-                              principals : users).collect{|s| [s.name, s.id.to_s] }
-    add_available_filter("assigned_to_id",
-      :type => :list_optional, :values => assigned_to_values
-    ) unless assigned_to_values.empty?
-
-    if categories.any?
-      add_available_filter "category_id",
-        :type => :list_optional,
-        :values => categories.collect{|s| [s.name, s.id.to_s] }
-    end
-
-    add_available_filter "status_id",
-      :type => :list_status, :values => IssueStatus.sorted.collect{|s| [s.name, s.id.to_s] }
-
-    add_available_filter "estimated_hours", :type => :float
-    add_custom_fields_filters(issue_custom_fields)
-
-    add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
-  end
-
-  def backlog_version
-    @backlog_version = project.shared_versions.open.where("LOWER(#{Version.table_name}.name) LIKE LOWER(?)", "backlog").first ||
-        project.shared_versions.open.where(:effective_date => nil).first ||
-        project.shared_versions.open.order("effective_date ASC").first
-  end
-
-  def backlog_version_issues
-    return [] if backlog_version.blank?
-    backlog_version.fixed_issues.visible.joins(query_includes).where(statement).sorted_by_rank
-          end
-
-  def current_version
-    @current_version = Version.open.
-        where(:project_id => project).
-        where("#{Version.table_name}.id <> ?", self.backlog_version).
-        order("effective_date DESC").first
-  end
-
-  def current_version_issues
-    return [] if current_version.blank?
-    current_version.fixed_issues.visible.joins(query_includes).where(statement).sorted_by_rank
-          end
-
-  def no_version_issues(params={})
-    q = (params[:q] || params[:term]).to_s.strip
-    scope = Issue.visible.joins(query_includes)
-            if project
-      project_ids = [project.id]
-      project_ids += project.descendants.collect(&:id) if Setting.display_subprojects_issues?
-      scope = scope.where(:project_id => project_ids)
-    end
-    scope = scope.where(statement).where(:fixed_version_id => nil).sorted_by_rank
-            if q.present?
-      if q.match(/^#?(\d+)\z/)
-        scope = scope.where("(#{Issue.table_name}.id = ?) OR (LOWER(#{Issue.table_name}.subject) LIKE LOWER(?))", $1.to_i,"%#{q}%")
-      else
-        scope = scope.where("LOWER(#{Issue.table_name}.subject) LIKE LOWER(?)", "%#{q}%")
-      end
-    end
-    scope
-  end
-
-  def version_issues(version)
-    version.fixed_issues.visible.joins(query_includes).where(statement).sorted_by_rank
-          end
-  private
-
-  def query_includes
-    [:project]
-  end
-end
diff --git a/plugins/redmine_agile/app/views/agile_boards/_add_issue_card.html.erb b/plugins/redmine_agile/app/views/agile_boards/_add_issue_card.html.erb
index bb7b80e..8b13789 100644
--- a/plugins/redmine_agile/app/views/agile_boards/_add_issue_card.html.erb
+++ b/plugins/redmine_agile/app/views/agile_boards/_add_issue_card.html.erb
@@ -1,6 +1 @@
 
-<% if User.current.allowed_to?(:add_issues, @project, :global => true) && @project && RedmineAgile.allow_create_card? && @project.trackers.any? %>
-  <div class="add-issue">
-    <input class="new-card__input" placeholder='<%= l(:label_agile_add_new_issue) %>' >
-  </div>
-<% end %>
diff --git a/plugins/redmine_agile/app/views/agile_boards/_board.html.erb b/plugins/redmine_agile/app/views/agile_boards/_board.html.erb
index ea98ab8..c60d1b5 100644
--- a/plugins/redmine_agile/app/views/agile_boards/_board.html.erb
+++ b/plugins/redmine_agile/app/views/agile_boards/_board.html.erb
@@ -1,7 +1,7 @@
+
 <%= form_tag({}) do -%>
   <%= hidden_field_tag 'back_url', url_for(params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params) %>
   <%= hidden_field_tag 'project_id', @project.id if @project %>
-  <% board_statuses = IssueStatus.sorted %>
   <div class="agile-board autoscroll">
     <div class="flash error" style="display: none;" id="agile-board-errors">
     </div>
@@ -10,42 +10,17 @@
       <thead>
       <%= render_board_headers(@board_columns) %>
       </thead>
-      <% if @swimlanes.size > 0 %>
-        <% @swimlanes.each do |swimlane| %>
-          <tr class="group open swimlane" data-id="<%= swimlane && swimlane.id || swimlane %>">
-            <td colspan="<%= @board_columns.size %>">
-              <span class="expander" onclick="toggleRowGroup(this);">&nbsp;</span>
-              <%= swimlane.blank? ? l(:label_none) : format_swimlane_object(swimlane) %><span class="count"><%= @query.issue_count_by_swimlane[swimlane && swimlane.id || swimlane] %></span>
-              <%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}",
-                                   "toggleAllRowGroups(this)", :class => 'toggle-all') %>
-            </td>
-          </tr>
-
-          <tr style="text-align: center;white-space: nowrap;" class="swimlane issue <%= cycle('odd', 'even') %>" data-id="<%= swimlane && swimlane.id || swimlane %>" data-field="<%= @query.grouped? && "#{@query.group_by_column.name}_id" %>">
-          <% @board_columns.each do |column| %>
-            <td class="issue-status-col <%= 'closed' if column.is_closed? %> <%= column.wp_class if column.respond_to?(:wp_class) %>" data-id="<%= column.id %>">
-            <% @issue_board[[column.id, swimlane && swimlane.id || swimlane]].each do |issue| %>
-              <%= render :partial => 'issue_card', :locals => {:issue => issue} %>
-            <% end if  @issue_board[[column.id, swimlane && swimlane.id || swimlane]] %>
-            </td>
-            <% end %>
-          </tr>
-        <% end %>
-      <% else %>
-          <tr style="text-align: center;white-space: nowrap;" class="issue <%= cycle('odd', 'even') %>">
-          <% @board_columns.each do |column| %>
-            <td class="issue-status-col <%= 'closed' if column.is_closed? %> <%= 'collapse' if RedmineAgile.hide_closed_issues_data? %> <%= column.wp_class if column.respond_to?(:wp_class) %>" data-id="<%= column.id %>">
-              <% @issue_board[[column.id]].each do |issue| %>
+        <tr style="text-align: center;white-space: nowrap;" class="issue <%= cycle('odd', 'even') %>">
+        <% @board_columns.each do |column| %>
+          <% column_issues = @issue_board[[column.id]] || [] %>
+            <td class="issue-status-col <%= 'closed' if column.is_closed? %> <%= 'collapse' if RedmineAgile.hide_closed_issues_data? %> <%= column.wp_class if column.respond_to?(:wp_class) %> <%= 'empty' if column_issues.empty? %>" data-id="<%= column.id %>">
+              <% column_issues.each do |issue| %>
                 <%= render :partial => 'issue_card', :locals => {:issue => issue} %>
               <% end if @issue_board[[column.id]] %>
-              <%= render(:partial => 'add_issue_card') unless column.is_closed? %>
+              <%= render(:partial => 'add_issue_card') if @allowed_statuses.include?(column) && !column.is_closed? %>
             </td>
-
-            <% end %>
-          </tr>
-      <% end %>
-
+          <% end %>
+        </tr>
     </table>
 </div>
-
 <% end %>
diff --git a/plugins/redmine_agile/app/views/agile_boards/_index.html.erb b/plugins/redmine_agile/app/views/agile_boards/_index.html.erb
index 3bd1aa0..8afe358 100644
--- a/plugins/redmine_agile/app/views/agile_boards/_index.html.erb
+++ b/plugins/redmine_agile/app/views/agile_boards/_index.html.erb
@@ -1,78 +1,55 @@
 <div class="contextual">
-<% if !@query.new_record? && @query.editable_by?(User.current) %>
-  <%= link_to l(:button_edit), edit_agile_query_path(@query), :class => 'icon icon-edit' %>
-  <%= delete_link agile_query_path(@query) %>
-<% end %>
+  <%= link_to l(:label_agile_charts), @project ? project_agile_charts_path(project_id: @project) : agile_charts_path, class: 'icon icon-stats agile_charts_link', onclick: 'chartLinkGenerator();' %>
+  <% if !@query.new_record? && @query.editable_by?(User.current) %>
+    <%= link_to l(:button_edit), edit_agile_query_path(@query), class: 'icon icon-edit' %>
+    <%= delete_link agile_query_path(@query) %>
+  <% end %>
 </div>
 
 <% html_title(@query.new_record? ? l(:label_agile_board) : @query.name) %>
-<h2>
-  <%= @query.new_record? ? l(:label_agile_board) : h(@query.name) %>
-  <span class="live_search">
-    <%= text_field_tag(:search, '', :id => 'agile_live_search', :class => 'live_search_field', :placeholder => l(:label_cards_search)) %>
-  </span>
-</h2>
+<%= form_tag({ controller: 'agile_boards', action: 'index', project_id: @project }, method: :get, id: 'query_form', onsubmit: 'DisableNullFields()') do %>
+  <h2>
+    <%= @query.new_record? ? l(:label_agile_board) : h(@query.name) %>
+      <span class="live_search">
+        <%= text_field_tag(:search, '', :id => 'agile_live_search', :class => 'live_search_field', :placeholder => l(:label_cards_search)) %>
+      </span>
+  </h2>
 
-<%= form_tag({ :controller => 'agile_boards', :action => 'index', :project_id => @project },
-            :method => :get, :id => 'query_form', :onsubmit => 'DisableNullFields()') do %>
   <div id="query_form_with_buttons" class="hide-when-print">
     <%= hidden_field_tag 'set_filter', '1' %>
     <div id="query_form_content">
       <fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
-        <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
+        <legend class="icon icon-<%= @query.new_record? ? 'expended' : 'collapsed' %>" onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
         <div style="<%= @query.new_record? ? "" : "display: none;" %>">
           <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
         </div>
       </fieldset>
       <fieldset id="options" class="collapsible collapsed">
-        <legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
+        <legend onclick="toggleFieldset(this);" class="icon icon-collapsed"><%= l(:label_options) %></legend>
         <div style="display: none;">
-          <table class="options">
-            <% if Redmine::VERSION.to_s > '2.4' %>
-              <tr>
-                <td>
-                  <%= l(:label_agile_board_columns) %>
-                </td>
-                <td class="card-fields">
-                  <%= render_board_fields_status(@query) %>
-                </td>
-              </tr>
-            <% end %>
+          <table class="options agile_options">
             <tr>
-              <td><%= l(:label_agile_fields) %></td>
-              <td class="card-fields">
-                <%= render_board_fields_selection(@query) %>
-              </td>
+              <td colspan="2">
+                <fieldset class="card-fields">
+                  <legend><%= l(:label_agile_board_columns) %></legend>
+                  <%= render_board_fields_status(@query) %>
+                </fieldset>
             </tr>
             <tr>
-              <td><label for='group_by'><%= l(:label_agile_swimlanes) %></label></td>
-              <td><%= select_tag('group_by',
-                                 options_for_select(
-                                   [[]] + @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]},
-                                   @query.group_by)
-                         ) %></td>
+              <td colspan="2">
+                <fieldset class="card-fields">
+                  <legend><%= l(:label_agile_fields) %></legend>
+                  <%= render_board_fields_selection(@query) %>
+                </fieldset>
+              </td>
             </tr>
-            <% if RedmineAgile.use_colors? && @query.respond_to?(:color_base) %>
-              <tr>
-                <td><label for='color_base'><%= l(:label_agile_color_based_on) %></label></td>
-                <td><%= select_tag 'color_base', options_card_colors_for_select(@query.color_base) %></td>
-              </tr>
-            <% end %>
           </table>
         </div>
       </fieldset>
     </div>
     <p class="buttons">
-    <span class="contextual">
-      <%= link_to_function l(:label_agile_fullscreen), '$("html").toggleClass("agile-board-fullscreen"); saveFullScreenState()', :class => 'icon icon-fullscreen' %>
-    </span>
     <%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %>
     <%= link_to l(:button_clear), { :set_filter => 1, :project_id => @project }, :class => 'icon icon-reload'  %>
-    <% if @query.new_record? && User.current.allowed_to?(:add_agile_queries, @project, :global => true) %>
-        <%= link_to_function l(:button_save),
-                             "$('#query_form').attr('action', '#{ @project ? new_project_agile_query_path(@project) : new_agile_query_path }').submit()",
-                             :class => 'icon icon-save' %>
-    <% end %>
     </p>
 
   </div>
@@ -80,7 +57,8 @@
 
 <%= error_messages_for 'query' %>
 <% if @query.valid? %>
-  <% if @issues.empty? || @board_columns.empty? %>
+  <% empty_data = @issues.empty? || @board_columns.empty? %>
+  <% if empty_data %>
     <p class="nodata"><%= l(:label_no_data) %></p>
   <% else %>
     <% if @query.truncated %>
@@ -91,6 +69,9 @@
 <% end %>
 
 <% content_for :sidebar do %>
+ 
+  <%= render :partial => 'upgrade_to_pro' %>
+ 
   <%= render :partial => 'issues_links' %>
   <% if @project && @project.assignable_users.any? %>
     <%= render :partial => 'members' %>
@@ -99,22 +80,19 @@
 <% end %>
 
 <% html_title l(:label_agile_board) %>
-<%= javascript_tag "agileContextMenuInit('#{ url_for(issues_context_menu_path) }')" %>
 <% content_for :header_tags do %>
-  <%= javascript_include_tag "redmine_agile", :plugin => 'redmine_agile' %>
-  <%= javascript_include_tag "jquery.ui.touch-punch.js", :plugin => 'redmine_agile' %>
-  <%= javascript_include_tag "visibility.min.js", :plugin => 'redmine_agile' %>
-  <%= javascript_include_tag "redmine_agile_context_menu", :plugin => 'redmine_agile' %>
+  <%= javascript_include_tag "redmine_agile", plugin: 'redmine_agile' %>
+  <%= javascript_include_tag "jquery.ui.touch-punch.js", plugin: 'redmine_agile' %>
   <%= stylesheet_link_tag 'context_menu' %>
-  <%= stylesheet_link_tag "redmine_agile.css", :plugin => "redmine_agile", :media => "print" %>
+  <%= stylesheet_link_tag "redmine_agile.css", plugin: "redmine_agile", media: "print" %>
 <% end %>
-<% if User.current.allowed_to?(:edit_issues, @project, :global => true) %>
+<% if User.current.allowed_to?(:edit_issues, @project, global: true) %>
   <script type="text/javascript">
     var agileBoard = new AgileBoard({
       project_id: '<%= @project && @project.id %>',
       update_agile_board_path: '<%= escape_javascript update_agile_board_path %>',
       issues_path: '<%= escape_javascript issues_path %>',
-      create_issue_path: '<%= escape_javascript(agile_create_issue_path(:project_id => @project)) if @project %>'
+      create_issue_path: '<%= escape_javascript(agile_create_issue_path(project_id: @project)) if @project %>'
     });
   </script>
 <% end %>
diff --git a/plugins/redmine_agile/app/views/agile_boards/_issue_card.html.erb b/plugins/redmine_agile/app/views/agile_boards/_issue_card.html.erb
index a674c5c..9dc7ec1 100644
--- a/plugins/redmine_agile/app/views/agile_boards/_issue_card.html.erb
+++ b/plugins/redmine_agile/app/views/agile_boards/_issue_card.html.erb
@@ -1,5 +1,10 @@
-<div class="issue-card hascontextmenu <%= agile_color_class(issue, :color_base => @query.respond_to?(:color_base) && @query.color_base) %> <%= class_for_closed_issue(issue) %>" data-id="<%= issue.id %>" style="<%= agile_user_color(issue.assigned_to, :color_base => @query.respond_to?(:color_base) && @query.color_base) if issue.assigned_to %>">
-    <% if issue.closed? && RedmineAgile.hide_closed_issues_data? %>
+<div class="issue-card hascontextmenu <%= agile_color_class(issue, :color_base => @query.respond_to?(:color_base) && @query.color_base) %> <%= class_for_closed_issue(issue, @version_board) %>"
+     data-id="<%= issue.id %>"
+     data-estimated-hours="<%= estimated_time_value(@query, issue).to_f %>"
+     data-story-points="<%= story_points_value(@query, issue).to_f %>"
+     style="<%= agile_user_color(issue.assigned_to, :color_base => @query.respond_to?(:color_base) && @query.color_base) if issue.assigned_to %>">
+
+  <% if issue.closed? && RedmineAgile.hide_closed_issues_data? && !@version_board %>
       <span class="fields">
          <div class="tooltip">
            <p class="issue-id <%= 'without-tracker' if @query.has_column_name?(:tracker).blank? %>">
@@ -11,10 +16,9 @@
       </span>
     <% else %>
       <span class="fields">
-          <% if User.current.allowed_to?(:edit_issues, @project, :global => true) && RedmineAgile.allow_inline_comments?  %>
+          <% if User.current.allowed_to?(:edit_issues, @project, :global => true) && !@version_board %>
             <div class="quick-edit-card">
-              <%# link_to image_tag("/images/edit.png"), edit_issue_path(issue), :class => "", :style => "margin-right: 3px;", :title => l(:button_edit) %>
-              <%= link_to image_tag("/images/comment.png", :alt => "Edit"), "#", :onclick => "showInlineComment(this, '#{j(agile_inline_comment_path(:id => issue))}'); return false;", :title => l(:label_comment_add), :class => 'add-comment' %>
+              <%= link_to image_tag("/images/comment.png", alt: 'Comment'), '#', onclick: "showInlineComment(this, '#{j(agile_inline_comment_path(:id => issue))}'); return false;", title: l(:label_comment_add), class: 'add-comment' if RedmineAgile.allow_inline_comments? %>
             </div>
           <% end %>
           <% if @query.has_column_name?(:project) %>
@@ -131,7 +135,7 @@
     </script>
   <% end %>
 
-  <% if @update %>
+  <% if @update && !@version_board %>
     <script type="text/javascript">
       $("table.issues-board thead").html("<%=escape_javascript render_board_headers(@query.board_statuses) %>");
     </script>
@@ -139,19 +143,6 @@
       <script type="text/javascript">
         agileBoard.initDroppable();
       </script>
-      <script type="text/javascript">
-        $("td.issue-status-col[data-id=<%= issue.status.id %>]").removeClass("over_wp_limit under_wp_limit");
-        $("td.issue-status-col[data-id=<%= issue.status.id %>]").addClass("<%= @wp_class %>");
-        <% if @old_status %>
-          $("td.issue-status-col[data-id=<%= @old_status.id %>]").removeClass("over_wp_limit under_wp_limit");
-          $("td.issue-status-col[data-id=<%= @old_status.id %>]").addClass("<%= @wp_class_for_old_status %>");
-        <% end %>
-      </script>
-    <% end %>
-    <% if @not_in_scope %>
-      <script type="text/javascript">
-        $(".issue-card[data-id='<%= issue.id %>']").toggle( "highlight" );
-      </script>
     <% end %>
   <% end %>
   <%= init_agile_tooltip_info %>
diff --git a/plugins/redmine_agile/app/views/agile_boards/_issues_links.html.erb b/plugins/redmine_agile/app/views/agile_boards/_issues_links.html.erb
index 60ac827..d25a0ff 100644
--- a/plugins/redmine_agile/app/views/agile_boards/_issues_links.html.erb
+++ b/plugins/redmine_agile/app/views/agile_boards/_issues_links.html.erb
@@ -1,21 +1,17 @@
 <h3><%= l(:label_issue_plural) %></h3>
 
-  <ul>
+<ul>
   <li><%= link_to l(:label_issue_view_all), _project_issues_path(@project, :set_filter => 1) %></li>
   <% if @project %>
-  <li><%= link_to l(:field_summary), project_issues_report_path(@project) %></li>
+    <li><%= link_to l(:field_summary), project_issues_report_path(@project) %></li>
   <% end %>
-
   <% if User.current.allowed_to?(:view_calendar, @project, :global => true) %>
-  <li><%= link_to l(:label_calendar), _project_calendar_path(@project) %></li>
+    <li><%= link_to l(:label_calendar), _project_calendar_path(@project) %></li>
   <% end %>
   <% if User.current.allowed_to?(:view_gantt, @project, :global => true) %>
-  <li><%= link_to l(:label_gantt), _project_gantt_path(@project) %></li>
+    <li><%= link_to l(:label_gantt), _project_gantt_path(@project) %></li>
   <% end %>
   <% if User.current.allowed_to?(:view_agile_queries, @project) %>
-  <li><%= link_to l(:label_agile_board), {:controller => "agile_boards", :action => "index", :project_id => @project} %></li>
-  <% end %>
-  <% if User.current.allowed_to?(:manage_agile_verions, @project) %>
-  <li><%= link_to l(:label_agile_version_planning), {:controller => "agile_versions", :action => "index", :project_id => @project} %></li>
+    <li><%= link_to l(:label_agile_board), {:controller => "agile_boards", :action => "index", :project_id => @project} %></li>
   <% end %>
-  </ul>
+</ul>
diff --git a/plugins/redmine_agile/app/views/agile_boards/_issues_sidebar.html.erb b/plugins/redmine_agile/app/views/agile_boards/_issues_sidebar.html.erb
deleted file mode 100755
index 77c8cc8..0000000
--- a/plugins/redmine_agile/app/views/agile_boards/_issues_sidebar.html.erb
+++ /dev/null
@@ -1,8 +0,0 @@
-<% if User.current.allowed_to?(:view_agile_queries, @project, :global => true) %>
-  <%= link_to l(:label_agile_board), {:controller => "agile_boards", :action => "index", :project_id => @project} %>
-  <br>
-<% end %>
-<% if User.current.allowed_to?(:manage_agile_verions, @project) && @project.versions.open.any? %>
-  <%= link_to l(:label_agile_version_planning), {:controller => "agile_versions", :action => "index", :project_id => @project} %>
-  <br>
-<% end %>
diff --git a/plugins/redmine_agile/app/views/agile_boards/index.html.erb b/plugins/redmine_agile/app/views/agile_boards/index.html.erb
old mode 100755
new mode 100644
index 269b466..e798b5b
--- a/plugins/redmine_agile/app/views/agile_boards/index.html.erb
+++ b/plugins/redmine_agile/app/views/agile_boards/index.html.erb
@@ -1,52 +1,3 @@
 <%= render 'index' %>
-<%= javascript_tag do %>
-  var fullScreenState = localStorage.getItem('full-screen-board');
-  if (fullScreenState == "true") {
-    $("html").toggleClass("agile-board-fullscreen");
-  }
-
-  function updateBoard() {
-    if ( $('html').hasClass('agile-board-fullscreen') ) {
-      if ( $('.ui-sortable-helper').length == 0  &&
-        $('textarea:focus').length == 0 && $('.lock:visible').size() == 0 )
-      {
-        var lastScrollPosition = $('.agile-board.autoscroll').scrollTop();
-        $('.lock').show();
-        $.ajax(location.href, {
-          dataType: 'script',
-          contentType: 'text/javascript',
-          success: function(){
-            $(".agile-board.autoscroll").scrollTop(lastScrollPosition);
-          },
-          error: function(){
-            $(".agile-board.autoscroll").scrollTop(lastScrollPosition);
-          },
-          complete: function(){
-            $('.lock').hide();
-          }
-        });
-      }
-    };
-  }
-
-
-  Visibility.every(20 * 1000, function () {
-    updateBoard();
-  });
-
-  Visibility.change(function (e, state) {
-    if (state == 'visible') {
-      updateBoard();
-    }
-  });
-
-  function checkBoardVisibility() {
-    if (Visibility.state() != 'visible') {
-      updateBoard();
-    }
-  }
-
-  setInterval(checkBoardVisibility, 1200000);
-<% end %>
 <%= init_agile_tooltip_info %>
 <%= call_hook(:view_agile_board_bottom, { :issues => @issues, :project => @project, :query => @query }) %>
diff --git a/plugins/redmine_agile/app/views/agile_boards/inline_comment.html.erb b/plugins/redmine_agile/app/views/agile_boards/inline_comment.html.erb
index 62349b2..a030bdf 100644
--- a/plugins/redmine_agile/app/views/agile_boards/inline_comment.html.erb
+++ b/plugins/redmine_agile/app/views/agile_boards/inline_comment.html.erb
@@ -1,3 +1,3 @@
 <%= text_area_tag "issue[notes]", "", :cols => 60, :rows => 5, :class => 'wiki-edit', :no_label => true %>
-<button type="button" onclick="agileBoard.saveInlineComment(this, '<%= update_agile_board_path(:id => @issue) %>'); return false;">  <%= l(:button_submit) %> </button>
+<button type="button" onclick="saveInlineComment(this, '<%= update_agile_board_path(:id => @issue) %>'); return false;">  <%= l(:button_submit) %> </button>
 <%= link_to l(:button_cancel), "#", :onclick => "cancelInlineComment(this);return false;" %>
diff --git a/plugins/redmine_agile/app/views/agile_charts/_agile_charts.html.erb b/plugins/redmine_agile/app/views/agile_charts/_agile_charts.html.erb
index 9621af2..acbe13e 100644
--- a/plugins/redmine_agile/app/views/agile_charts/_agile_charts.html.erb
+++ b/plugins/redmine_agile/app/views/agile_charts/_agile_charts.html.erb
@@ -1,13 +1,5 @@
 <% if User.current.allowed_to?(:view_agile_charts, @project, :global => true) %>
-<h3><%= l(:label_agile_chart_plural) %></h3>
-<ul>
-  <li><%= link_to l(:label_agile_charts_issues_burndown), {:controller => "agile_charts", :action => "show", :chart => "issues_burndown", :project_id => @project}, :class => "#{'selected' if @chart == 'issues_burndown'}" %></li>
-  <li><%= link_to l(:label_agile_charts_burnup), {:controller => "agile_charts", :action => "show", :chart => "burnup", :project_id => @project}, :class => "#{'selected' if @chart == 'burnup'}" %></li>
-  <li><%= link_to l(:label_agile_charts_issues_velocity), {:controller => "agile_charts", :action => "show", :chart => "issues_velocity", :project_id => @project}, :class => "#{'selected' if @chart == 'issues_velocity'}" %></li>
-  <li><%= link_to l(:label_agile_charts_lead_time), {:controller => "agile_charts", :action => "show", :chart => "lead_time", :project_id => @project}, :class => "#{'selected' if @chart == 'lead_time'}" %></li>
-  <li><%= link_to l(:label_agile_charts_trackers_cumulative_flow), {:controller => "agile_charts", :action => "show", :chart => "trackers_cumulative_flow", :project_id => @project}, :class => "#{'selected' if @chart == 'trackers_cumulative_flow'}" %></li>
-</ul>
-<% end %>
-<% if User.current.allowed_to?(:view_agile_queries, @project, :global => true) %>
-  <%= render_sidebar_agile_queries %>
+
+  <ul class=agile-chart-queries>
+  </ul>
 <% end %>
diff --git a/plugins/redmine_agile/app/views/agile_charts/_chart.html.erb b/plugins/redmine_agile/app/views/agile_charts/_chart.html.erb
index 6a30b07..8fb57da 100644
--- a/plugins/redmine_agile/app/views/agile_charts/_chart.html.erb
+++ b/plugins/redmine_agile/app/views/agile_charts/_chart.html.erb
@@ -5,15 +5,29 @@
 <% else %>
   <div class="agile-chart-container">
     <canvas id="agile-chart"></canvas>
+    <div style="clear: both;"></div>
   </div>
 <% end %>
 
 <%= javascript_tag do %>
   $(document).ready(function(){
+    Chart.plugins.register({
+      beforeDraw: function(chartInstance, easing) {
+        if (chartInstance.config.options.tooltips.onlyShowForDatasetIndex) {
+          var tooltipsToDisplay = chartInstance.config.options.tooltips.onlyShowForDatasetIndex;
+          var active = chartInstance.tooltip._active || [];
+          if (active.length > 0) {
+            if (tooltipsToDisplay.indexOf(active[0]._datasetIndex) === -1) {
+              chartInstance.tooltip._model.opacity = 0;
+            }
+          }
+        }
+      }
+    });
 
     if (document.getElementById("agile-chart")) {
-      $.getJSON(<%= raw url_for(:controller => 'agile_charts', :action => 'render_chart', :project_id => @project,
-                                :chart => chart, :version_id => @version).to_json %>, function(data){
+      $.getJSON(<%= raw url_for(controller: 'agile_charts', action: 'render_chart', project_id: @project,
+                                chart: chart, version_id: @version, query_id: @query.try(:id), chart_unit: chart_unit).to_json %>, function(data){
 
         Chart.defaults.global.defaultFontColor = 'black';
         Chart.defaults.global.defaultFontFamily = '"Arial", sans-serif';
@@ -25,40 +39,58 @@
           stacked: data['stacked']
         };
 
-        new Chart(document.getElementById("agile-chart").getContext("2d"), {
-          type: 'bar',
-          data: chartData,
-          options: {
-            maintainAspectRatio: false,
-            legend: { position: 'right' },
-            title: {
-              display: true,
-              fontSize: 16,
-              fontStyle: 'normal',
-              text: data['title']
-            },
-            elements: {
-              line: {
-                  tension: 0, // disables bezier curves
+      new Chart(document.getElementById("agile-chart").getContext("2d"), {
+        type: data['type'] || 'bar',
+        data: chartData,
+        options: {
+          tooltips: {
+            onlyShowForDatasetIndex: data['show_tooltips'],
+            callbacks: chartTooltipCallbacks(data['type'])
+          },
+          maintainAspectRatio: false,
+          legend: {
+            position: 'right',
+            labels: {
+              filter: function(legendItem, chartData) {
+                if (legendItem.text) { return true }
               }
-            },
-            scales: {
-              yAxes: [{
-                stacked: data['stacked'],
-                scaleLabel: {
-                  display: true,
-                  fontColor: 'rgba(255, 0, 0 ,1)',
-                  fontSize: 14,
-                  labelString: data['y_title']
-                }
-              }],
-              xAxes: [{
-                  ticks: {
-                      autoSkip: true,
-                      maxRotation: 0
-                  }
-              }]
             }
+          },
+          title: {
+            display: true,
+            fontSize: 16,
+            fontStyle: 'normal',
+            text: data['title']
+          },
+          elements: {
+            line: {
+                tension: 0, // disables bezier curves
+            }
+          },
+          scales: {
+            yAxes: [{
+              stacked: data['stacked'],
+              scaleLabel: {
+                display: true,
+                fontColor: 'rgba(255, 0, 0 ,1)',
+                fontSize: 14,
+                labelString: data['y_title']
+              }
+            }],
+            xAxes: [{
+              ticks: {
+                autoSkip: true,
+                maxRotation: 0,
+                userCallback: function(value, index, values) {
+                  if (data['type'] == 'scatter') {
+                    return data['labels'][value]
+                  } else {
+                    return value
+                  }
+                }
+              }
+            }]
+            },
           }
         });
       });
diff --git a/plugins/redmine_agile/app/views/agile_charts/_versions_show.html.erb b/plugins/redmine_agile/app/views/agile_charts/_versions_show.html.erb
index 16f2117..79c9725 100644
--- a/plugins/redmine_agile/app/views/agile_charts/_versions_show.html.erb
+++ b/plugins/redmine_agile/app/views/agile_charts/_versions_show.html.erb
@@ -1,17 +1,31 @@
 <% if @issues.any? && User.current.allowed_to?(:view_agile_charts, @project) %>
   <fieldset>
     <legend>
-    <%= l(:label_agile_chart) %>
-    <%= select_tag('chart', options_charts_for_select(params[:chart] || RedmineAgile.default_chart),
-                       :id => 'chart_by_select',
-                       :data => {:remote => true, :method => 'get', :url => agile_charts_select_version_chart_path(:version_id => @version)}) %>
+      <%= l(:label_agile_chart) %>
+      <%= select_tag('chart', options_charts_for_select(params[:chart] || RedmineAgile.default_chart),
+                     id: 'chart_by_select',
+                     onchange: "toggleChartUnit($(this).val(), 'chart-unit-row'); updateVersionAgileChart('#{agile_charts_select_version_chart_path(version_id: @version)}');") %>
+
+      <span id="chart-unit-row">
+        <label for='chart_unit'><%= l(:label_agile_chart_units) %></label>
+        <%= select_tag 'chart_unit', options_chart_units_for_select,
+                       onchange: "updateVersionAgileChart('#{agile_charts_select_version_chart_path(version_id: @version)}');" %>
+      </span>
     </legend>
     <div id='agile_chart'>
-      <%= render_agile_chart(params[:chart] || RedmineAgile.default_chart, @version.fixed_issues) %>
+      <%= render_agile_chart(RedmineAgile::Charts.valid_chart_name_by(params[:chart] || RedmineAgile.default_chart), @version.fixed_issues) %>
     </div>
   </fieldset>
 <% end %>
 
 <% content_for :header_tags do %>
   <%= chartjs_assets %>
+  <%= javascript_include_tag 'redmine_agile', plugin: 'redmine_agile' %>
+<% end %>
+
+<%= javascript_tag do %>
+  var chartsWithUnits = <%= raw RedmineAgile::Charts::CHARTS_WITH_UNITS.to_json %>
+  $(document).ready(function() {
+    toggleChartUnit($('#chart_by_select').val(), 'chart-unit-row');
+  });
 <% end %>
diff --git a/plugins/redmine_agile/app/views/agile_charts/show.html.erb b/plugins/redmine_agile/app/views/agile_charts/show.html.erb
index f9d0804..a92392f 100644
--- a/plugins/redmine_agile/app/views/agile_charts/show.html.erb
+++ b/plugins/redmine_agile/app/views/agile_charts/show.html.erb
@@ -3,32 +3,33 @@
 <h2><%= @query.new_record? ? l(:label_agile_chart_plural) : h(@query.name) %></h2>
 <% html_title(@query.new_record? ? l(:label_agile_chart_plural) : @query.name) %>
 
-
 <%= form_tag({ :controller => 'agile_charts', :action => 'show', :project_id => @project },
             :method => :get, :id => 'query_form') do %>
   <div id="query_form_with_buttons" class="hide-when-print">
     <%= hidden_field_tag 'set_filter', '1' %>
     <div id="query_form_content">
-      <fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
-        <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
-        <div style="<%= @query.new_record? ? "" : "display: none;" %>">
+      <fieldset id="filters" class="collapsible">
+        <legend class="icon icon-expended" onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
+        <div>
           <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
         </div>
       </fieldset>
-      <fieldset class="collapsible collapsed">
-        <legend onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
+      <fieldset class="collapsible">
+        <legend class="icon icon-expended" onclick="toggleFieldset(this);"><%= l(:label_options) %></legend>
         <div>
           <table>
             <tr>
               <td><label for='chart'><%= l(:label_agile_chart) %></label></td>
-              <td><%= select_tag('chart', options_charts_for_select(@chart)) %></td>
+              <td><%= select_tag 'chart', options_charts_for_select(@chart), onchange: "toggleChartUnit($(this).val(), 'chart-unit-row');" %></td>
+              <td id="chart-unit-row">
+                <label for='chart_unit'><%= l(:label_agile_chart_units) %></label>
+                <%= select_tag 'chart_unit', options_chart_units_for_select(@query.chart_unit) %>
+              </td>
             </tr>
-            <tr>
-              <td><%= l(:label_agile_date_from) %></td>
+            <tr id="interval-size">
+              <td><%= l(:label_agile_interval_size) %></td>
               <td>
-                <%= text_field_tag 'date_from', @query.date_from, :size => 10 %><%= calendar_for('date_from') %>
-                <%= l(:label_agile_date_to) %>
-                <%= text_field_tag 'date_to', @query.date_to, :size => 10 %><%= calendar_for('date_to') %>
+                <%= select_tag 'interval_size', options_for_select(RedmineAgile::AgileChart::TIME_INTERVALS.map { |i| [l(:"label_agile_#{i}"), i] }, @query.interval_size) %>
               </td>
             </tr>
           </table>
@@ -48,9 +49,19 @@
 
 <% content_for :header_tags do %>
   <%= chartjs_assets %>
+  <%= javascript_include_tag 'redmine_agile', plugin: 'redmine_agile' %>
 <% end %>
 
 <% content_for :sidebar do %>
   <%= render :partial => 'agile_boards/issues_links' %>
   <%= render :partial => 'agile_charts/agile_charts' %>
 <% end %>
+
+<%= javascript_tag do %>
+  var chartsWithUnits = <%= raw RedmineAgile::Charts::CHARTS_WITH_UNITS.to_json %>
+  $(document).ready(function() {
+    toggleChartUnit($('#chart').val(), 'chart-unit-row');
+    /* Hide chart_period checkbox so that it couldn't be unchecked */
+    hideChartPeriodCheckbox();
+  });
+<% end %>
diff --git a/plugins/redmine_agile/app/views/agile_colors/index.html.erb b/plugins/redmine_agile/app/views/agile_colors/index.html.erb
deleted file mode 100644
index 8440f02..0000000
--- a/plugins/redmine_agile/app/views/agile_colors/index.html.erb
+++ /dev/null
@@ -1,29 +0,0 @@
-<h2><%=l(:label_agile_color) %> <%= select_tag "object_type", options_for_select([[l(:label_agile_tracker_colors), "tracker"], [l(:label_agile_issue_priority_colors), "issue_priority"]],  params[:object_type]), :onchange => "location = this.options[this.options.selectedIndex].value" %></h2>
-
-<%= form_tag(update_agile_colors_path(:object_type => params[:object_type]), :method => :put) do %>
-  <% if @coloreds.any? %>
-    <table class="list"><thead>
-    <tr>
-        <th><%= l(:field_name) %></th>
-    </tr></thead>
-    <% @coloreds.each do |colored| %>
-      <tr class="<%= cycle('odd', 'even') %>">
-          <td class="name">
-            <%= hidden_field_tag 'coloreds[][id]', colored.id %>
-            <%= select_tag 'coloreds[][color]', options_for_select(AgileColor::AGILE_COLORS, colored.color), :include_blank => true, :id => "color_select_#{colored.id}", :class => "agile-color" %>  <%= colored %>
-          </td>
-      </tr>
-    <% end %>
-    </table>
-    <%= javascript_tag "$('.agile-color').simplecolorpicker({picker: true});"%>
-
-  <% end %>
-  <%= submit_tag l(:button_save) %>
-<% end %>
-
-<% content_for :header_tags do %>
-  <%= javascript_include_tag 'jquery.simplecolorpicker.js', :plugin => "redmine_agile" %>
-  <%= stylesheet_link_tag 'jquery.simplecolorpicker.css', :plugin => 'redmine_agile' %>
-<% end %>
-
-<% html_title(l(:enumeration_issue_priorities)) -%>
diff --git a/plugins/redmine_agile/app/views/agile_journal_details/status.html.erb b/plugins/redmine_agile/app/views/agile_journal_details/status.html.erb
index 90f6d07..10dcb43 100644
--- a/plugins/redmine_agile/app/views/agile_journal_details/status.html.erb
+++ b/plugins/redmine_agile/app/views/agile_journal_details/status.html.erb
@@ -2,7 +2,30 @@
 
 <% html_title(l(:label_issue_status_plural)) %>
 
-<% if @statuses.any? %>
+<%= form_tag({ controller: 'agile_journal_details', action: 'status' }, method: :get, id: 'query_form') do %>
+  <div id="query_form_with_buttons" class="hide-when-print">
+    <div id="query_form_content">
+      <fieldset id="options" class="collapsible collapsed">
+        <legend onclick="toggleFieldset(this);" class="icon icon-expended"><%= l(:label_options) %></legend>
+        <div style="display: none;">
+          <table>
+            <tr>
+              <td><label for='group_by'><%= l(:field_group_by) %></label></td>
+              <td><%= select_tag('group_by', options_for_select([[]] + [[l(:field_status), 'status']], params[:group_by])) %></td>
+            </tr>
+          </table>
+        </div>
+      </fieldset>
+    </div>
+  </div>
+
+  <p class="buttons hide-when-print">
+    <%= link_to_function l(:button_apply), 'submit_query_form("query_form")', :class => 'icon icon-checked' %>
+    <%= link_to l(:button_clear), { }, :class => 'icon icon-reload'  %>
+  </p>
+<% end %>
+
+<% if @statuses_collector.data.any? %>
   <table class="list"><thead>
   <tr>
       <th>#</th>
@@ -10,18 +33,38 @@
       <th><%= l(:field_status) %></th>
       <th><%= l(:field_duration) %></th>
       <th><%= l(:field_author) %></th>
+      <th><%= l(:field_assigned_to) %></th>
   </tr></thead>
-  <% @statuses.each_with_index do |status, index| %>
-  <% issue_status = IssueStatus.where(:id => status.value).first %>
-  <tr class="<%= cycle('odd', 'even') %>">
-      <td class="index"><%= index + 1 %></td>
-      <td class="name"><%= format_time(status.journal.created_on) %></td>
-      <td><%= issue_status.name %></td>
-      <td><%= event_duration(status, @statuses[index + 1]) %></td>
-      <td><%= status.journal.user.name %></td>
-  </tr>
+  <% if @group %>
+    <% @statuses_collector.grouped_by(@group).each do |group_id, group_data| %>
+      <% group_object = @statuses_collector.object_for(@group).where(:id => group_id).first %>
+      <tr class="group open">
+        <td colspan="6">
+          <span class="expander icon icon-expended" onclick="toggleRowGroup(this);">&nbsp;</span>
+          <span class="name"><%= group_object.name %></span>
+          <span class="badge badge-count count"><%= group_data.count %></span>
+          <span class="totals">
+            <%= l('datetime.distance_in_words.x_days', count: @statuses_collector.group_total_for(@group, group_data)) %>
+          </span>
+          <%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}",
+                               "toggleAllRowGroups(this)", :class => 'toggle-all') %>
+        </td>
+      </tr>
+      <% group_data.each_with_index do |data, index| %>
+        <%= render partial: 'status_detail', locals: { issue_status: @statuses_collector.issue_status_for(@group, group_id), data: data, index: index } %>
+      <% end %>
+    <% end %>
+  <% else %>
+    <% @statuses_collector.data.each_with_index do |data, index| %>
+      <% issue_status = IssueStatus.where(:id => data.status_id).first %>
+      <%= render partial: 'status_detail', locals: { issue_status: issue_status, data: data, index: index } %>
+    <% end %>
   <% end %>
   </table>
 <% else %>
-<p class="nodata"><%= l(:label_no_data) %></p>
+  <p class="nodata"><%= l(:label_no_data) %></p>
+<% end %>
+
+<% other_formats_links do |f| %>
+  <%= f.link_to 'CSV', :url => params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params %>
 <% end %>
diff --git a/plugins/redmine_agile/app/views/agile_queries/_columns.html.erb b/plugins/redmine_agile/app/views/agile_queries/_columns.html.erb
deleted file mode 100644
index 117acd8..0000000
--- a/plugins/redmine_agile/app/views/agile_queries/_columns.html.erb
+++ /dev/null
@@ -1,38 +0,0 @@
-<table class="query-columns">
-  <tr>
-    <td style="padding-left:0">
-      <%= label_tag "available_columns", l(:description_available_columns) %>
-      <br />
-      <%= select_tag 'available_columns',
-              options_for_select(query_available_inline_columns_options(query)),
-              :multiple => true, :size => 10, :style => "width:150px",
-              :ondblclick => "moveOptions(this.form.available_columns, this.form.selected_columns);" %>
-    </td>
-    <td class="buttons">
-      <input type="button" value="&#8594;"
-       onclick="moveOptions(this.form.available_columns, this.form.selected_columns);" /><br />
-      <input type="button" value="&#8592;"
-       onclick="moveOptions(this.form.selected_columns, this.form.available_columns);" />
-    </td>
-    <td>
-      <%= label_tag "selected_columns", l(:description_selected_columns) %>
-      <br />
-      <%= select_tag tag_name,
-              options_for_select(query_selected_inline_columns_options(query)),
-              :id => 'selected_columns', :multiple => true, :size => 10, :style => "width:150px",
-              :ondblclick => "moveOptions(this.form.selected_columns, this.form.available_columns);" %>
-    </td>
-    <td class="buttons">
-      <input type="button" value="&#8593;" onclick="moveOptionUp(this.form.selected_columns);" /><br />
-      <input type="button" value="&#8595;" onclick="moveOptionDown(this.form.selected_columns);" />
-    </td>
-  </tr>
-</table>
-
-<%= javascript_tag do %>
-$(document).ready(function(){
-  $('.query-columns').closest('form').submit(function(){
-    $('#selected_columns option').prop('selected', true);
-  });
-});
-<% end %>
diff --git a/plugins/redmine_agile/app/views/agile_queries/_filters.html.erb b/plugins/redmine_agile/app/views/agile_queries/_filters.html.erb
deleted file mode 100644
index f12d2b0..0000000
--- a/plugins/redmine_agile/app/views/agile_queries/_filters.html.erb
+++ /dev/null
@@ -1,28 +0,0 @@
-<%= javascript_tag do %>
-var operatorLabels = <%= raw_json Query.operators_labels %>;
-var operatorByType = <%= raw_json Query.operators_by_filter_type %>;
-var availableFilters = <%= raw_json query.available_filters_as_json %>;
-var labelDayPlural = <%= raw_json l(:label_day_plural) %>;
-var allProjects = <%= raw_json query.all_projects_values %>;
-$(document).ready(function(){
-  initFilters();
-  <% query.filters.each do |field, options| %>
-  addFilter("<%= field %>", <%= raw_json query.operator_for(field) %>, <%= raw_json query.values_for(field) %>);
-  <% end %>
-});
-<% end %>
-
-<table style="width:100%">
-<tr>
-<td>
-<table id="filters-table">
-</table>
-</td>
-<td class="add-filter" style="width: initial;">
-<%= label_tag('add_filter_select', l(:label_filter_add)) %>
-<%= select_tag 'add_filter_select', filters_options_for_select(query), :name => nil %>
-</td>
-</tr>
-</table>
-<%= hidden_field_tag 'f[]', '' %>
-<% include_calendar_headers_tags %>
diff --git a/plugins/redmine_agile/app/views/agile_queries/_form.html.erb b/plugins/redmine_agile/app/views/agile_queries/_form.html.erb
deleted file mode 100644
index 455ace0..0000000
--- a/plugins/redmine_agile/app/views/agile_queries/_form.html.erb
+++ /dev/null
@@ -1,80 +0,0 @@
-<%= error_messages_for 'query' %>
-
-<div class="box">
-<div class="tabular">
-  <%= hidden_field_tag 'gantt', '1' if params[:gantt] %>
-
-  <p><label for="query_name"><%=l(:field_name)%></label>
-  <%= text_field 'query', 'name', :size => 80 %></p>
-
-  <% if User.current.admin? || User.current.allowed_to?(:manage_public_agile_queries, @project) %>
-    <% if Redmine::VERSION.to_s < "2.4" %>
-      <p><label for="query_is_public"><%=l(:field_is_public)%></label>
-      <%= check_box 'query', 'is_public',
-            :onchange => (User.current.admin? ? nil : 'if (this.checked) {$("#query_is_for_all").removeAttr("checked"); $("#query_is_for_all").attr("disabled", true);} else {$("#query_is_for_all").removeAttr("disabled");}') %></p>
-    <% else %>
-      <p><label><%=l(:field_visible)%></label>
-        <label class="block"><%= radio_button 'query', 'visibility', Query::VISIBILITY_PRIVATE %> <%= l(:label_visibility_private) %></label>
-        <label class="block"><%= radio_button 'query', 'visibility', Query::VISIBILITY_ROLES %> <%= l(:label_visibility_roles) %>:</label>
-        <% Role.givable.sorted.each do |role| %>
-          <label class="block role-visibility"><%= check_box_tag 'query[role_ids][]', role.id, @query.roles.include?(role), :id => nil %> <%= role.name %></label>
-        <% end %>
-        <label class="block"><%= radio_button 'query', 'visibility', Query::VISIBILITY_PUBLIC %> <%= l(:label_visibility_public) %></label>
-        <%= hidden_field_tag 'query[role_ids][]', '' %>
-      </p>
-    <% end %>
-  <% end %>
-
-  <p><label for="query_is_for_all"><%=l(:field_is_for_all)%></label>
-  <%= check_box_tag 'query_is_for_all', 1, @query.project.nil?,
-        :disabled => (!@query.new_record? && (@query.project.nil? || (@query.is_public? && !User.current.admin?))) %></p>
-  <% if Redmine::VERSION.to_s > "2.4" %>
-    <p>
-      <label>
-        <%= l(:label_agile_default_board) %>
-      </label>
-      <%= check_box_tag 'query[is_default]', 1, @query.is_default? %>
-    </p>
-  <% end %>
-  <fieldset><legend><%= l(:label_options) %></legend>
-    <p><label for="query_default_columns"><%=l(:label_agile_board_default_fields)%></label>
-    <%= check_box_tag 'default_columns', 1, @query.has_default_columns?, :id => 'query_default_columns',
-          :onclick => 'if (this.checked) {$("#columns").hide();} else {$("#columns").show();}' %></p>
-
-    <% if RedmineAgile.use_colors? && @query.respond_to?(:color_base) %>
-      <p><label for='color_base'><%= l(:label_agile_color_based_on) %></label>
-      <%= select 'query', 'color_base', options_for_select(options_card_colors_for_select(@query.color_base)) %></p>
-    <% end %>
-    <p><label for="query_group_by"><%= l(:label_agile_swimlanes) %></label>
-      <%= select 'query', 'group_by', @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]}, :include_blank => true %>
-    </p>
-  </fieldset>
-</div>
-
-<fieldset id="filters"><legend><%= l(:label_filter_plural) %></legend>
-<%= render :partial => 'agile_queries/filters', :locals => {:query => query}%>
-</fieldset>
-
-<% if Redmine::VERSION.to_s > '2.4' %>
-  <%= content_tag 'fieldset', :class => "card-fields" do %>
-  <legend><%= l(:label_agile_board_columns) %></legend>
-  <%= render_board_fields_status(query) %>
-  <% end %>
-<% end %>
-
-
-<%= content_tag 'fieldset', :id => 'columns', :style => (query.has_default_columns? ? 'display:none;' : nil), :class => "card-fields" do %>
-<legend><%= l(:label_agile_fields) %></legend>
-<%= render_board_fields_selection(query) %>
-<% end %>
-
-</div>
-
-<%= javascript_tag do %>
-$(document).ready(function(){
-  $("input[name='query[visibility]']").change(function(){
-    var checked = $('#query_visibility_1').is(':checked');
-    $("input[name='query[role_ids][]'][type=checkbox]").attr('disabled', !checked);
-  }).trigger('change');
-});
-<% end %>
diff --git a/plugins/redmine_agile/app/views/agile_queries/edit.html.erb b/plugins/redmine_agile/app/views/agile_queries/edit.html.erb
deleted file mode 100644
index d2b3111..0000000
--- a/plugins/redmine_agile/app/views/agile_queries/edit.html.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-<h2><%= l(:label_agile_board_edit) %></h2>
-
-<%= form_tag(agile_query_path(@query), :method => :put) do %>
-  <%= render :partial => 'form', :locals => {:query => @query} %>
-  <%= submit_tag l(:button_save) %>
-<% end %>
diff --git a/plugins/redmine_agile/app/views/agile_queries/index.html.erb b/plugins/redmine_agile/app/views/agile_queries/index.html.erb
deleted file mode 100644
index e545c5e..0000000
--- a/plugins/redmine_agile/app/views/agile_queries/index.html.erb
+++ /dev/null
@@ -1,25 +0,0 @@
-<div class="contextual">
-<%= link_to l(:label_query_new), new_agile_query_path(:project_id => @project), :class => 'icon icon-add' if User.current.allowed_to?(:add_agile_queries, nil, :global => true)%>
-</div>
-
-<h2><%= l(:label_agile_board_plural) %></h2>
-
-<% if @queries.empty? %>
-  <p><i><%=l(:label_no_data)%></i></p>
-<% else %>
-  <table class="list">
-  <% @queries.each do |query| %>
-    <tr class="<%= cycle('odd', 'even') %>">
-      <td class="name">
-        <%= link_to h(query.name), :controller => 'agile_boards', :action => 'index', :project_id => query.project_id, :query_id => query %>
-      </td>
-      <td class="buttons">
-        <% if query.editable_by?(User.current) %>
-        <%= link_to l(:button_edit), edit_agile_query_path(query), :class => 'icon icon-edit' %>
-        <%= delete_link agile_query_path(query) %>
-      <% end %>
-      </td>
-    </tr>
-  <% end %>
-  </table>
-<% end %>
diff --git a/plugins/redmine_agile/app/views/agile_queries/new.html.erb b/plugins/redmine_agile/app/views/agile_queries/new.html.erb
deleted file mode 100644
index 870ed12..0000000
--- a/plugins/redmine_agile/app/views/agile_queries/new.html.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-<h2><%= l(:label_agile_board_new) %></h2>
-
-<%= form_tag(@project ? project_agile_queries_path(@project) : agile_queries_path) do %>
-  <%= render :partial => 'form', :locals => {:query => @query} %>
-  <%= submit_tag l(:button_save) %>
-<% end %>
diff --git a/plugins/redmine_agile/app/views/agile_versions/_board.html.erb b/plugins/redmine_agile/app/views/agile_versions/_board.html.erb
deleted file mode 100644
index e62b5d1..0000000
--- a/plugins/redmine_agile/app/views/agile_versions/_board.html.erb
+++ /dev/null
@@ -1,43 +0,0 @@
-<%= form_tag({}) do -%>
-  <%= hidden_field_tag 'back_url', url_for(params.respond_to?(:to_unsafe_hash) ? params.to_unsafe_hash : params) %>
-  <%= hidden_field_tag 'project_id', @project.id if @project %>
-<div class="autoscroll">
-    <div class="flash error" style="display: none;" id="agile-board-errors">
-    </div>
-    <table class="list  versions-planning-board">
-      <thead>
-      <tr>
-        <th style="width: 33%; text-align: left;">
-          <%= text_field_tag "search", '', :placeholder => l(:label_agile_no_version_issues) %>
-          <%= javascript_tag "observeIssueSearchfield('search', '#{escape_javascript autocomplete_agile_versions_path(:project_id => @project)}');" %>
-        </th>
-        <th style="width: 33%; text-align: left;" id="backlog_version_header" data-estimated-unit="<%= estimated_unit%>">
-          <%= version_select_tag(@backlog_version, :version_type => "backlog", :other_version => @current_version) %>
-        </th>
-        <th style="width: 33%; text-align: left;" id="current_version_header" data-estimated-unit="<%= estimated_unit%>">
-          <%= version_select_tag(@current_version, :version_type => "current", :other_version => @backlog_version) %>
-        </th>
-      </tr>
-      </thead>
-      <tbody>
-        <tr>
-          <td id="no_version" class="issue-version-col" data-id="">
-          <%= render :partial => 'version_issues', :object => @version_issues  %>
-          </td>
-          <td id="backlog_version" class="<%= 'issue-version-col' if @backlog_version %>" data-id="<%= @backlog_version && @backlog_version.id %>">
-            <%= render :partial => 'version_issues', :object => @backlog_version_issues if @backlog_version %>
-          </td>
-          <td id="current_version" class="<%= 'issue-version-col' if @current_version %>" data-id="<%= @current_version && @current_version.id %>">
-            <%= render :partial => 'version_issues', :object => @current_version_issues if @current_version %>
-          </td>
-        </tr>
-      </tbody>
-    </table>
-</div>
-
-<% end %>
-
-<%= javascript_tag do %>
-  recalculateHours();
-  $(document).ajaxSuccess(recalculateHours);
-<% end %>
diff --git a/plugins/redmine_agile/app/views/agile_versions/_version_issues.html.erb b/plugins/redmine_agile/app/views/agile_versions/_version_issues.html.erb
deleted file mode 100644
index 278ce44..0000000
--- a/plugins/redmine_agile/app/views/agile_versions/_version_issues.html.erb
+++ /dev/null
@@ -1,22 +0,0 @@
-<% if version_issues.any? %>
-  <% version_issues.each do |issue| %>
-    <% issue_estmated_value = estimated_value(issue) %>
-    <div class="issue-card hascontextmenu" data-id="<%= issue.id %>" data-estimated-hours="<%= issue_estmated_value %>">
-      <% if issue_estmated_value > 0 %>
-        <span class="hours"><%= ("%.2f" % issue_estmated_value) + estimated_unit %></span>
-      <% end %>
-      <%= check_box_tag("ids[]", issue.id, false, :id => nil, :class => 'checkbox') %>
-      <%= link_to_issue(issue, :project => (@project != issue.project)) %>
-    </div>
-  <% end %>
-
-  <% if version_issues.first.fixed_version_id.blank? %>
-    <div class="pagination-wrapper">
-    <span class="pagination">
-        <% pagination_links_full(@issue_pages, @issue_count, :per_page_links => false)  do |text, parameters, options| %>
-          <%= link_to text, autocomplete_agile_versions_path(parameters.merge(:q => params[:q], :project_id => @project)), :remote => true %>
-        <% end %>
-    </span>
-    </div>
-  <% end %>
-<% end %>
diff --git a/plugins/redmine_agile/app/views/agile_versions/autocomplete.js.erb b/plugins/redmine_agile/app/views/agile_versions/autocomplete.js.erb
deleted file mode 100644
index 3c6c1ac..0000000
--- a/plugins/redmine_agile/app/views/agile_versions/autocomplete.js.erb
+++ /dev/null
@@ -1 +0,0 @@
-$('#no_version').html('<%= escape_javascript(render :partial => "version_issues", :object => @version_issues) %>')
diff --git a/plugins/redmine_agile/app/views/agile_versions/index.html.erb b/plugins/redmine_agile/app/views/agile_versions/index.html.erb
deleted file mode 100644
index 318d206..0000000
--- a/plugins/redmine_agile/app/views/agile_versions/index.html.erb
+++ /dev/null
@@ -1,56 +0,0 @@
-<h2><%= l(:label_agile_version_planning) %></h2>
-<div class="contextual">
-<% if !@query.new_record? && @query.editable_by?(User.current) %>
-  <%= link_to l(:button_edit), edit_query_path(@query), :class => 'icon icon-edit' %>
-  <%= delete_link query_path(@query) %>
-<% end %>
-</div>
-
-<%= form_tag({ :controller => 'agile_versions', :action => 'index', :project_id => @project },
-            :method => :get, :id => 'query_form') do %>
-  <div id="query_form_with_buttons" class="hide-when-print">
-    <%= hidden_field_tag 'set_filter', '1' %>
-    <div id="query_form_content">
-    <fieldset id="filters" class="collapsible <%= @query.new_record? ? "" : "collapsed" %>">
-      <legend onclick="toggleFieldset(this);"><%= l(:label_filter_plural) %></legend>
-      <div style="<%= @query.new_record? ? "" : "display: none;" %>">
-        <%= render :partial => 'queries/filters', :locals => {:query => @query} %>
-      </div>
-    </fieldset>
-    </div>
-    <p class="buttons">
-    <%= link_to_function l(:button_apply), '$("#query_form").submit()', :class => 'icon icon-checked' %>
-    <%= link_to l(:button_clear), { :set_filter => 1, :project_id => @project }, :class => 'icon icon-reload'  %>
-    </p>
-  </div>
-<% end %>
-
-<% if User.current.allowed_to?(:edit_issues, @project) %>
-  <%= javascript_tag do %>
-    new PlanningBoard().init({
-      project_id: '<%= @project && @project.id %>',
-      update_agile_board_path: '<%= escape_javascript update_agile_board_path %>',
-      issues_path: '<%= escape_javascript issues_path %>'
-    });
-  <% end %>
-<% end %>
-
-<% if @project.shared_versions.empty? %>
-  <p class="nodata"><%= l(:label_no_data) %></p>
-<% else %>
-  <%= render :partial => 'board' %>
-<% end %>
-
-<% content_for :sidebar do %>
-  <%= render :partial => 'agile_boards/issues_links' %>
-  <%= render :partial => 'agile_charts/agile_charts' %>
-<% end %>
-
-<% html_title l(:label_agile_version_planning) %>
-<%= javascript_tag "agileContextMenuInit('#{ url_for(issues_context_menu_path) }')" %>
-<% content_for :header_tags do %>
-  <%= javascript_include_tag "redmine_agile", :plugin => 'redmine_agile' %>
-  <%= javascript_include_tag "jquery.ui.touch-punch.js", :plugin => 'redmine_agile' %>
-  <%= javascript_include_tag "redmine_agile_context_menu", :plugin => 'redmine_agile' %>
-  <%= stylesheet_link_tag 'context_menu' %>
-<% end %>
diff --git a/plugins/redmine_agile/app/views/agile_versions/load.js.erb b/plugins/redmine_agile/app/views/agile_versions/load.js.erb
deleted file mode 100644
index 496b465..0000000
--- a/plugins/redmine_agile/app/views/agile_versions/load.js.erb
+++ /dev/null
@@ -1,5 +0,0 @@
-$('#<%= @version_type %>_version').html('<%= escape_javascript(render :partial => "version_issues", :object => @version_issues) %>');
-$('#<%= @version_type %>_version').attr('data-id', '<%= @version.id %>');
-
-$('#<%= @version_type %>_version_header').html('<%= escape_javascript(version_select_tag(@version, :version_type => @version_type, :other_version => @other_version_id)) %>');
-$('#<%= @other_version_type %>_version_header').html('<%= escape_javascript(version_select_tag(@other_version_id, :version_type => @other_version_type, :other_version => @version)) %>');
diff --git a/plugins/redmine_agile/app/views/context_menus/_agile_colors.html.erb b/plugins/redmine_agile/app/views/context_menus/_agile_colors.html.erb
deleted file mode 100644
index c719d73..0000000
--- a/plugins/redmine_agile/app/views/context_menus/_agile_colors.html.erb
+++ /dev/null
@@ -1,11 +0,0 @@
-<% if @safe_attributes.include?('agile_color_attributes') %>
-<li class="folder">
-  <a href="#" class="submenu"><%= l(:label_agile_color) %></a>
-  <ul>
-  <% AgileColor::AGILE_COLORS.each do |k, color| %>
-      <li><%= context_menu_link "<span style=\"background-color: #{color}; width: 8em; height: 1.2em; display: block;border: 1px solid #222;\"></span>".html_safe, bulk_update_issues_path(:ids => @issue_ids, :issue => {:agile_color_attributes => {:color => color}}, :back_url => @back), :method => :post,
-                                    :selected => (@issue && @issue.color == color), :disabled => (!@can[:edit]) %></li>
-  <% end -%>
-  </ul>
-</li>
-<% end %>
diff --git a/plugins/redmine_agile/app/views/issues/_agile_data_fields.html.erb b/plugins/redmine_agile/app/views/issues/_agile_data_fields.html.erb
index d32aa6e..be8cdbf 100644
--- a/plugins/redmine_agile/app/views/issues/_agile_data_fields.html.erb
+++ b/plugins/redmine_agile/app/views/issues/_agile_data_fields.html.erb
@@ -1,9 +1,9 @@
 <div class="attributes">
-	<div class="splitcontent">
-		<%= render :partial => 'issue_color_form', :locals => { :form => form } %>
-		<%= render :partial => 'issue_story_points_form', :locals => { :form => form } %>
-	</div>
+  <div class="splitcontent">
+    <%= render :partial => 'issue_story_points_form', :locals => { :form => form } %>
+  </div>
 </div>
+
 <% content_for :header_tags do %>
-	<%= javascript_include_tag "redmine_agile", :plugin => 'redmine_agile' %>
+  <%= javascript_include_tag "redmine_agile", :plugin => 'redmine_agile' %>
 <% end %>
diff --git a/plugins/redmine_agile/app/views/issues/_issue_color.html.erb b/plugins/redmine_agile/app/views/issues/_issue_color.html.erb
deleted file mode 100644
index 467ad9b..0000000
--- a/plugins/redmine_agile/app/views/issues/_issue_color.html.erb
+++ /dev/null
@@ -1,11 +0,0 @@
-<% if User.current.allowed_to?(:view_agile_queries, @project) && @issue.color && RedmineAgile.issue_colors? %>
-
-<%= issue_fields_rows do |rows|
-  rows.left l(:label_agile_color), "<span class=\"simplecolorpicker button\" style=\"background-color: #{@issue.color};\"></span>".html_safe
-end %>
-
-  <% content_for :header_tags do %>
-    <%= stylesheet_link_tag 'jquery.simplecolorpicker.css', :plugin => 'redmine_agile' %>
-  <% end %>
-
-<% end %>
diff --git a/plugins/redmine_agile/app/views/issues/_issue_color_form.html.erb b/plugins/redmine_agile/app/views/issues/_issue_color_form.html.erb
deleted file mode 100644
index 2cc4f25..0000000
--- a/plugins/redmine_agile/app/views/issues/_issue_color_form.html.erb
+++ /dev/null
@@ -1,14 +0,0 @@
-<div class="splitcontentleft">
-  <% if @issue.safe_attribute? 'agile_color_attributes' -%>
-    <%= form.fields_for :agile_color do |f| %>
-      <p><%= f.select :color, options_for_select(AgileColor::AGILE_COLORS, @issue.color), {:label => l(:label_agile_color), :include_blank => true} %></p>
-    <% end %>
-
-    <%= javascript_tag "$('#issue_agile_color_attributes_color').simplecolorpicker({picker: true});"%>
-    <% content_for :header_tags do %>
-      <%= javascript_include_tag 'jquery.simplecolorpicker.js', :plugin => "redmine_agile" %>
-      <%= stylesheet_link_tag 'jquery.simplecolorpicker.css', :plugin => 'redmine_agile' %>
-    <% end %>
-
-  <% end %>
-</div>
diff --git a/plugins/redmine_agile/app/views/issues/_issue_story_points_form.html.erb b/plugins/redmine_agile/app/views/issues/_issue_story_points_form.html.erb
index ab0f94d..9761dd2 100644
--- a/plugins/redmine_agile/app/views/issues/_issue_story_points_form.html.erb
+++ b/plugins/redmine_agile/app/views/issues/_issue_story_points_form.html.erb
@@ -1,14 +1,9 @@
-<div class="splitcontentright">
-  <% if @issue.project.module_enabled?('agile') && RedmineAgile.use_story_points? && RedmineAgile.use_story_points_for?(@issue.tracker) %>
-    <%= form.fields_for :agile_data do |f| %>
-      <p>
-      <% if RedmineAgile.sp_values.any? %>
-        <%= f.select :story_points, RedmineAgile.sp_values.unshift(@issue.story_points).uniq, 
-          :include_blank => @issue.story_points, :selected => @issue.story_points %>
-      <% else %>
-        <%= f.text_field :story_points, :size => 3, :required => @issue.required_attribute?('agile_data_attributes') %>
+<% if @issue.project.module_enabled?('agile') && RedmineAgile.use_story_points? && RedmineAgile.use_story_points_for?(@issue.tracker) %>
+  <div class="splitcontentright">
+      <%= form.fields_for :agile_data do |f| %>
+        <p>
+          <%= f.text_field :story_points, :size => 3, :required => @issue.required_attribute?('agile_data_attributes') %>
+        </p>
       <% end %>
-      </p>
-    <% end %>
-  <% end %>
-</div>
+  </div>
+<% end %>
diff --git a/plugins/redmine_agile/app/views/projects/_project_color_form.html.erb b/plugins/redmine_agile/app/views/projects/_project_color_form.html.erb
deleted file mode 100644
index 5b5d658..0000000
--- a/plugins/redmine_agile/app/views/projects/_project_color_form.html.erb
+++ /dev/null
@@ -1,11 +0,0 @@
-<% if @project.safe_attribute? 'agile_color_attributes' -%>
-  <%= form.fields_for :agile_color do |fr| %>
-    <p><%= fr.select :color, options_for_select(AgileColor::AGILE_COLORS, @project.color), {:label => l(:label_agile_color), :include_blank => true} %></p>
-  <% end %>
-  
-  <%= javascript_tag "$('#project_agile_color_attributes_color').simplecolorpicker({picker: true});"%>
-  <% content_for :header_tags do %>
-    <%= javascript_include_tag 'jquery.simplecolorpicker.js', :plugin => "redmine_agile" %>
-    <%= stylesheet_link_tag 'jquery.simplecolorpicker.css', :plugin => 'redmine_agile' %>
-  <% end %>
-<% end %>
diff --git a/plugins/redmine_agile/app/views/settings/agile/_general.html.erb b/plugins/redmine_agile/app/views/settings/agile/_general.html.erb
old mode 100755
new mode 100644
index 5373c1c..f0059ee
--- a/plugins/redmine_agile/app/views/settings/agile/_general.html.erb
+++ b/plugins/redmine_agile/app/views/settings/agile/_general.html.erb
@@ -3,16 +3,6 @@
   <label><%= l(:label_agile_board_items_limit) %></label>
   <%= text_field_tag 'settings[board_items_limit]', RedmineAgile.board_items_limit, :size => 3 %>
 </p>
-<p>
-  <label for="settings_status_colors"><%= l(:label_agile_status_colors) %></label>
-  <%= hidden_field_tag 'settings[status_colors]', 0, :id => nil %>
-  <%= check_box_tag 'settings[status_colors]', 1, RedmineAgile.status_colors? %>
-</p>
-
-<p>
-  <label><%= l(:label_agile_color_based_on) %></label>
-  <%= select_tag 'settings[color_on]', options_card_colors_for_select(@settings["color_on"]) %> <%= link_to l(:label_agile_manage_colors), agile_colors_path("tracker") %>
-</p>
 
 <p>
   <label><%= l(:label_agile_board_default_fields) %></label>
@@ -20,31 +10,21 @@
 </p>
 
 <p>
-  <label><%= l(:label_agile_esitmate_units) %></label>
-  <%= select_tag 'settings[estimate_units]', options_for_select(RedmineAgile::ESTIMATE_UNITS.map{ |eu| [l("label_agile_#{eu}"), eu] }, 
-    RedmineAgile.estimate_units), :onchange => "toggleTrackerForSP(this); return false" %>
-  <script type="text/javascript">
-    function toggleTrackerForSP (node) {
-      if ($(node).val() == 'story_points')
-        $('#trackers_for_sp').show();
-      else
-        $('#trackers_for_sp').hide();
-    }
-  </script>
+  <label><%= l(:label_agile_story_points) %></label>
+  <%= hidden_field_tag 'settings[story_points_on]', 0, id: nil %>
+  <%= check_box_tag 'settings[story_points_on]', 1, RedmineAgile.use_story_points?, onchange: "$('#trackers_for_sp').toggle(this.checked);" %>
 </p>
+
 <div id="trackers_for_sp" style="display:<%= RedmineAgile.use_story_points? ? 'block' : 'none' %>">
   <p>
     <label><%= l(:label_agile_trackers_for_sp) %></label>
     <%= select_tag 'settings[trackers_for_sp]', options_for_select(Tracker.all.map{|tr| [tr.name, tr.id]}, RedmineAgile.trackers_for_sp), :prompt => "All", :style => "width:150px" %>
   </p>
-  <p>
-    <label><%= l(:label_agile_sp_values) %></label>
-    <%= text_field_tag 'settings[sp_values]', RedmineAgile.sp_values.join(", ") %>
-  </p>
 </div>
+
 <p>
   <label><%= l(:label_agile_default_chart) %></label>
-  <%= select_tag 'settings[default_chart]', options_charts_for_select(RedmineAgile.default_chart) %>
+  <%= select_tag 'settings[default_chart]', grouped_options_charts_for_select(RedmineAgile.default_chart) %>
 </p>
 
 <p>
@@ -61,11 +41,6 @@
   <label for="settings_hide_closed_issues_data"><%= l(:label_agile_hide_closed_issues_data) %></label>
   <%= check_box_tag 'settings[hide_closed_issues_data]', 1, RedmineAgile.hide_closed_issues_data? %>
 </p>
-<p>
-  <label><%= l(:label_agile_allow_create_cards) %></label>
-  <%= hidden_field_tag 'settings[allow_create_card]', 0, :id => nil %>
-  <%= check_box_tag 'settings[allow_create_card]', 1, RedmineAgile.allow_create_card? %>
-</p>
 <p>
   <label for="settings_auto_assign_on_move"><%= l(:label_agile_auto_assign_on_move) %></label>
   <%= hidden_field_tag 'settings[auto_assign_on_move]', 0, :id => nil %>
diff --git a/plugins/redmine_agile/app/views/users/_user_color_form.html.erb b/plugins/redmine_agile/app/views/users/_user_color_form.html.erb
deleted file mode 100644
index d2ae41c..0000000
--- a/plugins/redmine_agile/app/views/users/_user_color_form.html.erb
+++ /dev/null
@@ -1,11 +0,0 @@
-<% if @user.safe_attribute? 'agile_color_attributes' -%>
-    <%= form.fields_for :agile_color do |fr| %>
-        <p><%= fr.select :color, options_for_select(AgileColor::AGILE_COLORS, @user.color), {:label => l(:label_agile_color), :include_blank => true} %></p>
-    <% end %>
-
-    <%= javascript_tag "$('#user_agile_color_attributes_color').simplecolorpicker({picker: true});"%>
-    <% content_for :header_tags do %>
-        <%= javascript_include_tag 'jquery.simplecolorpicker.js', :plugin => "redmine_agile" %>
-        <%= stylesheet_link_tag 'jquery.simplecolorpicker.css', :plugin => 'redmine_agile' %>
-    <% end %>
-<% end %>
diff --git a/plugins/redmine_agile/assets/images/pro_version_agile.png b/plugins/redmine_agile/assets/images/pro_version_agile.png
deleted file mode 100644
index 5288542f4b91df719ddb392848fe95a2211083d8..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 49763
zcmb??Wl$W^*6lD18ba^@L56|g?k-_)2~Kc#cXxLS4nYG1cY?b+1b26Lf86h@TlN0F
zs#i5V-MhQbsqU`RyVqW8oiI6Bv5(03$N&J~ql7qA0RVvEdf$FS1it^niLf`nuaIoT
z)f@nT519X1U;t?uxbHg=93^B#5Z2)Ek%&J0Y}!Bq0KfnVsF0HD;&H2sE5V)<gXb8J
z(-ZK=HEJwC+#l{(8Eb}Cs@`z;t9@7^XV}Snv;oCK+u5q-+PI`kn1dn#k6j#XM7@!G
zHJKztnNt3^HNX^l)@qfRnR>S@VC+%_z(MQyG45bfz1K-x6X9ZPvg-EzE%OS3E|fO)
zv*2wj>~|qp#!T|c{68UUMFEwJnYN7Ld$2ph`uo2@OIpEuFj`_J`F`Yo0ynnzMv7^w
z)9;Ob|4;CL_wc{n{r~OZe=q3&@fxnPB72OY$aCL@5`sbx)E;GIuzsh00wl9>wpRY<
z_ATAnFv36qIbRQx-aMhua0pN4SZ_#B_l6whvyO98=^y(>&3H08W`E3&f!!HbMwDQ6
zX%W(vC7(S_m_2G2^@60QPt=N=`Jwvdqqkpnl_B4H4i3zE^g^`QA3Sox%No0u*rFAF
ziA$W3*<A!_OKu`l(FcVV=t;|R<7~{_)5=J0{)I$bkt4ksIIr2*(rwsu0cBp=K*0%$
z!QfD$7(*HHp@cm0cI=B1=iBmzAOIjz7rorLGXISkIA+ZG6D9?~`Q)fxZPfHL?!yW$
zEC5r635BcJ>OjE@&pnZ8*LI*7(ydwJ_R5^rP3FKqhR5z>D`~*LbNBO5WGnYghDv>p
zV7ldNAi)o)0km>rTYZg%oHL`EHv9w2JmjurLTQA!X5eR67c6C>tOVC5s`1`lpJ-pS
zRGi)vat%kkMY3J2owo>owstjULDr93t<72xF!-0N6_!RW>(j}%;u%q?SJlgK32ow^
z`M8SZS}87>+rOazUkMJ0j;%g)3|qiAL0~27=68gY>t-pYg-}W&;Q9x|3PVwuWpGyi
z_)kgykt~q<`r}pq$cua$*POedbP<CzyQ&|6k)Hz8f&Sxp_`X=27IrhjyfL3*+mO69
z`JPVUnGgns#8y&L`A0WSJnMxBS~KwLM+tSl8gJ{>F5mmGP!`pW{NA)5sstw1wCpLq
zENGi1b}1ZSiF{auwq?0Vt56(icQUCo@~e&vSWPHh5AmnHP;+2(RU)exGef=tjskk1
zuP-n*j1o3Q(0H>C?e~0sTo@&Q5ey3hz>7r!TqJkeNLedn;Em*e#_>?F=<Pq7U_NJL
z3SreK(nFr+v$N|`Q?NWm`Oy->!oq8Tk_+g8NxeBX^nwM(#{K03NT+d!%HWHjg9Iae
zsQi0fh9j_r{UtbUBa!5eb_4I*K_D<A3~pbmthf>XSD$eM2U$cQ(4G+h%BYhw!NuOg
z<Cn;YygGKkLYINySrMQFNLw(%gi(?(2IK<35QFUfeArmu5ilwM$P8kYKtL-E5ozO{
z+pWJUA&}SK*WcGQmHItRDG`V{`iMJC7MkmO3)&k;BvJzS=6sZwP-jJw)gG`)c_W%6
z<LBfpNdGsx%Ez>UfQ5wUYq$RLBAl7&H9hSuXy(p6F}rGjaj;B4oCVVL?fSVUz(okc
zGEPA-8;mq(3SbkV?^{M8v@-}K4M(vGg$N@6z(a_%7m+9@k&U<xOto8@3s@RVfI!L`
zwVGsFQA~rLzv7b8O2<TipvkJiO5@0w?@mrP=zv@_<e;U+W>R@Vxk@rbbSFpW@6yt+
z;egV1pP~vIM7di8|5sf!IY7rooLy%6MO~m2#}oz!Lyo?QERVxt3Te5x%u!D1TeyTx
zr#dw!OYrUwboO0a#=?L`s3FMEfF9}hJyp$IKhWPb#}(e6i;H)pgJ9o0lZY5I4kiT=
zoXg0@&AL48=L?bCh;47*!ihuGm5qr(3`JqWN<)>FL8iRuiGk45z?1O4-fu}I_~Rl6
z8FE38BYn|!E-u1g$J@ORw?*XjA0FK73M6R}K^;Bc8cN9{#{-;#PJg{hC0qYY{v2Od
zTv`;Mcl`;)gLl=Se>^58=JWPvbX+7l6S_YOhd@PYWneUAUBg{e)PsilW3GJbpT_>J
zs^$z-!JIsY?5aOv%3np|a+|!LUHLeo>g!1whE;rOnoDVk0i@`yZEbn^WS_U(X>osM
z$d2b~<H83BiA+NMU?finGi}<On%de9hGrhe%F7d#zXE)VoSp78+b6ql2?Xrg%#9@8
zIzsa$bHVT5K_aic<;D-H6<QW48ELeXL)a5(+&IaG5Wg@=j)vMy9v_39A!guo8lPl5
z`YhG-Ye!H1+JWOp(pqRoZm{>3s0SF=%=-8uq_)%VB17{m-9{Ve`<SkUz0(N91T0ME
z%?an&Iyl%Qjjyb*%-dV&x*ATgj#h<r{4->vpum(rJvbpI&Kw)Ng~3In%}ouZS60Tk
zn&td>sHcX5C<t}aH(Wn~ZEv&Nt<6SmTL!}B0z-k!s0F@VJrihR<wNYDU%=m_&6#2F
zgtv8>brH#d3G!`j56fXX?f<4%(-al9m9$J^TE-;C2S!*rQwl{EFkoGh25?VKGKmPj
z9Pfw23kzGV#LGDUt-(bO$UJGCONVsE`WW`a?X<dWgV0b{&0P##n=06LekOwf9IkV9
zkENRsDS33j!Ox2MWFigyVb)_r09sJk;9&b>NmeAv_wDV+*A3#^_M2HMXfK%5I20D$
z7Kk$o0iXyXTIfN;0JJ$>gHSXc?nfOyM|?S`5;-~fw>ZY!*4DhYrL|Wz@Om;fI{H>*
zIDvEmqWtaC3wZ<W4|T9{LW<|=3R_Cn86Ex5Vw+Xju+gWGvZv0C4W^lbri|$2jOX3m
z)%eYpm1dWtV1732l#FX)KIfYojxmpA+3xjjQcT}X`;WZ9Z?T6*OWJM^`DVG+vE>@o
z(x53G?vPJ}+@rCB)yJO#+n-imqM|a<JRqJ2>tVYE^dZ80=gUju0xmrYrcHXbK<L6*
z7C8nQnO_bi^83gFJ$tXLhhhM0`cequxYk^+jxoOzWDr)?{VblJWHPLwa#mih{p~y#
z9ksRf+85k*Y<V_D=yA;cyV(R+J~QnXD#+i%`K+{<+QRzq@aW4@V~z9O>5bNk@N)L-
zY#YZ~i|xbVXn1(nuT^;nVRB6dqAvi>P@eSs*~JGOVQ;5A<FNK3<k9_+V|c=e4(qnW
z6xLVu^`-EJVl0$0UH3=$KR`mjAOMB5_y`@(Dk-m&acN;rTj2TZy9pK!O5F|b%{_;(
z`uS#wjngcL6eUH=#RLD#H@y+A*>!P~7#@z{^en|zufp!5sQ`Rf!R(ydvFeW&b1QaY
zruI!w>cYl)le?KNkoD{Nzc|S3ggg#6b$Qk1m>@mRY@KgzYVZRmk0pkUFo)@4tJMpM
z0#6BRuu9a_m)L6VCs78l0S4_)^M&4*d4(z!^yz8W91Yh{h<v)t#RG3L1BcJP@Dnvn
z_^;;!DV;asEFPa*QAMe{L**Uo2I09pf!B+OChNeixTn*l*?<4o*swiMZdf9#T9zXV
ze|a|+fuG8o>%ERNzVqGABhYJ`n_3?#VLeQWosAEUNvjh(bju<Z9Zl-WjSpS#?lqRL
z6rG*mFI2Vx2SCu6CA`V&+vv{D93}~1x)v~;lhFRhG*oE0q2aWwf&Tg~=I_ek#JQoY
zbNyH5BpyY7Fb<aCn%A5PU9krrD&YC`E-UjE0ey1s>K?Y!ud^zFqC!R%ZjH;m%G@Zf
z`xF2aL3cS>v>)dqMg~F3PFihRoemG{RAeH&w@II5f3>Wx@a@~qx83)~!1=4|jHCHq
z59N@4jLHNW#JtWvU0&9B-=85oK8ad7-{gecva&nh+{I*Yxj6OLM?vw&80loC?@v&j
zc7_h+Tp#N=WK~qL&C+s;A#wv7a=GeQfV$A~MeOXNZhBN)a(}QSNU$0lL^_#4Fn)Vd
z$3V#2XzbLecP>bIT)mV`^-mfk5wqX<hYD3mxq-d8XhJo^r(~<IF<E<#G>)y=`ENDf
z7Ob#U@)66Wefj%sbo*RBj9GjfHDRb&sX4lrVsNXj7IlBT_1q4aaY}cb>MnTe%HcSB
zz3#s|ZMC`f3ybJqofIZ%9{x(j+z~0(DqgL3>zm4m4NS`yd4@4bnzI;goAER%Dx+yA
z&IkwtM8;B$fVASV=`d<N_|P}2yURn^pqh0qV%>jO3^MuHV?5kVX)7w1D_edViGH*0
z*7*v+$H%9kq5=R`2>z6ml$EV;GIH{>bHn)x3vaz1oKLKS_RbM}&Ib}{WZ0Aw&pg(m
zNyX^zVJBZ-<pIbbVc~!+=l~bqGA;dJ+f{klMy2zs=hiR2^cCx;fKR7p7H40Geo;~n
zw7LFym`c=WvPMCPY<IpNA#|}M!cV(@82>~ne)W%v{0I$*0mVTKTwJs%(`l}<o-Q6v
zR!6hSpd_8Hsj_~#Jvr*{hgI+S2jR}fM>zmuGBam}hEE%9fpRHv-KTEs+!mGNdpdJ2
zh~Ks!&R&Mczh|_#-a2|&XpCpQ9M~i$?i@8S@nJWqt<`(nJ&s7<Wx7~9&#$NiiYD#M
zY4hBc*}Uw8VX6|h_{{CQX4lp2-My`o&XqUZUZcG9Zt2u6B<}t4@z96-Tj297X}wzS
zOyFs#_!LyD!}qkiWTUfxg7=v9c6Fy(L9dKuc2$tJx!FRvGz%+Y=Zq2RAe{77CY|4I
zbFuFuO>xOA+J$Wos5Zo3ULp=AwRmVF7M>D8IGBuCHrR}Do!0!Yn%2n34esX&dm<)M
zsG+)E@o!mlAeb@0F`4BG^X;Uhdxh^kJ^9!jFMO_*yU%<XU&@Ha{xVDacrmSA!BA0}
z@4h6equr@%@);cp+z88wGo@r+UhtTs;8%CKTXpe%@RvnL-&x<T9UN`hj7K^}keGK^
zLm<FYb7$oi^O}l0gT@Z==Be2eqs7A5u1AK+(=D$$w>C9d-;`^eBwLFYg%_n|B)~%?
zCqoM;qfHr0ArZk4X?^DyG10<9TL2nzq%<aAzkU-sJY9l&7c3=ZA5GLsz{aXJ^l<R~
zuEe=_t6Jte2*xUsNllNky1Lp^M@7ZiSyWzv2vc4jDo8&J$t46Bseu6USTBm5v$NWT
z(+7?VSRy!3d9)NQrzJ~jiuro47oIase$V~+^2{Sv8G}d>C$FV?<!UlaD3b`a6S){e
zL^UNd#Psh14;$gZ*_Wltww6D?MdEuV<NoyZ5kUt=Z<3OC+8v(N!D#S-mRSuxx&#0e
zEed}*%Cz(&nCzWA1T;Y2q^X46>D=41REA^UGDFL`r?YcG=}&AN9A{_L(D<T7C3ze?
z|CLiAG%izsX<AD`f#>in8&OPaY5wD3oraTBiM@kLR16^%^^k~)rn<t^;-a>S`o7~I
zgFJ5`-6p+K4V{Av&L#DyaH$W5zg>ruZs-fAmX}FrkaHuZp}d~h9;nH~cu}~8<LppY
z19$HEDf^mS$lE~Nu*ul3cL@Bo*FrXntG&K{YNwW1z|Psy(tK}>ty>tgP*w}UH(o!>
zT9CCEGuAB4U1<-%o}Pwftdsk14}-cJc10$+g7<dsjhNzb!{_NOhR`)Gy?`=2e7OF=
z%Y|783=SEki=ajxp`RDQ0I5O1-&IuN8U$(XvY51|A!)jbns`JKD%|*AimA;XDIT*P
z$8-eFn_~vqmoj|G#!PR1jJW-3=bnpT^@A$GseuE6t(`;)V5hqcL}n7b2uCxny3!I)
zlId?6HH(+zTk3Zo;|!9oi*iI*?61h(g8?YkX#m|ow)CamusrMrgR`#{>TX!M1Fd(K
z?XEco7ya#7>FG{8=2vrX{U%~!{n~tvyL0W{1!ZL}??WK2Qq&jP_fJW1E-$S>M)uLc
zlSpPd%k@%~F!Keq5I;&ZPrlVh_j<8T!{gR6BLk7LT3>Z%P~uIBq5Y3Yt(nin&WKAR
zfytQmTi0iPNO<nBQP!_Z7nhGV3+k|7j1*1|Ef@(mH!21J2T#vOKZJ(5T1TtZT4oLo
z4}E=2w7~GouG?sSAuP#od$fgBzN28Vevae{EYNcO?_k#=>n+KZ>{$(UJ3Z7`qU5f>
zye=sJNj6J9Z=^EQHJ-ojRvJ!D8W>(SKMR<dA7X(>@T1_Lh2*hhP#M}i^Pg#m6GT!0
z<P<MKK?_H7){{~406u~+bM%jtl_qzO#4q>j4&!`whyP~2`#g4vbK)bJ)z$5^(T;ED
z`(*pD(vWVIduhB~WO-kVuzV-xalVMqc{Vjpx;soHRV~+CuGDi{hOgJR7gcjRO&l((
zvlt5Si!J}++`Q??dD>P|Ug2Z7vKUoOmYBFh#cp<%d8DrKR8kUQU(NFV)1*hBpk^5U
z6eK)=M%i>W_OZJ=AvIN2LLrP5q^HL91!(QSV?!h`w`5!L*WHK}L%Y?&!pGuCTs6zV
zl6mva!;>qQHp_cwBEaW9#kWgJ;Bjz!peW}c7c3hU)%tv7!fmrmTVC$C*n%HqW!k*8
z^{M@0n<0e9J-Er8F!i)$sJf#kFy{-Eml-g)X7(e=`L>w2_74=sJW3yTSId{DQ#?~M
zdbQ;AR8_1Lq8+I9_j<DMI9sy97LdGpjm_S6hppL)xqLiL!<-owRcYBb*pOzAs}dTS
z37Ej80XTZ3W*fg=Q86)sXe4EvvPvt?(*G1b_WI`2yKKE8LF6$R{9c5B7VU=dJtX)p
zX*B8~<S{MN9}VRYf~iFwcn%x&mHGMEg@yM_=L<A8{_Ei(#%^8I<a)5XfA6Smodg*!
zQBl@xT*R7$;AuC|4kzvyW%8{Az+!4ND3K1q1_YRlEe{M1&Q}?fq1F7w>D;fhRXRGw
zFE6KtLjy>I3FX3fj^yFN;LK7K5=23H001i#1?pz#NJ|j?6YDeDfr_n#D|B#}PL0!6
z48Y0DyY2j)Djt#Yz4mOVy|zt28B-QnuJI5SGx0Gn(2$VuqeNWbS}iZ{l=-4sS9kYb
zgC4Kebdu8m95?hGgoKRsgisLza^BOKk!?*uF^X?por_NUtb&?ai?`MZ=6Ale*~Kdk
zpPRb4s?K+4=e~TK**r^A{a(O4Ezax~A5O8Alys|~AE!P(w?w9;Nmf_)+hKun=Vw>b
z6A})bo9HMJV^KQ*U%`aFu#$rC0DvTb_W)O)lujH#U3Q|y#ow@W0%1nCwO2PO?bx!T
ztImZ4yV0>4Hw=@<21=4~YWeJHnl9>SH#gN5_hs*oD()5b_mwnQjSSKd5gu4H+dJIc
zM!kmpDG%OVp3<M$#XTeDzUb=ois5JGV{5b?M^#fPWi5!b_fZvls&f|$ZgzfR7gErm
z*Sg$&vPYIgnM%;bK=K5R?J8%q=Yyj~SUYg7PiTKMDhLK8n8_(YVE`!1Uw@*d$jSpT
z<aH^JG$Zy@WsttUZM)i<GdI+Bfj<f{`dwwWO=i|ybDm$i&Ya>l4BGSR4B-|7#L?iw
zfsD}iuSh)^0^lH&Iu3pS4VsIX?GNq~W-GY)nrrSyfrKb7T-$3W0?LOOs&epy9STK`
zmB4aqz+H{35HckH-AIL0@)N*2kx}c45|7<4;@ex&ZLY0-G3BZZT=#|eXSg0fNd)oa
z>wOWz`VoGjljT-^wLrH5iE5@g2=@9D0|415V3Lmep7nOmvyQfA_Ope$calvBhSDtD
z5F9{>0tOd@$YYa$KmCEdj3`K6QWtFDIFPSqM&rJI@p05D_LR}KU7QuW?KBnIAEUVl
zm|-wtL#0I#0QOUme3I-gOCX|r_<nv^(NGbTDiAD5`30*k$9`+^p~Qyxt!M5v5YWB+
zU7`{h4jWw=ptwbXDQ?6bARUlfv%rjJbxH54BMFrK(PGjWZ_H4L*CEQhMBzs-DA?)$
z9UKdjf$$*OaMrr1ELB0C7hSqT)Nb}cRiuUl%p0vErR!*{#e@1oDx-@MkCi3jk1-?P
zm2}zz=X}um5EXh&B|y#@-nX~&2YH1820ztBe(buR;ScfWFh=@(jO<jk`8-fQNXQp=
zmRgc!of;-#6JjU+QLJNOnhh4cw>KW479fqU=#S6%8ctD|CpA}s)tolCC1BEdy;=1A
zgAEi!-bWxs<gy9^Fm_05`ymntri5rPZ^0@UBqjj}ndzD%<Nw+rN$}*uu_6IeI@~5S
zaVl1W+qoS&1T#AK0bR5@a9cFNohaOq@i?Q>Xr^OV;}YFJmMusalNlAIV5+Arap~v*
z;=fekD@QnOA&z=@+c-_QLV;vV9B&O6#r$UaOh|R<DN@}E<hbpeBj7D#vX$eK(Fc+r
zSY=^<Ds!ni@>6nob-CoE0RX?z`k?qsQBcqQw<rh8Tv5@Kmv!LC;}i%DgV@fG^<x`P
zKQfYh%HK<ZwoKKjAgbT%KKVTd&gUIT_uGJ`oUmL(L0SnteGH{U<A1v-$>g;kfw+c<
z7xVU>WHH3!+n;e4IYp30@)JY;;NZ|%eN({Mq*Zc_tu;*ktl<wTr=vGQ@`X%>LLyMG
z0c2Cso51S)8Bqylrp`d##y&9K2icmA@j*2qGB7GRY^15YvV456qNjU;{F5=t`rr9&
zTJ+$k;T@H5zYYV>;ZA9VV&i3jYTOxWA~env)}?~Hg;EPU)n{#)6hFK**(8kJ;A{{G
zQ&8O0OcTuU^d*03umTdUH+p~T;{Dk7W$zR3-L2nVG)b{9h5<bk&X=~524G-X6I)^m
zXRlvKgQ68$EOE`@^D<qqOVU>j=pl=&!t_-`BY+0wT>Va7+dn1JGtF!K9ehPWY{G!A
znJpfq5Nx$aCzFvFrEv@WgHxN5^U;@-8rl`}_7XH1r>iLSG}$nYArT|+n)j<sqK|p2
zN~k1FT1W}63GJ%|Jgt|$ml;eYI`(`2u=2h0N&!xSg1!o3d+Qu;rzWtl1_F;4or2(z
z38H2!Nf3cAlVe}ia>vBjMB;VYt~4ASEzF+Oh|#(qoos@fJ#)hl!R%oJQ%HcD2o^vZ
z+lqMzM``}s=i(6|LF8iD-6Xk9-QuEwqQw*z?`~rPu+?UyK34o{RjQI=Tt>T_iz3}1
zANub4lVX%aYYU?Y(mxM>4IqHq_56o{aDNEVqQ~L&M+mUCu5OltwZ1}Ixhl8%cxdJe
zHg*tYW&P~#$0LhHo+f~+h#OU$HH=z%`Vxob4`t1t9BkOfM@Pi?_(i3(^z_t+XJ_s9
zb(0&lC-ZGW5vDJ&q=5MqzWc|cy^xSXfveQJ;ase{yIo2?^$w-<fa5|DnVpBDc<W*K
zdPlf+=d!lyTtomj_v36?iP_oy_VRMI)ko&5^Bl$2R?>%{HaW({nT4g*#d`Of@C*Xt
zxM*<gvboU!cQd?7MR7_>1`#g7N^6a*@=U?5hD;P;?{ByCLzM;>N4qf(5BO>a&#zzI
z8m!r7tcp;By6Na->MW>@Bqp333=I`jdZL_W{6l5vMnfl7nJT?khWuOn&EB^EC>GW@
zFtb}}3imMRw5~3({LpBw&TZkuxzqxUv#7?>X*)mBYbS<I4G5l<(af!=sVQsB4V&7u
z*_q`R+MJXVIyW@eJp|`2275db5}6DZCz1<UbVnr~&D}WFIxSoNXfzY{_Lk5XmQhpg
zdkRspTmk3xm?(VfvV>*x6V|_yd+PGsNoAgN_h79+#7$P5^VXR6l0cTq1pq!XaJ;jW
z)@$-hOFY}Q^Ga~-cNOEidpm9Co0DC+$8)Qkn8-oGLMLahw}bE0(nTI|YZ0~N%#ftu
z#&ILf(;e~H$cXFfLjyHU#25U<Zgi_`pX2?>h$WZOhXX|h6GLMy&b5hYo{YG-iw2*k
zgt#~=wWPlOLSCZ1#k$0J%Ffbxlmh#r-69pq-X_NHR#U*j@qb@H!kbf-Pw4Wxp+k4=
zZ?u@U=G5n-HgCtP9u05jAq+*~uGjmC&;G}4ujoGD7GtM{n=fU6iN!^i!|ggR7wd0=
zf)_e%ca{c<CX}Ubhp8M`%6!hpl9afEBbTRZZ-?R7iQ~Li?)3~;&kyMpZSF%m&ir@F
zOYej9JO9IF+G66(%}GUz=jN)13vm7Z-q6eID2)yKJD-#3((KIJeM~$4m%C4>k;DSd
zZvEwKZ`a?)=Lv783oFFxOm*JQ$M4#;6>B!HQ%mYBXpAIs)#8F4>P3fs<9r|&V{OAa
zevL)P`HCg~>lg0J!q(al1KpXKrL#MqmW+&QOhzRaL7@(>W?TL4&KaNE(I#8+oWsTp
zshZn^fmgt1ZI9`MEsv+q2+R&w(R@$(5;sE$2-<DeeeyyWG;?M8lw-{gJsLe^zxx$7
zif9vuF&LGljmqe(Nf_`;rEvSmVf;lO-sQy;?<5mgQuR8^!=^fm7Hw6To2H^MY&Bl*
z&8a?_67QqQwWa#j+xxJRW)~|<)8pgg!-_vtJZ^`~d!AQGGdLsr`;)^i$7N6d-rvQ&
zx0gFlHF}xp+tZbcd4m`z<Q-^No7}mJe3Qp|R@577oHl#QV-R@!^W7V&ygizJL~vV5
zmW*xurM9++N&1gF?ZjrEa5#+AjVhg()^eSPmzS4@>YVP)P<#Rf19RZ0#w@f}+*<aw
zIBhC7ce(D!oHTC0>+a;GheBZg6Ucw#U9|)B+0LCtwZ9yE3deZ5?>}vS^Wx!ddh+BS
zf6o9iikKpuXLqFCbw?`RmkC*y?wJ|gyL9ihbkpMeME%{E45B?`COR~p49F~ke%5L-
zUMTQVX}zCJ&whIs#Z720TJU5n?4hk~?5%C=AvXR_xQ3JIj|Anw88aJ&;wG1T<%BVO
z;IjO$7XXHoQMs_p-pk6zqnToEZ*Zi^?zpy;lD$DO#uQd-C71fd^WHDU^Ee^V>Bw5E
zdC~c$?~rgwqvBsWyefgSe60JTO-|_1>HNdwxHnPCK*G;$f~N!pHrAZRiEn!H-g$75
zCh9|yU5y*vcT+kt|5}dRe?&n2Ai;W~A6ZDd`6f6}JhLlYdW;p>txL^pjx5Yh-)U4A
zzPIN|lnS1uJ<=(dl;M;86T+_!=QG_D!NKIAa=HcOA!HJVhx<O8VXBhD5iHEh1$rq3
zRN=YZf6%f}L+UOrNF>)ob|1z6`sRGNs_nGo;%Qc(O$;wss&k74H`y7;I5@ayXuNfQ
zI^nThKPf#%$zou^jkL86{uXypHK=Qw<`4_*X6-8Ax-xcJ!NNV5WZV<X4tXa!W%#_0
zH!UhM-<e1Z5kE}4;4nl)l4;hDE6dG=r}tIEFE4pZ^%W%H07gjp5j;h>s1sy*qLIvg
z8u6&<z`j`8cXQLf>Enz>eJLc@>&|ku&1&@ZB_utqxm>%MpO+b)vQJp;Y7g|;&zuh7
zQzT+h5UE?@Zx_$!je4n@J=FHou^~At^M$mu!<BRvdxyx-(*eit=sK+iS7jgOc#bqw
ztX2kahr%@#TC2_bgS*1R#TmD+-SZ4-1IPMPUG0&zBv}qh)JhXgv17?%?_2gXM=WIO
zaBSMAv71{JEu9yVPvCq}Dul6m5QyhVL%VdIckZMU1g<BD(l<;0P>m0dYd4fr^Y9pK
z6%@OgGd*6!tPq|fv|mIK%gQ`PAUm{Z7v@UFlezMi(`Gfa<nAB;Dan!R*{S-FyME<{
zBrVuzZ7`}~qlJq{6x}bf%ag(SePp@|>Bo=kIDA?H?WU-S__&iRW!2=}y(f0dAE}v{
zRd!pF!6SQDkj*qU8VZWS7{X>b&5Vf1!T6sCSCHJVk1Q-J?8-PINhenj-)SQk&g0*|
z;phur?k}WJLyC*5mv(HMj{)n$x5$v6iDmT*R&XMX+Q~5iFoM;zkDl6U7@ln?LTELf
zL=qTeYL48N$}>AIYpo{}8`J&KT4N*Uq5iJhQCG?IY-h_=Iq#ap=kb<>X&!>_2_sC}
zrq0=@d)P@E@m96-i;0GHd&a{-!eYE!YHUmO$FJuBIfCc&3_2*^;al5iO@dW_e$C8v
zGy2;2yBCgTD5Jb|^sIS!<A&g6rlP;;u~m&fq@VPU!-r8@`SZQwpWeBHAI1<!#&$jR
zoOg{SCJlfcWWTcHFD>1Y2CQ^R^wqpen*24wRDL3MoMkzxof4#C*~oPnXsK`B#X$CW
z(XN}d@Bjr8+_1sE<SD?Rrg8)zOvPOHpkZhRWzx7{fe2|eSphSV=_F3zUsGeBII^DQ
zi{VZ>8TB8Cp<?K0v9L_K07DFu(9qm+F&HibpfE^KmN%{~JPM|knY(Ji*q)Mr@#Co@
zH)7;p;UIE5sO#y2fej(o-HgC%Pq8vh7(@l~3$wTI-476q;-`;5`SgMrPxhx67T~Cr
zo{@HGWJCg_fb}DpXs#<v=h>UJ*Man%o7;{Y$Vr1?@L<h6bJwgL{uBoQq!m8|KWnSj
zBL(9Hh#~_*JLrx<!swkDmoXlisQ`Z=b;u2JgruqFNQkDX-sGk^=Gv&24A&GOAt_9w
ziH|lAuKrtSnLy>SM#Hd_AsRX`VsC*?G*jXa=0WJl$t9Wyj9_REl{f%>AQqYww-NMX
zC=Me`3|u~7URv0%aKTz>8;i8t%knv#Xia;}Hrla)e5i{;?>OiEbQ1nI5CDiACX(f9
zdjw`*%3o6c0tVV;aK(YJ5Gg5^UP65CCzVU6eOV(d0`6J$pA}|5X2Xd)Hfjn&^I?3+
zsx5Wc<77d!b#)?AKgx$$1WSGCC;|fF;^J)La}rQH;=-(x86@Qa9b_#Zd;L9i<D)zH
z%B2%%V<ThZU*aEg=zwHBy;qVS94<Ap-!>l!6kR1MOTDEU=A4-tU%mq~piuZR$LrL_
zoCUrDMt<gtHvNvK`RDksKkkMYsVUqy--hj&&0wxvNq#`aqVVWU!Vx)pds2fOg{4<I
z&=U&u(K|>na4+svm}w(nJK9=#kU}ZrBT*s(l=O&<BS-|9)7CKlhQ;yD7RFG((4pZR
zb=M!$I<|16!L0>wYJWxo0j<&c-6liGZUupzq1ee)53PuMAwFg>YHb4$ae7uAFd4}R
z%hCv3`0;L!taX2hy77xSvfy&YV3tSZpGt<5$KKzsVIu81`4?y30u)6flOcG18&g`?
z!1YjOSU*Z20Jw-GoxKk<=lz|-zo*kNA%XTzHldW@MFky*VF6+(BPoGjnL`F9gwUdr
z6UXeB4KR{=J6<NYokQMRI))ywqJn!S-%_Ye#VVfzNhk!t)rVPv9SZ|ul9?aEs>Brh
z`~Xz$CJ<U$SyPy~_rVoSMYoiHk4b(LhJ{nLxr@lMJ$$8_+*tzcX~g?%aBppur>Ey(
zPofp_!9)c|p7TmiBBC8pvaV(IW-JbqBe|(Cpe}a%^w(-<XINRfDojW3@Z5Xy+A$J=
z%7gUS1&QOEjz=@UN_G(u@i`YXHMttP^0tFb%%{e=l$8^K>-+OPbry6_w^k_ku*K9;
zx!8)n4to_!DmeGm%#^c)2^0%~vtk>YJ6|3iQsExBpuDdo$IA~E*okm2UZmmFO$4`-
zc5h8}G@;TUQ6@@ypsh4t$uR6(kJr`%%RoX`7BAJ3-b=aB-WAm2=|0(^`MA6Ewu;u<
zi2aGQ$AE_M%N$-a`Pj#v%LO{KgbH@C?W=qvGF^q$eZEq*WHL9;Z%CnCCu}1x?P<8a
zdd%`l$wg{Np*i*!`rU&G9Q9UzV0FiN9So-nu(5ZPp#wrDMHmNIXEHGzz9=P$cG8Kx
zhn6t%l|$BHkzp5MMcU^jI)o4F!Y<22HY`pxO~q+ojG0^mgM{UtGf$JZ5dIb$#T9i~
zJ-~AN+st>b!&gfkbotN@LsL>@(jh>{Al8JaE)nPXZkty+@&}du-M<4JK!fMY)NW$8
zZ6XO#@*<P584h;GavfE%Z4N4GMxHo5JrTcJH`F&~_rE=w7vi`Db!%YPB<s0m1E~(K
zmXey+a*PP(6HPh5$j+^X`V^+xhoPgJrWK-l2I=7?`EYS%B?%07cMr68324Qd0$Jc<
z`-7Jk>DS+G?~1RNz{>%R%SxRDT41ua`<{i)%XQ6oTWZ2do5k8`5z%u4@$2Ec4jc&F
z;E2h18Y1?xe|uG>QN?0}aT>MdJkF(DP*VGBFjE*j7`DItXZQs+&!)xBSj5P0>4aqJ
z#Q9yyB*r^=Iry#<2CXgAm~A<0<7sYM%<_0OWh7XrYQ0@;zAJW~*}BN!b<1u1bLArN
zBy=?U-i}O4o(>`rOdGM#nEC>n`Q-C@xc9amH(3>ztiwMh<G$`kI5R_oaYCMSRIR`(
zZe7e=T-{4Wz}csv#X!ZJn9rzJ-^J=zqf~6OQBtKu%r#OgCixH%#{NVA_|dXU)_6(A
z|MRO>)FxN@9c}v%?yKsn208mrs01zn{#%2O$0>pU=fs-8_60X;n|*Dw$NBq%G4!xF
z;}K&RsCK>8d?`yRuQ54VT6Cw^G9MVYMzd9`4)NcR`urt5jeGo8>%!`)*NwV^alzB^
zLh!rMiX6<rRR}^AOUXdV;IuP(Su7qwxl0-?S+V{UtM_$VI^pfSA0Z<AlK5!^L5JJ5
z^g-ol`bmd|n(BVUg#Wy&Vy$sz_1A$DvHMNWkxhozIjjdGPK4-KBN*|9qd85ZNlUiw
zM&+*k`HIEkY<}CGtNrZ-Rz#$+(Jl4u{(F1)p<d((a+M`x6Sh7{<%ARvA={OT0ET|!
zI~XlKIu77=ELch33&RCQ@avDp+?re*Bt0<LL>@?>7r$_z@+M5<k|naSLI>x!e9vf9
z_R}Av_}1aN_Hx$&Kg&vo75+T8IxSzlbRxyk<EX_zJ@U}U;Z5xO(yQ|-9)th>g4bD;
zEdQE0npLfHLRt2@yUpUe(a$eEfBAI$?t<Y-O+9@t?ZQ~0ORKG4KDBw~wzRCbc{K39
zbxpSLt_>yruBN3f9zlac$Ag$KiD_ZLB@b^na)IKEjCV+=GgtxS68g{&PlYdEILaBQ
zw35@`LHa>xAnBK**~G+IV`HQ1Th!rdR(Ad!XPy)=45JhmawJUb3ds@{UMeFWp^~!N
z&mKX!51?<sNZJ(^N3+T~a?o2!!DoT`5W=*Jd;R63I!^&27oH$8f-G#NPBQRo^ES&~
zFPA=_-~{zUy@Ek3HHiC3pHp#+Y9z*?U=i&^YiM&7(}%51V*{%YJ)Qc(==eZf3bpYi
z>!bpLzl=-lGJo=0O50C*`dW?=BEb&AK{3yfGaOEudAe&)N>8wRysi`D_cW1FKG@&S
zr!C2PIqueZy<Fpa-1mYwq+o$V%XJzq5d>Vv(l^|<H=5fn``2EV_xNeEt@QP|@!4QI
z#g(SJ<BI<DKf)ivXJw%L;3+2NbJrhD<5l~~c|Iu7PP?F5PU&{=dv3PWjPOhPa$QY%
ziO2o#fQmV#2GFzLO&!gu>&taNzqgLh+JsBn%XUNCjS)@s&Zj`96%=?jW%PHfTT(K7
z+HBL$(f-iW(DsmsO%u*WcUZfGC35Y)%$RVm=DZQgri}vkwY`%J$3huiw~vEO7&PYh
zMU-_|C|9}z9ZI8R881QAgq6?vix8O{6f3uroo+bOc}dp2lA*JO&k!L2O$iEWS>Wo_
zR*%GtU7hAmx8>Y+7C9--r<PU@gnR9|_vv+8qj<F-WUxoqyTxTPJI^=^TKJ5m=!Io{
zWHT#6U7cMdJxvy3Fz8_jkiej!yiQEq1=^+yQ_~IVwAas`D5s@2owj*gMH7AjQ1bLE
zYG}x7DW}D4EZ3LK?o_LN1;&#-dH!;;peZSqs%BnUs-NK|2p=Uc{~`Y~bBmsaW?;k-
z`}5BBt})P7sHc>6E_^6{Gd{ytKu%t}sE8WSu<$#PCcOUahX4~77dQLEzRikiY?h9O
z20s@Y8x#M-KE>(i_;9kJy1d$7v+1-Vq6;IB6K5W@1%l1+AxPHvJCm1?bv8U?Z&R$X
zXFps_z2dPpc6(cH=CeN5s=;fmst^WIf3EA{V-Ak2BtrrX05>GX3g&h$7BywnZWdSg
zLwLG(D<i@c<sA6%^nZT$jjoQ=O<N8P{3frc_%)H_(zPMk)zHHjK`_IN1#3DNM3y@;
zx<8;>K_=n6@H@fy1Y$J9s6eZ4sW3GI@a>4aQ(YhYvNt-~>6=;`f;)1s!OSo!I9yhv
zW^BXy^j9X%Oz7t=Bt3L97iZ{Of2D7%`nJnnL!`L#?;psDMx93^L&vGQ(X<t7@ko>g
zyzTgB@&%;DRl9sFYYy3zxpY*YkpY3G5k~xzve_`Hv$!CtKTn#h<@eaRwxu@&K?ZnA
zN{Ei&@P_p1vij^$YB1^4=I0oo9n_;m;0vS}<gRc>3y&pKi-{{pK6V%uQgf@LB}&|F
z*Q~YK5h(FS?J}nGR^iiT*GkfT#O-tO4UDC(sFjd@a&&U^bg24jx<<%j0}{h>3AP4(
zdRaGNFT}(<biW=GBevtz!k`9a+kNzfV!>hIRWg3Uw8g_lhYOI#bPPa2N*t`(s9_+i
z=hE5=2sk=hRa9)gcx;@WNoNNL!x*89M1`P<0Ll~S^STBFp?FsC2lC;eMU1+TaQSfD
z8bO{jeTA|9Z@7Zn97io`QhPH?`#Ulc#mgAFHQbLqLS&3JU)4rxNzzvQ%x<-VsWRa-
z(Z1CpE*j4Mrc6Q<Lef9k!&tgfyZ5{AQ&BMo;esPNk>td`v8m%m8=YYGhvpLK$MPj~
zsT8`&Zjc2am&G8ODlo3fQQv-(L00Qp%J;otu!w|%4^E|P4E4nfLW`x;$9C?$Yy6}U
zjZ-M7aEFsXiQ7L%QkZ??FMU}vUqW&e5-(ytMF=c>2Uqb)KvFf=q*57;Mt~<TR@<uW
zR;O|!c9Hkqdc^5Rp8i7eK7SIX;H%D}@;q?MS%bhqqE<!A`SH7?=)CrJo><fWw_Md`
zfo#CCvVwt=i!dccz0NBE!ri2iP7WVZgM78zMw>qUJKO$dKWwC><&)2PHwU*y`cZ|k
zp-~pU>kp-#w2vBTHky~;rDv^|*tz)0O2X&TSy<<HY@0&YFMD0=!^6W>%khbhql3G;
zwOSLMELK({L4U(>Rt(+ceVp)EoR>Vf)zQqwJePV6Eu-ifdw%A?GHGLmhR<w&buOvW
z`m%Qn6ZYZpkg=WC&Rb-McH$EP0bx41E^NTN?-qoSqJc$GSRmF<js^!MA((_m2tfj6
zrhEHzG{N|QaS=rT3ETp7joxfkKQZUkaS(9TH94B?umIq6<qjd7!1+xRoX^g*vmW@E
za7K&)>3TRceVy}6RiSp%SSIz1GAL3R`Y+*(SF?g0_XlASHJnX|TSl-S?k%JuXGW)B
zNXYk#n;;BZ7BOD>S|`CgD#>wX$i+PPVUWHNd8AY`+)S&{QLQKguNSokxPI-|n}6$}
zcx<cQyKdc#tDc9+X#%WMlO9_=3vJP}G&Y&GCWdV{*EOwCWyp$bQ|9&moEnxNd;Q+E
z-(Y9HbC{J0?B0U;<2l(ZT(dJ(vZqwlHC)q^zs{Sb;X^y!Vidt*<HYVPEHo7t%ZaDE
zeNq+e)9!Udaha~Czd^I+YI-Se;vK;&ILYeTFg#AX-yEc9vI*AU6ApsyF0X^k-k+HL
zKStSKO^hdZu*`xNqF#!_WzLoL+0cv49Se@bFC;3v!9NxD3)p4$@-I!jIXe!y{>`wx
zix~EY*P{iyuX$&hUx>4Sy1x1zVrK`_A3rnHEnDs*hLFCSwRd($e@z!2QBVL?%j;o=
ze5;ArY?e*4V9HljWck~Sh+?*OOJi7u@#QPI$<P@s=BCLwkm3dU?gk78*`_a;!fwVf
z3&~^)p{Z=)e|m`NEA{XT#=^!jAGl_)8vGo0B9Yupn_6q~+(*s%`DXqJ)Kd%ecQWp0
ziB4^~Spoe-NaIe^Wkx^KtJ;(dmrxR&ovdAThB&Xhc=rXq&PFfCPkrRMEpIk(A;Yq4
z*4AmTm`kclFKMveAvf{mHe2`_M122zhNYzBxN7QpviE)OGxlC$GEP!boz9=Rs8AfF
z=ufD|$vF6awyG{Q6iX(-*Ey%Z{zDM*&#xFcEX-N0RXMHk0g=;)A`N7btYJS?k6?XX
z)-G$jnMHsXecK@Rx%j6TLI=Xh1rDH$`8*8IJt2t3Eg+@nrL^DgY;<-a0bqo9J?}?;
zM{uBHgJFa&pM-Pd5QRWLxzKV^YVI^s%Z%;(7CPX})zAS=+##3tD?}Ar<*gda^h@`y
z06BnfLBX$44V4wAy@J)%$&xwlh=}U5#=U4?0amtycWlz?@I1xSPj77OUR%k;&JH7i
z%{Yu@uF}9kk!yJJF^ZDn)|Y(UcWPk&pN37A;mE_WAIH0!dhiE%HkQpz9%Jojhok&s
z196+{;k8p=HFy^K8O><eJ`9dm&S)QsRThk<V!;N`AHhb%_jy|>gb$$re3g`U3};dE
zs?31nkGW%1E(Pti<}JmVlOf4z?OV-^&R$emGWTI=sHj<s$cw53FKsiKwwd9w7do7E
z$1GpP#Z)Y{C@SR_<$Ooz55SuJCe&MwA^#Ji&Kl?&o*Nm75*#Y(-i&8JATG3Y0gLct
zU3WHLd(^h(?$}~u>Ta$u{S7{TyVB@0wsN_ahLUosqSE%*80X8-M*Pf7@vyN2H;skG
z@Z7)c#N=2#`%aV#K$exhJhF4<nvgyYBH8N9Lu)5&H;Q*6TK*OVz0I;5;dzMlzlJi6
z=F{uc>56vm`oEmte|~LVvAftz3}7Ipp&wJm7KsN*O&S?CEiECu9%N;)I$aI>To)CH
z;0Y(xp1mB^1Y6HrnakVVA`rFEAR)s_(Kn`jLHnlL^hf52ls~JXvT}Rz=1V4_@Rvgs
zKq>)Z+=4O;Tr89d1Ic)A8^%mF89y2tc3-YlXVdQV$4B?yjywW9#k(~Tv{*e|)q%d#
znG>hc;BIa%>EOu)D+~+V>{u`AJ}51vtW}TA<Z^gf?{q0H85R>G|MotcL8=v8ITWrB
zu(WJ>$D4=jv*P}OhK#x|Ucy`pr@_G`L@{~U;Lm_1o+gAxu{oO}du{?jV}q?SKfg(*
zBUM8qcMp$8Wo7&?S@aAvO$)9f7`>~jdUP0C`5}EutqGw_3x+_(PBg3pkUoSll_e|>
z0LIwd{Ca<X+S#1{PvqEF(fe0LF~r8!b-wD72%nJEsUS)^5o-qnh?a9xGGJ+~5s2`y
z+;+k$-eP8~i~_YK5JID+oS^lCatH#HEFMXbYp^-1(joS|O5Dp!BNvAN?HFfQtG13m
zggmLM!zwCTI9rdN8`C&H-C!27uX*gvG<^S9=Q1p-Y+T<p6h2FHvtSg2k>DT|F;2{n
zf%?d?=F_lQ$s(`YVJT~pn?b7_Xy)B~j0J<8hK+tNb7kH#U4h0H@R@;~|8~ARnf_lE
zpQ=uC!R0~WC!fdZPp-?+Qpxy*g@rz^5#7X3^DDB2FPpu=pxlK}{El-MpS_&+r`?2i
zhj@9j$-qf{yTf&t;M>64^CR(d_uC>d8ww~6d}$qLsM!&aiH)%Pu3>zmERBkGs?brT
zV7!_7z3b)S!aTvn@o`KE%}Kan;ivdKs5XeSsULQMzk!yUrA2mnJD&FN@q}#!46Xa~
zF#A4xftHSj*Y!NJ!s|xyjG)=|!QMexd0}VxqtC5LduwiWq@tmvp{8zWUd{MHJqMff
zVUyc~!WIY7+cz(-;Tak__K=g~#>WcJgDn;0&VyQ=wtuY5+)g!ZHy1XZZ8<qFJF3y2
zleY=EJr9p`-dY{@%Coo>b>!79Gi?N33Vj|2iC^}6ZJ{Mv8V`NxdlY@|N^-U1O6|6B
zaTDB_6RT5@(ce&U4#Zpnh#<Te1b`U|i^iCcGl=Wk12-w?mj{w&PZb2COOg$-Ca875
zeTGE9VTS?yd`a*-O{aVNE`D`hDc8|JCRVr{ucJ6T9~Ou`-a<fr=l}{~1<NP?8zy)N
z3|O2yNY!RpK|#?Bzo7plLcwvopLkYcoOWoOjvjCSbcBY6UQ?cli+OkGHVw*su;BLf
zllQ5MXfVRDY{=5iw5#h{9&xm(P)J(iULIbKEtrK=bxU1V?V$hlSPCTG<HCFXS``M<
zJZ+Vnaf(ol3x+v;y+^<ceb!P^M>gOXD{}mTdVF$&ko4tCbFdgWDk1mk#J|<m*`<{w
z3W?HkYpu~@e2XP!`JSdGr#cH2H(u24$U;8pccN?LM6I>D*wEE*<RJFM*~ropA~3#Z
zTmJ4|f1i`FIz0(w6RkD<fCyNxCi?)y4R(9=lrnG-{%+%AXJ>8Up`o*;tERU4;PBBz
zR>!6Jgsnu<y3Sdr$x~=9s+86STm<8bGs7iMhUL_pXmoS_3n|&xc7Ng^>PvC^^KXIj
za(a)g0lar}=rA(>TjRK~p({5xJQ^stepF-5$hh&{Wrv3%N<$qX@Yc9?W&GEA)soBY
zvZ?=wo_fUE{CI!=5aA~b7-O>DTIVTf+=oO!;ORbWm_k<5`wiBk{dtE$D*k?KGU8}X
z_1%hKKmS{hk(X;HM)U?jz;-X-)AGW<`P;WwfvlHpiqmAW&TZC;1H0pmzp?|dgT5F@
zs90M5>*&3-YS&c$<Mf6+Y6TH)t?NU~mZ=4?zYiMYbgfMx6!TeoXJZ!)yV4xSF>3?c
znfx^YPhb3BlXNTT2u_jAUeK+e5wg(ti9${Z(hx`jOhctN#BYE;v(0sDdFR0P{<FZE
zz^v_VnE?g*!hVOi#}yrxNN8%OCJ5cQUKX1bdFh0{kVh&H5m-I%0|&4*F%B+#xhWi1
zE!Rj;)g<~F<(Hk%hqt|(%%!zTDMFW$qvdpzKUka=7gv&i<8q1TYX&^XH!;SdjK}{?
z2}`xQsFQD0uOh;@mjXFMa`tIczB(N4=P0ds?4dYK|Ap#v*ZicR;T|4-<^8;6^5N3!
zgz&|!kw2#YC4u7C@aW+238Bn#x!L)<htbfO*XMP4blm5(``wBeitqUL<m~;#^PTTv
zyPciQxl~iFNzgbE$JDf5E*qB)la*p=>ki?p-A77Txc^<Jo|^LS%+V7<n=REQ3D4ET
zWn=3FCcKljL<IQl4rlK2IERPSVUTy=1Y23J@p8m*PiuK%1xhj$K@NlnZs;;*B&2uB
zoH`Z(;0;g=a#*LF=x}F4S=X1XYhX+xnf#C<{iY9oROMoYnz&}p;;cilkb`}@Hsqjj
z%0g&eonQs%bS``PNrzv<M_A`GH3mc!00`#8WevzrIsquuZk9G#RAmcI`iqzDP!gCP
zpUnnk3G>0Cvu&Me`X*jH4+{aH0s*O-99*`LOvb>TQQX@Z^Alfyco1c_p*`G4C>m<u
z+r5L1$FX8eoNqip8TGN(Ml6LasBU6cCOx1NARY+|MtqEP7b=--lYsXn0d6sUjwA6E
zD(F5~^nMF~$Ae_!l#E50@$&t_Nd}bui;w_iQ*0naL79rpjJkwUYKZ56{68!;G8ewZ
zb(bnE{d|a6qpbh}7-e&;mQ?2cDPe76f8M@Wdwcmbe{##<X?;=>68&VqtaIN*`c5C}
zJWh#yBIeJ`JbBpsOguPbuT~Y_e7b;zTy}6m=zTruam4-)!!|^?ZfTpxxk5)x`7{f)
z?-CC5K}mPqL{?MR;KMgTa9(lK-x>!w<$qC5768uJq~xO-Fla{E2!NQ6egFr@6$A$d
zl?M#n8lfF%fA2d#H<d{J9%lu8yiCtpoDu%77XS+JdmRWleZC`ZJLm9uS}|36J3ixo
zKKdM^?n<tPBf>O0_`8TP9p&}dZ_dg&j0ptrV+!ehm=N~R@wzeed6RRFo;$h$qz3vg
zotRpmKJL1-nb>;oWjghKU3kv`7ReQ?51y-hE|&Ypr^I-8*5-$3Ais1?Wo(%UL*vrr
zy5`ffx7UNNT!LmT^dI3EIv<3`*<j?rkwM#Gt{JL$0F2QdX|x1)pGqhJzC56Mk#&&r
zhb6DTH**YhfIN9a{Szs~#BfMcqZ$ch{QC_>_6`dI@e33TZa0|9lPp+Tj}-i-2#lwz
z2~Vvu(Ys(A8)@T&kCPPwu^PK;R+2%{^-BBr)cy|us6bc0Ghov45|Z370g@WNq}oz2
z@p;Bmoo(g`vt2m4jr{8E?Gp!xHXetq#J8?65pC8Eh(r+pM6|_=fTFRT5mCn2E98m2
z4Ey@Kp8DCLtgMX5$>~pj>i&X)Jc{j;nX<D!%!5hL0%mXmOf!>gm|ba|yqgQn-rg^k
z^+6PGcTY!ams%;Tg@*=43kq^%Sq+8#h-gz(e<-NPqAV%^98mE41BPL!HW>hIis}yp
z6jfFv=g8P_Fc2y!DM*w@2g6}W5=Mr{GqW;nssh0L{^{|Fsp6sn#*m12cMrJTPDPbO
zk!evwRTW8)0I<J*(B*OnB2!dJ0Ac>#c?3K>C`Cr{M4wDL2_h;ovs7m!!VQxnB5*5+
z9@C%J`bPw!rOB?uE{BO$+bof|0x~)08~`{0BjB8Xh)iOH3=lbxX#yt#Fo_d#LNFOO
zMIr%A2BrdB0Fr=Y;4*LqAc8R~5y-48Hvn)TmI9qWbE7b?9mMP}DXF&xrtBa?v(K8}
z8SFOpiMx&cS0#wm8OF-(lbV3Wn&04D^Oa$W&^C)<HBr$GePm=b+m|ItauRF}76l=u
zETw>j_lbjf)3uO(PK{e!{{E{JYukkqkWR^+1W-uov_ckwD*lc2Un`6(0kBzHr&>@d
z6oCv2;*KZ+h(ys?rvj7#ObZGW2Vnhg_TD>8j_bM;J?Gv^ox|h-CIW*Q<eUfs3?K=L
zQC6U61yhzSIjqy#+Fk3t@7w3~`n~mMTi*4q{p5YJsGLQL0Tc;<36Y2c3^E2`5+<ka
z=~P`+_n!Ahb@%inBuN$|v%Vp}Mt5~}b@e@U((n9!Zfi%)K~_l45d+IiuWC+I^B_~1
z(lgL~;OJYr?g<AYYnE;d1;bg}!h9lLBqNqHH6)j@i3)Z>M1{|qUF;kQ8IqW4&xn&#
zq70_l%w&%!@0?Kp2y%^A3Q5w1a1j7FC?%H?ffRBSAQglHLM5pIaR3E{>e4;9!E*t!
zQbbobnHU2Vc~k_*(RWT(&q(I~Iji3&ym1l{4L~VpWw7Mu#ba26j$$)J2MhN}EWVTF
zT^1__9u|Y_h-`&pbyYQyP5HcHHk-KtHh89wrP=DnH-I|Lfv>)!o^?^-p1BbOP728)
z0=nAGlM1UjE(n&>dLt*df1WO^aH`~@g(;iI8@bOolaWH+uyK-2&V6i6r8t}NrQ9(z
zvN$pzlBx<leO>Ro_vRzp9zS{J*nuPO{^@7G5D5pRBt^%seebJ{wawc$-jhr@Dpwrg
zfCP!s&1qft%NeQ)2L#T6B&5?~wt^;-8$7cde3K|AUV)gygCZl4M6>BQAqN1IYGIj8
zt&|E-DK8%<fE<Zgl9GrdNdh8?Btc3f$v`MXijWFO4yK=yp-CewrW(8fW9?iTzjO4p
z2mu|EAa1X?bjLh~G_{{D3o48Sbz?U!$DAy8HXmKcVnM+iBozda%T9WW19Ui>A<-O&
zVweNEcq>d%#mnFhxN8ddXDFIxq-j8?JeSXO8EBl*#>5%AbvT9VY}$a>6g2Nt>g^e*
zX#gQOGDaj3A(=|duc-Odr#?Q`AN!C0_0KL`JujtXj47NiYFt_ttr#0mafO>%)9W?7
zUjFhMubA2F$DaIXG9^tb{V!ktUjR^AQYIzURM&6Yu$?3k2}DjHNV=!ORfQ=E%a~$%
zUsmL=HzBcH_&kN(0iehs%PAPpe9b;L9jdzjRHosrT%lzELP;Qj90Ea@5y_DZHnT#7
zb}ovuxlrjdW8ImwU@i(vO7v*@Z4HAfcVwt850HVN<Z@9|bF{Rm09o!!l1xPHWZM~r
zuyVx;Noj^Y03gaBU{01BB!J}LBXIH~BeNEkW^$w94Fla->ryyZd^?B5;!ojMZyg+a
zgW-f(pU0bn3}*Sh)1v*^E{nrJMK@hB&CKxFz!(4i?+3>Q>*hB^LXrRZ{C}HQRkwcS
zJx5v(KC%6SZ|r?JlgVVvblv>=t?M4#cjRrZC~KE*sH?8CZT{GUPaSJJ{N9Ou8&=;7
zAa#vpZDDAdnGw37X$m-w1Yl<`oxR%C{(%QSC><BPoueQ^WVuR}m?V#wwJk<Os=|Z>
z&VVuINSOmUA&?{@3B-^PP?8&^P9~3e-c=VC4GbA`xD{l2wcmGlj#?{s<|_dC#r7-R
zT?5OOE{H^f-QAswn(ES-OkeNF!i6;#FSPeuA6l_|VQE?P<(KyfNli_4!0_17!<@18
z8x~txI~Ge=wqxjOAml%C_-JKCblK7cbHutdx8>k$I^{P;JJB2%W1K@V`OY?!03#!U
z=mvv+FbAL3P3H}7I&4TcyPZurCX#eWswQd%x>Sv^#Mu0*`mO7?CQ_+&D>rxdU-cNl
zfB#$mfiu2)-|L3q9~c?fy6(Qlnx-%Q%m1@|%Z{>WxySGY{UKWbSNMumOUL6Q7p|WB
z+^0W#@?_gTeC2Pw9`C}2#Sd(L@Ji>Ei&xKAl~-?EyRM`Aa@&OyAAaIPX4<hF>Gf!C
zCD+VKN4Nu8xn5Duud*rLm55YT85rqT6_qiTHq!xb(ChUP0SQo5&bdMYvW^3U9zzp?
zEJv_>kp&TQSppFffdXVn@__^bf`1+U6a0DGDxz`t=*eAsj;va-;ER9%t<QYssV{%|
zrT_5x-#B~j>iP3mw`^JY=9@<sFK+nTFMjJcKm8;kSXpamX!MQUM+`$B8;!M{xw`e<
z)&Ka9->#}E-?VYr-hHhrRxE03YtLHN+BGZO?CyrEaOT7u-i(8PE(5;7qzKLNp|^ND
zBL+}Xa>rv#bo=uwcW{!E138X^sX-Nx7%(Cj867Gwt=O<-eKzK(x-xKTkSomR_4oAk
zlXNsivn;Enu|-vNpC=gf2LW)^^5x~#;rNJ!3}gldhlUN+kS2`B;!B!W*_OHUtru&m
z>i_3g|MsDKAAaZ1ZbSE4j;O6}ID6szt9xGh^fSNHezm*pLTe}x-nQ|ffIlFlbPZ<G
zuTBkvkulRUFLj(>-n63i!jVX@WO!^~>B3b6psLFC!JaE!m#WIDo9mZ2B(8Q}^?3Xh
z(Ta>^A|N7Xf)L$`Nj_jEV#@6+-rdE16$tExhRTiW7b9Rgonc&AvSh*Gqi1@1ht{lF
z+|p7vIub*~iFhIyiZnIVnWkA^zhw2QkyJ99NF;}c#vBJ2<A3repYZuSZKp2}4UIH3
zRM*TagWO~$&gN8chW*gop6~`5gtOW$&JiENPw~SMN4VjTJ<fJJn8^l+H#5jvBoL(8
z=YicyE@PNy>dj{52Os^Is;J3fX$dEQO3)u(v3NBAq|Hn?R3b>)wq+Rp-hmz=EzZ>O
z*l73lUNZrlaR~w$Br+2Lf53L2yT8-W^h`G8GyKtTbk)+eS30jXFIZYrUDtNu`00x$
z+pk`D?wQZ&n*P7O{>7&r`cNVjZ@X~ffqNcwGLEYpk_(0lH7B<mF38Y$+r^8~P&AoN
z2qCJ==aoe(h)@Acq$XO=y|-oc{m0K84*G(9!`D-0(#)F67OonJ4VQ+(4Ydts7A{;n
zTT@x<_XHi`Ah^muaMy0^wr}?U2>MGe9XNC5TI0f6&N)N4=bjaNb{`q&AKkEF**Cwn
z>++S}#s#&EA&^L?GwF<#P7i<UTf20_UtU@Y076Kw*Aor}%xrq|=H(ARwE5Vv3oq|H
zh@j7rP0dv3gV{71=UT)!DDcjVO7N#nRuQJxa;8dzO;@!em~7nLN&-Cd2$Q%=nykIU
z+*!bsvmyW?CKZN-pr-nTb=9?kNa1S66f0U*e*DSjG8s9qa{eED`af2dRsHTK{x}c_
zmzGpK{m3(_ra!dp1A{{Yv57H7<*oya$HxMJV8%?odvJGUMWv=FQi|d6fq4}*Pd@zA
z?tME?ojc|@j?eG`XR5-20sk|Pf8t8#B}d4{x<$gycLejs339TLO9T=m0gS|kBcX_@
z>LsDlE1m5}S`TpKoU_5vzS2lVbzOOFWldLqM=Ux1(B_90H@0;3b=X$+T2IH(w&RX1
zR7L6S?^YBbx86lkQ5gZ<oxEUoRtJ_96_LKap)+llG)>K9%vCEF8-`X}TjlkXl$HeA
z+pnKKec3P!$F`O&Tk!l>UOs;OTy1UD`SX|48Pn@C7!w|k4kQ$0hYp>tD2s+dzGX`q
zXv!E7-7Hm`wHico$eG`YhrC(jc^i5*B!Nao<&F2ybqEJ}zCw<O3XxMJ+>Wzmp~b=L
z%xi^{4gF~a=UMYuw<AJiF#w+Hsv%@d3ji4BM1UM@AxTIC;HdZwMIbD!Uy#j;@{)>q
zl~ra&Jg|BDeVe!2ju4Juz|E}u&||+20DiCkcRuyUS358JJfU?fHwAs+vzOX>`a5c>
z8|rH6_a1rYiHDyY9v%Jq55Mxs5C8V{fu4^){oEJ7{NGef{f!U(hLx1a_+({xdSQvV
z7ngIDF}`{A1}W{A_PlI6StF>MX2#Gx;D9ZqX=OczPY4hWV~kCt6OlmB5q9hO_nI4*
zoNhn0L;?bH0UoFokI~;<+Z|#d0C`{Epy5${J`Vuss-DT3oO4Z40kOAt!0YpPy*gv4
zD#qCOxX<fBWc`Cfl@%pYlB#hl>v#<vkP9ihyZS;Qe>7SGWY1w;H<fIJ8@@Ex4mUQ=
zDHd{@nvR;~*rTI?^70BPh0D0h0Eq}WbM3))Xnd@@y3#$oA|)kLalbFb7|W|dOPB=2
zxg@mie+rNVs1zErIBCYraU1|pRUJS8iD@XDNu13j9)k#^HO|vLm8{eNhX4Rs(X<T1
z0#M2A;Q}FnK*oVcl4OjN6bLNG6YBO#G!7tbiwuJ^28iI;Bnb$_z|deQ5DECb?bj}s
zL`yV9-*@Dl)yvktcYI&}P_J%y9=-pm>hgJ(;~?SmW*N)fjf50lT|0F0V10FcSAVA?
zY|a!!TvWf*v@+>*qO<?njxCS=Wbe+3(s^U?p&i?Iw4P{9nWp2|>z8j#q$k=t+FKfz
zoH&1U`^HCY#{tIVNXp-&sR8X@t+w>9;8y|2sjjX9kOj1j$MEFiFF9}CyvdiyiAEys
z<GPw!cW4s;6hj3dBEb+F8tMVWY=R3ghwLKEhHbz(TBA3RY=oOxKK(T6=9<P7h0T>r
zGDZq_XAG3Fv9Xt5IRF3|)Bd#&Zmp`UBoZt~P?pm*yn3}`e0*Zn>Xxp~&IOINFTecm
z<Bva}DH>6x07HNNtFH(lRnAahAN|M!KCcf!L108Cxa}Ac?j-svJKx#5byXx3%K0pZ
zCK-AW5qN&BN_XUh@^m1`kR-9ZHFM;yB;{mMB>@;%mIcTd$Fyl-4(h=O0ST+C<~fcu
zO=nSKlVeMX@Z=*;*jf3}CqI@-TbiZ-fNcqc9QhAT>(dDLa~MgeEUOwA>EFC+%f*gM
z0MuO9(mT`}pBQg$T-G<#yYt<bqoImr3s=AS-m8ZWy*D^Mv}Wnr(-%)Lu8qb<Rj#DX
zl)^aYVA&Gg=B#qpmX_|=Rwtn_xmLS-kI(BeT1D3u3)+E{v&Mo)%Y(pka|}2~d^;C%
z3vSr1m<wQtHxo6Vn<?^EV_+<#RRG{PLQ#~nr%rzRe}8G^#!b(D`ZH2WL?9|$J>!*~
z2kPtRKltF|$4|U{^yt|qo`{Z(#jjr<uB)wZ9H+B$Xx;jjl9FIB=xIH1Vc*`?=bn3F
zd_3OXe$}$<RVx-E0sz{Mh{qGZ^;=JdgJmziwCk<6k3Idwj^nK-rNFi8n$xMYZ99>0
zczk@!Fg&9pG23z=rzHi#W}xJX7ilNvki(H9GKL(9F=U9G0U|QY{XfUKlvC$MnY&RV
z<*|VrfLyBv02hSlUO&rp-1cVLbli4sGUYJfw&O5VEL&opi*7RJV=yfkMvj&(D@*4C
zV%8Qd4b1>xnxdk#s;a!ovE`Pv_hrniSJx%Mw)GEn^>sAVEDHJ}O?4(`%+S34a{s_c
ze|1HT8$9F!2L)aJ|6h7rE#@4YIzut@B`OA!%-CmEG00h`-fn<onyc4;1AD{SJngKR
zA<j~rqC5Ew70#3C%$wi%(gO|Ao!|R%L-XQ|n>P#L6!8X#mgc%cht426jg7VI)~r5!
z<oN#mts6Hk|MFK}TDhWmWF$5^I^NJYudA!i@AH{vHfve2Sn}%C>!V}w-rm6{AAg7_
zg8+)6#9|3e)#4MW`nvLW_8v&5t#l?EpNL1Jk%9if#~*)s@7_H%b@hQjApgyY0Hm!J
zRL(T+Riq^mBncty94apd7s^HA%w<<&41qI1#(3@-;E0HXi~|8L=(@Xs6}b~U0`r7x
znCH`ThpUjl^=~sENpeaI>{=`lan9>1>THKH!WCBLLP$pd5;_j)x^4>zAQh%BU9iM*
zq!6-U{zBVwYO3lI$#|;!%8I77*{ncB5`+wpGq_u~+tPF_!1QmR>N`zdk>+m4&jet}
z8&YiH)X&9YCEwXx>s&2Um;+rntraATB#iS%xb^AasbaJwoF(%sS1xKifBwus$2Hiz
zxsVPbpd@qC##J@*E6<(3_Tr0gty<OM@fb@M*KOVQNPBzNn$=5GP2Iiwpk)aHSh8e6
zU;l8;{P~)$KKaB0!=q!bzxJ++G*&pvWV3I--KuFyH0r%?+uH8y{qgbSm8-oYBZ){P
z!W9l66qO@@m?EAaNw~9b1ON(Gk!eL2)I~Mt3T6b7NRX5QggXmklORd1=x5=(=X#ic
zMb4QsWQ<{M>Qw?`ZtyG%7nll>8O;{UP=2r$a-e?_V53By7s3$$?rlN<5K1RsU`LrY
z9Yh9%wq*fuDI^iDT(TB9N+AIdq(oLE)bUGgtDN((QLd?s2<CQKH*TW94Vm)rX4Tr8
zdc~Wb72WJ0vp<i>96^Zc9YjY`N}t!;)Uvqa!o{FBRNqhw?r6&7IH>l$@9n&I>&6{B
zKD2)Q)o*_Dr4=g{8y*cQ!8l`_ve`7}Tnaz{Gn=t38$jxs24D*(%b4OWcUwr0p+Ebv
z9bS(Q00o)5cOScF^UCV#vi`nNDIjY(fS%E@WOcPD;&TQ7ARCcmeq4~}bdlV!mdl)o
z3;+r@l~q6itq_~y<glv*B6soyNJ>E<B?*UY04ZgjlQEb7AQB>SL}b7@aAb(U5f~z4
z96=&ud0c+(E|5!^iBW#&)`eZ=^=pxUie`5TP8bo9=aEm`?Mnr`eu>}+iG;v`1TX~v
zk?zu#&FyG9Mk0}5Fbw~OH_mVQM=ZLZk(69f0t=V+9XT*Ok*sKJ0RYaK5R<N}WpU$=
ze)Ptort|&%Bbzp@V2sD(832WI#t=AXi~&~}0F{=6`}#-Do@@8{3<6LX<Gg_N2Srf`
zY$A(5xaEM(&g-UW_w^62S<`apz1E++_T$l!n67J_xr~{)$pk4>)PMonWSL?Vh>-v!
z#<;?4*CleszzuQ1%|r<SooVR~;{d=xDv)D1WkpeqD{V)Ff&g;P8HJRRKv-at;|`us
z@vvPk#zyW2*qkv$hJ+wFA_BVL;X=qW#S0gv<#TdVMX@v@I8ss}kwzp*T)V4<)o!V0
z^Zs_rNRcT$oA^YiysUyqN&+kEyz@_gm5tP_dF1Ixz@zG#s%oPn9pz;b00H$xBG%PC
z5RC?_tEwzJO(1o}NShhM&;THtwN;JVw(#iQSUiphnywj|N<gM*dkx+F_;e=Y_2@Y|
zeB@@<>hA8HKff|-TEUP%kw{HUq$(?;2<SLMH&g};KuQu5=b{jZa)qbuI0RB<CFPte
zlE|?g+qUhj?FcDsDQqEyRM9Xn2#m-bq2)l52*BmF$W6K-PPu302UD?t<(m8-n+(MX
zhzO7*NFpK<k|d!JBq#^Qmy^`Dm?I*0Ju^d&01Oqzc!94xUuBshj$Vjp3a5t(I`ORL
z6TSu?%3R6(mpBHtZQC@hf6JPVnx^QwemmhW5kV@IwN1w`G_mMjgR3FGCzG*LX){_9
z$-6@&G6Mns@})}%9HOkIXm0f8H9YR3^5_NtDVhr8ghRpnY&s#J*Dwky9zL%x-$g?p
zX=v)ArY0cU>+u4Ka3~ZGMclTTs;E$){1%LeGejgQg;N?S-Lw1kV@D4+Ha3OB5q~gP
z5{(4Ip`btLD-UO~>G(j_qcABQ!%%ETIzlprD(AM9pKc>W2D#AGrH^2F{T-Pvf4dnl
zA%K`Rq>T)@Q~&{)I-Pfd3LNE<+&o)Kfr3a1u%(m)rVHE1gTNR9GGuNAn<Hn$fiVV*
zAxDlNk=&?P5K|H1?iDk<4Nb>bY}~d-43Cm8hsxPhrD6Is#4IpIIkgf$^Efb@QZ-B+
zBAxjbn5sNI>xbU-JZJ8fTR)ED#N)pC^KV7dF}E5SXNaI_s@KpNXIr*xNG4OEaKNzz
zfpqh;$<b3164FqZN+-8$E@$+6-TCt3vb>xmtjSG|V9`%!x^{~IF2rIc36d19%Bjm_
zAv9I_;g4Q;{`s%hR|YetH5fPRtHY|H+Gh6m|KLwQ|L6ZXAx1#~#@VS0ZSyPYN~2*j
zo4GzTQeIlc7y_V_1QJxv1b}o1@^*^|3|U?i6sHu8Tx=ghkS_X{a-hO#wWWN+2BiXw
zI4Q`ctp_MLX96ifl9ZAh2}C4qvfOcVw;BgTDmT15<g!d~?XLDix8YY2F&b4|b`m#z
zE97x=JeX_db{amK&-T1B^;7r_-&-i;@*5k^%}#jQ@rxg$1U53l=$4vqF20YY)0y${
z80CEdS6Db292*-$L`1Z0VLJlya!)X2%6wMRnF8CuoOAXwInaxUAgD-As<_4`!9huk
zVUGC45z=z(aMWmNs0kPzI~fxohfR5;dst3sY0$pIZ++r}pURl&ufOno%Yr2hH8rLs
zr9&!b$l2)Fh{xm86kSM%0h1sYa$8s&m^-05nP3v;?pM=BYJi9&0xxdW<iqS-BuvsB
zNtH8AngQpa6#b0T?OB3whhTFjqOe~PTLl2=mgeVdX3`v^r!=<-(cI79RMv5>F&dg}
z2e|!nkVW(huA13x+}RX`mzG*22cdX@0T_Djn2JK-kTRQ{&)RUBs|z)ao@S;2&-Dc-
zX@sPhgb2AbF1<Z{u`$z!)*pT1nMI45PQLy6U;b#n$51OP=946l3=uM>>G%0ZW8*Kr
z{=<zcH?Lo__R{6fpg#}|m5fh}86MBhw_n`6ZtLO&i-A;0pmJp_o_b^N%O8FGql_tL
z#s$wX#=wyh3Gyu;hLjsLS2f1DVrGPEsE9d|8=RyE%3p3)R1Ge)%G`@S>73;>W9Fy;
zBa$wGj$~Z<)v?tg#-!qr5uD1`NcmM>P;-P?y@boQniqYXMyppmvPE+Yn!?;O+(nOb
znvOij96$NGyE^kC+Q{<vZI+jE;l)4jtxCipVm|Bf=*o>6utamr58XJlzLoCR4Wt&Q
z=rZJfIE%SwO+d&%I8Oh-V5v|2==SYle~FW};t=u}2G`VRH0lV4A!i6gG&nr)gPq@=
zS2=I}nzfDvN89$VXjxNPTC)4VYm4ezx_djyODb!t>fhY=dOR6h(XzU}W<er3aq4`V
zNAoq+Hv>b<vQ5hl1pIbZ8iv9-WU^8^M1l-MIe+<FS8wNz`ybC{9d|iNDH(7{P7WcB
z3;?;JNJqGF!=zOg>vGCG%S54Pn<sO_Jc$GlfHobmb60J5rW1z@Jq&#ey$t;fy~U6t
zM1`Tx{VQl9%{FU>3_Sphp&xVNPQ}9_Y&OqKbCW#KY|exTaAG1!s7xdhmLn#G(oo5m
znM5iDs30)Mk@1NMKt%#&vaUi0rcE<rS+{<Pa&&-FI*uR^0)!x2kS)N;{ki`v{&RQB
z{kgk|>3`%1a%Owdp8HjM^2y>?XTHl34%m(bgbWCX1lM^wosLaRg#Dfkix<{J{Hej~
zPi$FNTN>qx7Kub`%Rz>WK`NcpHT^&Q&KIPVhmIarJRpez{;+AwY}RaPY;LS;dg$IA
zyZ64P>-wYjKXvBPsiBd<vGI{qI<xoio(osc#S^igyz}GN-+k%KxwfpGJappFu6=uA
zu~C;-&343ltq0rAx5ndfMNxEJ)m6o7s9rCB`@KEGqdkGJMu1$wSZqYPWVcdCApnKz
zcL|9k<>tvH72T|g*nLI5MvZP7@GIk2^TqDglV?(?tk-K~GN!I+Yt}8~zzGC`G9HV!
zU%GBPw#Tb2Zm#!weMI(T^&p=?Wo_%ig)0jdR-ZY2X8H030iUO6>XJ;$F*IdHenqp5
zq0dfn=n`%A^bCxS#vDQ8BNIZ(&G#$`2mOGow|DSV+ciX3)HJWQuJWz7P9XAVB(PvX
z)%gqE+wNTpK&jM3=e7Rz8<rBuTe;V|?o`)}Sj?AH_%B5>jEE$YET$MLg3HazFp*3;
z+02!Zi9?sqJ;B$79nY~#r+1$`8}Ryr{-EOs1O++7;v>O8&=)ecZ+Z0luYP-3%kpeC
z8}$3)i805qZ9#~rsxUG>{PCwh9twwk^O@f~b?*H9d3D=Au$3#$$k_0i_EUbpZ)L-(
zJ^ObNh&}svKDgx%*D<yL03ZNKL_t&oy6&|dVR)2dZATZ@EoxcNeDTWJtsA!-I(Fph
z^{e4P^x>^LCQ`BPzTRXi-B{P0G0m5DegCN)&sLO_xMO`%Qr421rZ5HwV47k|WFe=7
zRwUfq6#%CZZvV2|YJNuf*zpS;*RBr?jFy*|)YO)*UAqJr15%M`moA<8hky9q(xnT=
z#}g#wpZ(b<=gq4GU;u)F+o^XDwRk-B&;R_RFZ}s)U7gn#E}R$imq<z@D2(a7ecfOF
z|9<>upZ}EK=OuD*_K}yla<k$r4!Sz~YwOE9t_|&Y^!_79PF=Xry?N700CDzA$C@?G
z;gEOV{?p^*nX;0|`VFggzV=Q@sV^2!9XQmgD7bOM@-wG9WARwHFpP2wTR0pJhkj`g
zBSe4<5~-wR5=W3yXsR-LePm>0>{HMEreD?1jtvC7#$SEy)!xC0+J-<d6tc56=c;fZ
z8Y$nj`o45RHrCd!T)d|JYI{@tqL+8QU>Js`YO1OZ4G#{Fjs^U|)^n#9)Hb!AJyKRu
z=FxpJ1F?7}6!2utbXlalr?01>c3~_rv3B{!2kzgR9CwHa2=*U&cm2u@=}dYwHnz^(
z^v2%Te*J^L(b;{qf1oRqPWBJ=_73)T^mNqCYq;Fmex>ug&se=@|1MkD>sM^5sjlxG
z=(TN2NV~CSk>iM&p`@FIxZKQO6LQD_z}T;bt>(pHfA_P`4h{|em%sey-}}918XFpS
zzV>=s+m(vSlFvN%6lZFAdE^U!{+kNd|MIWC^n)M1`KN#KnO9%i)z)^ky1MiuA9*O?
z^8gTI==B<mqpGQzrX6o>J#zTGV_OeDvZ?L#)oUHsUwP%-k3aj!Yy|u;8vuBE@`ivy
zTI2D|y7EYUO>I0r)Ym_7&*r57jwC4og8@y`RYg&gsYGpUC6Ek-Jn6J$TlUJ83-<19
z8yFY~hK&Bcv2ZBr5>(z+R{zVgNdRdBW6WhNXAGm!=%0W7Pe1wZ{-|cW{m9W{gTt}S
zo9`cG!3Xc#7LJ6?q+<Ycq$r7$mY0=hvm%voc5Hjpa%|3(#@a=O?qQr6y7%F&k0q1o
z$L@c6_x{&Adai6*eQ(B0MMF^l$Ye70^K0f;S*og(giFTbF#@S;hRlE?oPf_bd8Sp@
zG)K_z=*ZCMAOrlh4?OeUiG5Y&^CRIhN$}+Mr-X1`eB*_C*Kb?gv~1Jr&EI<Q>kTyv
zYUkCz_V!C3{=g@Gxbs`f7A?O%*fTIZux;bEOvXasll&4a4^cz_j#3Dw-0i>IopFu=
zfi0x%IMOhbW5<u~-hJ%zpa0bNzrX97-+6t-vPDA3w3!b2eQVb)Yi&Ju=*W?`-a7H8
z|NfKT|G}>Bes|aJeD-4mmRl#cEa%;KTi2{!{GIRYZf>q$*fcMdO0Qh8@X(=CYuC0+
z<)@#kMg?>5u)0&%WN1d;K;M}&UH5HU9f^d91Vc?qDF}(U5Dr(=Y!(1@D=Pt@va%%L
zFE1|(*^bb4)yz6jq-ApRrJqR3WCD==(ikgBN@*B0+@ZBtV!X1l^8fyuzsXvbGQaxa
zRog+@4<(Ynok*6Hlo+~^j*kLzB5+(`Ozvhh0vUH~sw}H=K}?P<RxDoO0**fN*hhtw
zoH5gM0A(gFw{3jD&^?Vc&4*7OxYBj)-gVmsh6gxjLV|*dZNrJP$DerU$ps4*fP^o9
z`^&rbzwYyTpMCndFMZ=56h-xUy^3P=5BB&BpJ}E8KEG{SX*0ED^Jc@RSDdN5+<B?I
zwEXcMk6pNQzV*!M$qJ|IHbqfM*d64Q<bIJ~F<YH962?$bl=k+X=0$b2wHwx~89aFK
z@Zy$*nx=S+Ab_W*yDt>>_4EubYO1TP+puo^@SD5$61b!Xz&RpfAmCF}<#V5XdjEm8
z6DKZi-n^{3y2@jyb#-;QA*tzN=P*ad2&PTRX{x3xd}K8C=36J%uU$Mbkup5W(BP=o
z<Eg5OoH%~j=hY2EZET!(;>0D!WioBHMCbSQbpkkMHY){X(snrFpIZ99aX^XA*r=RH
zFhkE3m<#8_=4KL=G(GN;Q-QRAAd6XTi@x=g_hL?^#C_n3@fL1m(~%|!l7J+TAgdG@
z6Ot6JY1z<NZ$`=_azaKD0TEP<`@KOkV<l5b;Sl3UNCgBN5fl*fmgD3X2WCvmoqIDK
zNr*reQxqT+4uwNu=>Ra=zU4sxuxuF)MHxWW6v)xGt*0OPNWdS8k7o^C-@N8NWY~56
z+7Dm-R&(Rx1$E71W5c@6GiFjzHALRs+mp>&D%UPuyjmJ9866#3)VNsTDgcbdCIWtc
zVS0uNYU8ZfTmdAZj(2el_zrEg&@P0DiIiz(*Q{y&r+@n4k;8B7+k0sBsuo3ozP{1#
zerM-IB7N-m`Tye!&q*o&@gIM9<nSAN-#xT=NdtmH;E<$=iL?|Vkw}|n_Q1i@t5&tN
zwqDw^``F@^roo}u-n~b6JhBOA8B(1~A9;%15=cb7tRgfz8r!gbaXOuePb8xyepS<K
zL2FhoK7F>`w#3F&i*-%UX0wBXWA|@c?(t~Lm(>$xTUu%rMZ4C~w`_SG<QP?M%}X)H
zd_Lwe^0qn`HO$OA;4fmCkR&0=RGH$Mw&GC&#n;&FsEf3aBqd1%#luxss%9GcFPHui
z7P?W4bGJI{_BxSsatUHMIF3X@5Rgb&;Gzlv1Oaj+NsRMwFzh%|Q54$}%?(Y2(A>~u
zW-VP+Z7Fv=_`$3x=T+60msW>^;SDP{CsL^=ANugy@4aDJ)`MFfQxxVgyr4iTlTuaP
zZK-0uRa5BS6ej-fe&9X;@W1$rzqrM=VLF}GG*jU`kDZZ<sx4}oUsqEbiTaKmJKM6P
z?y099ASpCmaij<a^k<*l(X_C>v@{Ye2^>9scFB^ur=Pl?xujN%7~?IAYgJWQzHDJI
z=s$AwjL)Zk?AeDZE6a3UnMfp;EUClER^haa^d>rS2nYy%pD{X?T(+dSva+ORUZtk-
zK)@dec!8XXipuJ$a)l9rj7FoiHPyPtIb(rffXMdwLWzk4XHZ>L;m)iOP%5SP{h=HE
zq=<520&D?X0h0MXt?S5$$W=U?e5UxX@aojvT;NXOLE%-`2IQ=Vf>}PLPyi(trtBUH
zfXJ5FxQA5LorohB-^h^|fs3MLz&TgAqH>ih8k0Q7{Ztr3D{w-=6az*E$iNkN%Ik#W
zI-d-4eY)JhIw5j&QN$q89m#Qdo)Ey6QVL-^f}!F_Nd$_bNI|-$c|3-2sHvgeYxq50
z@A8%v%Uf2LMN16L*I3sCwh|14s>-W5^W;9;1w|B230?RHQqoa^s0P$RJ^yY@R&Jjg
zO)G$A5e*^}Kq4AB2_VV<G6cPl*5-tgigzer<<KC?BI-n0K-D!%0s&+rdLX9_IjiTq
zfq=-gCtZ0^nU~5f^o<HJEfz`KvOEj}DK|#N3mvzcbE6RfqTSOYvo<pfAW2dHGDIXI
zkP<m70`3wJA!9eb>@EtPNFw4K;4qxBKM~|$#&84t6Olw@L;xf~N;9S)DUz7u5aTMM
zBBTVyi6Q5Tu4^_(ha^{+>XIP|0X#S3nm-{b<bEV+Qt@<F00_*k1eph?6b!q_{&BW;
zi_HCQL;_h}PKi*0;GDale6An>ID%Ytw@Hs$<XVBF7)cxRxlyJg;azL1cb2cZb|{IG
zZvG@GllN;b20c}TM`cj7156S+lB6^ON=gGDkXbZ6<vux!_|lBUdIPdT%8O!Qk)~j=
z02C|iUxd4I(YBOVtdw_B+r^waWj;Sy7C8<whCVN4Ov-V|6Ecp5P8kz&{tb%KzuX`-
zDhld4rBh7P$#H<>Y;a2|tbSw+4TH=qNhdee>4MmC@?GxE`2*&Z3O%KAJU$2#!qHGF
zJ(kVdLBG!t#8EM`PJ8!Yd891p4LXkK7>Vhcr!ricva(#&6s`yaMNxq>fs!j+0s(U=
zmI(6~eul;Y<-Q8#85-R{ROG$1oN~$Ch#EOS5%!40<Q|9|2w47A#(3^F$5A*`2(l0-
zGu7fdg?nA@a^ODPX)h@UX~2A;19MzEtk5sX4fy9H*uoCQ(yRqvhm&TBMF*T!l(D$Y
zI}<iM3!x#+Nwzi<-#6v$l=lI*OJ#@>fFarTYp=gHIMC0$o{4z;)tx)ji3F-DAOiA<
zcx=y}J<_%S02xQdT(mJsiHrj>RMhLe*LS|Q6Oi@y_e$Fa&hrK7g3>RNBxDQ~4uH0q
z*|lePDw$xKPPq--Pb3$W@AB(2*4NuBZQEry1I9@}+vQG$YpCnHk00$A>{9}U>P7Bx
z_Fq1lfRRJj-gCU8rw5J?q|cx2J2aR%du`;{M7mwqh62Xe;K1pjzH_=cBFFk+d<=$$
z<#0@nrJd2F7*2_iv>Y*IEGs4~nY3lvky*;g7&8V60%xcos)!1r`&e-U)Er0|U{d8V
zA9c@I#nTX?5{n2Ta#QA1lnP++jX=7F&G7E71K*ady3rNpBmKeyHlwy#3@2o>VGMD`
zU%`@b>ORvRu%Ztw-kHsIOw7CS;<KH0oVkuL<?iFY%5Lpy5d)CKyx#q%PVYK&hz9}(
zPPXoP?>%26>|`=#I>r3H;aKdYciuK_8@=9aIwdTN=^m1j>AI6OtxSe_JxHi&nw3uf
z-B-SPp|c~u+89Ym!f^v>rt8vi%tVs;{mE?h)py^WNTp=fM8;83n5IpPjM%1`bEL=s
zU?Lv>+pj)<`Fb}h3Xnui8yp?s41$#v?N_h(43F1i0038wzLA)M>OET@tSPTNeWhz6
zoxN}4J*$^4=^7p)PHopOy?O4)wULfc8M{1oQ7f^HXvR~LFpNIWysXip8I4S@1+|Kl
zGNA-4u3HREbTX7q$wX3&rNnSj4yWWuT8w1Gs43!ECt-_}BTYdL6&j(SxV$k)yvUw8
zxPPwsAprsb64Ip3G9b+~>`7_%DM%(ne)qDC-gXQurA#Jm=>?KMbMze1Bq8;UE24KG
z%5=LG*I5pWh>qjf_O0<P5d)H-st;}4@YdnOB9Umlc=4glo3D0szPx9TAo<9aEu|$T
zK95J!)HmPWbEx$cW9*SFTQ+XK=g@(Ddk!739sB+b>+0s$_74y5Z#@x9r4F5HJAA6G
zq9pp%lTZAQ|M#EoU9)EGnzbOs`HSa&{N~$PE4zGA^NuZB0zUt1`}TKS@4jc%s>klT
z|DV479nM)im3(H$j=sU6BWKSbf!FJ?9cTCcgA1zXDY}7(Bk_175S$no%-Bxz!lq;_
z2AqSCwsia+o$RdN=bsoF^yoTCG8pFyvmLQ;-u$|X>UUaOA6~!CvZS;nV=6L~T;(d4
zI!6w`T<a++d`JLyVpd2ZAREY*Ac<^}4v<6S5ZM3%CAkDEZq$X8D|8`p#yCi1z|hS$
z^HQ@|EIKgxLl;c}Q(<(rcn(?uFhT|HTDH;KuvPaO1OkCnie)mtJYD{u@$H0yLG3n*
zc9y-ex%t(1-)%j9A~um|sjdCmi!ZENvcwVcD=)tIJ0Jb1$MAG?_w0Ok-~ajBpX(hR
z{^l#MG}YApaQB|y`p}2GUeD#uu92~^vsbS@`+>*aIC${kd+r&HjlcNjo9k9A@9gjY
zbZs3-2OQ^{uk75h_1+Z=oA#YJZklG=%xqe<VtI4Z_kZ&G0~<Fjtesz1Tl0-qUOjR4
zY$zDK($n*W&wVc9_5H;^f9a8}_o_a>%w|#5h@_%vodbO%<FVf1p{!{wX{_hLkj~X~
z)@D9`I%|5ozIY<RG}T5RNnXP!2}L}H;nDRR=>mXDREV4*<0?~Raq?NjO-MkT11KB-
zz#w9iY5=*^!^sVpf&<ba5K=lswyWVzWD{8k0!TuxMxIMX5flV;DIZ+#%8&tYhI#J9
z!f0^bWC;MIO~`=pyFFjM4ZrH~csw2tyuaPtOeG+aZIzXm*Hu@4{gqd$%F8Os%HzqT
z!mun9*}h?e5JFYdSTdpOdijDzEf`=xgCoNVVtr$S&*wKZ{d#}D&toWx!Vpo{*RELk
z`rf_Ye(9B!&5J@6Wt1^3)5>PehQ`LyhQ`&I3=w&Cy?)-jq4Dv6*E2Rg-rm&{3Isx4
zucoO|$fXOK>X$Cd^!6f=FBk%@0GU8C6bSZ@3~yS!Mw*$kot>6tF-1Li=-{Sh%TIS)
zJ-l!4!0^b!n>Mwzx9>l8G?7lPXj(WhI4nuHs4~+LnxZhxAYtbiz7Y|CagLMh{gM@i
zP775;w?8z~m@!W!s1OGrHl<$RI%gu10#K5a5^Nxcq(k6HasV8V0w7By8Rd$}2rjM_
z8Sq>=g>jGos47UpWqP@5*+%cA4*YMNNbUBj`6NNrRxVn!>%f5rHmr-zpWj^HFfty`
zS`K3@5C{&A4lk;22>bp2^8E7?iDZ5C{FcSbstz4`{yX0{Ez4)<8&<B2ClbgQ=ln-6
zz5Mw<`2F=ORy_aW3;*>G{}=?3aP(ksMRUvde*EK5AUHHK{QJ*7JCRCdEz8WBSu5K+
zJapmOm2Df=k4;Qu(k6Kzl}VAARu}?fFYes=hoATaDhk<lO?i1&f4|IT(eNy-Z;(;~
zK-2tMrm7FETi@B=zofq2@cA|_U*6T%>(Pz!ii)T|Xs9YMwr%yQtG&JR%1cpWlz}||
zpj#>^)LvaAt~)P^$R<05e8#$R8-zLg&O5=$9rCu0=u(6N2LQLh$ENH;65KEw1d&Zr
z652vK0&E~b(gB$z5I}&^1U1Zl3N4hoslWREcB@G@qTSO&W)?jjx1yU$CEMEDmo_$r
zOG@mFd9d|(+BCPVU8gF_nX6aUE?Jt%m<LWC_h|a&RjavS*rs{##0esC|HjP|iR9J3
zo;6FC_w@8!>FL_EV%1Of?>~0t^k4tk=K+zVM8<&R;ZrB$$<)@h>ph0vdg1)4re@R1
zws&`~Uc9vR?AcgiqP(;$<nwam$!uoPf`uSuSMT+Xp6+dH)-uf?>7c6h_V)TbMmQ94
zd0a>ermE!FfD9Flq)m=ML{wEGBHKcy<e=JIx!Q59zN(6An){VyVnV4b=aDGTt>%9-
zyH;~KiJT=DEH+O{Trg@VHfosBEXpU(NG>m;iw!}>7}q#QemDEo_igp(Wvf6$M94UL
z49a9lI>;D$Jb*xH6M)b#WIBzEqt^fe%4A3iWD0r=0HAalkfEVbCIgDfgTdB=`@j3f
z?oU4c`0}MorIkfwMCA5Vfgzb$5CT0O%4BkHG_z>vfDA~2BivOkEeimcrh%$bCX?^k
zl3Pq7%D2-H^Zt!U66DD)+{S1Q-BH+0Q9w$P5)lCJ#8!V+!QHGzKGP@XicER>4&?6F
z%Tb5FZ>#^NY}H1Ok!$Veu(LeJ!IcLtAm&=BxgByu^jN%)drV}ENP;Bf990znTth8%
zm~%v7$XqTGWXydsM?q8+!55HHL=uP!>ZV2I@mtN<h+ICS!V=2a^1p(u-hm}gdop*U
znWCvQNxt(dlCQqM-O_SlvcOB_x|)KqKoL|BSx#*P8832Z#jK!izDJ16P3&B+n8zgL
zQ9BIt_)RJxCS8lop&bfU&jJM%GQcTxYVHXj-dLsxrolqU)j`C$MCk99n!?N{r&;eW
z1?%<uw)%5eF2y)mypiM&=IJbu!Q3K1IOmdbX&s6zds<gG@7ZP({+UEW!(>HhmJ=w@
z=VP$|V6I6smHIjt2H)Zx?H60g!C(Bz+%8P&eOvu`_*HPHSW=E%RNt5j3puOM^Q_bJ
zH|1XaDHku@@*1C+7J|;0t|)WdZhi(U_5JOATmAX@RaBA5N!yz#B#Sdt+u>#d^mF)y
z*_o(dHv7l9pTRjeTxo7^Ul?woVvl;$8VCVRI0-@W-|QkGAPv=k;(y;(f1y7^UiMp8
z)S^{QIbTv-aVeH&n&zj9YeE3C)M=)cpr#yiu1K`_Ew{4sjgK?)^lsVqENNfNp7AQP
zYn<HTHdET<#(VzjZ*zSv0&~bg;a#n)ecx7p;f@vaOTB<f#o5g=H+Ks8K1gv=N>dDj
za^1xOS|I;M$j^@FtFgJsJIK#_VNu<vz#(2Bv!3ZT^M@ii)%{L!%lS$c(F``Kn`W&9
z;NeP`sdCOB9OoE*q&WhLpBa?lr#5VyiwC#pBbWpyNy=?i%HOxupW|lQmMr?+i<X{i
zH>4w16eTD6=C)z-4Ui&u02Nh#u-KUME{%!`Pr3F9NtrJKPga0Px_l0@&>xzk{)xqc
zjQO|IjCqEsHK`kA)U%tbm^nr|DH%Y;9vq95opF}%{ze~4x4@uNoE6A9WJusfDFhbj
za}*{qbH6ec4g2Kxkq(f&&3nT8w)%6+SEU?RcraWdog7~&XD)3Sxx@{&r1bcBnTyCX
zbjsQQz<gdZEfNlD8UT=O=Z%MoB2z4Kw}gzN!lh;9Ce)Dt8PgTA?V{T@0U+~vK?-0B
znOTwo6@?t9;G!peb%8udQ2>!FtDr|()Y^eO^~|gtk=t`XG%03`MNA!Y<>dqbEU#K6
z0oV=8RCo*jEHc9BtyctKph$24fCEY(0^+~{K!CtJ?*a$LkUQjHPF_XLzlBSXm_JD>
zx;E@Kt846i)B1U3tx|doBODAyB9Uk`8jeP_aM&9T^I%x`Llg*w!y(4GYmg^Sv=T{F
zltTv(j*pKrpReQU)$Xn?rfJA{&VrBwSCFxsM?-LjRL90f&z(JsiVDb3Q2@D}$(%iR
z&P7~I##o#mdGGLdfAr#OZ@g}0O;mV%d^`uV07QjTKHEg5xQ80(@9(<S!F*nqa3%+n
zVti7Biz|f?ptuHhTir?o@SyHTpAUT=)NksSJ?P<}=EPNjd(p#wijp!-t*UY09`qEO
zF#|{@lL{&vxB~(KR8-TMOvW@-R3ZN^Aw@iqV7VaxoJdSCKn7@9rh9x#+NPPspTgqa
zx7DB5CJ8iEIeoe35C85P|NZ-Kf8*udfBAp?=fC;uzwC=m3|lmq5_uU?LQOk(@>D!N
zL6-G{-Mi0Sxyn4AcaI%UJ2t3lGB%E?ij3K2+Ds(Kw#ac@Ja9UhK&~j9>$;AbDr|dV
zbObe?%9`(;K8>0Jh%%ccBJ_9<A3t{G`t@g?c$_iz!W*yKX7&fWc4yORrs=}5)A0$H
zO#&6gN++#Mh6ntey*-zDx@0;-LLy^KRjqW&%A}dD6G`AoW@3V-<DYJK{h<Upf9BfJ
zLv4rlwO+b#&0XuRn=QzuP|^P{oWBx}#~2_3mP)73p1ve=Dfpy_Vc~I+oDvlNIh87;
zz#K;mSR#=;d-{^wcauOWDuX>^105r_v<~k&ef`pq7d;)9d(Xdj?aclwJskrIDhx24
z&K!U9;>9Cf=Z{@u$WQFO)PAh{#J-D`WuHIX$$@F8a$r|Z^+*s5?og2VR(TBH-)_xE
zpq!W>DNxgdo#o-sxpU|K<PZMnWb0AS@Rc$v`Nm5x42{Mg*#5Azt+1h{<8hCa%<muQ
z>r*u?X__1v0MwV2oo>IhYRS@9e)9UI?yifMFSpd!zjgFTduQj)efvVe;K_4m&s@HI
z<jfginW13d`oN$9@Zz35JwwCSdwXjtDtm^8S{fUF@XE{OWo3SU02SWZ(b+pP(p*=!
zq^Y@~w)X6$OZ!irs4Ok@7}^{A_h0Vm$)wYMzxQiDdSPH}^uVdMa*u~=+MyGz10y2`
zPn}xR(%jMAwYRnPd`Cyv>lqvy`^h`+boLJ%J#%`+!iC6{9Dy4mStk1Y%pISc!^1ix
zM#X^6m@5?c{9pg5uY1^W#JjH?vn*%n;)Mz-4mcWWE(w8w1_9`30K(V)_Ju$+&{Eq-
zh}SN4zwj@+H+^8G4-Jl-BUg|+APm$QG8I*hoItu(YM`zlClChe5~PZ%iV7ks$OT9p
zH7^=nm-~MBFK^uQ=xPRxfEe;iZJqTk)$!3}_r<|TMX<6gdhS$5)AE{NBrte=w7RyG
zBX4i(3PgPCHZN&E*KJwZOd`8|`-bkG0mqc-iOlFgtfynJy1q0%ma;Q;BocB!5RfBT
znNszBZRr;q{uVHR$O(c{_%pGZ$nx?~U{T|eeY@VsDm)sl{=y%B4qfs#N%N|!`iBNI
zMO(9c>4nQz&tJY=Ra$oP%<0mS@Q%lyeBp=RJ8<IIV^2MOzV$>b9$(hdviHc5ryqJK
z8VtU$XLm3h9vB%~*ig4_`7-3}jlKKUwk$~`lK=SCuRVP4mdbharJ13$vvu3Os;2$u
zjory~`u>d@)-GRm_Ue^2D_4E@#TWg-(4qy6KY9C2jv%Cb`jH(20|R>w9a_9#L3v5!
zBhNnj{cnHk<fV(J&R<yGyvULCll=#p>g#k>edgI`zy7taUcTPbvUoA2(s_B$TaSTr
zPEX;=mL03N-MisS|NT2zGn*mvm2bS2PGnV&`tV0K`~034zq@Ood!)9x`jHQ9Hhg+G
z5ZHI<*yvzv;gVXz&^6H7uXOEy<ro2Od2IEvmPJPnpE~yL*?9{pge|szc$1;&9F+?f
zt{mHQ1{tesu3mNT;<sLSSNCY07kf8sU%qkEsyE*_c<tPEU)UQ6`SU8>%oJHOVe3Av
zEEq9YJ6Xpfpz6lb<8NOiNlUjh3IdK~Sx%%PD1m$-V`3~FDD~Q42ZG*g)-p48<BIwH
z*G8^g=nItuhI&S88>;f2{QZ3O7w%kLs6B#kWNC%(p2s#W-g;l8yfQhFDi20hFJIbm
zrS0<B!?vAez+4u!rg^np{euIeqYLXAqJhABCr{Sb%ulD&)$?kY*IQ9J&&=BP`1tX*
zQ_p_%Sv2%$Fi;*2Yl`CWc!9vsJ+bk4S8tzXTH#QLsY)i3j)cR5Bcq^lAYz(+;^@(W
zHF0zR03ZNKL_t*MrAt5i?|$#MKlaS7g9nC3M-ANog(pmtBPC<wYg?8uz<CwrJQOOe
ztOUe#Haowv8h|3ffNj~f<7Bg0bnI2li#Wji>M8(;1pI}j&7GoZNl}%3KRNzC{^sk~
zE?!@-xHcY7B4_tJy6Vd5&h}Fs2VOtX-#z@qC+{2V8t&^F_6NNCUO#%|&9=?kSGqTb
zWHR~U|K7c1-NNM?7QXWQ+nxQ_cm3eNrtK@jL4WK1^HNB6crG?HQNMJ4<Kp@6ynJMQ
zB!24Pg>WRWXj$Ffmych*(y@Q%@%x`%9|(A|X=^f(5dt}Ih8EbCV=0W;AUe<V-ur7S
zw>-V#+L>Mkq=O0w(jpJ2wk4R#oUHJm=GYPlf)Q_dStME!kb+c|Tei64f%f-p_2(K9
zgK61g<J~|*!)Bs?WVmCn*XC?uJZ@TX&RvEdAlnXAlnsxK+qTU^p|aAFQ|*`PtLH6h
zY&dZE@VP^W+D@NYw4m{yzWxn=AawcC`QENhO9;!hrIZ=dbc7?NICb%YWX$XJ4h|0w
zrJZF^99{J0hru<tySoJs?(Xiv-Q5NW1b24^OVHph3GM-cyXzpqg73Wl-L2a1JKv_O
zdTvkG?Y`%p^PJz4lqgM;awPh8X3K8c?RL-l5luX^rN9+*8JLww0c}?6WyMbTB1uD2
zx<OXJl9xwH1$`X-s7*DooxA+5_X96`-VXE1camq$6zFkA|4DOO`}LsT9C1yz*X0?+
zF)1=<AXwRdWFd7Q(Hm`XA923`c?^J_)QW=t(L9{LEOzCjBLjNKq(uAl6i++&e%Ng6
z-rY@zy>!S)S;H_{gO+~K$S@KDfK%!t2z%K3na+-_P6h+G=k0z4jzJqw2NRhbe<Oum
zR`gS8qZ)qtQD>q=1xrS&+nYbG%1kMd+=T}8IR0pEw3-0(R_n6a5KlBgspM7Sc1Gc_
zGzy6{0&WY{7{(-!K0ackKueq$784Pj7CIBiYbA%Nf?76e<}?W;uBgMqyB$>HWGYa=
z1FtaQMLXs+%q?tK66x91`Vi9l)HI{_6YE=*Cep}RgYrma*2{%wkwLrLV_f~km|~6D
z-Fa*61Hbkc-TF1Viqt}3&)t{5b28}YqAI9FvQjF}ItKRsGCKWxC&w47jW*Elyd}4R
z!h1M4SZ3xwUsmlnNAc*eQFLWXQ_TImCw9+ZgUMCQI)BXC`ayZ8k|ChrDPeoT6!|YT
zPR=I5zc0JyX2&pFx0;4XkiNif*4WpXHZW%>-`?Ik8#L*rk|qpM?_eICmjt%@>D01f
z-;v0w^D|S=(y6GJEY(+XpRlp$MZ$(P+6~&;No*LAC!FTSy5t}b85rC?y5!a;Mu$YW
z!=+O`^aM~B@QgHFF9sh~pDKO)em)jV&??q(GWyB+hL~ldx>WAIu+{51&@7<V-v0G_
z1OY{`j}ddBXz#l*<X6?+^R(#8Y~VDh3k6w>N$)Z*H_scC*V6|zlb(!GB3laB9f<M%
zz^bK#9@=~D%Q017&0HH>DCFTjba<E+I8(pAnkrwWdLagSPb0yAxU_hkd7YJqU8Dq3
z7!8CXQ<AmB#FT{}e|XA4g#Dc4@Xh*0$}$26&}?PWV;NIpKU2>phY_RSX&r1bN^q4$
zEEU`kRlYF$_s7LTH;mNXmHD5gC0;`ft=&5k7nI<x$J6JVYOw*H7Qe-H2Ir3#cbR(H
z`%&F6K5zeOKW14t^1Z%8dTi5;mNMu&A39)40J!Rci1-GRLzFf#+LRf!b}OlPtZi$p
z>wAmKBK4JPJ~K7Eu08zqwwvu+w??hcD=s5Gnm<@IQkgWdhw>G;@ZzlCb*&DV_q_&I
zE=S<i#bFqAHNbQU7pBI>{k3th+q)r0`8^M9eLcH%Y$`yhgq!QPH*guJSei98wT_HU
zdiF>(w#-g(Rauo?8w?C5oD4ibDxQQchKCFeK}aqSkB2Nd%_Ti!B!?EJ(dx61Ej~#!
z5TL=M+Bsaq2iU$QY+8$FI~Uhm%26Vq@Y~mj9bqDd$3=6EALBkDjV_%+8D%7vf{97y
z$r$5yciFd3$maZj@Z~RegI&eb0-+u|OC>7&Z1fg+;6uEt@HfZKi9P*3Z#uwv@N2eJ
zhE=t;saB(v{rsD+Dw-EMo5|X`$NaBXR|>Ob?wN}p<g4DD{?@@}M^}eki~s0@pMCRz
zp?Q~`-@Cj2{h8IJ10#Wd98K+Q{Szx}9><T892qKHw1il<40Tp<7=OH+`_AI%YJ~q`
zM2(`O`R*+t%dl&jyvKN68wSqN#f(0@7?waBN}L{e-Sm&ELch$7m=0B7#155YhNY*}
zQb^OV>c^e)+QyEh=0tCo&HVya%#8+v88@;J2w3Y6#L?7YMZgLZH7qJMA?f-D?inl2
z!sMv<xasK`?j~}>m?@sbWFOMAgrbuL<Rl0oXCHq!>|4SloC;7EGOHb#k8;xv^`^z)
zS#wjP@v*Djrish*Ey;VW$Onn$I-rF|4~J^ekX%-t$u1HQDdB*&6@+kAUdN8%(zIjc
zF`2XbBSIR$L5GH(u+=USJiV!SwLn4?JKS;sfWA6C0O4DBRf^5Nu=lom?AmW)2J}#)
zS#T(GIAQ6x=u|Xpz&Gf0=y|&F^;XfMG4$42X+?C^m4^{0qKg<14@DWWZf3+q1Q4-l
z#2RW~_x}?Ph)>7WiJ&8{p#7JV&G@3CPS5?A@VyRWOgF{;gFrG85D`d;pp3{?rJ!^e
zuJaKih{op^a;~AVj%C%jH`{D=+zr@5iKl{)43$S^wj&XoOo^b19IY&(s+&WCltUjS
z8l03f>S?20W<f7Q<^N7QZEGKSGAzQ16rSuboFZ$ZJcnnniZyvCVq8h;L%V_<nd(%?
zDdT>6F`~W;C*F@Flkk9{u?+YKgii8e;u9^bFC)D{59061tV$Vp){2c&X8Gy3$%H?U
zB8fNwvFcx7;!-4wX^F?nO{5FwB$W3)mKP<Xyr?6r*`F%Y=&%5lQc##`-Ivd&22T?#
zB#S8xEoaruT!#ZJ_LtK}-Bbzwl^C}L`s|#cJtnP?ehE78i%Ha<uYB@KxI<%#;i<Yp
zn?6CK5b!Q-`ZJ<fYxDh6A@_+N6XB2V<l)~#Sdc;yXo*j&1l8kr?ISAXk%r5X<!c+H
z3)!e7(_SBs4fSw5@G#5RGc^dt=6gBl?NaG=#V{tA0o3hQXSa)X+2wM{$1qt<Sewn$
z8re~4R-h_c|IAc)e$JtPBh9T|oaI<)(zy*Od{K(*Wsllf6^4lAju@(YBs1UVBDwjK
z#|}{aZ3z0LSu6oqa8b&1c&Sbd08H`ZS!^ZNU&-u9<TNq>8wmrtggL0QstI?lW`$!k
zEHIYQ14#DAUJJGee7ILrQFPW$Z8Lp1bJ2{Aghm*_<6VkYYy*eHNCl2#eH2rgyo(NH
z{rM&n@tmNgxDv1O$~#-h<SzV(ovH20l~}s-kzo^vm5@b3Vy=u{8036vS$Q!%h-$FQ
z2GH_&4z-^OBm8yV{(LgTf|d;n!B2+~mrMz>lZyL-4HLu2W$xl-EVT4^c)8UCmH{Y8
zj>R^{YYGqwy1&$Xv2Y;8k|CmF8P`om?EGr>Zwv#Fs?IE*I3td$(Fk^uAPR*`hD)xo
z<FZrkG8ObYz4B+kN9vECfwnnE;j@fmD-Jv4`?y!O!anwzhdh|Oi1RHid=POQxK$^_
zgMZ6HU!CeUq-0`7Y3isSU8YcElmVxp)<OzBe4R~(zRZjeCVqH|RYX;ai<4y-v5pfy
zEc$_~PmHgJxL2aD9D`ty3+CrtXeD>uy8Bzw7z-X><%Wx8y7m(wPhf6vbF%mJT&6g4
zIJ2evQo7@L^ek+B(HW&!zx4s>?Yfv%Nr(5qw2uNScn@ZUdP!GLn?FNn9o}+Bq2Me{
zDkN(}L(^~&87fF+<r}lesKuz(X(e1c&++zc%vy1*pP<)$cY=0RHFPSnay&PiAuDuN
zygNv1!DC9M{GNGVqsYTwAPc-)U;Mh%R(R?lHm?N@w<}*yo8EVJO1oy7iy+?}7};vs
zq0N+r%3F84KOfodvQ)h{o^NY=V*o%eljG_dwhZ-Chs(enxlj08Jbe#y{r%1~-U%5P
zl~b4K-7lpDcT+lntrT5*v*>A-Z#b8&iySqbUWenJ?<dzc3~Mu_?b^^WUyWz<dT*8w
zv_$X{=m-+MO*?*@8DCsLNQ2(`guS0ehb+5hc-g;q_*I?7xUTovxu;<zj(;5{I&7MZ
zrA@Q^H@jI8U&0C+f1hF>=fNw6!o>*DX3TW;G_^>PWslfugWozTPYJZi120(txLI9&
zZ!ZL_K1#Hhbiftr;FF1@Nmu*!Rl!D^`M<!d*Tul46KDh5QU`5Z66mpaq&MK<yOi^r
z8xnw(&wuD}=)k*t(7hcL@3?Mq`ZT^IYNmO3a?+`oA3JVr7JPA2sHu0EQBAzWBcrwJ
zpkuk>x#ee!Jv80g`%#Wv<o3^^?c1)z3bTDx;b{%j?hU1KH|enRX*RdwtP>2XvSooA
zqc%w+5suW3;@|2TXC8Cy@l)5~jWb!)HbLBQL-sbQc41AE08-O)PIpXwh6hMkMPq8=
zs?77uEo-|5(8{~H#H7(Gi9qy{dc6;KI9ysj)iQFDee1V|2h36vmhg7XNA>^bIREo%
zIR?d;58X=vKf7OAf{>V)<@=4Dy9y?e2@6nIN$pQOEGS)rPC7rIaH(0r09&j87Y8m{
znm4I(@ua&#(kvWLNl5JJKIiGsA#Yw&b4O!$HDfW1;^o-)l}rkB-Imn*@T4(i7Zr&N
zmeZt!hdNEvF>2#M63ig}|5lvgNOOvrSCzyzRy<Emqdpm-kYSiC!Aq#?)IN-x(-P7Z
z0K(2f`@^secCC(pfI+Kf{@F(HBjeP;=b~OnEy4XGJrK-#O8ixsY&`#h(c0y!0r&Qc
z#05%P+wEsC9srunL@^0bNEdZ$FZ_#yG!yif6>)IoE`Hciz<B;$7HN_|ov@mj)oYs5
zE1o4QeV$z6PZ(|bZtUE~<|UH{pYFmtv@EA(ytCppqe$Oq(KbSIg`8^FhA+MM*<^vP
z8aIiNJ?kTB45pi2*u>GpNU5Sm$4FG`M+U?9+pb`<*9*&rc1;s4tod|#N@MMPA|mn+
zUrXum$l(A8ox=qfa+H|q&fH6aN9&(CI13%B=%eF#fN)(?7Y|P+<Rc9a(=w6`osSvy
zuNDtYpl3H3neUPgvHP5R<2j=bRuoI(NmD6pRu*?s!>7l`(eo#5<J2le&P<N5@*S;T
zlFPfjRlLQ<d4Z-%bfh==IX53?C}|40xPdna+F?4TIMqyvR4~Z>R?T+BRQ(OFwvfoy
z78czvOf?bgC4(_%#i*2=R`~)_6)D5wZ~!uGWI6;w1{qoVt5f;2n8Ysf-l1(O<WL|A
zGtm)ehToim6)6vvjOu*hsr@)xJ}dz=cnS!7O1iXP;OVgrzDdn|Wd1(CKh5|~FIIUD
zj#)@T)iq%+l1WWiqy8?LdMuwCw<y`M4VPRppRp=hPFVTT?p}(#csHKKsu2SXo>F9@
zVONV^dDrp>?k8&haH{U`-q2tAcjpbct@Jd|kms;p3pEQPh`1qhP5>?EgE)Y`l(bxb
zH8L05`DgQ4RA`Vx|7{Uf`JV6{j=s7LC80`zmbA<$Z^SgVl0r?aQcbl@(tmLatUIcB
zG`182nD>41Q#o>W47DUK)&UPf(KQbP>wM8}eAm|cL|2u4Yy~#btRJqrB29m+JFhAw
zVG^Foo^&)AgIG;fR)Y@1-mC8{F1}(LtGrz@7l4NCRN{rgPH!tB-{1pp{+))@?+aMK
z+k<;OT>x?3hE#bx{j+8-QJNZ;%p2z>ovLCwm`l?Fcjgh^e(}0&(rn}0_U0NYta6i{
z*#7Mn7^tSAGW1e`Kp7cYnRYfW-KkFx$)rItt>?gw(bLPoJVpX9oh3-`LP3pi!<{4U
zr#jg~hCPQEd=N?$+v(;deqG~}Q&JKrWw_OduLTnm?Dq0+sKLE&ih0X@q^gePHH#gc
zayOHWEsNs;>Jp`+qYbcgt}RGN$dHk7p>)1~pg1tdLQ_<aotG0Nwy{3nnD3TQoa;*c
z+57!cL2Gp{M^97%rkpi**2`TzH&;NQ42za3$td*_W)glGx(=buswdhB`d;kzy#~nb
zb)OQL`8{M#u@nXTg@KbSb&J{!ji#jL6yK2(d)zq9h+C{R8In*!5-$yY2)y+2O?ue?
zy&zn3a{F&>iZ~MlzU_3G@vAHLQ*ljIZgqOqB8yl)C;_5(|9w$gX!qq-hVxx-a3}`p
zALP>N%Z|`P!MWrC+fdigq@qt-BAlE7BV^Tq&$816XU+Qi6OiDI4cLh+UR_;$GndP?
z_L5^m3d)gQEP(uHLvc#Jukyr`e)C7?W3WrO12Yr~5iftYvgQ7Ls+<S|z%h35TD%AW
z94+n;oI{bJ@b&$C${&4i)s^+PUM8K(=m6-ghlpK;D8w8!%(##c6WKdLVydfV2s)Rc
zNM0}S_wT3ck9CWv1@Q2#Ga{`EE=_XOvjnD3mb8R#aU5dQ<3JoZK&$I}O4E^$Zk>L+
z^^J<SL)|BaHOHwXr&z%9@84fG=;f$)8o1tLES+`pWGsLH`0x}kH6kSo3llLBS=$OL
zUv+uSpr9xpQQR=hkDBh|?UL3t7c=)$FM<?4hU+yZ!ck-99F=Wn8`Ix%%TL}IxWUwb
ze0(p?*M%fM6Kl9@If{+}hm5}ayZ4v5iOU+pp7u7-wi)PYb@+}&R*nI5cP6Hy`X%Jf
z6@>CMP_4(|A%sdyQjJy8({psF!qVA=(B=xN@3$u-&eiY!(c9(w??ZjJWrO~?&qM=G
z<=bs#1b?mJ%k$Qyu-g?bI3d*bNg`X!D;-^1+&mU+2=QR8(AB})2zhq4EPeY~f-H>)
z{KgEU&#NLg?3I8ioe)u%#u_s<)uNKHkjhBtTUcf9+4;!*>8QLp2sxAln_)~R7fFCN
ziB>I7C~R&zEZ%c&q-?USINw(FnIED&DwM4oox2A&?u~i<WQ&7S&5u@B_Wbhms0j4Z
z_qNpMBZvy^WqsL***kJ{U|w`?f1Y-3GwYb6Kp(-MtoMJ)`KrrmX5A2dcZW`{rl1g0
z-3O9xjSi<A0xNoYzQ$(wZhA~aU0j6JdB(ANdwD6P;vz{CaM(k2!U~9ft5w|2O8K5<
z3QT>gE=|${4V|lv3a)le-9@2mPMOnnO67EsIB8)&OISpD^p1FS?K^!sO1o-nXZGCf
zjF+@!7I0-2ptusGR7TUK#>``)1fs?bqIb?}v~X@%>l3wk_Mh4<n>?~u2Pg*zn`8%|
ztf0Ngpk&ehIala(g@g%$y^rb!-e3FPnenw^l~7=c{Uru||F&~HdkaQP{PRbfGeySI
zoidV<G>#bpDS&c`p7tmnYK=Pxu_8(ssZHB|FU<NM|GQ3WY02oUgO}V{U3HlDx712l
zgE-lJoAln&%&gAfpl}pkaCA_5?UncTmhsN0Y`Gg>^^RM=02YZId*C~*w*Rv*(!2R}
z+PNC5LPGEXLj`xaEYn(rmltaGAcec~_aLFP^ljII0W9rV)=uBK$fR75&RbOTY&FGT
zFS_!tvVz>t8vLCWMG57mvKRJ{z3pJ7j<F&#*IY3Mv?^4@MLtFSO(`^I;fjAynQY*L
zzaaY80emH6TsS~adHKp2>V^MhJD3q_aQv_<6Ni%jce97#obT-8%sQ%czTHYAz4Xt6
zM$_&;O#ye3Uk5g4XSEDZ>#EsUI5-;DoHq(ym95l7{9SqQ35dF$FO0wqqa_ix>n=x6
z=o55d61uU}kf;IS?&v?^QE=-P4e*i@^yZeq!BSEk^PlCQ&@@HB)QJ}#-(m)CSB!S`
z!m7HNmWc^QF#&!jYicwgGP3ZZ@a<7$fiKT$-^9|LY}UTT(579P_+;<tM*<gM4yai!
z+_$Wu!I*(NUe5k~nGT=8P)XP$M~R)OG0SopTc(~821b7t96m0%lA{AFve3z#Bh5SR
zF9T$^+`XYJ#t`l~{TfDDjHGZ99L$VU82x~v^#P8iDc1k~YRNeNrW=!Yo0(vI5G4)u
zT7#`J#~2A0lNF9RT)r4~h--EU2^sB<Ert;R5Q+~cTMR@3kV+^PChuz}14jXHl<>2P
z#Z=L7D9Ren9$lp$MYB5#1Ta&k)#bVPk^FL<Bm*w_6zAcjfOR+o8sXCQ{I~FbR@~6i
zgrdqG4+2IkR+tZB2QjIidfvE7w0@*~nd{KX;wXYp6)a8HFS|W6s|ra;0Bn-e1l$V#
zT?gA5Epnj!lcFmx>`naH4z6>wM^@K|MF+4l`nEif`5(o1EfZnLX4Mw;m05?W8q3BZ
zR4G;gutV!$S7uk|`8n6cS}zVW37G`qT)aH@B_|WwtS-V_w~3X?JW*i3=Tl3&1NLzb
zaQASj=EA&GXRT%|_KIb3X*2~R!+0nYw6eA0wbI2A=c0Gv2ANVxwbX8unoP7N!A9nb
zbGdWuk}xv)V06MsTAo6S#s#oDP+0>DDJ;e>xR6<bSOzsz;3)qX>c6)8{?gS4f%F0a
zopmG)TB@|AJSR5=%bsrywwEEh)eq=iN%3fj4#pHjWM`-6u*LTi8>KXRz{aX5Zu1{U
z3zp86UBI`|<KI?B4;e8E^<S_NQ^iRsa=o$y#cl>6zQWCKqiePU*YZ60!S7aQ`-5d)
zQpL8H!aTRBcyaHc-Z2c3CnwmGN9#71C0t7q1OG_tb7B@qa1p1Yx6H%oEZ3j3c!-tL
zAc`f_F<-s1SR?V3rBEmEo<^)3D330jhzptY#H|c_lmDKjsTVq-Oe5DL2f!S~H^91H
zW(R>>HQcr<x1LAr&6H_2E`F?$E&7^@t<SDJ^f&pY>t(!NSGu;5pS8PzZ^}U%yj;6$
zkGmyf$Y!>VTgMAye$sb||J3{IZh~Pqq+DNZ`ou7k#ka-5v?$=>lnTdAR)x*kerQd0
zR>ygXMUH-?=c`Y??rtox)_u2+F>bb1ir;3C*05#{PFuRzlk;$<sOT^jTQvUo`KC$^
zl7h7{Bi56eFg;}yU%p`@C&X%RD1R;&q$Ruc*~(6-m)2why9s|Y#}+i1COm0?z&YVI
zsm!nvR&UnocJq|i98;psNF5ympFridok}Tof6x`jVoZ?O()YKtU=z8IRw=Ko&Y;Ww
z`w(^T;o=(0nG!uhu%b2_+ry-hVbgg<B=wJBI^HCgt|oA+Uzw}1bP6m1-L<0QdlyK!
z`=M6Lv5l>|hRI}k?)Y9H-ECcr93Osvuu^(4n(_k^b=p_yrI+3lF&rS2hw;R9Z8F>G
z`<~MCoXE`zFds{%k!sc){~(Pu>wt-dB?E|J#CDS=(s<fHb@ehiL2OpX4jUdru)wmG
zR`S@e$qr(Za%9APy>m6YLyQQe2E${s1vh!trn9grKjPqL$2{f_Ei@gmeST6l|5L${
z-6sxc=xg$IzX2L#`f2v&Lu2gT)lXi>8(K#E8!s)k<Q&z^qM&`vlCIFWfOr<Zue^#}
zk`_Q%1fin_yY*z2=4DnWB7RVv(`qUkTaTc=@Zq;@n+Wvw>9U*5d4KQQ3reb-gt!sN
zr+#D1Rrw)KYQY7M6~;g-c>~de66|Y4K1}<Ier$NM9bXAuUR<4@<-&wIBu7w5V`;=Q
z8A4-@3U_HN_4~(H2$_ky4!rqKH+_cSAPZ~_@*={AN7#|1HuMiF)X@M^4Q<}dwJj1Y
z9@r460UEu|KqNv$@<iEE_l2_?8pYB*nzXdufv+=i)WiT&m&@D7*HBG%ZsX}eR;w{w
z+W|rkwowX3qp)b>qdI?7vO<fk1=Er;!_0E+YHxtLLiINma*DsN_d}5S?xu-PMEZ?B
z611cYQPF5#rAMI+Y!X(-Zg}~^K~#0$(86GpywKziozRlB;dwEt>P}14y}r3lej?A#
zzVaVO@$L579u2gOw*h_eM7gr5Z`1lv>1?Qso|Tj|F}^{*l=BfH_C7??BNFkx0}513
z9i({MwvLlo<au@Ny^B=J3iLS_Fg7vagZjhQ_r4v^guKae64uq$*3{QS&E-eKA6sI<
zs}Nn-d+7gtzm%)Tl%T|XTIBC@`}=38wbi9-%A&=9BFOFL1Lz7HXZdtts-fNIe)1qT
z7Q1?*>f0>n2^3<Ye@sYL;CJ<t4GO2Fj<vi<aPcAZ&s5gW<?`WOE8wJ?MH&6^xj{1s
zDsDAmpa(Xx3Xy^voGG_~<4Hk}b%oPme%b~d?iyh|+lyZp6~om{_0E}$_KtQEeCx2S
zOKM8wTgMzP_4!24Jv&jeLrpKX{Eieg@O9?2f8*CX@8*MXp~-!%APuFo36tW}Cmc|4
z5wQ-62x@>(9e$<&3Mrqt{@iwqR{qp(;B35_=Ra&4rxQyYqhI>UOdKpvhc{;5UB3lf
z_Wb9v(q2_{OVRtx_I2Rt^gj5`I|It~a&@XXIUyn@yGu-ZhBk{ooD*CI-b@9niTXe2
z^*;Te<W$WM#bo=mDP-3&{{9FxV@;83`_?(Ptl#ljO3B{ez{to}n4F|t=x@$5U%g?A
z)l48m@9V>i=vBW>-HC%S2{}0dT&u&uuU!j*(84|e_D@q0A3-g~{5!@(9SYb$7=V2)
zIohCP*f(|EdXDbe?(T3e^4rLWpP1CdabS(;Hgk!AznMe2c`8&3CvJe^=<p4jqhhF2
zc_ZbDvky!*K+@Z~W5c0IQ5R#~#d9=#Fiap>r**ZqwakUlp=|Mw&a`7qi;;67^#>SU
zetuj6ys=ane5<a7=^OSsPg7U-L%T-k9^T@E(_qlI{?CYBc=$r;?Zk1zB9W@%S@Q>f
zwC|3u7ZaypD(6q#mH|I4u=Hx|@ab}*&wa8OamO3W9Aip;4HFt*|6eUYiT>x3v>!i~
zwUF}fAz~t;7v5dx_;Hb*9$f`2dK`1!*fNK1?I@&?@nKdC!q4ZgcXxP*7=@x9?RXO7
zIAvuo8FMRMjH3s;)1*ApXTKRD*87uTLZ@sRR`+vr$Mg8>Ihd~7<BW%(`JoMrG0u4R
zreP-XawNoQfZ#EpKMZZ#{dfF`Y^`xVF4^sV(I4%9UnA-wFtl6%!wNBJzrq~Lu*K7N
znZJ{IVdNiCOB&-82|i%VSARVkMdxb>*xObF_p=)xzYKAJg3ga0^ITn^+(F27TCLdI
z^}`y6@0S13aBpvlIpzKx0x;}H6|^UR$O0=Wk=HTn?d7Qc<t3|+g5m)gEK#h{w$V{n
zTKe$y{V#{uza5J3l7GwJ_zBU}lmnodq&oXpaHQnV;i#(4&Z_q_3J?^I_G#ChmpH~{
ztCxdZi?xw8`d5nw;v<|{W0uxr-(DVx@0Qz6bpP4i*MSmwT2kc5Ut#9K&b*j1;XnOo
zu~EK8gmKb#j!G~iX08g(gpy`1$}l(WdO3XAhfAB)MyW)Zy2&KAQ8DQ_84)xn7-O<@
zVW}Q7dUCtY*=17*b@H>y_ar7J=kO9d<X?c@-rjbzUFrwR+*y}f&*~rFo=nKdu;(qD
zUJKAONDY;<+e*M-6^N|4w2b+5P3`#z6kt%3X+RyH+Wi^=XMaWgZH!g8v^$F|%+1w|
zs`)l3$i3@m$B2_dM;ur5X~|Ug@bhSugQ8T})yaGo3yTr~M<HU_(N@iFi!S_Rn#vU6
zRQrkTm{Dh0_a#1UX>}t0!O1OI0a7Kg29TRxzn;S@_c$nG$wVjlyL_~NF4Lr@2h$zT
z3P{J8j{uzjy4wR*hs4psxX~DdQ)0WxyCGY{<xpaj0g<A0728vULIK4jhH}x1R>Znr
zaRrfO;FNY-7^z0#hyeimQ0!y}Gz(5l7=XM=Dk*QVU*Yaiz8oVc@?M!0Y45m3Fv@oQ
zS0!I_jLrK(($b|w89+RB#nBB}2M8ZZ^b13Jr<qPkY1SnM3@rduE#*PuX8=%1gin<+
zmT0hIKYQ36q3n8J$)-VN%H=k@=bPl>ra)kua9&rEm!`M4NX)(Csc(qeGo@>a_J@Nd
zJiPHo1#RoZqM^Z4^jkg84BgMEs_Ep!%FO6={z@mw#qs$J71xMd5V*1$x)g5mquau5
z!$f`6CDt~2sm7A08fG3D`qBMFhgAmWdljJ-dpV?!<W}!YOJ&i-G4T4Fm5D9%x~|(C
zasJj}o;IR?hi<M7CFJsl%-k1`X6QF_i~_<ds-)`u*J38lmBbD%mPn11^7MPcv^XCg
zz28pkoTUKxMEMwe3_@^V|DtUDSHJ!Ro9E{Bcxg%$f#;UoXkZxSQPE~(1{9m0FSA5}
zQw>5W0~%Oo;+2hc&`<T^q3^4KCv_@t#I7pGL(r!@KGakNP;%<)3#erA+fF`$rXclo
zjv;+RJ8s$T+etx6epCJStvIOZ;G&~@SEgzSgI-xe!B6Y^H)2eBS?Hr%)yxdD-Y!ha
z)IV0FZDA%r1~!+HGc~Fx9lOgsaWGtc-3a@S^Jqm>YckmB?b*Kx_#2*}%)U^#b&B5!
zw=Ej-A`sf?pBA4nx{~WKi&zx6Jw4mdiZyi!`5YO?*{6TSN1QaAX;%{_euN>pK^Kjv
zth(_!6`K(w+Yd6EWJLxO>Dd(O*e8FmoX16-=a4*wq3eoOL4IMFp)Z}w^e}lzKUMil
z#DC@_%AQB6voP?vYt2M?kPTl+xXx`F3k!=#YPzFNi;{Eq2ZM9~mNj)`Nd_Z^cy#!z
z2en3R?xJ)AHjo9`fBF_5I~qGY`Fy$z$Vo}20jH_ueVUzqrF3<oPOq6p#?QGDgpBPP
zizYACjL9_SNvWrRe!!tz<jlzKs6J`IVfmNE5+B+BX|=H8IxhP<)t-^J&JE#e4kjry
z6qth2k4coAC?1n)KBrkqg#wrq2oDXVPR@KG^Y<V{7__}y$kTtlfqbde9ozYFlDoR(
zSst<p9L5BAsi4Lu`7u{{7wmFbj$wn{t&fZXyL-*UO1FQ`KsU*XQ0bVC&Ve)v7_FbN
zWGXTg4JJ%m(OiL66T6Lhd_*nZ*|}%e4gbqPN*+HOY9!d(TZfEHghiKU%~|$)g8Nr0
zDHaAitR&JvkcOvJN@ctjtze?GxV$iSLgcR$s7?x++i+d~ZIN;=tk5~yGNyJHpLYgL
z%?p4B#Z_s-_Ti|wB4%Y8jPn!X(S@Y~8^g$80bqG%JR#?_ncs!lITKNsr_wlkIM(Im
zRaM6Ue;gWsyH}$EQmia2z`2BBv7hS8N2q#wS)91II%MSJY-~5nRx4+BT^ks1_&U1{
z-*(-*qP@D=_15<<wg^EPNPrGiK&Ubl75{dT0aYG}hybpB#c{m7Ei6$ny}*k{w=S7&
zxi+o7{hd%-b;@kp66E6%c>7D?BWI~0owsyEK}yPVRT^=47a%7$0+5mB!i~AVdw@k6
zR*9iBwd>N<GrQZib?JEDOh`a)Ihyu~qM6Oj^`I6N65<x(ySRB4EFLc@t)3u3qSR+M
zyU5GFN{TmK@CM*wmRAo9?Cs%<p4-lNcRwCV=;*AiJ&%mgBYs6BLg~n)AHSTMKDB^R
z)T_sU1lT53Rei68D;7ugCkEmo0n~+q&i5Y|QA0g!_S}@~l9_g7THpIq^h`Gz0w3NW
zt`|@9iN1SP;f!;C0^&k}fbX51w(md1DUleJq4`LPnrbLc|2F|jq-n4>DDa4q`x*N7
zUt_<8t_T|eJ1E7`jPYv!?$1TG7I>RM@BY-QJ<qR&g|j_@({SM_IF&-Y2>^JF^y#8Y
zU2XV!ejA&*YW`6(mGdux48zOA=p-Rm5flNtr7G3@kY&&<=%oz=7fSny4Dju@&hPn8
z>$BeeYppG*w2%!?3p=|YuR#(t6Oo{Q`4gX;R#%}8)O%{6H^bmy@0pBhUndhSEv;V9
zqt9RG-4B&VB8pZl;w_sDy$TSeX6<-PHJSCD@ezJSE=~`{olO^B#>)Pgp5BBKcAbul
zpa$|Au$Kh^=S^#@jooJjdw2&jVdBS1W&y`nCG(bhn|{Vd4Zp;=cOeima*~@<;;p8K
zS2uyh-|FrU1x5lgH?C1fq>-=yxa$Q7;^uNMG%Hwd)D$kRrK6)U0PkbT(f{jn-_)VG
zokn^)bsSQoDseG;@zAmZP*F$^6Fv$%8DUgbHrT`jmSDAxhi{RGS0@gP8xzHfpVtou
zsHk$6Nf0<6YFOUC47u@vT1b5UY@mESo|>-f2@B}1^!3|&8v3aojU3}b`6lip;n@QI
zR$6K*CGfqcr>~^MW9Tp4Ctt`X&(;l_w$09iMOefKUo#?@0tx{)QT!y=jn>rz)^;dR
z`<p+x3NJcz6E`~&miCMv1-M#167^kkHY)7%ggm@mUIt&+_vx6Jl%92l>A=y(u6xd8
z;xNpRiFo+<`FX6hdOI$%l91i;{aJe5{@V9?Zn?(HQU$udAy{MgMh^e?+qSi^%dMlb
zvaQXRlXyw5t|k!DvJr>R%g12{t;W?rByCxVF}~9H3Zf(<!|`w*n)H1>k@_HfwL4M2
z;l1><3;s2vl&2Ug41tIQ2bTgH^Y~k83_Et98SL)BhZH4(=fxV6!<)wkh*;oSts~am
zX_cW&%oga~1Z^db148?olO|i5qF;r#6>k^H$@;y09Q@$A+V}qI?dW=H=epH#Lx|ZP
zm#-!qa&&3t$3Pe_T{;V8Q}APJ!qNM`?rpulp}Hh##jyCg7?H#=eNAds1ape+fG#@Z
zSjH!!(`SP3-{g62A3*PFY9f#3!NHFYeeXsR$+$95`>)G~=Lpwce<T1iv*JY`NULP+
zbJHl)IctKhyzcq3YvYP3-i)J8+EQc6A;Y+?Qzj0a&jL_4I1zNa5<UtV=#&{ISmL?x
zl9%SH)VfEsh-7C`Z!YeyI5kvWaNTMy>#^M4jqC;Q{wgf|DKq8P!z)#>VLtEO)7$%2
z*L87ksDv7;SQzq`34r;fIb0KWGr-Z+^CsN3@NvSnD0siXHwivMLIP6|lS+^j17H+3
zK%OwDq$muajiE%*6t3$e)gAt~${V*H=|fZXQ(~hNa{ra$?H57sRpeZ%qKXct26oPZ
z!zaJ(&=H7!o6A}M!fc$go`Ahel2WQPJqku8th$F#ewes^x^!G59zmCj;R>mqMKl^F
z8AjIzJC#E!>%eXCwXwnTJbyn@X6c9_8O8{7;;Gr%f;a`BR)`NnaUyIi_)FRu@d*JB
z-M-*Pu-7#hK?9fF<bsSSXZdZFTrK77%q+OM-qmW3BsUw$>j5qJI_`sP>G&+o5vCQ8
zI(jJmN0rAk&8&QJYwOGX+LDhChH?AvmDPaq(o)btl}tIEYTX%_G58&k0}fd7e6a!4
z66dwJ`+^qAMIGr0pMv%MXxbNaKf6Zm|8LRrs2&pw437oYkYtUP*6z}Y{jwK&?`KQ}
zY@4|75;9>&#1Y5MuK0U4t&wwfny?NFOZa2oxXlofB=Wqj|MKkQ5Rs2|6G1yM*Y|pw
zad)P7dyCF$tZ$}yesa<!#3Rt`<-W%P3aV@b^N=R88n-{~4w(XIRcEt3=1*mwG4he&
zz)}M^B>XLem~%WrP}<P`VCWWwagPHRwL>m_^c>X1U~|>qZmr<#14T&Q8K>Y$5=)T9
zB)>2>exmedz}fcIc#IN6ulIHc@!{f=s31}75N_6lu9n{QeO-heB4V`ok|2y-Rc;PO
z$%k*T!vN%6=wg+a7yx>I7K@~rEghNO^nI@Yc9laU<ruL|Tb+Kk^*%-}F4lP-J+V|P
zn$!Um@EL-`qMjU942?832MMVyETuB$b<A`Oz?Ig@aKrzp9MIqfB491)=wu>YD#3QX
zTRo<kgEvQOIx<F$UWHUN<S25y1xi2~`Js?ehXy1x9hnSgpE^%fp>U|{k>ArToyi{W
zLb;`7TB+q5{EcH0Ssx)<l(vppFW7!*8izrZoP_AwWwrTye@qEbcE)@pj2$CL+IrSo
zJsHAB!L5k%>Ljgk6u?UViFBGpuTJ>&cAmX*?G!2tt#$3b9vL1UPEEx&Ihy^pr6CS@
z>h?#cE044+-Q(pwFygDvw1z@wzHk{gSbS&X7Jr>~CdpR~)Ul44w`X(HXC9BMs&+JH
zUt7-hov+B0)T0W+=x1S8Buxay4LLNeD8&|_;{U<J>!_+$+K8Nf8Yb4j4qb+#%@md5
zUV8fv*{btZ&tfy-p+l9J`<;<FuDd*R`!ARlZJjjNnsf~wure~Uv;Hu`=UeJ9_EEsE
zW}e8$2z;y9=bQ-P>zuaaG7BTabUrfTxb!=_;x7UP-DEE04kuCc^?Q4pyIDLoR=_@Q
z74;SSJOnZ<))|Fd7=NAkeDm}R+~4ZdanhpqVA}KFL*aT*WmCkzyu|PE>`by1Vp~6P
zU}tZjj4Ou=Zdk6azvy{QY$4$Ap%4ssTV!*5fPCus(Gg;2SJMXaI!S#Dyw5job2;e^
zzI>nB683z$J@7nQ<#*DDFJ@q|6#$c>!RzR7{B1`?DcenzG_Yy$E}ymQ?*Hs;Aesb=
z4Aj-l>@wyv(bB6oHE&w2s*Vf)c@>Ly6B}nto;2W+2JvxpcmE81JIueFh67oEh{bNw
zZn*7JhBry$Dg{-%dOQn9o|##CPeGfA6hU^7)s}EdD2vb;<o(>%_Y8Ty=u*?*xwJAf
zV4`zHMGZ$1kLiXt1Td}uztVjmHuyxNRS7dbR>x;(DAJjZ7Fz{?8-zh0191^;#ltF&
z-3!X<N{M44;ZoyCa9Vwhu>io?DMVWmcSz&TcYai}g?iECul&hni9NjY&cdT9{<AR;
z4I4IogwTZxoa6}ywmJ`uA3ms=5*J<78n*cDgjd`UuosGL8R(ca9%SZCn;m-zor4+k
zg=e9<`kVTYyQkJqOYDY!1Ox<p=BqO2ZD&3ez^mMx#%RM;oR04d)UvUm=EFn#tSM~6
zw>?%S#^W5SCwwnOFFr&q)fue*R`<W!&S`PXf|F`!vSr`!Idfp5qI5}TK-rNm1DSh4
z!}8Pyi@|T!0DvZCimnb!`Cx=J1x<`N{BCIY^^zUW;{3;UKX;mBn`q&rc!dM&yZ9Xa
zSGxx0iCE$f2qJWJ)&Nc(9@>e;U5li;v<k*X9a(9U%IgLf*lPQL7c{M$c&iVL@)=bO
z`qD(o#PV8rVAhV`&2Jj%87?`hr2{;NTB$BS)Aq_*yB~RS%<nrf6>7Kvz;vj?oCbi?
z1_cQJGd0_V5yOsGIyJ^3lsbIfr{ONCp4=@kU;gSm>%x0^Bc_bS5Jn&wh<u7fy^6Fe
zPCA>Jug*h>KATBuCew^PgiTD0JNBeP1XH#wZ{LSO`Gd!SK;jU2?DSpQfCGjS<vZ(z
zxB0>?)YK{@L#9-b5qFkn>c9h*p?)bT8q7Ex@mcyePo2GREw5bwzEx~|rVnW<q);<E
zZ*xdk<sxY=n_tjUOmNLg*%DQpeuH6+Wy*$ihSzW`S{nmJNf}*8gD@29OX^lM))01r
z7$620Xnnma1LE(a!n`HG%P5|WXYr@Enx@Z}9KIc4N`D$h>aYCs@wcBKbn`|i;A}eZ
zJ+3I;gn>iJCfxSa!CVN!<oyeGV>e(u5SlPyZl#TxJc(yiB*ffv7x6a>2h+L|f+hJQ
znEsmU&T+|Ae_QFk<LNZx!*MfwzB$iQn-M<H5d2b;QcpvYV(7!TV8N18BEN*M+)^$D
zpp8|cW?^ThnnMCe$oq)f&M&LlZ}jLNrhhpR^;vC>UV3u)II3$spL(RN_}Su)#?TP!
z$8RQjS0wvk<?~4}dOE6nHVAXF=~$u=$6WRVId=$|qAYgmYM~ZRC?&Ubph(uz3fcKh
zdW4O701pvvJN>*pYhM`WkS(qxpQ<6}HTNZYN-)Q!nr`na*Y}*qg%4N-vftr}f>)wL
zaom6+kDzG~DfgSkU(mR)d`?nj0UzXxJ*;G%yGNly&e4t4L`D$9Q4m$RjJO@XI9*gL
zwCv*Lb<5R^!x@DU)!H044#Rd?)bVoJ_t=v-9U@K?N@Ym6ib*FOR)WJdtC3rhPy(CK
zua?+>@UiRP{=CB%qEA~>l-(#z$#=tNzcv38p$6{70RUh|)p*oY+Oh|g+_N`-_CC7l
zgHhh{JR1RLUDq#2Tr#Ze*ftM%NE(DB6v2>|*PGlbsQhBr{a|@^ue|&sGgC%E!V<0+
z7EvuJUYS(L%VsD1aQ1rEjujt87TWMzFdZ)SHIa{3XU5fmQgb>Vs?#7g>vS?TakjP@
zc`$IXYQ)OhsP>6c%5nvz^wRadsn^9Ud{|LoV`kpz{--5s+pyA#PY1w9iXyaxX0W@3
zo+iwCHB2=cUAIo+vYyD_UwWi9q#Z%;+oM<ezP+#KuAnZhy_Ea=u%g%V1$fj`erE^4
z5O7OttHtGvs_5gs$gG{y!v&5~7Tdqghmf0s%b@R#;CT6glSC=ia+%4!d^N4)FE#%J
zr<Xr{uyNY4z6cXT@U5cUu6(4(llovztC@Sqo0Wm*yx!~)M2#CW-1~M{FZwnJE0<KL
z^@kHEg{fB9Nr*9`oe}i5-O)s*TMZT6ybk#G)_v>l^DXQC<J@GFNZ$3Er_V=52}i2V
zKFEU`X)F{+*_H!V`9r)Gdn4^Z80q1q!l!}Zyu;PYk-;i^imtH)`50MC(fTb1Jem~3
z>zJ`IE)8564LEb+q<jJQlCtHE)kOnsO|PrpE9#7gUU&dQt1!5fg+IFWW^G=tPjr3H
z8`Ft(JBNp|0BpF)SSpFSC%^G|!CV_Gr#!(Z{u?Fbi(dO8;rcLu+d@^OD@53$X^e!A
z^_}DWIcY2SVHrnleM}vQ`{~oOmr3B;me_R`=z3DG_hoeXxNJ+zz}Q$GxAQSGBQA@l
z=j~}Tje#T64+W|yK?r)Aw_PQ}AUS$WRTf13;8F3JQ|)V=latn)C*{msfC!UrTAS6X
zoVk5B-5wvV<j>*-2Ryp9e}msPT$<KMrQM+m0yEYqSh^o38_XOX8l6fBW&H5*37vWg
zIxhkN!Cp?kY+9Dd@Tsvi6q(t6#?K|BD<QBQx$v2dV20;NuX#G!=a5RxiyG+TRb1HP
zrUoz~Fc~Uglf(z+*fJPm=jf-0-b%Zp#VX7Znp7)U_eL}=iIf|oq$Zn%vB6%^kNj>{
z%T$5)j^<I;-gG?qBP{PwQGS)9&;S=9@fom2NSj3BuabVa1~!A@eII-3ieAhu7Mhc&
zj4S;{bG}$92XZH*=LvB`+<Pl3KkoC{Cn_Fi%gGaYl>6I=@n-AuLiR9p#|LW4UPNFh
z&lYExIP$f=6WE}^AJE9##!*QX{=7D1VJ;cl;D_MY5yqT`fG}!rXa~49^cT&vHd!aU
zg<sVK<?;?tJrg+xjP`@&RJ+UMlj&@r*Pyo9=+K7DM>28n)*#$#aY{NF`8hN4+wHFc
zdloh&60=d`50R`xdqDt8`%;NFwN!y<tu6$d>WW|w`~})WPLLhL)}j|*4&LZ$u0>&I
z`FRfTuXKd%xA@YR9map#z11-~^P&jkXUZ6xUZPTluI`hU?V9Z0v{u)w`0v_nUXcGX
z*ieFOIkQvXt^d0kG(|4logGdT<?VhVPD;w(FqkP+U{&L#+g<8(x=i*C@&2Z8p80VD
z4^DhENqBI!@)-BH`erealBY+%sM@>0s3&(&vigtzM%@*0qeZz7XU^MjV9Z0I#(}Rm
zE3S&29<yoiW1!!==7N@g#2GYqQ78$e=_uJ^V!qoL55weR$K`5<iKp|J6y=k8ccEI3
z$>M%Abp0$VCScj`Wx`TOFAJZ;%+|CbW?ARGd8?=LTm}h&hl`VWqIiFKMI~VG*qBbM
zREzvIdw@!i(Jlfj6*2SPb;P&T%|uFsAT6U4@{+c6&c_0SgeLa>=nT(CLYx~Pa~Pm0
znU(@hPL9Gw;sL0Z(V&>tU$#J==@jzwZ?`;Wo+4543xtAw9{)zB0%Gt3ab<7;qsR4f
zgUHDxk>aqiG>QN;3EF6ep)?yeM|b>SUzM8N`xpZ~CMx!Cg9yV6YWafRjs>MZRo)w4
z`RUm5<X+Q~5GB#j=~;*I5(GqUZeQ`^jt<0}#D2Wr_B^<Jx_e{NIjGiS!<Hr_Bfoi!
z2m$#`<;nn?d>(ES<KveW)4Pl@DhHy>gMA;mpi&n^3O{hq>%YiFr3=X!Xnc%E_j#z1
zuGTpVHf%eWcY=Xo*qLwTz#}O!aTUTR9`ydI#mkF#Lwq(oELXat@A`fW4f)x#XjZNh
z{&KS0Fy?lVe>`(iem-vd2`HB57BEhrn|{B&?CtcPXUikt`)-T_VV9XDqQ_^%!isr9
zjFOcNkQ?bPKN$Mt)4jIh?d?H*)kWZAPyBu|V+zg5K(mjHASm4V71k9ZKs}ZTdOf+1
zBY4KFg}a8j)fo5w=?Q+W=jFY>8t@$gf2QXgUT(m#6%u;lMTeHD-A+6%H-jrNA)kCm
z$;k`<%_mC1`;7dQvK9Ii==*Xqnquj^qkdbSwWTuAE1p}F|4KNQze5C6rPoz?UcL;2
z5x0^Q#y&njF<foTe-5q;9KqMi5P-wK|C%xaNNb=7i==MY8(G_2^R!rL96o&HHQ9(r
z67_fpe%P4u-dtHQGtmk;J5*`g=oBSHDH2`qxnbk4?Kpv^n+U3_Q2GU!rf$x9?-#zt
z3CkdUD0+U4b6xzi#LK(DGvz1{0#;e#oo?CeE$QrRH0NAg@*RS>N6`eR&4gA|cTRs2
z3OL6&t59LN>^SlPMzhRX+3;5W+e3>X;`T;^PHSQYY@(@ZH=l3afgWJAX4UGRZ^Iju
z5hHtl{J>^u74{gmKP{6s*y6YWvKAqAjt%<KtWcfgc;g@;jXDq8FG5SSZc`3*FTcSv
zroRBUs6_&^%?2-pn?tsT^=6;*qW4FJkb1S4Y9LLX4%D2~*{NnhyQQw~@89y?f-VfF
z%|=;l_^yr3?qZlQ|MmL^HM2~*-~QR3r}xpP14bM@pjQa@R1FY^%djpiN3D{hA9hwT
zx|@~Ik=P({{#FfVg=AHxMPw1SAc;$}D|F0PZ3$VW<LGIQL;lt6ujAc<_w`yB&@{?@
z)a-mVoMq;Jl)%4*1ph15%C7e>I!UH82qG>Q_)x~*?y@mQtro-B0E?*Vyw+*671s0$
zyO#=}ZkjGK3xw(s{1fmR85b7QAuEA%N2pF~t(z7p$y8F!hSr1jD}T=Or_i_^%@=d?
z?#F)IJ}q1;^!#w!OaeVcESZ9W!M^*BX8x^IJXdjP1t339Icv*1G3d7O^#%6Bvt(AB
zF(0tbA(u5)i&K2%Ebk9bZF5<?{H&J>HZ7SK%$K+7fryB`galq_nZNllQ%B{77_Gde
z9lJ+}bJuNkLUAILC&Mwtt?aiBF4|MXPd95F_*7MK;v;NJfP8;cR8#?b<@}CjOEhCN
z(l-agM#vOt>(fbGl|di0_%`P#69uIyc$^lLCQ?6eg#4}F(m!ABIHLxjd7D9;oN>rs
zSv?{i0ga56woEa&No(i;X%wJhIGi{dVez(ykXlkR6oSvo@y#(;4;5DqGa5IJb}DZP
zsTgMnOWi`UWI_ekSBOm2mMX;}RKdbR^HsmTstUL*?XJMNNk+>>C&Wck0e5(!ZC(dm
zX)G3X5JHDL0}gM;W>YjI>1o(JXLm}FP&5UwSDW1}b#xLce7f@a6q_>@5XHd;_%<UZ
z8gQ5~JknT<g&4Hsc6AxE!YH188PfAgwp>Y~6SqCDX!+7WQEvLYvnu-{&LVHzoK-CV
z0H#wxT0+~&$;n>{6$dSx3aVA6rN-nM-o0M__fKCVC4Eua(a@4{^1G{a8LVB@Z#WxS
z$B%3N<w>xDIS<)kGx?~NK2r{&Y)EOAcB>3{mD@}f*M6aArffe6;E<{6h<t^RN5{vd
z<0k(?6S%j2Oz7tGU+6vRJko9Vglf?MjSl0U_kYJK#EqL6lK)%4hBFldInDV{m3%1Z
zOCwA}E?tOc_<G!UicAO5z?hF1yDj>1D`Z@r7(*UEKV3`y&uShuQRP4iR~UY<{wORR
zon0zG?;mu^1fbMB{fQX=rMy4Wv;W+8CL}hxbC|av=fA+h1w1PKQ$%!HY?<>TL7Z7r
z(zBEA$>U5>g81iFFE{FPrv`Z^c01m^{)(J?<ZI$dCG%W+=-ch%6c02C$bk;ZGq3!y
z!6Dba0{V0R=U^`M@|8bMK_k-teQ;j6CfDZwoc2El|DV(Tzkkao0`iWS$<3^#@j#;w
PfPNHYRHf@A&BOl}p8#!3

diff --git a/plugins/redmine_agile/assets/javascripts/jquery.simplecolorpicker.js b/plugins/redmine_agile/assets/javascripts/jquery.simplecolorpicker.js
old mode 100755
new mode 100644
diff --git a/plugins/redmine_agile/assets/javascripts/redmine_agile.js b/plugins/redmine_agile/assets/javascripts/redmine_agile.js
old mode 100755
new mode 100644
index 9f86d08..d4ea53c
--- a/plugins/redmine_agile/assets/javascripts/redmine_agile.js
+++ b/plugins/redmine_agile/assets/javascripts/redmine_agile.js
@@ -45,7 +45,7 @@
       }
     },
 
-    errorSortable: function($oldColumn, responseText) {
+    errorSortable: function(responseText) {
       var alertMessage = parseErrorResponse(responseText);
       if (alertMessage) {
         setErrorMessage(alertMessage);
@@ -54,25 +54,25 @@
 
     initSortable: function() {
       var self = this;
-      var $issuesCols = $(".issue-version-col");
+      var $issuesCols = $(".column-issues");
 
       $issuesCols.sortable({
-        connectWith: ".issue-version-col",
+        connectWith: ".column-issues",
         start: function(event, ui) {
           var $item = $(ui.item);
-          $item.attr('oldColumnId', $item.parent().data('id'));
+          $item.attr('oldColumnId', $item.parent().data('version-id'));
+          $item.attr('oldSprintId', $item.parent().data('sprint-id'));
           $item.attr('oldPosition', $item.index());
         },
         stop: function(event, ui) {
           var $item = $(ui.item);
-          var sender = ui.sender;
-          var $column = $item.parents('.issue-version-col');
+          var $column = $item.parents('.column-issues');
           var issue_id = $item.data('id');
-          var version_id = $column.attr("data-id");
-          var order = $column.sortable('serialize');
+          var version_id = $column.attr('data-version-id');
+          var sprint_id = $column.attr('data-sprint-id');
           var positions = {};
           var oldId = $item.attr('oldColumnId');
-          var $oldColumn = $('.ui-sortable[data-id="' + oldId + '"]');
+          var $oldColumn = $('.ui-sortable[data-version-id="' + oldId + '"]');
 
           if(!self.hasChange($item)){
             self.backSortable($column);
@@ -84,13 +84,15 @@
             positions[$e.data('id')] = { position: $e.index() };
           });
 
+          var issueParams = {};
+          if (version_id != undefined) issueParams['fixed_version_id'] = version_id || "";
+          if (sprint_id != undefined) issueParams['sprint_id'] = sprint_id || "";
+
           $.ajax({
             url: self.routes.update_agile_board_path,
             type: 'PUT',
             data: {
-              issue: {
-                fixed_version_id: version_id
-              },
+              issue: issueParams,
               positions: positions,
               id: issue_id
             },
@@ -98,20 +100,21 @@
               self.successSortable($oldColumn, $column);
             },
             error: function(xhr, status, error) {
-              self.errorSortable($oldColumn, xhr.responseText);
+              self.errorSortable(xhr.responseText);
+              self.backSortable($oldColumn);
             }
           });
         }
       }).disableSelection();
 
-      $issuesCols.sortable( "option", "cancel", "div.pagination-wrapper" );
-
+      $issuesCols.sortable('option', 'cancel', 'div.pagination-wrapper');
     },
 
     hasChange: function($item){
-      var column = $item.parents('.issue-version-col');
-      return $item.attr('oldColumnId') != column.data('id') || // Checks a version change
-             $item.attr('oldPosition') != $item.index();
+      var column = $item.parents('.column-issues');
+      return $item.attr('oldColumnId') != column.data('version-id') || // Checks a version change
+             $item.attr('oldSprintId') != column.data('sprint-id') || // Checks a sprint change;
+             $item.attr('oldPosition') != $item.index()
     },
 
   }
@@ -176,6 +179,7 @@
           var oldSwimLaneId = $item.attr('oldSwimLaneId');
           var oldSwimLaneField = $item.attr('oldSwimLaneField');
           var $oldColumn = $('.ui-sortable[data-id="' + oldStatusId + '"]');
+          var $sprintField = $('#sprint_id');
 
           if(!self.hasChange($item)){
             self.backSortable($column);
@@ -204,6 +208,17 @@
             }
           params['issue'][swimLaneField] = swimLaneId;
 
+
+          if ($sprintField) {
+            if (oldStatusId == '' && newStatusId != '') {
+              params['issue'].sprint_id = $sprintField.val();
+            }
+            if (oldStatusId != '' && newStatusId == '') {
+              delete(params['issue'].status_id)
+              params['issue'].sprint_id = '';
+            }
+          }
+
           $.ajax({
             url: self.routes.update_agile_board_path,
             type: 'PUT',
@@ -212,7 +227,7 @@
               self.successSortable(oldStatusId, newStatusId, oldSwimLaneId, swimLaneId);
               $($item).replaceWith(data);
               estimatedHours = $($item).find("span.hours");
-              if(estimatedHours.size() > 0){
+              if(estimatedHours.length > 0){
                 hours = $(estimatedHours).html().replace(/(\(|\)|h)?/g, '');
                 // self.recalculateEstimateHours(oldStatusId, newStatusId, hours);
               }
@@ -311,64 +326,39 @@
       });
     }
 
-    this.saveInlineComment = function(node, url){
-      var node = node;
-      var comment = $(node).siblings("textarea").val();
-      if ($.trim(comment) === "") return false;
-      $(node).prop('disabled', true);
-      $('.lock').show();
-      var card = $(node).parents(".issue-card");
-      $.ajax({
-        url: url,
-        type: "PUT",
-        dataType: "html",
-        data: { issue: { notes: comment } },
-        success: function(data, status, xhr){
-          $(card).replaceWith(data);
-        },
-        error: function(xhr, status, error){
-          var alertMessage = parseErrorResponse(xhr.responseText);
-          if (alertMessage) {
-            setErrorMessage(alertMessage);
-          }
-        },
-        complete: function(xhr, status){
-          $(node).prop('disabled', false);
-          $('.lock').hide();
-        }
-      });
-    }
-
-    this.createIssue = function(url){
-      $('.add-issue').click(function(){
-        $(this).children('.new-card__input').focus();
-      });
-      $('.new-card__input').keyup(function(evt){
-        var node = this;
-        evt = evt || window.event;
-        subject = $.trim($(node).val());
+    this.createIssue = function (url) {
+      $(".add-issue").click(function () {
+        $(this).children(".new-card__input").focus()
+      })
+      $(".new-card__input").keyup(function (evt) {
+        var node = this
+        var $sprintField = $("#sprint_id")
+        evt = evt || window.event
+        subject = $.trim($(node).val())
+        sprint_id = $sprintField ? $sprintField.val() : ""
         if (evt.keyCode == 13 && subject.length != 0) {
           $.ajax({
             url: url,
             type: "POST",
             data: {
               subject: subject,
-              status_id: $(node).parents('td').data('id')
+              status_id: $(node).parents("td").data("id"),
+              sprint_id: sprint_id
             },
             dataType: "html",
-            success: function(data, status, xhr){
-              $(node).parent().before(data);
-              $(node).val('');
+            success: function (data, status, xhr) {
+              $(node).parent().before(data)
+              $(node).val("")
             },
-            error:function(xhr, status, error) {
-              var alertMessage = parseErrorResponse(xhr.responseText);
+            error: function (xhr, status, error) {
+              var alertMessage = parseErrorResponse(xhr.responseText)
               if (alertMessage) {
-                setErrorMessage(alertMessage);
+                setErrorMessage(alertMessage)
               }
-            }
-          });
+            },
+          })
         }
-      });
+      })
     }
 
     this.routes = routes;
@@ -546,7 +536,6 @@ function changeHtmlNumber(element, number){
   }
 }
 
-
 function observeIssueSearchfield(fieldId, url) {
   $('#'+fieldId).each(function() {
     var $this = $(this);
@@ -554,44 +543,90 @@ function observeIssueSearchfield(fieldId, url) {
     $this.attr('data-value-was', $this.val());
     var check = function() {
       var val = $this.val();
+
       if ($this.attr('data-value-was') != val){
+        var request_data = {}
+        $.map($('#query_form').serializeArray(), function(n, i){
+          if (request_data[n['name']]) {
+            if ($.isArray(request_data[n['name']])) {
+              request_data[n['name']].push(n['value'])
+            } else {
+              request_data[n['name']] = [request_data[n['name']], n['value']];
+            }
+          } else {
+            request_data[n['name']] = n['value'];
+          }
+        });
+        request_data['q'] = val
+
         $this.attr('data-value-was', val);
         $.ajax({
           url: url,
           type: 'get',
-          data: {q: $this.val()},
+          data: request_data,
           beforeSend: function(){ $this.addClass('ajax-loading'); },
           complete: function(){ $this.removeClass('ajax-loading'); }
         });
       }
     };
-    var reset = function() {
+    var reset = function(e) {
       if (timer) {
         clearInterval(timer);
         timer = setInterval(check, 300);
       }
     };
+    var skipSpecialKeys = function(e) {
+      if (e.keyCode === 13) { e.preventDefault() }
+    }
     var timer = setInterval(check, 300);
+    var skipSubmit = function(e) {
+      if (e.which == 13 || e.keyCode == 13) {
+        e.preventDefault();
+        return false
+      }
+    }
+    $this.bind('keydown', skipSubmit);
     $this.bind('keyup click mousemove', reset);
+    $this.bind('keydown', skipSpecialKeys);
   });
 }
 
 function recalculateHours() {
-  var backlogSum = 0;
-  var unit = $("#backlog_version_header").data('estimated-unit');
-
-  $('.versions-planning-board td:nth-child(2) .issue-card').each(function(i, elem){
-    hours = parseFloat($(elem).data('estimated-hours'));
-    backlogSum += hours;
-  })
-  $('.versions-planning-board .backlog-hours').text('(' + backlogSum.toFixed(2) + unit +')');
-
-  var currentSum = 0;
-  $('.versions-planning-board td:nth-child(3) .issue-card').each(function(i, elem){
-    hours = parseFloat($(elem).data('estimated-hours'));
-    currentSum += hours;
-  })
-  $('.versions-planning-board .current-hours').text('(' + currentSum.toFixed(2) + unit + ')');
+  $('.version-column').each(function (i, elem) {
+    var estimatedHours = 0;
+    var storyPoints = 0;
+    $(elem).find('.issue-card').each(function (j, issue) {
+      estimatedHours += parseFloat($(issue).data('estimated-hours'));
+      storyPoints += parseFloat($(issue).data('story-points'));
+    });
+
+    var values = [];
+    if (estimatedHours > 0) {
+      values.push(estimatedHours.toFixed(2) + 'h');
+    }
+
+    if (storyPoints > 0) {
+      values.push(storyPoints.toFixed(2) + 'sp');
+    }
+
+    if (values.length > 0) {
+      $(elem).find('.version-estimate').text('(' + values.join('/') + ')');
+    }
+  });
+}
+
+function recalculateSprintHours() {
+  var unit = $(".planning-board").data('estimated-unit');
+  var dataAttr = unit == 'sp' ? 'story-points' : 'estimated-hours';
+
+  $('.sprint-column').each(function(i, elem){
+    var versionEstimationSum = 0;
+    $(elem).find('.issue-card').each(function(j, issue){
+      hours = parseFloat($(issue).data(dataAttr));
+      versionEstimationSum += hours;
+    });
+    $(elem).find('.sprint-estimate').text('(' + versionEstimationSum.toFixed(2) + unit + ')');
+  });
 }
 
 function showInlineCommentNode(quick_comment){
@@ -627,6 +662,35 @@ function showInlineComment(node, url){
   };
 }
 
+function saveInlineComment(node, url){
+  var node = node;
+  var comment = $(node).siblings("textarea").val();
+  if ($.trim(comment) === "") return false;
+  $(node).prop('disabled', true);
+  $('.lock').show();
+  var card = $(node).parents(".issue-card");
+  var version_board = $('.planning-board').length;
+  $.ajax({
+    url: url,
+    type: "PUT",
+    dataType: "html",
+    data: { issue: { notes: comment }, version_board: version_board },
+    success: function(data, status, xhr){
+      $(card).replaceWith(data);
+    },
+    error: function(xhr, status, error){
+      var alertMessage = parseErrorResponse(xhr.responseText);
+      if (alertMessage) {
+        setErrorMessage(alertMessage);
+      }
+    },
+    complete: function(xhr, status){
+      $(node).prop('disabled', false);
+      $('.lock').hide();
+    }
+  });
+}
+
 function cancelInlineComment(node){
   $(node).parent().hide();
   $(node).parent().siblings(".last_comment").show();
@@ -651,7 +715,7 @@ $(document).ready(function(){
     var searchTerm = this.value;
     cards.removeClass("filtered");
     cards.filter(function() {
-      return $(this).find(".name").text().toLowerCase().indexOf(searchTerm) === -1;
+      return $(this).find(".name").text().toLowerCase().indexOf(searchTerm.toLowerCase()) === -1;
     }).addClass("filtered");
   });
 });
@@ -679,3 +743,45 @@ function linkableAttributeFields() {
   var progress_label = $('.progress.attribute .label')
   progress_label.html(linkGenerator('/done_ratio', progress_label.html()));
 };
+
+function chartLinkGenerator() {
+  var filter_values = $("#query_form").serialize();
+  event.preventDefault();
+  window.location.href = $('.agile_charts_link').prop('href') + '?' + filter_values;
+}
+
+function hideChartPeriodCheckbox() {
+  $("#cb_chart_period").hide();
+  $("label[for=cb_chart_period]").removeAttr("for");
+};
+
+function toggleChartUnit(chart, target) {
+  var showTarget = chartsWithUnits.indexOf(chart) > -1;
+  $('#' + target).toggle(showTarget);
+};
+
+function updateVersionAgileChart(url) {
+  $.ajax(url + '&chart=' + $('#chart_by_select').val() + '&chart_unit=' + $('#chart_unit').val());
+};
+
+function chartTooltipCallbacks(chartType) {
+  if (chartType === 'scatter') {
+    return scatterChartTooltipCallbacks()
+  } else {
+    return {}
+  }
+};
+
+function scatterChartTooltipCallbacks() {
+  return {
+    title: function (tooltipItem, data) {
+      return data.labels[tooltipItem[0].xLabel] || '';
+    },
+    label: function (tooltipItem, data) {
+      var label = data.datasets[tooltipItem.datasetIndex].label || '';
+      if (label) { label += ': ' }
+      label += tooltipItem.yLabel;
+      return label;
+    }
+  }
+};
diff --git a/plugins/redmine_agile/assets/javascripts/redmine_agile_context_menu.js b/plugins/redmine_agile/assets/javascripts/redmine_agile_context_menu.js
deleted file mode 100644
index 0d757e6..0000000
--- a/plugins/redmine_agile/assets/javascripts/redmine_agile_context_menu.js
+++ /dev/null
@@ -1,222 +0,0 @@
-var agileContextMenuObserving;
-var agileContextMenuUrl;
-
-function agileContextMenuRightClick(event) {
-  var target = $(event.target);
-  if (target.is('a')) {return;}
-  var tr = target.parents('div').first();
-  if (!tr.hasClass('hascontextmenu')) {return;}
-  event.preventDefault();
-  if (!agileContextMenuIsSelected(tr)) {
-    agileContextMenuUnselectAll();
-    agileContextMenuAddSelection(tr);
-    agileContextMenuSetLastSelected(tr);
-  }
-  agileContextMenuShow(event);
-}
-
-function agileContextMenuClick(event) {
-  var target = $(event.target);
-  var lastSelected;
-
-  if (target.is('a') && target.hasClass('submenu')) {
-    event.preventDefault();
-    return;
-  }
-  agileContextMenuHide();
-  if (target.is('a') || target.is('img')) { return; }
-  if (event.which == 1 || (navigator.appVersion.match(/\bMSIE\b/))) {
-    var tr = target.parents('div').first();
-    if (tr.length && tr.hasClass('hascontextmenu')) {
-      // a row was clicked, check if the click was on checkbox
-      if (target.is('input.checkbox')) {
-        // a checkbox may be clicked
-        if (target.attr('checked')) {
-          tr.addClass('context-menu-selection');
-        } else {
-          tr.removeClass('context-menu-selection');
-        }
-      } else {
-        if (event.ctrlKey || event.metaKey) {
-          agileContextMenuToggleSelection(tr);
-        } else if (event.shiftKey) {
-          lastSelected = agileContextMenuLastSelected();
-          if (lastSelected.length) {
-            var toggling = false;
-            $('.hascontextmenu').each(function(){
-              if (toggling || $(this).is(tr)) {
-                agileContextMenuAddSelection($(this));
-              }
-              if ($(this).is(tr) || $(this).is(lastSelected)) {
-                toggling = !toggling;
-              }
-            });
-          } else {
-            agileContextMenuAddSelection(tr);
-          }
-        } else {
-          agileContextMenuUnselectAll();
-          agileContextMenuAddSelection(tr);
-        }
-        agileContextMenuSetLastSelected(tr);
-      }
-    } else {
-      // click is outside the rows
-      if (target.is('a') && (target.hasClass('disabled') || target.hasClass('submenu'))) {
-        event.preventDefault();
-      } else {
-        agileContextMenuUnselectAll();
-      }
-    }
-  }
-}
-
-function agileContextMenuCreate() {
-  if ($('#context-menu').length < 1) {
-    var menu = document.createElement("div");
-    menu.setAttribute("id", "context-menu");
-    menu.setAttribute("style", "display:none;");
-    document.getElementById("content").appendChild(menu);
-  }
-}
-
-function agileContextMenuShow(event) {
-  var mouse_x = event.pageX;
-  var mouse_y = event.pageY;
-  var render_x = mouse_x;
-  var render_y = mouse_y;
-  var dims;
-  var menu_width;
-  var menu_height;
-  var window_width;
-  var window_height;
-  var max_width;
-  var max_height;
-
-  $('#context-menu').css('left', (render_x + 'px'));
-  $('#context-menu').css('top', (render_y + 'px'));
-  $('#context-menu').html('');
-
-  $.ajax({
-    url: agileContextMenuUrl,
-    data: $(event.target).parents('form').first().serialize(),
-    success: function(data, textStatus, jqXHR) {
-      $('#context-menu').html(data);
-      menu_width = $('#context-menu').width();
-      menu_height = $('#context-menu').height();
-      max_width = mouse_x + 2*menu_width;
-      max_height = mouse_y + menu_height;
-
-      var ws = window_size();
-      window_width = ws.width;
-      window_height = ws.height;
-
-      /* display the menu above and/or to the left of the click if needed */
-      if (max_width > window_width) {
-       render_x -= menu_width;
-       $('#context-menu').addClass('reverse-x');
-      } else {
-       $('#context-menu').removeClass('reverse-x');
-      }
-      if (max_height > window_height) {
-       render_y -= menu_height;
-       $('#context-menu').addClass('reverse-y');
-      } else {
-       $('#context-menu').removeClass('reverse-y');
-      }
-      if (render_x <= 0) render_x = 1;
-      if (render_y <= 0) render_y = 1;
-      $('#context-menu').css('left', (render_x + 'px'));
-      $('#context-menu').css('top', (render_y + 'px'));
-      $('#context-menu').show();
-
-      //if (window.parseStylesheets) { window.parseStylesheets(); } // IE
-
-    }
-  });
-}
-
-function agileContextMenuSetLastSelected(tr) {
-  $('.cm-last').removeClass('cm-last');
-  tr.addClass('cm-last');
-}
-
-function agileContextMenuLastSelected() {
-  return $('.cm-last').first();
-}
-
-function agileContextMenuUnselectAll() {
-  $('.hascontextmenu').each(function(){
-    agileContextMenuRemoveSelection($(this));
-  });
-  $('.cm-last').removeClass('cm-last');
-}
-
-function agileContextMenuHide() {
-  $('#context-menu').hide();
-}
-
-function agileContextMenuToggleSelection(tr) {
-  if (agileContextMenuIsSelected(tr)) {
-    agileContextMenuRemoveSelection(tr);
-  } else {
-    agileContextMenuAddSelection(tr);
-  }
-}
-
-function agileContextMenuAddSelection(tr) {
-  tr.addClass('context-menu-selection');
-  agileContextMenuCheckSelectionBox(tr, true);
-  agileContextMenuClearDocumentSelection();
-}
-
-function agileContextMenuRemoveSelection(tr) {
-  tr.removeClass('context-menu-selection');
-  agileContextMenuCheckSelectionBox(tr, false);
-}
-
-function agileContextMenuIsSelected(tr) {
-  return tr.hasClass('context-menu-selection');
-}
-
-function agileContextMenuCheckSelectionBox(tr, checked) {
-  tr.find('input.checkbox[type=checkbox]').prop('checked', checked);
-}
-
-function agileContextMenuClearDocumentSelection() {
-  // TODO
-  if (document.selection) {
-    document.selection.empty(); // IE
-  } else {
-    window.getSelection().removeAllRanges();
-  }
-}
-
-function agileContextMenuInit(url) {
-  agileContextMenuUrl = url;
-  agileContextMenuCreate();
-  agileContextMenuUnselectAll();
-
-  if (!agileContextMenuObserving) {
-    $(document).click(agileContextMenuClick);
-    $(document).contextmenu(agileContextMenuRightClick);
-    agileContextMenuObserving = true;
-  }
-}
-
-
-function window_size() {
-  var w;
-  var h;
-  if (window.innerWidth) {
-    w = window.innerWidth;
-    h = window.innerHeight;
-  } else if (document.documentElement) {
-    w = document.documentElement.clientWidth;
-    h = document.documentElement.clientHeight;
-  } else {
-    w = document.body.clientWidth;
-    h = document.body.clientHeight;
-  }
-  return {width: w, height: h};
-}
diff --git a/plugins/redmine_agile/assets/javascripts/visibility.min.js b/plugins/redmine_agile/assets/javascripts/visibility.min.js
deleted file mode 100644
index 3733ccd..0000000
--- a/plugins/redmine_agile/assets/javascripts/visibility.min.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(e){"use strict";var i=-1,t={onVisible:function(e){var i=t.isSupported();if(!i||!t.hidden())return e(),i;var n=t.change(function(){t.hidden()||(t.unbind(n),e())});return n},change:function(e){if(!t.isSupported())return!1;i+=1;var n=i;return t._callbacks[n]=e,t._listen(),n},unbind:function(e){delete t._callbacks[e]},afterPrerendering:function(e){var i=t.isSupported(),n="prerender";if(!i||n!=t.state())return e(),i;var r=t.change(function(i,d){n!=d&&(t.unbind(r),e())});return r},hidden:function(){return!(!t._doc.hidden&&!t._doc.webkitHidden)},state:function(){return t._doc.visibilityState||t._doc.webkitVisibilityState||"visible"},isSupported:function(){return!(!t._doc.visibilityState&&!t._doc.webkitVisibilityState)},_doc:document||{},_callbacks:{},_change:function(e){var i=t.state();for(var n in t._callbacks)t._callbacks[n].call(t._doc,e,i)},_listen:function(){if(!t._init){var e="visibilitychange";t._doc.webkitVisibilityState&&(e="webkit"+e);var i=function(){t._change.apply(t,arguments)};t._doc.addEventListener?t._doc.addEventListener(e,i):t._doc.attachEvent(e,i),t._init=!0}}};"undefined"!=typeof module&&module.exports?module.exports=t:e.Visibility=t}(this),function(e){"use strict";var i=-1,t=function(t){return t.every=function(e,n,r){t._time(),r||(r=n,n=null),i+=1;var d=i;return t._timers[d]={visible:e,hidden:n,callback:r},t._run(d,!1),t.isSupported()&&t._listen(),d},t.stop=function(e){return t._timers[e]?(t._stop(e),delete t._timers[e],!0):!1},t._timers={},t._time=function(){t._timed||(t._timed=!0,t._wasHidden=t.hidden(),t.change(function(){t._stopRun(),t._wasHidden=t.hidden()}))},t._run=function(i,n){var r,d=t._timers[i];if(t.hidden()){if(null===d.hidden)return;r=d.hidden}else r=d.visible;var a=function(){d.last=new Date,d.callback.call(e)};if(n){var o=new Date,u=o-d.last;r>u?d.delay=setTimeout(function(){a(),d.id=setInterval(a,r)},r-u):(a(),d.id=setInterval(a,r))}else d.id=setInterval(a,r)},t._stop=function(e){var i=t._timers[e];clearInterval(i.id),clearTimeout(i.delay),delete i.id,delete i.delay},t._stopRun=function(){var e=t.hidden(),i=t._wasHidden;if(e&&!i||!e&&i)for(var n in t._timers)t._stop(n),t._run(n,!e)},t};"undefined"!=typeof module&&module.exports?module.exports=t(require("./visibility.core")):t(e.Visibility)}(window);
\ No newline at end of file
diff --git a/plugins/redmine_agile/assets/stylesheets/jquery.simplecolorpicker.css b/plugins/redmine_agile/assets/stylesheets/jquery.simplecolorpicker.css
old mode 100755
new mode 100644
diff --git a/plugins/redmine_agile/assets/stylesheets/redmine_agile.css b/plugins/redmine_agile/assets/stylesheets/redmine_agile.css
old mode 100755
new mode 100644
index e319206..d8ce691
--- a/plugins/redmine_agile/assets/stylesheets/redmine_agile.css
+++ b/plugins/redmine_agile/assets/stylesheets/redmine_agile.css
@@ -53,6 +53,24 @@ table.options tr > td {
   white-space: nowrap;
 }
 
+.agile_options_field {
+  display: inline-block;
+  min-width: 30%;
+}
+
+.agile_options_label {
+  display: inline-block;
+  min-width: 100px;
+}
+
+.selected_sprint {
+  font-size: 14px;
+}
+
+.selected_sprint > select {
+  font-size: 14px;
+}
+
 .card-fields .floating {
   text-align: left;
   width: 200px;
@@ -75,6 +93,13 @@ table.options tr > td {
   padding: 1px 5px !important;
 }
 
+.card-fields .floating .wp_input:not(:focus) {
+    background-color: transparent;
+    border-color: transparent;
+    font-size: 90%;
+}
+
+
 /**********************************************************************/
 /* ISSUES SIDEBAR
 /**********************************************************************/
@@ -128,6 +153,7 @@ table.list.issues-board th {overflow: hidden; text-overflow: ellipsis;}
   background-color: #ffffdd;
 }
 
+table.issues-board .issue-status-col.empty { padding-bottom: 30px }
 table.issues-board td.issue-status-col.closed {background-color: #FAFAFA;}
 
 table.issues-board tr.group.swimlane {height: 30px;}
@@ -364,6 +390,10 @@ table.list.issues tr.issue:not(.context-menu-selection).bk-gray td.id a {backgro
   width: 100%;
 }
 
+.issue-card .quick-edit {
+  margin-right: 3px;
+}
+
 .issue-card .last-comment{
   font-style: italic;
 }
@@ -452,8 +482,8 @@ table.issues-board tbody td.over_wp_limit {background-color: #FFF6F6;}
 
 .agile-chart-container {
   margin: auto;
-  height: 400px;
-  max-width: 800px;
+  min-height: 400px;
+  max-width: 1200px;
 }
 
 .issue-card.filtered {
@@ -482,6 +512,99 @@ table.list.issues-board.sticky {
   html.agile-board-fullscreen #wrapper > div.flyout-menu.js-flyout-menu { z-index: 22; }
 }
 
+.planning-board {
+  display: flex;
+  overflow-x: auto;
+}
+
+.planning-board p {
+    margin: 0;
+}
+
+.planning-board .column-content {
+  width: 272px;
+  min-width: 272px;
+  margin: 0 4px 4px 4px;
+  background-color: #f6f7f8;
+  border: 1px solid #d7d7d7;
+}
+
+.planning-board .column-header {
+  display: block;
+  justify-content: space-between;
+  align-items: center;
+  min-height: 28px;
+  padding: 5px;
+  background-color: #eeeeee;
+  border-bottom: 1px solid #d7d7d7;
+}
+
+.planning-board .column-header .version-name {
+  color: #555;
+  font-size: 90%;
+  font-weight: bold;
+}
+
+.planning-board .column-issues {
+  padding-bottom: 30px;
+}
+
+.planning-board .closed-issue {
+  color: #999;
+  display: block;
+  white-space: inherit;
+  background-color: #ededed;
+  border-color: #d7d7d7;
+}
+
+.planning-board .closed-issue p.name a, .planning-board .closed-issue p.issue-id {
+  color: #999;
+  text-decoration: line-through;
+}
+
+.issue-edit-modal {
+  min-height: initial !important;
+}
+
+.modal-action-button {
+  height: 28px;
+}
+
+.agile_sprint_list .spring_column { text-align: center; }
+
+.controller-agile_boards .query-totals {
+  margin-top: -2.3em;
+}
+
+.agile-board-fullscreen .controller-agile_boards .query-totals {
+  margin: 0px;
+  position: fixed;
+  top: 0px;
+  background: inherit;
+  left: 0;
+  right: 0;
+  padding-right: 32px;
+  min-height: 20px;
+  line-height: 20px;
+}
+
+.backlog-column-header {
+  min-width: 15%;
+  border-right: solid 3px #d7d7d7;
+}
+
+.issue-backlog-search { padding: 5px 5px 0px 5px; }
+.issue-backlog-search input#search {
+  max-width: 100%;
+}
+
+.issues-board .backlog-column-header { background-color: #e1f1f8; }
+
+.issues-board .issue-backlog-col {
+  background: #e1f1f8;
+  border-right: solid 3px #d7d7d7 !important;
+}
+
 html {
   overflow-y: inherit !important;
 }
diff --git a/plugins/redmine_agile/config/locales/de.yml b/plugins/redmine_agile/config/locales/de.yml
index e576b63..428cde2 100644
--- a/plugins/redmine_agile/config/locales/de.yml
+++ b/plugins/redmine_agile/config/locales/de.yml
@@ -19,13 +19,16 @@ de:
   label_agile_board_default_fields: Standardfelder fĂĽr Taskboard-Karten
   label_agile_charts_issues_burndown: Burndown nach Aufgaben
   label_agile_charts_work_burndown:  Burndown nach erfassten Zeiten
+  label_agile_charts_work_burndown_sp: Burndown nach Story points
+  label_agile_charts_work_burndown_hours: Burndown nach Stunden
   label_agile_charts_number_of_hours: Stunden
   label_agile_charts_number_of_issues: Anzahl Aufgaben
-  label_agile_charts_cumulative_flow: Cumulative Flow
-  label_agile_charts_trackers_cumulative_flow: Cumulative Flow nach Tracker
+  label_agile_charts_cumulative_flow: Kumulativer Fluss
+  label_agile_charts_trackers_cumulative_flow: Kumulativer Fluss nach Tracker
   label_agile_charts_issues_velocity: Velocity
-  label_agile_charts_lead_time: Lead Time
-  label_agile_charts_average_lead_time: Durchschnittliche Lead Time
+  label_agile_charts_cycle_time: Zyklus
+  label_agile_charts_lead_time: Durchlaufzeit
+  label_agile_charts_average_lead_time: Durchschnittliche Durchlaufzeit
   label_agile_chart_plural: Agile Diagramme
   permission_view_agile_charts: Agile Diagramme ansehen
   label_agile_ideal_work_remaining: Soll
@@ -123,3 +126,108 @@ de:
   label_agile_inline_comment: Inline-Kommentar
   label_agile_hours: Stunden
   field_color: Farbe
+
+  #1.4.0
+  label_agile_esitmate_units: Einheit für Schätzung
+  label_agile_trackers_for_sp: Tracker fĂĽr Story points
+  label_agile_story_points: Story points
+  field_story_points: Story points
+  label_agile_charts_number_of_story_points: Anzahl Story points
+  label_agile_board_columns: Board Spalten
+  lable_agile_wip_limit_exceeded: Work-in-progress Limit ĂĽberschritten
+  label_agile_wip_limit: Work-in-progress Limit
+  label_agile_add_new_issue: '+ NEUES TICKET'
+  label_agile_allow_create_cards: Ticket aus Taskboard erstellen
+  label_agile_auto_assign_on_move: Automatische Zuweisung bei
+  text_agile_create_issue_error: Beim Erstellen der Aufgabe ist ein Fehler aufgetreten
+  label_agile_inline_comment: Inline Kommentar
+  label_agile_hours: Stunden
+  field_color: Farbe
+
+  field_duration: Dauer
+
+  field_closed_on_trendline: Closed trendline
+  field_created_on_trendline: Created trendline
+
+  label_agile_sp_values: Story points Werte
+
+  label_agile_interval_size: Interval Größe
+  label_agile_day: Tag
+  label_agile_week: Woche
+  label_agile_month: Monat
+  label_agile_quarter: Quartal
+  label_agile_year: Jahr
+  label_cards_search: Suche nach Thema
+
+  field_agile_sprint: Sprint
+  field_end_date: Enddatum
+  label_agile_sprints_on: Aktiviere Sprints standardmäßig
+  label_agile_sprint: Sprint
+  label_agile_sprint_plural: Sprints
+  label_agile_sprint_name: Name
+  label_agile_sprint_description: Beschreibung
+  label_agile_sprint_status: Status
+  label_agile_sprint_start_date: Startdatum
+  label_agile_sprint_end_date: Enddatum
+  label_agile_sprint_new: Neuer Sprint
+  label_agile_sprint_status_open: Offen
+  label_agile_sprint_status_active: Aktiv
+  label_agile_sprint_status_closed: Geschlossen
+  label_agile_sprint_duration: Dauer
+  label_agile_sprint_duration_select: <<Wähle Dauer>>
+  label_agile_sprint_duration_week_1: 1 Woche
+  label_agile_sprint_duration_week_2: 2 Wochen
+  label_agile_sprint_duration_week_3: 3 Wochen
+  label_agile_sprint_duration_week_4: 4 Wochen
+  label_agile_sprint_errors_crossed: Sprint Daten ĂĽberschneiden sich mit einem anderen existierenden Sprint
+  label_agile_sprint_errors_end_more_start: Das Enddatum sollte nach dem Startdatum liegen
+  label_agile_sprint_default_chart: Diagramm
+  label_agile_sprint_chart_units: Einheit für Schätzung
+  label_agile_chart_for_sprint: "FĂĽr Sprint: %{sprint}"
+
+  label_agile_charts: Diagramme
+  label_agile_chart_burndown: Burndown Diagramm
+  label_agile_chart_burnup: Burnup Diagramm
+  label_agile_chart_units: Einheiten
+
+  label_agile_planning_board_more: Lade mehr...
+  label_agile_version_query_new: Neue agile Abfrage
+  label_agile_version_my_boards: Meine agilen boards
+  label_agile_version_board_plural: Agile boards
+  label_agile_version_board_edit: Bearbeite agile Board version
+
+  label_agile_edit_issue: Bearbeite Ticket
+
+  label_agile_board_type: Art des Taskboards
+  label_agile_board_type_kanban: Kanban board
+  label_agile_board_type_scrum: Scrum board
+  label_agile_board_totals: Gesamt
+  label_agile_board_totals_story_points: Story points
+  label_agile_board_totals_hours: Stunden
+  label_agile_board_totals_spent_time: Aufgewendete Zeit
+  label_agile_board_totals_percent_done: Prozent erledigt
+  label_agile_board_totals_velocity: Velocity
+  label_agile_board_totals_interval: Intervall
+  label_agile_board_totals_remaining: Verbleibend
+
+  label_agile_sprint_list_active: Aktive Sprints
+  label_agile_sprint_list_future: ZukĂĽnftige Sprints
+
+  label_agile_mixed_trackers: Verschiedene Tracker
+  label_agile_moving_average: Gleitender Durchschnitt
+
+  label_agile_board_backlog: Backlog
+  label_agile_board_backlog_column: Backlog Spalten
+  label_agile_board_search_backlog_issues: Durchsuche Backlog Tickets
+  label_agile_version_plural: Versionen
+  label_agile_sprint_add: Sprint hinzufĂĽgen
+  label_agile_version_add: Version hinzufĂĽgen
+
+  label_agile_sprint_query_new: Neue agile Sprint Abfragen
+  label_agile_sprint_my_boards: Meine agilen Sprint boards
+  label_agile_sprint_board_plural: Agile Sprint boards
+  label_agile_sprint_board_edit: Bearbeite agiles Sprint board
+  field_sprint: Sprint
+  label_agile_no_sprint_issues: Tickets ohne Sprint
+  field_closed_sprints: Geschlossenen Sprints
+  field_closed_versions: Geschlossene Versionen
diff --git a/plugins/redmine_agile/config/locales/en.yml b/plugins/redmine_agile/config/locales/en.yml
old mode 100755
new mode 100644
index f74ba5c..a223a40
--- a/plugins/redmine_agile/config/locales/en.yml
+++ b/plugins/redmine_agile/config/locales/en.yml
@@ -25,9 +25,12 @@ en:
   label_agile_charts_cumulative_flow: Cumulative flow
   label_agile_charts_trackers_cumulative_flow: Trackers cumulative flow
   label_agile_charts_issues_velocity: Velocity
+  label_agile_charts_cycle_time: Cycle time
   label_agile_charts_lead_time: Lead time
   label_agile_charts_average_lead_time: Average lead time
   label_agile_chart_plural: Agile charts
+  label_agile_chart_new: New agile chart
+  label_agile_chart_edit: Edit agile chart
   permission_view_agile_charts: View agile charts
   label_agile_ideal_work_remaining: Ideal
   label_agile_actual_work_remaining: Actual
@@ -42,7 +45,7 @@ en:
   label_agile_too_many_items: "The chart can't be created because it exceeds the maximum number of items that can be displayed (%{max})"
   label_agile_time_reports_items_limit: Time entries based charts issues limit
   label_agile_total_work_remaining: Total
-  label_agile_default_chart: Version default chart
+  label_agile_default_chart: Default chart
 
   #1.1.1
   label_agile_charts_average_velocity: Average velocity
@@ -134,4 +137,95 @@ en:
   field_created_on_trendline: Created trendline
 
   label_agile_sp_values: Story points values
+
+  label_agile_interval_size: Interval size
+  label_agile_day: Day
+  label_agile_week: Week
+  label_agile_month: Month
+  label_agile_quarter: Quarter
+  label_agile_year: Year
   label_cards_search: Search by subject
+
+  field_agile_sprint: Sprint
+  field_end_date: End date
+  label_agile_sprints_on: Activate sprints by default
+  label_agile_sprint: Sprint
+  label_agile_sprint_plural: Sprints
+  label_agile_sprint_name: Name
+  label_agile_sprint_description: Description
+  label_agile_sprint_status: Status
+  label_agile_sprint_sharing: Sharing
+  label_agile_sprint_start_date: Start date
+  label_agile_sprint_end_date: End date
+  label_agile_sprint_new: New sprint
+  label_agile_sprint_status_open: Open
+  label_agile_sprint_status_active: Active
+  label_agile_sprint_status_closed: Closed
+  label_agile_sprint_duration: Duration
+  label_agile_sprint_duration_select: <<Select duration>>
+  label_agile_sprint_duration_week_1: 1 week
+  label_agile_sprint_duration_week_2: 2 weeks
+  label_agile_sprint_duration_week_3: 3 weeks
+  label_agile_sprint_duration_week_4: 4 weeks
+  label_agile_sprint_errors_crossed: Sprint dates are cross another existed sprint
+  label_agile_sprint_errors_end_more_start: End date should be more than start date
+  label_agile_sprint_errors_open_issues: Sprint with open issues can be closed
+  label_agile_sprint_default_chart: Сhart
+  label_agile_sprint_chart_units: Estimation
+  label_agile_chart_for_sprint: "For sprint: %{sprint}"
+
+  label_agile_charts: Charts
+  label_agile_chart_burndown: Burndown chart
+  label_agile_chart_burnup: Burnup chart
+  label_agile_chart_units: Units
+
+  label_agile_planning_board_more: Load more...
+  label_agile_version_query_new: New Agile versions query
+  label_agile_version_my_boards: My agile version boards
+  label_agile_version_board_plural: Agile version boards
+  label_agile_version_board_edit: Edit agile version board
+
+  label_agile_edit_issue: Edit issue
+
+  label_agile_board_type: Board type
+  label_agile_board_type_kanban: Kanban board
+  label_agile_board_type_scrum: Scrum board
+  label_agile_board_totals: Totals
+  label_agile_board_totals_story_points: Story points
+  label_agile_board_totals_hours: Hours
+  label_agile_board_totals_spent_time: Spent time
+  label_agile_board_totals_percent_done: Percent done
+  label_agile_board_totals_velocity: Velocity
+  label_agile_board_totals_interval: Interval
+  label_agile_board_totals_remaining: Remaining
+
+  label_agile_sprint_list_active: Active sprints
+  label_agile_sprint_list_future: Future sprints
+  label_agile_sprint_list_old: Old sprints
+
+  label_agile_mixed_trackers: Mixed trackers
+  label_agile_moving_average: Moving average
+
+  label_agile_board_backlog: Backlog
+  label_agile_board_backlog_column: Backlog column
+  label_agile_board_search_backlog_issues: Search backlog issues
+  label_agile_version_plural: Versions
+  label_agile_sprint_add: Add sprint
+  label_agile_version_add: Add version
+
+  label_agile_sprint_query_new: New agile sprints query
+  label_agile_sprint_my_boards: My agile sprint boards
+  label_agile_sprint_board_plural: Agile sprint boards
+  label_agile_sprint_board_edit: Edit agile sprint board
+  field_sprint: Sprint
+  label_agile_no_sprint_issues: Issues without sprint
+  field_closed_sprints: Closed sprints
+  field_closed_versions: Closed versions
+
+  label_agile_sprint_sharing_none: Not shared
+  label_agile_sprint_sharing_descendants: With subprojects
+  label_agile_sprint_sharing_hierarchy: With project hierarchy
+  label_agile_sprint_sharing_tree: With project tree
+  label_agile_sprint_sharing_system: With all projects
+
+
diff --git a/plugins/redmine_agile/config/locales/es.yml b/plugins/redmine_agile/config/locales/es.yml
index 7b6cec1..5891751 100644
--- a/plugins/redmine_agile/config/locales/es.yml
+++ b/plugins/redmine_agile/config/locales/es.yml
@@ -4,13 +4,12 @@ es:
   label_agile_board: Tablero ágil
   label_agile_board_plural: Tableros ágiles
   label_agile_board_thumbnails: Thumbnails
-  label_agile_board_issues_per_column: Peticiones por columna
   label_agile_board_more_issues: Más peticiones
   error_agile_status_transition: No se puede cambiar el estado de la peticiĂłn
   label_agile_board_new: Nuevo tablero ágil
   label_agile_board_edit: Editar tablero ágil
   label_agile_my_boards: Mis tableros ágiles
-  label_agile_issue_id: ID de PeticiĂłn
+  label_agile_issue_id: ID de peticiĂłn
 
   permission_manage_public_agile_queries: Administrar tableros ágiles públicos
   permission_add_agile_queries: Agregar tableros ágiles
@@ -19,27 +18,31 @@ es:
   #1.1.0
   label_agile_board_default_fields: Columnas por defecto
   label_agile_charts_issues_burndown: Peticiones burndown
-  label_agile_charts_work_burndown: Trabajo burndown
+  label_agile_charts_work_burndown: Horas burndown
+  label_agile_charts_work_burndown_sp: Story points burndown
   label_agile_charts_number_of_hours: NĂşmero de horas
   label_agile_charts_number_of_issues: NĂşmero de peticiones
   label_agile_charts_cumulative_flow: Flujo acumulado
   label_agile_charts_trackers_cumulative_flow: Seguimiento de flujo acumulado
   label_agile_charts_issues_velocity: Velocidad
+  label_agile_charts_cycle_time: Ciclo de tiempo
   label_agile_charts_lead_time: Tiempo de ejecuciĂłn
-  label_agile_charts_average_lead_time: Tiempo de ejecuciĂłn promedio
-  label_agile_chart_plural: Gráficos ágiles
-  permission_view_agile_charts: Ver gráficos ágiles
+  label_agile_charts_average_lead_time: Tiempo medio de entrega
+  label_agile_chart_plural: Gráficas Agile
+  label_agile_chart_new: Nueva gráfica Agile
+  label_agile_chart_edit: Editar gráfica Agile
+  permission_view_agile_charts: Ver gráfica Agile
   label_agile_ideal_work_remaining: Ideal
   label_agile_actual_work_remaining: Actual
-  label_agile_chart: Gráfico
+  label_agile_chart: Gráfica
   label_agile_date_from: Desde
-  label_agile_date_to: Hasta
-  label_agile_chart_dates: Intervalos gráficos
-  label_agile_weighed_ideal_work_remaining: Weighed ideal
-  label_agile_status_colors: Color de estados
-  label_agile_charts_burnup: "Peticiones burnup"
+  label_agile_date_to: a
+  label_agile_chart_dates: Gráfica de interválos
+  label_agile_weighed_ideal_work_remaining: Ideal ponderado
+  label_agile_status_colors: Estado de colores
+  label_agile_charts_burnup: Peticiones burnup
   label_agile_charts_number_of_days: NĂşmero de dĂ­as
-  label_agile_too_many_items: "El gráfico no puede ser creado por exceder el número máximo de items que pueden ser mostrados (%{max})"
+  label_agile_too_many_items: El gráfico no puede ser creado por exceder el número máximo de items que pueden ser mostrados (%{max})
   label_agile_time_reports_items_limit: Tiempo de entradas basado en gráfico de peticiones límite
   label_agile_total_work_remaining: Total
   label_agile_default_chart: Versión gráfica por defecto
@@ -47,4 +50,172 @@ es:
   #1.1.1
   label_agile_charts_average_velocity: Velocidad promedio
   label_agile_charts_avarate_number_of_issues: NĂşmero de peticiones promedio
-  label_agile_charts_avarate_number_of_hours: NĂşmero de horas promedio
\ No newline at end of file
+  label_agile_charts_avarate_number_of_hours: NĂşmero de horas promedio
+
+  #1.2.0
+  label_agile_color: Color
+  permission_manage_agile_verions: Administrar planificaciĂłn de versiones
+  label_agile_version_planning: planificaciĂłn de versiones
+  error_agile_version_transition: No se puede cambiar la versiĂłn de la peticiĂłn
+  label_agile_tracker_colors: Colores de tipos
+  label_agile_issue_priority_colors: Colores de prioridades de la peticiĂłn
+  label_agile_color_based_on: Color por
+  label_agile_color_no_colors: Sin colores
+  label_agile_manage_colors: Administrar colores
+  label_agile_fullscreen: Pantalla completa
+  label_agile_no_version_issues: PeticiĂłn sin version
+  label_agile_charts_work_burnup_hours: burnup de horas
+  label_agile_charts_work_burnup_sp: burnup de Story points
+  label_agile_completed: Completado
+  label_agile_exclude_weekends: Excluir los fines de semana del esfuerzo ideal
+  label_agile_board_truncated: El tablero se truncó porque excede el número máximo de elementos que se pueden mostrar (%{max})
+  label_agile_board_items_limit: Límite de elementos del tablero ágil
+  label_agile_swimlanes: Swimlanes
+  label_agile_minimize_closed: minimizar las peticiones cerradas
+  label_agile_fields: Campos de la tarjeta
+  label_agile_default_board: tablero por defecto
+
+  #1.3.6
+  text_agile_move_not_possible: Esté movimiento no es posible ()
+
+  #1.3.8
+  label_agile_parent_issue_tracker_id: Tipo peticiĂłn padre
+  label_agile_sub_issues: SubTareas
+  label_agile_color_green: Verde
+  label_agile_color_blue: Azul
+  label_agile_color_turquoise: Turquesa
+  label_agile_color_lightgreen: Verde claro
+  label_agile_color_yellow: Amarillo
+  label_agile_color_orange: Naranja
+  label_agile_color_red: Rojo
+  label_agile_color_purple: Morado
+  label_agile_color_gray: Gris
+  label_agile_has_sub_issues: Tiene SubTareas
+  label_agile_light_free_version: VersiĂłn gratuita Agile
+  label_agile_link_to_pro: Actualizar a PRO
+  label_agile_link_to_pro_demo: demo version PRO
+  label_agile_link_to_more_plugins: Encontrar más extensiones de RedmineUP
+  label_agile_button_agree: Acuerdo
+  label_agile_license: Licencia RedmineUP
+  label_agile_saving_boards: Guardar tablero
+  label_agile_horizontal_swim_lines: swimlines horizontales
+  label_agile_board_sub_columns: sub-columns del tablero
+  label_agile_additional_agile_charts: Additional agile charts
+  label_agile_coloured_issue_cards: Tarjetas de colores
+  label_agile_4_more_features: 4 funcionalides más...
+  label_agile_upgrade_to_pro: Actualice a la version PRO apra usar estas funcionalidades
+
+  label_agile_day_in_state: En estado
+
+  #1.3.10
+  label_agile_hide_closed_issues_data: Ocultar datos de las peticiones cerradas
+  project_module_agile: Agile
+
+  #1.3.13
+  label_agile_last_comment: Ăšltimo comentario
+
+  #1.4.0
+  label_agile_esitmate_units: Unidades estimadas
+  label_agile_trackers_for_sp: Tipos para  story points
+  label_agile_story_points: Story points
+  field_story_points: Story points
+  label_agile_charts_number_of_story_points: NĂşmero de story points
+  label_agile_board_columns: Columnas del tablero
+  lable_agile_wip_limit_exceeded: Excedido lĂ­mite de dedicaciĂłn
+  label_agile_wip_limit: lĂ­mite de dedicaciĂłn
+  label_agile_add_new_issue: "+ AĂ‘ADIR NUEVA PETICION"
+  label_agile_allow_create_cards: Crear una tarjeta
+  label_agile_auto_assign_on_move: Auto-Asignar al mover
+  text_agile_create_issue_error: Se produjo un error al crear la peticiĂłn
+  label_agile_inline_comment: Comentario en linea
+  label_agile_hours: Horas
+  field_color: Color
+
+  field_duration: DuraciĂłn
+
+  field_closed_on_trendline: Trendline cerrada
+  field_created_on_trendline: Trendline creada
+
+  label_agile_sp_values: Valores Story points
+
+  label_agile_interval_size: Tamaño intervalo
+  label_agile_day: DĂ­a
+  label_agile_week: Semana
+  label_agile_month: Mes
+  label_agile_quarter: Trimestre
+  label_agile_year: Año
+  label_cards_search: BĂşsqueda por asunto
+
+  field_agile_sprint: Sprint
+  field_end_date: Fecha fin
+  label_agile_sprints_on: Activar sprints por defecto
+  label_agile_sprint: Sprint
+  label_agile_sprint_plural: Sprints
+  label_agile_sprint_name: Nombre
+  label_agile_sprint_description: DescripciĂłn
+  label_agile_sprint_status: Estado
+  label_agile_sprint_start_date: Fecha inicio
+  label_agile_sprint_end_date: Fecha fin
+  label_agile_sprint_new: nuevo sprint
+  label_agile_sprint_status_open: Abierto
+  label_agile_sprint_status_active: Activo
+  label_agile_sprint_status_closed: Cerrado
+  label_agile_sprint_duration: Duraccion
+  label_agile_sprint_duration_select: <<Seleccionar duraciĂłn>>
+  label_agile_sprint_duration_week_1: 1 semana
+  label_agile_sprint_duration_week_2: 2 semanas
+  label_agile_sprint_duration_week_3: 3 semanas
+  label_agile_sprint_duration_week_4: 4 semanas
+  label_agile_sprint_errors_crossed: Las fechas del Sprint se cruza con otro Sprint
+  label_agile_sprint_errors_end_more_start: La fecha fin debe ser mayor que la fecha de inicio
+  label_agile_sprint_errors_open_issues: las tareas abiertas del Sprint se puede cerrar
+  label_agile_sprint_default_chart: Gráfica
+  label_agile_sprint_chart_units: EstimaciĂłn
+  label_agile_chart_for_sprint: "Para el sprint: %{sprint}"
+
+  label_agile_charts: Gráficas
+  label_agile_chart_burndown: Gráficas Burndown
+  label_agile_chart_burnup: Gráfica Burnup
+  label_agile_chart_units: Unidades
+
+  label_agile_planning_board_more: cargar más...
+  label_agile_version_query_new: Nueva consulta de version  Agile
+  label_agile_version_my_boards: Mis tableros Ágiles
+  label_agile_version_board_plural: Tablero versiĂłn Agil
+  label_agile_version_board_edit: Editar tablero versiĂłn Agil
+
+  label_agile_edit_issue: Editar peticiĂłn
+
+  label_agile_board_type: Tipo de tablero
+  label_agile_board_type_kanban: Tablero Kanban
+  label_agile_board_type_scrum: Tablero Scrum
+  label_agile_board_totals: Totales
+  label_agile_board_totals_story_points: Story points
+  label_agile_board_totals_hours: Horas
+  label_agile_board_totals_spent_time: Tiempo dedicado
+  label_agile_board_totals_percent_done: "% Realizado"
+  label_agile_board_totals_velocity: Velocidad
+  label_agile_board_totals_interval: Intervalo
+  label_agile_board_totals_remaining: Restante
+
+  label_agile_sprint_list_active: Sprints activos
+  label_agile_sprint_list_future: Sprints futuros
+
+  label_agile_mixed_trackers: Tipos mixtos
+  label_agile_moving_average: Moving Average
+
+  label_agile_board_backlog: Backlog
+  label_agile_board_backlog_column: columna Backlog
+  label_agile_board_search_backlog_issues: Buscar peticiones en backlog
+  label_agile_version_plural: Versiones
+  label_agile_sprint_add: Añadir sprint
+  label_agile_version_add: Añadir versión
+
+  label_agile_sprint_query_new: Nueva consulta Sprint ágil
+  label_agile_sprint_my_boards: Mis tableros Sprint ágil
+  label_agile_sprint_board_plural: Tablero Sprint ágil
+  label_agile_sprint_board_edit: Editar tablero Sprint ágil
+  field_sprint: Sprint
+  label_agile_no_sprint_issues: Peticiones sin sprint
+  field_closed_sprints: sprints cerrados
+  field_closed_versions: Versiones cerradas
diff --git a/plugins/redmine_agile/config/locales/pt-BR.yml b/plugins/redmine_agile/config/locales/pt-BR.yml
index 22a783f..5580ab7 100644
--- a/plugins/redmine_agile/config/locales/pt-BR.yml
+++ b/plugins/redmine_agile/config/locales/pt-BR.yml
@@ -1,70 +1,222 @@
-# English strings go here for Rails i18n
+#  Portugues - Brazil strings go here for Rails i18n
 pt-BR:
-  label_agile: Agile
-  label_agile_board: Agile board
-  label_agile_board_plural: Agile boards
+  label_agile: Ágil
+  label_agile_board: Quadro Ágil
+  label_agile_board_plural: Quadros ágeis
   label_agile_board_thumbnails: Thumbnails
-  label_agile_board_more_issues: Mais Tarefas
-  error_agile_status_transition: NĂŁo Ă© possĂ­vel alterar o status da tarefa
-  label_agile_board_new: Novo agile board
-  label_agile_board_edit: Edit agile board
-  label_agile_my_boards: My agile boards
-  label_agile_issue_id: NĂşmero da Tarefa
+  label_agile_board_more_issues: Mais tarefas
+  error_agile_status_transition: NĂŁo Ă© possĂ­vel alterar o status do problema
+  label_agile_board_new: Novo quadro ágil
+  label_agile_board_edit: Editar quadro ágil
+  label_agile_my_boards: Meus quadros ágeis
+  label_agile_issue_id: Id. Tarefa
 
-  permission_manage_public_agile_queries: Manage public agile boards
-  permission_add_agile_queries: Add agile boards
-  permission_view_agile_queries: View agile boards
+  permission_manage_public_agile_queries: Gerenciar quadros ágeis publicos
+  permission_add_agile_queries: Adicionar quadro ágil
+  permission_view_agile_queries: Visualizar quadros ágeis
 
   #1.1.0
-  label_agile_board_default_fields: Default card fields
-  label_agile_charts_issues_burndown: Tarefas burndown
-  label_agile_charts_work_burndown: Trabalho burndown
+  label_agile_board_default_fields: Campos padrĂŁo no post-it
+  label_agile_charts_issues_burndown: Burndow de tarefas
+  label_agile_charts_work_burndown_hours: Burndow de horas
+  label_agile_charts_work_burndown_sp: Burndow por pontos por histĂłrias
   label_agile_charts_number_of_hours: NĂşmero de horas
   label_agile_charts_number_of_issues: NĂşmero de tarefas
-  label_agile_charts_cumulative_flow: AfluĂŞncia 
-  label_agile_charts_trackers_cumulative_flow: AfluĂŞncia de tarefas
+  label_agile_charts_cumulative_flow: Fluxo cumulativo
+  label_agile_charts_trackers_cumulative_flow: Fluxo comulativo por tipo
   label_agile_charts_issues_velocity: Velocidade
+  label_agile_charts_cycle_time: Tempo de ciclo
   label_agile_charts_lead_time: Tempo de espera
   label_agile_charts_average_lead_time: Tempo de espera médio
-  label_agile_chart_plural: Agile charts
-  permission_view_agile_charts: View agile charts
+  label_agile_chart_plural: Gráficos ágeis
+  label_agile_chart_new: Novo gráfico ágil
+  label_agile_chart_edit: Editar gráfico ágil
+  permission_view_agile_charts: Visualizar gráficos ágeis
   label_agile_ideal_work_remaining: Ideal
-  label_agile_actual_work_remaining: Real
+  label_agile_actual_work_remaining: Atual
   label_agile_chart: Gráfico
   label_agile_date_from: De
   label_agile_date_to: Para
-  label_agile_chart_dates: Chart intervals
-  label_agile_weighed_ideal_work_remaining: MĂ©dia ideal
-  label_agile_status_colors: Status colors
-  label_agile_charts_burnup: Tarefas burnup
+  label_agile_chart_dates: Intervalo de gráficos
+  label_agile_weighed_ideal_work_remaining: Ideal ponderado
+  label_agile_status_colors: Cor dos status
+  label_agile_charts_burnup: Burnup tarefas
   label_agile_charts_number_of_days: NĂşmero de dias
   label_agile_too_many_items: "O gráfico não pode ser criado porque excede o número máximo de itens que podem ser exibidos (%{max})"
-  label_agile_time_reports_items_limit: Time entries based charts issues limit
+  label_agile_time_reports_items_limit: Limite de problemas de gráficos baseados em entradas de tempo
   label_agile_total_work_remaining: Total
-  label_agile_default_chart: Version default chart
+  label_agile_default_chart: Gráfico padrão
 
   #1.1.1
   label_agile_charts_average_velocity: Velocidade média
-  label_agile_charts_avarate_number_of_issues: Número médio de tarefas
-  label_agile_charts_avarate_number_of_hours: Número médio de horas
+  label_agile_charts_avarate_number_of_issues: MĂ©dia de nĂşmero de tarefas
+  label_agile_charts_avarate_number_of_hours: MĂ©dia nĂşmero de horas
 
   #1.2.0
-  label_agile_color: Color
-  permission_manage_agile_verions: Manage version planning
+  label_agile_color: Cor
+  permission_manage_agile_verions: Gerencia planejamento da versĂŁo
   label_agile_version_planning: Planejamento da versĂŁo
-  error_agile_version_transition: Can’t change issue version
-  label_agile_tracker_colors: Tracker colors
-  label_agile_issue_priority_colors: Issue priority colors
-  label_agile_color_based_on: Colored by
-  label_agile_color_no_colors: No colors
-  label_agile_manage_colors: Manage colors
-  label_agile_fullscreen: Fullscreen
-  label_agile_no_version_issues: Issues without version
-  label_agile_charts_work_burnup: Work burnup
-  label_agile_completed: ConcluĂ­do
-  label_agile_exclude_weekends: Exclude weekends from ideal effort
-  label_agile_board_truncated: "The board was truncated because it exceeds the maximum number of items that can be displayed (%{max})"
-  label_agile_board_items_limit: Agile board items limit
-  label_agile_swimlanes: Swim lanes
-  label_agile_minimize_closed: Minimize closed issues
-  label_agile_fields: Card fields
\ No newline at end of file
+  error_agile_version_transition: NĂŁo Ă© possĂ­vel alterar a versĂŁo do problema
+  label_agile_tracker_colors: Cores por tipo
+  label_agile_issue_priority_colors: Cores pela prioridade das tarefas
+  label_agile_color_based_on: Colorir por
+  label_agile_color_no_colors: Sem cores
+  label_agile_manage_colors: Gerenciar cores
+  label_agile_fullscreen: Tela cheia
+  label_agile_no_version_issues: Tarefas sem versĂŁo
+  label_agile_charts_work_burnup_hours: Burnup de horas
+  label_agile_charts_work_burnup_sp: Burnup de pontos por histĂłrias
+  label_agile_completed: Completado
+  label_agile_exclude_weekends: Exclua fins de semana do esforço
+  label_agile_board_truncated: "O tabuleiro foi truncado porque excede o número máximo de itens que podem ser exibidos (%{max})"
+  label_agile_board_items_limit: Limites de tarefas no quadro ágil
+  label_agile_swimlanes: Rais do quadro
+  label_agile_minimize_closed: Minimizar itens concluĂ­dos
+  label_agile_fields: Post-its
+  label_agile_default_board: Quadro padrĂŁo
+
+  #1.3.6
+  text_agile_move_not_possible: Esse movimento nĂŁo Ă© possĂ­vel
+
+  #1.3.8
+  label_agile_parent_issue_tracker_id: Tipo parental
+  label_agile_sub_issues: Sub-tarefas
+  label_agile_color_green: Verde
+  label_agile_color_blue: Azul
+  label_agile_color_turquoise: Turquesa
+  label_agile_color_lightgreen: Verde claro
+  label_agile_color_yellow: Amarelo
+  label_agile_color_orange: Laranja
+  label_agile_color_red: Vermelho
+  label_agile_color_purple: Roxo
+  label_agile_color_gray: Cinza
+  label_agile_has_sub_issues: Teve Sub-tarefas
+  label_agile_light_free_version: VersĂŁo gratuita Agile Light
+  label_agile_link_to_pro: Atualize para PRO
+  label_agile_link_to_pro_demo: Demo da versĂŁo PRO
+  label_agile_link_to_more_plugins: Procurar mais plugins no RedmineUP
+  label_agile_button_agree: Aceito
+  label_agile_license: RedmineUP Licensa
+  label_agile_saving_boards: Salvando quadro
+  label_agile_horizontal_swim_lines: Rais horizontais
+  label_agile_board_sub_columns: Quadro sub-colunas
+  label_agile_additional_agile_charts: Quadro ágil adicional
+  label_agile_coloured_issue_cards: Colorir post-its das tarefas
+  label_agile_4_more_features: 4 mais funcionalidades...
+  label_agile_upgrade_to_pro: Atualizar para versĂŁo PRO version para usar estas funcionalidades
+
+  label_agile_day_in_state:  Em status
+
+  #1.3.10
+  label_agile_hide_closed_issues_data: Ocultar dados de tarefas fechadas
+  project_module_agile: Agile
+
+  #1.3.13
+  label_agile_last_comment: Último comentário
+
+  #1.4.0
+  label_agile_esitmate_units: Unidades estimadas
+  label_agile_trackers_for_sp: Tipos por pontos de histĂłrias
+  label_agile_story_points: Pontos por histĂłrias
+  field_story_points: Pontos por histĂłrias
+  label_agile_charts_number_of_story_points: NĂşmero de pontos por histĂłria
+  label_agile_board_columns: Colunas do quadro
+  lable_agile_wip_limit_exceeded: Limite de trabalho excedido
+  label_agile_wip_limit: Limite trabalho WIP
+  label_agile_add_new_issue: '+ Adicionar nova tarefa'
+  label_agile_allow_create_cards: Criação do post-it
+  label_agile_auto_assign_on_move: Adicionar o responsável ao mover
+  text_agile_create_issue_error: Ocorreu um erro na criação da tarefa
+  label_agile_inline_comment: Comentários em linha
+  label_agile_hours: Horas
+  field_color: Cores
+
+  field_duration: Duração
+
+  field_closed_on_trendline: Linha de tendĂŞncia fechada
+  field_created_on_trendline: Criar linha de tendĂŞncia
+
+  label_agile_sp_values: Valor dos pontos por histĂłria
+
+  label_agile_interval_size: Tamanho do intervalo
+  label_agile_day: Dia
+  label_agile_week: Semana
+  label_agile_month: MĂŞs
+  label_agile_quarter: Trimestre
+  label_agile_year: Ano
+  label_cards_search: Pesquisar por tĂ­tulo
+
+  field_agile_sprint: Sprint
+  field_end_date: Encerramento
+  label_agile_sprints_on: Ativar sprints por padrĂŁo
+  label_agile_sprint: Sprint
+  label_agile_sprint_plural: Sprints
+  label_agile_sprint_name: Nome
+  label_agile_sprint_description: Descrição
+  label_agile_sprint_status: Status
+  label_agile_sprint_start_date: InĂ­cio em
+  label_agile_sprint_end_date: TĂ©rmino em
+  label_agile_sprint_new: Nova sprint
+  label_agile_sprint_status_open: Aberta
+  label_agile_sprint_status_active: Ativa
+  label_agile_sprint_status_closed: Fechada
+  label_agile_sprint_duration: Duração
+  label_agile_sprint_duration_select: <<Select duration>>
+  label_agile_sprint_duration_week_1: 1 semana
+  label_agile_sprint_duration_week_2: 2 semanas
+  label_agile_sprint_duration_week_3: 3 semanas
+  label_agile_sprint_duration_week_4: 4 semanas
+  label_agile_sprint_errors_crossed: As datas da sprint sĂŁo cruzadas com outra sprint existente
+  label_agile_sprint_errors_end_more_start: A data de término deve ser posterior à data de início
+  label_agile_sprint_errors_open_issues: Sprint com problemas abertos pode ser fechado
+  label_agile_sprint_default_chart: Gráfico
+  label_agile_sprint_chart_units: Estimativa
+  label_agile_chart_for_sprint: "Para sprint: %{sprint}"
+
+  label_agile_charts: Gráficos
+  label_agile_chart_burndown: Gráfico burndown
+  label_agile_chart_burnup: Gráfico burnup
+  label_agile_chart_units: Unidades
+
+  label_agile_planning_board_more: Carregar mais...
+  label_agile_version_query_new: Consulta de novas versões do Agile
+  label_agile_version_my_boards: Meus quadros ágeis
+  label_agile_version_board_plural: Versões dos quados ágeis
+  label_agile_version_board_edit: Editar versão do quadro ágil
+
+  label_agile_edit_issue: Editar tarefa
+
+  label_agile_board_type: Tipo do quadro
+  label_agile_board_type_kanban: Quadro Kanban
+  label_agile_board_type_scrum: Quadro Scrum
+  label_agile_board_totals: Totais
+  label_agile_board_totals_story_points: Pontos por histĂłria
+  label_agile_board_totals_hours: Horas
+  label_agile_board_totals_spent_time: Tempo gasto
+  label_agile_board_totals_percent_done: Percentual de pronto
+  label_agile_board_totals_velocity: Velocidade
+  label_agile_board_totals_interval: Intervalo
+  label_agile_board_totals_remaining: Remanecente
+
+  label_agile_sprint_list_active: Sprints ativos
+  label_agile_sprint_list_future:  Sprints futuros
+  label_agile_sprint_list_old: Sprints antigos
+
+  label_agile_mixed_trackers: Tipos mistos
+  label_agile_moving_average: MĂ©dia mĂłvel
+
+  label_agile_board_backlog: Backlog
+  label_agile_board_backlog_column: Colunas do Backlog
+  label_agile_board_search_backlog_issues: Pesquisar tarefas do backlog
+  label_agile_version_plural: Vesões
+  label_agile_sprint_add: Adicionar novo Sprint
+  label_agile_version_add: Adicionar VersĂŁo
+
+  label_agile_sprint_query_new: Nova consulta sprint ágil
+  label_agile_sprint_my_boards: Meus quadros de sprint
+  label_agile_sprint_board_plural: Quadros de sprint ágeis
+  label_agile_sprint_board_edit: Editar quadros de sprint
+  field_sprint: Sprint
+  label_agile_no_sprint_issues: Tarefas sem sprint
+  field_closed_sprints: Sprints fechadas
+  field_closed_versions: Versões fechadas
diff --git a/plugins/redmine_agile/config/locales/ru.yml b/plugins/redmine_agile/config/locales/ru.yml
old mode 100755
new mode 100644
index 5a28e54..d4ad02d
--- a/plugins/redmine_agile/config/locales/ru.yml
+++ b/plugins/redmine_agile/config/locales/ru.yml
@@ -24,11 +24,14 @@ ru:
   label_agile_charts_number_of_hours: Кол-во часов
   label_agile_charts_number_of_issues: Кол-во задач
   label_agile_charts_cumulative_flow: Накопительный поток
-  label_agile_charts_trackers_cumulative_flow: Накопительный поток треккеров
+  label_agile_charts_trackers_cumulative_flow: Накопительный поток трекеров
   label_agile_charts_issues_velocity: Скорость закрытия задач
+  label_agile_charts_cycle_time: Цикл задачи
   label_agile_charts_lead_time: Цикл задачи
   label_agile_charts_average_lead_time: Средний цикл задачи
   label_agile_chart_plural: Диаграммы
+  label_agile_chart_new: Новая диаграмма
+  label_agile_chart_edit: Редактировать диаграмму
   permission_view_agile_charts: Просмотр диаграмм
   label_agile_ideal_work_remaining: Идеально
   label_agile_actual_work_remaining: Действительно
@@ -43,7 +46,7 @@ ru:
   label_agile_too_many_items: "Диаграмма не может быть создана потому что превышено максимальное кол-во задач (%{max})"
   label_agile_time_reports_items_limit: Максимальное кол-во задач отображаемых на диаграммах
   label_agile_total_work_remaining: Всего
-  label_agile_default_chart: Диаграмма для версии
+  label_agile_default_chart: Диаграмма по умолчанию
 
   #1.1.1
   label_agile_charts_average_velocity: Средняя скорость закрытия задач
@@ -55,7 +58,7 @@ ru:
   permission_manage_agile_verions: Планирование версий
   label_agile_version_planning: Планирование версий
   error_agile_version_transition: Невозможно изменить версию задачи
-  label_agile_tracker_colors: Цвета треккеров
+  label_agile_tracker_colors: Цвета трекеров
   label_agile_issue_priority_colors: Цвета приоритетов задачи
   label_agile_color_based_on: Цвета на основании
   label_agile_color_no_colors: Без цвета
@@ -77,7 +80,7 @@ ru:
   text_agile_move_not_possible: Данное передвижение невозможно
 
   #1.3.8
-  label_agile_parent_issue_tracker_id: Треккер родителя
+  label_agile_parent_issue_tracker_id: Трекер родителя
   label_agile_sub_issues: Подзадачи
   label_agile_color_green: Зеленый
   label_agile_color_blue: Синий
@@ -130,3 +133,88 @@ ru:
 
   field_duration: Продолжительность
   label_cards_search: Поиск по теме
+
+  field_agile_sprint: Спринт
+  field_end_date: Дата окончания
+  label_agile_sprints_on: Активировать спринты по умолчанию
+  label_agile_sprint: Спринт
+  label_agile_sprint_plural: Спринты
+  label_agile_sprint_name: Название
+  label_agile_sprint_description: Описание
+  label_agile_sprint_status: Статус
+  label_agile_sprint_sharing: Совместное использование
+  label_agile_sprint_start_date: Дата начала
+  label_agile_sprint_end_date: Дата окончания
+  label_agile_sprint_new: Новый спринт
+  label_agile_sprint_status_open: Открытый
+  label_agile_sprint_status_active: Активный
+  label_agile_sprint_status_closed: Закрытый
+  label_agile_sprint_duration: Продолжительность
+  label_agile_sprint_duration_select: <<Выберите>>
+  label_agile_sprint_duration_week_1: 1 неделя
+  label_agile_sprint_duration_week_2: 2 недели
+  label_agile_sprint_duration_week_3: 3 недели
+  label_agile_sprint_duration_week_4: 4 недели
+  label_agile_sprint_errors_crossed: Выбранные даты пересекают другой спринт
+  label_agile_sprint_errors_end_more_start: Дата окончания должна быть больше даты начала
+  label_agile_sprint_errors_open_issues: Нельзя закрыть спринт содержащий открытые задачи
+  label_agile_sprint_default_chart: График
+  label_agile_sprint_chart_units: Единицы
+  label_agile_chart_for_sprint: "Для спринта: %{sprint}"
+
+  label_agile_interval_size: Размер интервала
+  label_agile_day: День
+  label_agile_week: Неделя
+  label_agile_month: Месяц
+  label_agile_quarter: Квартал
+  label_agile_year: Год
+
+  label_agile_charts: Диаграммы
+  label_agile_chart_units: Тип
+
+  label_agile_planning_board_more: Загрузить еще...
+  label_agile_version_query_new: Новый запрос версий
+  label_agile_version_my_boards: Мои доски версий
+  label_agile_version_board_plural: Доски версий
+  label_agile_version_board_edit: Редактировать доску версий
+
+  label_agile_edit_issue: Изменить задачу
+
+
+  label_agile_board_type: Тип доски
+  label_agile_board_type_kanban: Kanban доска
+  label_agile_board_type_scrum: Scrum доска
+  label_agile_board_totals: Итоги
+  label_agile_board_totals_story_points: Story points
+  label_agile_board_totals_hours: Часы
+  label_agile_board_totals_spent_time: Потраченное время
+  label_agile_board_totals_percent_done: Готовность (%)
+  label_agile_board_totals_velocity: Скорость закрытия
+
+  label_agile_sprint_list_active: Активные спринты
+  label_agile_sprint_list_future: Следующие спринты
+  label_agile_sprint_list_old: Прошлые спринты
+
+  label_agile_moving_average: Скользящая средняя
+
+  label_agile_board_backlog: Backlog
+  label_agile_board_backlog_column: Backlog column
+  label_agile_board_search_backlog_issues: Поиск backlog задач
+  label_agile_version_plural: Версии
+  label_agile_sprint_add: Добавить спринт
+  label_agile_version_add: Добавить версию
+
+  label_agile_sprint_query_new: Новый запрос спринтов
+  label_agile_sprint_my_boards: Мои доски спринтов
+  label_agile_sprint_board_plural: Доски спринтов
+  label_agile_sprint_board_edit: Редактировать доску спринтов
+  field_sprint: Спринт
+  label_agile_no_sprint_issues: Задачи без спринта
+  field_closed_sprints: Закрытые спринты
+  field_closed_versions: Закрытые версии
+
+  label_agile_sprint_sharing_none: Без совместного использования
+  label_agile_sprint_sharing_descendants: С подпроектами
+  label_agile_sprint_sharing_hierarchy: С иерархией проектов
+  label_agile_sprint_sharing_tree: С деревом проектов
+  label_agile_sprint_sharing_system: Со всеми проектами
diff --git a/plugins/redmine_agile/config/locales/zh-TW.yml b/plugins/redmine_agile/config/locales/zh-TW.yml
index 821c70b..1751082 100644
--- a/plugins/redmine_agile/config/locales/zh-TW.yml
+++ b/plugins/redmine_agile/config/locales/zh-TW.yml
@@ -3,7 +3,7 @@
 # Author: zhoutt
 # Based on file: en.yml
 
-'zh-TW':
+zh-TW:
   label_agile: 敏捷
   label_agile_board: 敏捷看板
   label_agile_board_plural: 敏捷看板
@@ -89,4 +89,4 @@
   label_agile_color_red: 紅色
   label_agile_color_purple: 紫色
   label_agile_color_gray: 灰色
-  label_agile_has_sub_issues: 子問題
+  label_agile_has_sub_issues: 子問題
\ No newline at end of file
diff --git a/plugins/redmine_agile/config/routes.rb b/plugins/redmine_agile/config/routes.rb
old mode 100755
new mode 100644
index 2a340d4..21dc7b9
--- a/plugins/redmine_agile/config/routes.rb
+++ b/plugins/redmine_agile/config/routes.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -21,28 +21,19 @@
 # See: http://guides.rubyonrails.org/routing.html
 
 resources :projects do
-  resources :agile_queries, :only => [:new, :create]
-  resources :agile_versions, :only => [:index] do
-    post :index, :on => :collection
-  end
-end
-
-resources :agile_versions, :only => [:update, :show] do
-  collection do
-    get 'load'
-    get 'autocomplete'
-  end
+  resources :agile_queries, only: [:new, :create]
 end
 
 resources :issues do
   get "done_ratio", :to => "agile_journal_details#done_ratio"
   get "status", :to => "agile_journal_details#status"
   get "assignee", :to => "agile_journal_details#assignee"
+  member do
+    get "agile_data", :to => "agile_boards#agile_data"
+  end
 end
 
 resources :agile_queries
-get '/agile_colors/:object_type', :to => "agile_colors#index", :as => "agile_colors"
-put '/agile_colors/:object_type', :to => "agile_colors#update", :as => "update_agile_colors"
 
 get '/projects/:project_id/agile/charts', :to => "agile_charts#show", :as => "project_agile_charts"
 get '/agile/charts/', :to => "agile_charts#show", :as => "agile_charts"
diff --git a/plugins/redmine_agile/db/migrate/001_create_issue_status_orders.rb b/plugins/redmine_agile/db/migrate/001_create_issue_status_orders.rb
old mode 100755
new mode 100644
index 24edba7..c345ce3
--- a/plugins/redmine_agile/db/migrate/001_create_issue_status_orders.rb
+++ b/plugins/redmine_agile/db/migrate/001_create_issue_status_orders.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_agile/db/migrate/002_create_agile_colors.rb b/plugins/redmine_agile/db/migrate/002_create_agile_colors.rb
index c835f2b..1e705e8 100644
--- a/plugins/redmine_agile/db/migrate/002_create_agile_colors.rb
+++ b/plugins/redmine_agile/db/migrate/002_create_agile_colors.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_agile/db/migrate/003_rename_issue_status_orders.rb b/plugins/redmine_agile/db/migrate/003_rename_issue_status_orders.rb
index 751c90f..c21e129 100644
--- a/plugins/redmine_agile/db/migrate/003_rename_issue_status_orders.rb
+++ b/plugins/redmine_agile/db/migrate/003_rename_issue_status_orders.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_agile/db/migrate/004_rename_agile_ranks.rb b/plugins/redmine_agile/db/migrate/004_rename_agile_ranks.rb
index 1b8cddf..de7b86f 100644
--- a/plugins/redmine_agile/db/migrate/004_rename_agile_ranks.rb
+++ b/plugins/redmine_agile/db/migrate/004_rename_agile_ranks.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_agile/db/migrate/005_add_story_points_to_agile_ranks.rb b/plugins/redmine_agile/db/migrate/005_add_story_points_to_agile_ranks.rb
index cece10b..16ef93d 100644
--- a/plugins/redmine_agile/db/migrate/005_add_story_points_to_agile_ranks.rb
+++ b/plugins/redmine_agile/db/migrate/005_add_story_points_to_agile_ranks.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_agile/doc/CHANGELOG b/plugins/redmine_agile/doc/CHANGELOG
old mode 100755
new mode 100644
index d769450..35be743
--- a/plugins/redmine_agile/doc/CHANGELOG
+++ b/plugins/redmine_agile/doc/CHANGELOG
@@ -1,9 +1,130 @@
 == Redmine Agile plugin changelog
 
 Redmine Agile plugin - Agile board plugin for redmine
-Copyright (C) 2011-2018 RedmineUP
+Copyright (C) 2011-2021 RedmineUP
 http://www.redmineup.com/
 
+== 2021-05-04 v1.6.1
+
+* Redmine 4.2 support
+* Added assignee group board filter
+* Added past Sprints
+* Added reset sprint on project change
+* Fixed missied table SQL error
+* Fixed empty data error
+* Fixed FCSV warning
+* Dropped Redmine <3.0 support
+
+== 2021-02-15 v1.6.0
+
+* Added Agile Sprint sharing
+* Fixed sprint/story points copy
+* Fixed sprint for on-board created sprints
+* Fixed initial install error
+* Fixed Sprint query save bug
+* Fixed chart dates interval
+* Fixed for all-projects query bug
+* Fixed chart empty dates bug
+* Fixed chart version bug
+* Fixed Backlog load-more bug
+* Fixed Agile board remarks
+* Fixed selected sprint bug
+* Fixed Agile board issues display bug
+* Fixed missed filters for old Redmine versions
+* Fixed empty watcher filter bug
+* Fixed workflow issue creation bug
+* Updated BR locale (Adriano J. Baptistella)
+* Updated pt-BR locale (Adriano J. Baptistella)
+
+== 2020-07-24 v1.5.4
+
+* Added None for Sprint in issue context menu
+* Updated zh-tw locale (Hsiao Chung Chiang)
+* Updated es locale (Manuel Alba Ortega)
+* Fixed agile sprint remaining bug
+* Fixed issue copy error
+* Fixed submit error for issue search field
+* Fixed future data in the charts
+* Fixed error with all closed issues
+* Fixed full screen board update bug
+* Fixed first day for Agile chart
+* Fixed chart query visibility bug
+* Fixed no version column bug
+* Fixed sub project sprint assignment
+* Fixed backlog column saving
+* Fixed "For all projects" chart bug
+
+== 2020-03-18 v1.5.3
+
+* Added grouping for agine status changes view
+* Added issue list sprint filter
+* German translation updated (Peter Schossig)
+* Added Italian translation (Marco Congia)
+* Fixed bug with context menu story points
+* Added validation for sprint open issues
+* Backlog tab default menu
+
+== 2020-02-05 v1.5.2
+
+* Agile Backlog as a different module
+* Fixed between dates chart bug
+* Fixed context menu edit bug
+* Fixed sprint cards load bug
+
+== 2019-12-10 v1.5.1
+
+* Added Sprint view permissions
+* Redmine 4.1 styles support
+* New Story points board setting
+* Hide Sprint field without available sprints
+* Modal edit styles cleanup
+* Fixed sprint charts bug
+* Fixed MySQL order by bug
+
+== 2019-10-22 v1.5.0
+
+* Agile sprints
+* New Backlog tab
+* Board types: Scrum and Kanban
+* New lead time chart
+* Totals for board and swimlanes
+* Added board units
+* Added board default chart
+* Fixed chart caclulation bug
+* Fixed custom field js error
+
+== 2019-06-14 v1.4.12
+
+* Fixed light version Redmine 4.0.3+ bug
+* Fixed swilanes sorting
+* Fixed version planing Load more bug
+
+== 2019-05-15 v1.4.11
+
+* Fixed compatibility issues
+
+== 2019-04-25 v1.4.10
+
+* Fixed light version compatibility bug
+
+== 2019-04-15 v1.4.9
+
+* Redmine 4.0.3 support
+* Agile chart units setting
+* Charts end date fixes
+* Fixed expand all board bug
+* Fixed interval size for old user sessions
+* Fixed charts with current interval
+
+== 2019-02-08 v1.4.8
+
+* Added new Version planner
+* Added saving for Agile charts
+* Fixed "Related to" filter
+* Fixed "Parent task" filter
+* Fixed search sensitive bug
+* Fixed empty current version bug
+
 == 2018-09-25 v1.4.7
 
 * Added missing filters: Issue, Description, Private, Watcher
@@ -33,10 +154,10 @@ http://www.redmineup.com/
 
 == 2017-07-06 v1.4.4
 
-* Redmine 3.4 support 
+* Redmine 3.4 support
 * Color attribute for users
 * Agile board <Current version> query filter
-* Added initional state for status and assignee history 
+* Added initional state for status and assignee history
 * Chinese translation update
 * Fixed checklist items order
 
diff --git a/plugins/redmine_agile/init.rb b/plugins/redmine_agile/init.rb
old mode 100755
new mode 100644
index 53e8ad8..8cf0285
--- a/plugins/redmine_agile/init.rb
+++ b/plugins/redmine_agile/init.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -17,12 +17,17 @@
 # You should have received a copy of the GNU General Public License
 # along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
 
-requires_redmine_crm :version_or_higher => '0.0.32' rescue raise "\n\033[31mRedmine requires newer redmine_crm gem version.\nPlease update with 'bundle update redmine_crm'.\033[0m"
+requires_redmine_crm version_or_higher: '0.0.43' rescue raise "\n\033[31mRedmine requires newer redmine_crm gem version.\nPlease update with 'bundle update redmine_crm'.\033[0m"
 
 require 'redmine'
 
-AGILE_VERSION_NUMBER = '1.4.7'
-AGILE_VERSION_TYPE = 'PRO version'
+AGILE_VERSION_NUMBER = '1.6.1'
+AGILE_VERSION_TYPE = "Light version"
+
+if ActiveRecord::VERSION::MAJOR >= 4 && !defined?(FCSV)
+  require 'csv'
+  FCSV = CSV
+end
 
 Redmine::Plugin.register :redmine_agile do
   name "Redmine Agile plugin (#{AGILE_VERSION_TYPE})"
@@ -32,28 +37,34 @@ Redmine::Plugin.register :redmine_agile do
   url 'http://redmineup.com/pages/plugins/agile'
   author_url 'mailto:support@redmineup.com'
 
-  requires_redmine :version_or_higher => '2.6'
+  requires_redmine version_or_higher: '3.0'
 
-  settings :default => { 'default_columns' => %w(tracker assigned_to) },
-           :partial => 'settings/agile/general'
+  settings default: { 'default_columns' => %w(tracker assigned_to) },
+           partial: 'settings/agile/general'
 
   menu :application_menu, :agile,
-       { :controller => 'agile_boards', :action => 'index' },
-       :caption => :label_agile,
-       :if => Proc.new { User.current.allowed_to?(:view_agile_queries, nil, :global => true) }
-  menu :project_menu, :agile, {:controller => 'agile_boards', :action => 'index' },
-                              :caption => :label_agile,
-                              :after => :gantt,
-                              :param => :project_id
+       { controller: 'agile_boards', action: 'index' },
+       caption: :label_agile,
+       if: Proc.new { User.current.allowed_to?(:view_agile_queries, nil, global: true) }
+  menu :project_menu, :agile, { controller: 'agile_boards', action: 'index' }, caption: :label_agile,
+                                                                               after: :gantt,
+                                                                               param: :project_id
 
-  menu :admin_menu, :agile, {:controller => 'settings', :action => 'plugin', :id => "redmine_agile"}, :caption => :label_agile, :html => {:class => 'icon'}
+  menu :admin_menu, :agile, { controller: 'settings', action: 'plugin', id: 'redmine_agile' }, caption: :label_agile, html: { class: 'icon' }
 
   project_module :agile do
-    permission :manage_public_agile_queries, {:agile_queries => [:new, :create, :edit, :update, :destroy]}, :require => :member
-    permission :manage_agile_verions, {:agile_versions => [:index, :update]}
-    permission :add_agile_queries, {:agile_queries => [:new, :create, :edit, :update, :destroy]}, :require => :loggedin
-    permission :view_agile_queries, {:agile_boards => [:index, :create_issue], :agile_queries => :index}, :read => true
-    permission :view_agile_charts, {:agile_charts => [:show, :render_chart, :select_version_chart]}, :read => true
+    permission :manage_public_agile_queries, { agile_queries: [:new, :create, :edit, :update, :destroy] }, require: :member
+    permission :add_agile_queries, { agile_queries: [:new, :create, :edit, :update, :destroy] }, require: :loggedin
+    permission :view_agile_queries, { agile_boards: [:index,
+                                                     :update,
+                                                     :create_issue,
+                                                     :issue_tooltip,
+                                                     :inline_comment,
+                                                     :agile_data,
+                                                     :backlog_load_more,
+                                                     :backlog_autocomplete],
+                                      agile_queries: :index }, read: true
+    permission :view_agile_charts, { agile_charts: [:show, :render_chart, :select_version_chart] }, read: true
   end
 end
 
diff --git a/plugins/redmine_agile/lib/acts_as_colored/init.rb b/plugins/redmine_agile/lib/acts_as_colored/init.rb
deleted file mode 100644
index 3ff28d4..0000000
--- a/plugins/redmine_agile/lib/acts_as_colored/init.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-require File.dirname(__FILE__) + '/lib/acts_as_colored'
-ActiveRecord::Base.send(:include, RedmineAgile::Acts::Colored)
diff --git a/plugins/redmine_agile/lib/acts_as_colored/lib/acts_as_colored.rb b/plugins/redmine_agile/lib/acts_as_colored/lib/acts_as_colored.rb
deleted file mode 100644
index f8701bb..0000000
--- a/plugins/redmine_agile/lib/acts_as_colored/lib/acts_as_colored.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  module Acts
-    module Colored
-      def self.included(base)
-        base.extend ClassMethods
-      end
-
-      module ClassMethods
-        def acts_as_colored(_options = {})
-          return if included_modules.include?(RedmineAgile::Acts::Colored::InstanceMethods)
-          send :include, RedmineAgile::Acts::Colored::InstanceMethods
-
-          class_eval do
-            has_one :agile_color, :as => :container, :dependent => :destroy
-            delegate :color, :to => :agile_color, :allow_nil => true
-
-            accepts_nested_attributes_for :agile_color, :reject_if => :reject_color, :allow_destroy => true
-
-            alias_method :agile_color_without_default, :agile_color
-            alias_method :agile_color, :agile_color_with_default
-          end
-        end
-      end
-
-      module InstanceMethods
-        def self.included(base)
-          base.extend ClassMethods
-        end
-
-        def reject_color(attributes)
-          exists = attributes['id'].present?
-          empty = attributes[:color].blank?
-          attributes[:_destroy] = 1 if exists && empty
-          !exists && empty
-        end
-
-        def color=(value)
-          agile_color.color = value
-        end
-
-        def agile_color_with_default
-          agile_color_without_default || build_agile_color
-        end
-
-        module ClassMethods
-        end
-      end
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile.rb b/plugins/redmine_agile/lib/redmine_agile.rb
old mode 100755
new mode 100644
index 300f6a0..9e292d4
--- a/plugins/redmine_agile/lib/redmine_agile.rb
+++ b/plugins/redmine_agile/lib/redmine_agile.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -17,51 +17,31 @@
 # You should have received a copy of the GNU General Public License
 # along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
 
-require 'redmine_agile/patches/compatibility/application_controller_patch' if Rails::VERSION::MAJOR < 4
-require 'acts_as_colored/init'
+
 
 require 'redmine_agile/hooks/views_layouts_hook'
 require 'redmine_agile/hooks/views_issues_hook'
 require 'redmine_agile/hooks/views_versions_hook'
 require 'redmine_agile/hooks/controller_issue_hook'
-require 'redmine_agile/hooks/helper_issues_hook'
 require 'redmine_agile/patches/issue_patch'
 
-require 'redmine_agile/patches/compatibility_patch'
-
 require 'redmine_agile/helpers/agile_helper'
 
 require 'redmine_agile/charts/agile_chart'
 require 'redmine_agile/charts/burndown_chart'
 require 'redmine_agile/charts/work_burndown_chart'
-require 'redmine_agile/charts/velocity_chart'
-require 'redmine_agile/charts/cumulative_flow_chart'
-require 'redmine_agile/charts/trackers_cumulative_flow_chart'
-require 'redmine_agile/charts/burnup_chart'
-require 'redmine_agile/charts/work_burnup_chart'
-require 'redmine_agile/charts/lead_time_chart'
-require 'redmine_agile/charts/average_lead_time_chart'
-
-require 'redmine_agile/patches/issue_priority_patch'
-require 'redmine_agile/patches/issue_query_patch'
-require 'redmine_agile/patches/tracker_patch'
-require 'redmine_agile/patches/project_patch'
-require 'redmine_agile/hooks/views_context_menus_hook'
-require 'redmine_agile/hooks/views_projects_form_hook'
-
-require 'redmine_agile/utils/header_tree'
-
-require 'redmine_agile/patches/user_patch'
-require 'redmine_agile/hooks/views_users_form_hook'
-require 'redmine_agile/patches/queries_controller_patch' if Redmine::VERSION.to_s >= '3.4'
+require 'redmine_agile/charts/charts'
+require 'redmine_agile/patches/issue_drop_patch'
 
 module RedmineAgile
 
   ISSUES_PER_COLUMN = 10
   TIME_REPORTS_ITEMS = 1000
   BOARD_ITEMS = 500
-  ESTIMATE_UNITS = ['hours', 'story_points']
-  COLOR_BASE = ['issue', 'tracker', 'priority', 'spent_time', 'user', 'project']
+
+  ESTIMATE_HOURS        = 'hours'.freeze
+  ESTIMATE_STORY_POINTS = 'story_points'.freeze
+  ESTIMATE_UNITS        = [ESTIMATE_HOURS, ESTIMATE_STORY_POINTS].freeze
 
   class << self
     def time_reports_items_limit
@@ -84,7 +64,7 @@ module RedmineAgile
     end
 
     def default_chart
-      Setting.plugin_redmine_agile['default_chart'] || 'issues_burndown'
+      Setting.plugin_redmine_agile['default_chart'] || Charts::BURNDOWN_CHART
     end
 
     def estimate_units
@@ -92,7 +72,11 @@ module RedmineAgile
     end
 
     def use_story_points?
-      estimate_units == 'story_points'
+      if Setting.plugin_redmine_agile.key?('story_points_on')
+        Setting.plugin_redmine_agile['story_points_on'] == '1'
+      else
+        estimate_units == ESTIMATE_STORY_POINTS
+      end
     end
 
     def trackers_for_sp
@@ -100,18 +84,18 @@ module RedmineAgile
     end
 
     def use_story_points_for?(tracker)
-      return true if trackers_for_sp.blank?
+      return true if trackers_for_sp.blank? && use_story_points?
       tracker = tracker.is_a?(Tracker) ? tracker.id.to_s : tracker
-      trackers_for_sp == tracker
+      trackers_for_sp == tracker && use_story_points?
     end
 
     def use_colors?
-      COLOR_BASE.include?(color_base)
-                end
+      false
+          end
 
     def color_base
-      Setting.plugin_redmine_agile['color_on'] || 'none'
-                end
+      "none"
+          end
 
     def minimize_closed?
       Setting.plugin_redmine_agile['minimize_closed'].to_i > 0
@@ -124,19 +108,10 @@ module RedmineAgile
     def auto_assign_on_move?
       Setting.plugin_redmine_agile['auto_assign_on_move'].to_i > 0
     end
-    def color_prefix
-      'bk'
-    end
-
-    COLOR_BASE.each do |cb|
-      define_method :"#{cb}_colors?" do
-        color_base == cb
-      end
-    end
 
     def status_colors?
-      Setting.plugin_redmine_agile['status_colors'].to_i > 0
-                end
+      false
+          end
 
     def hide_closed_issues_data?
       Setting.plugin_redmine_agile['hide_closed_issues_data'].to_i > 0
@@ -147,15 +122,12 @@ module RedmineAgile
     end
 
     def allow_create_card?
-      Setting.plugin_redmine_agile['allow_create_card'].to_i > 0
-          end
+      false
+    end
 
     def allow_inline_comments?
       Setting.plugin_redmine_agile['allow_inline_comments'].to_i > 0
     end
-    def sp_values
-      Setting.plugin_redmine_agile['sp_values'].to_s.split(',').map{|x| x.strip.to_i}.uniq.delete_if{|x| x == 0}
-    end
   end
 
 end
diff --git a/plugins/redmine_agile/lib/redmine_agile/charts/agile_chart.rb b/plugins/redmine_agile/lib/redmine_agile/charts/agile_chart.rb
index a8df261..f152dc3 100644
--- a/plugins/redmine_agile/lib/redmine_agile/charts/agile_chart.rb
+++ b/plugins/redmine_agile/lib/redmine_agile/charts/agile_chart.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -22,22 +22,32 @@ module RedmineAgile
     include Redmine::I18n
     include Redmine::Utils::DateCalculation
 
+    DAY_INTERVAL     = 'day'.freeze
+    WEEK_INTERVAL    = 'week'.freeze
+    MONTH_INTERVAL   = 'month'.freeze
+    QUARTER_INTERVAL = 'quarter'.freeze
+    YEAR_INTERVAL    = 'year'.freeze
+
+    TIME_INTERVALS = [DAY_INTERVAL, WEEK_INTERVAL, MONTH_INTERVAL, QUARTER_INTERVAL, YEAR_INTERVAL].freeze
+
     attr_reader :line_colors
 
-    def initialize(data_scope, options={})
+    def initialize(data_scope, options = {})
+      @options = options
       @data_scope = data_scope
       @data_from ||= options[:data_from]
       @data_to ||= options[:data_to]
-      @period_count, @scale_division = chart_periods
+      @interval_size = options[:interval_size] || DAY_INTERVAL
+      initialize_chart_periods
       @step_x_labels = @period_count > 18 ? @period_count / 12 + 1 : 1
       @fields = chart_fields_by_period
       @weekend_periods = weekend_periods
-      @estimated_unit = options[:estimated_unit] || 'hours'
+      @estimated_unit = options[:estimated_unit] || ESTIMATE_HOURS
       @line_colors = {}
     end
 
     def data
-      { :title => '', :y_title => '', :labels => [], :datasets => [] }
+      { title: '', y_title: '', labels: [], datasets: [] }
     end
 
     def self.data(data_scope, options = {})
@@ -47,17 +57,22 @@ module RedmineAgile
     protected
 
     def current_date_period
-      date_period = (@date_to <= Date.today ? @period_count : (@period_count - (@date_to - Date.today).to_i / @scale_division - 1) + 1).round
+      return @current_date_period if @current_date_period
+
+      date_period = (@date_to <= Date.today || @options[:date_to].present? ? @period_count : (@period_count - (@date_to - Date.today).to_i / @scale_division) + 1).round
       @current_date_period ||= date_period > 0 ? date_period : 0
     end
 
     def due_date_period
+      @date_from = @date_from.to_date
+      @date_to = @date_to.to_date
       due_date = (@due_date && @due_date > @date_from) ? @due_date : @date_from
-      @due_date_period ||= (@due_date ? @period_count - (@date_to - due_date).to_i / @scale_division - 1 : @period_count - 1) + 1
+      @due_date_period ||= (@due_date ? @period_count - (@date_to - due_date.to_date).to_i : @period_count - 1) + 1
+      @due_date_period = @due_date_period > 0 ? @due_date_period : 1
     end
 
     def date_short_period?
-      (@date_to - @date_from).to_i <= 31
+      (@date_to.to_date - @date_from.to_date).to_i <= 31
     end
 
     def date_effort(issues, effort_date)
@@ -127,25 +142,24 @@ module RedmineAgile
       color = options[:color] || [rand(255), rand(255), rand(255)].join(',')
       dataset_color = "rgba(#{color}, 1)"
       {
-        :type => (options[:type] || 'line'),
-        :data => dataset_data,
-        :label => label,
-        :fill => (options[:fill] || false),
-        :backgroundColor => "rgba(#{color}, 0.2)",
-        :borderColor => dataset_color,
-        :borderDash => (options[:dashed] ? [5, 5] : []),
-        :borderWidth => (options[:dashed] ? 1.5 : 2),
-        :pointRadius => (options[:nopoints] ? 0 : 3),
-        :pointBackgroundColor => dataset_color
+        type: (options[:type] || 'line'),
+        data: dataset_data,
+        label: label,
+        fill: (options[:fill] || false),
+        backgroundColor: "rgba(#{color}, 0.2)",
+        borderColor: dataset_color,
+        borderDash: (options[:dashed] ? [5, 5] : []),
+        borderWidth: (options[:dashed] ? 1.5 : 2),
+        pointRadius: (options[:nopoints] ? 0 : 3),
+        pointBackgroundColor: dataset_color,
+        tooltips: { enable: false }
       }
     end
 
-    def chart_periods
-      raise "Dates can't be blank" if [@date_to, @date_from].any?(&:blank?)
-      period_count = (@date_to.to_date + 1 - @date_from.to_date).to_i
-      scale_division = period_count > 31 ? period_count / 31.0 : 1
-
-      [(period_count / scale_division).round, scale_division]
+    def initialize_chart_periods
+      raise Exception "Dates can't be blank" if [@date_to, @date_from].any?(&:blank?)
+      period_count
+      scale_division
     end
 
     def issues_count_by_period(issues_scope)
@@ -174,16 +188,17 @@ module RedmineAgile
     end
 
     def chart_fields_by_period
-      chart_dates_by_period.map do |d|
-        if @scale_division >= 365
-          d.year
-        elsif @scale_division >= 13
-          month_abbr_name(d.at_beginning_of_week.to_time.month) + ' ' + d.at_beginning_of_week.to_time.year.to_s
-        elsif @scale_division >= 7
-          d.at_beginning_of_week.to_time.day.to_s + ' ' + month_name(d.at_beginning_of_week.to_time.month)
-        else
-          d.to_time.day.to_s + ' ' + month_name(d.to_time.month)
-        end
+      chart_dates_by_period.map { |d| chart_field_by_date(d) }
+    end
+
+    def chart_field_by_date(date)
+      case @interval_size
+      when YEAR_INTERVAL
+        date.year
+      when QUARTER_INTERVAL, MONTH_INTERVAL
+        month_abbr_name(date.month) + ' ' + date.year.to_s
+      else
+        date.day.to_s + ' ' + month_name(date.month)
       end
     end
 
@@ -206,13 +221,12 @@ module RedmineAgile
     end
 
     def chart_dates_by_period
-      @chart_dates_by_period ||= @period_count.times.inject([]) do |accum, m|
+      return @chart_dates_by_period if @chart_dates_by_period
+
+      period = period_count > 1 ? period_count - 1 : period_count
+      @chart_dates_by_period ||= period.times.inject([]) do |accum, m|
         period_date = ((@date_to.to_date - 1 - m * @scale_division) + 1)
-        accum << if m == 0 || m == @period_count - 1
-                   period_date.to_date
-                 elsif @scale_division >= 13
-                   period_date.at_beginning_of_week.to_date
-                 elsif @scale_division >= 7
+        accum << if @interval_size == WEEK_INTERVAL
                    period_date.at_beginning_of_week.to_date
                  else
                    period_date.to_date
@@ -248,5 +262,17 @@ module RedmineAgile
     def predict(x, slope, intercept)
       slope * x + intercept
     end
+
+    def period_count
+      @period_count ||= ((@date_to.to_time - @date_from.to_time) / time_divider).round + 1
+    end
+
+    def scale_division
+      @scale_division ||= time_divider / 1.day
+    end
+
+    def time_divider
+      @interval_size == QUARTER_INTERVAL ? 3.months : 1.send(@interval_size)
+    end
   end
 end
diff --git a/plugins/redmine_agile/lib/redmine_agile/charts/average_lead_time_chart.rb b/plugins/redmine_agile/lib/redmine_agile/charts/average_lead_time_chart.rb
deleted file mode 100644
index a49e160..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/charts/average_lead_time_chart.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  class AverageLeadTimeChart < LeadTimeChart
-    def initialize(data_scope, options = {})
-      @date_from = (options[:date_from] || data_scope.minimum("#{Issue.table_name}.created_on")).to_date
-      @date_to = options[:date_to] || Date.today
-      @average_lead_time = !!options[:average_lead_time]
-      super data_scope, options
-      @line_colors = { :closed => '247,175,125' }
-    end
-
-    def data
-      chart_data = average_lead_time_data
-      datasets = []
-      datasets << dataset(chart_data, l(:field_closed_on), :color => line_colors[:closed]) if chart_data.any?
-
-      {
-        :title    => l(:label_agile_charts_lead_time),
-        :y_title  => l(:label_agile_charts_number_of_days),
-        :labels   => @fields,
-        :datasets => datasets
-      }
-    end
-
-    private
-
-    def average_lead_time_data
-      lead_time_by_date = closed_issues.map { |c| { :closed_on => c.closed_on, :lead_time => (c.closed_on.to_time - c.created_on.to_time).to_f / (60 * 60 * 24) } }
-      lead_time_by_period = [0] * @period_count
-      lead_time_by_date.each do |c|
-        next if c[:closed_on].to_date > @date_to.to_date
-        period_num = (@date_to.to_date - c[:closed_on].to_date).to_i / @scale_division
-        if lead_time_by_period[period_num]
-          lead_time_by_period[period_num] = c[:lead_time] unless lead_time_by_period[period_num].to_i > 0
-          lead_time_by_period[period_num] = ((lead_time_by_period[period_num].to_f + c[:lead_time]).to_f / 2).round(2)
-        end
-      end
-      lead_time_by_period.reverse!
-
-      prev_lead_time = lead_time_by_period[0]
-      lead_time_by_period.each_with_index do |c, index|
-        lead_time_by_period[index] = c == 0 ? prev_lead_time : ((c + prev_lead_time).to_f / 2).round(2)
-        prev_lead_time = lead_time_by_period[index]
-      end
-
-      lead_time_by_period
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/charts/burndown_chart.rb b/plugins/redmine_agile/lib/redmine_agile/charts/burndown_chart.rb
index 5d23a67..6a21a57 100644
--- a/plugins/redmine_agile/lib/redmine_agile/charts/burndown_chart.rb
+++ b/plugins/redmine_agile/lib/redmine_agile/charts/burndown_chart.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -21,13 +21,15 @@ module RedmineAgile
   class BurndownChart < AgileChart
     attr_accessor :burndown_data, :cumulative_burndown_data
 
-    def initialize(data_scope, options={})
+    def initialize(data_scope, options = {})
       @date_from = options[:date_from] && options[:date_from].to_date ||
                    [data_scope.minimum("#{Issue.table_name}.created_on"),
                     data_scope.minimum("#{Issue.table_name}.start_date")].compact.map(&:to_date).min
+
       @date_to = options[:date_to] && options[:date_to].to_date ||
                  [options[:due_date],
                   data_scope.maximum("#{Issue.table_name}.updated_on")].compact.map(&:to_date).max
+
       @due_date = options[:due_date].to_date if options[:due_date]
       @show_ideal_effort = options[:date_from] && options[:date_to]
 
@@ -56,10 +58,21 @@ module RedmineAgile
         :title    => @graph_title,
         :y_title  => @y_title,
         :labels   => @fields,
-        :datasets => datasets
+        :datasets => datasets,
+        :show_tooltips => [0, 2]
       }
     end
 
+    def self.data(data_scope, options = {})
+      if options[:chart_unit] == Charts::UNIT_HOURS
+        WorkBurndownChart.new(data_scope, options.merge(estimated_unit: ESTIMATE_HOURS)).data
+      elsif options[:chart_unit] == Charts::UNIT_STORY_POINTS
+        WorkBurndownChart.new(data_scope, options.merge(estimated_unit: ESTIMATE_STORY_POINTS)).data
+      else
+        super
+      end
+    end
+
     protected
 
     def ideal_effort(start_remaining)
diff --git a/plugins/redmine_agile/lib/redmine_agile/charts/burnup_chart.rb b/plugins/redmine_agile/lib/redmine_agile/charts/burnup_chart.rb
deleted file mode 100644
index 31a03b1..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/charts/burnup_chart.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  class BurnupChart < AgileChart
-    def initialize(data_scope, options = {})
-      @date_from = (options[:date_from] || data_scope.minimum("#{Issue.table_name}.created_on")).to_date
-      @date_to = (options[:date_to] || Date.today).to_date
-      @due_date = options[:due_date].to_date if options[:due_date]
-
-      super data_scope, options
-
-      @fields = [''] + @fields
-      @y_title = l(:label_agile_charts_number_of_issues)
-      @graph_title = l(:label_agile_charts_burnup)
-      @line_colors = { :created => '102,102,102', :closed => '80,122,170', :ideal => '102,102,102' }
-    end
-
-    def data
-      return false unless calculate_data.any?
-
-      datasets = [
-        dataset(@cumulative_data, l(:field_created_on),:fill => true, :color => line_colors[:created]),
-        dataset(@data, l(:field_closed_on), :fill => true, :color => line_colors[:closed]),
-        dataset(ideal_effort(@data.first, @cumulative_data.last), l(:label_agile_ideal_work_remaining), :color => line_colors[:ideal], :dashed => true, :nopoints => true)
-      ]
-
-      {
-        :title    => @graph_title,
-        :y_title  => @y_title,
-        :labels   => @fields,
-        :datasets => datasets
-      }
-    end
-
-    protected
-
-    def ideal_effort(start_data, end_data)
-      data = [0] * (due_date_period - 1)
-      active_periods = (RedmineAgile.exclude_weekends? && date_short_period?) ? due_date_period - @weekend_periods.select { |p| p < due_date_period }.count : due_date_period
-      avg_remaining_velocity = (end_data - start_data).to_f / active_periods.to_f
-      sum = start_data.to_f
-      data[0] = sum
-      (1..due_date_period - 1).each do |i|
-        sum += avg_remaining_velocity unless (RedmineAgile.exclude_weekends? && date_short_period?) && @weekend_periods.include?(i - 1)
-        data[i] = (sum * 100).round / 100.0
-      end
-      data[due_date_period] = end_data
-      data
-    end
-
-    def calculate_data
-      created_by_period = issues_count_by_period(scope_by_created_date)
-      closed_by_period = issues_count_by_period(scope_by_closed_date)
-
-      total_issues = @data_scope.where("#{Issue.table_name}.created_on < ?", @date_from).count
-      total_closed = @data_scope.open(false).where("#{Issue.table_name}.closed_on < ?", @date_from).count
-
-      sum = total_issues
-      @cumulative_data = [total_issues] + created_by_period.first(current_date_period).map { |x| sum += x }
-      sum = total_closed
-      @data = [total_closed] + closed_by_period.first(current_date_period).map { |x| sum += x }
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/charts/cumulative_flow_chart.rb b/plugins/redmine_agile/lib/redmine_agile/charts/cumulative_flow_chart.rb
deleted file mode 100644
index 3029f46..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/charts/cumulative_flow_chart.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  class CumulativeFlowChart < AgileChart
-    def initialize(data_scope, options = {})
-      @date_from = (options[:date_from] || data_scope.minimum("#{Issue.table_name}.created_on")).to_date
-      @date_to = options[:date_to] || Date.today
-      super data_scope, options
-      @line_colors = { 0 => '255,204,0', 1 => '0,153,0', 2 => '80,122,170', 3 => '102,102,102', 4 => '154,167,208', 5 => '224,112,235',
-                       6 => '235,17,142', 7 => '167,40,6', 8 => '108,97,58', 9 => '33,147,155', 10 => '43,237,59' }
-    end
-
-    def data
-      datasets = []
-      all_issues = @data_scope.eager_load(:journals => { :details => :journal })
-      data = chart_dates_by_period.map do |date|
-        issues = all_issues.select { |issue| issue.created_on.localtime.to_date <= date }
-        issues.inject({}) do |accum, issue|
-          status_details = issue.journals.map(&:details).flatten.select { |detail| 'status_id' == detail.prop_key }.sort_by { |a| a.journal.created_on }
-          details_today_or_earlier = status_details.select { |a| a.journal.created_on.to_date <= date }
-          last_status_change = details_today_or_earlier.last
-
-          status = if last_status_change
-                     last_status_change.value.to_i
-                   elsif status_details.size > 0
-                     status_details.first.old_value.to_i
-                   else
-                     issue.status_id
-                   end
-
-          accum[status] = accum[status].to_i + 1
-          accum
-        end
-      end
-
-      IssueStatus.where(:id => data.map(&:keys).flatten.uniq).sorted.reverse.each_with_index do |status, index|
-        datasets << dataset(data.map { |d| d[status.id].to_i }, status.name, :color => line_colors[index], :fill => true, :nopoints => true) unless data.empty?
-      end
-
-      {
-        :title    => l(:label_agile_charts_cumulative_flow),
-        :y_title  => l(:label_agile_charts_number_of_issues),
-        :labels   => @fields,
-        :stacked  => true,
-        :datasets => datasets
-      }
-    end
-
-    private
-
-    def available_statuses
-      @available_statuses ||= IssueStatus.find(@data_scope.group("#{Issue.table_name}.status_id").count.keys).map
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/charts/lead_time_chart.rb b/plugins/redmine_agile/lib/redmine_agile/charts/lead_time_chart.rb
deleted file mode 100644
index dc7bc72..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/charts/lead_time_chart.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  class LeadTimeChart < AgileChart
-    def initialize(data_scope, options = {})
-      @date_from = (options[:date_from] || data_scope.minimum("#{Issue.table_name}.created_on")).to_date
-      @date_to = options[:date_to] || Date.today
-      @average_lead_time = !!options[:average_lead_time]
-      super data_scope, options
-      @line_colors = { :closed => '102,102,102' }
-    end
-
-    def data
-      chart_data = lead_time_data
-      datasets = []
-      if chart_data.any?
-        datasets << dataset(chart_data, l(:field_closed_on), :type => 'bar', :fill => true, :color => line_colors[:closed])
-        datasets << dataset(trendline(chart_data), l(:field_closed_on_trendline), :nopoints => true, :dashed => true, :color => line_colors[:closed])
-      end
-      {
-        :title    => l(:label_agile_charts_lead_time),
-        :y_title  => l(:label_agile_charts_number_of_days),
-        :labels   => @fields,
-        :datasets => datasets
-      }
-    end
-
-    private
-
-    def lead_time_data
-      lead_time_by_date = closed_issues.map { |c| { :closed_on => c.closed_on.localtime, :lead_time => (c.closed_on.to_time.localtime - c.created_on.localtime.to_time).to_f / (60 * 60 * 24) } }
-      lead_time_arr_by_period = {}
-      lead_time_by_date.each do |c|
-        next if c[:closed_on].to_date > @date_to.to_date
-        period_num = ((@date_to.to_date - c[:closed_on].localtime.to_date).to_i / @scale_division).to_i
-        lead_time_arr_by_period[period_num] = [] if lead_time_arr_by_period[period_num].blank?
-        lead_time_arr_by_period[period_num] << c[:lead_time]
-      end
-
-      lead_time_by_period = [0] * @period_count
-      (0..@period_count - 1).each do |period_num|
-        next if lead_time_arr_by_period[period_num].blank?
-        arr = lead_time_arr_by_period[period_num]
-        len = arr.length
-        half_len = len / 2
-        sorted = arr.sort
-        median = len % 2 == 1 ? sorted[half_len] : (sorted[half_len - 1] + sorted[half_len]).to_f / 2
-        lead_time_by_period[period_num] = median.round(2)
-      end
-      lead_time_by_period.reverse!
-    end
-
-    def closed_issues
-      @closed_issues ||= @data_scope.open(false).where("#{Issue.table_name}.closed_on IS NOT NULL").
-                         where("#{Issue.table_name}.closed_on >= ?", @date_from).
-                         where("#{Issue.table_name}.closed_on < ?", @date_to + 1).
-                         where("#{Issue.table_name}.created_on IS NOT NULL")
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/charts/trackers_cumulative_flow_chart.rb b/plugins/redmine_agile/lib/redmine_agile/charts/trackers_cumulative_flow_chart.rb
deleted file mode 100644
index 2b26388..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/charts/trackers_cumulative_flow_chart.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  class TrackersCumulativeFlowChart < AgileChart
-    def initialize(data_scope, options={})
-      @date_from = (options[:date_from] || data_scope.minimum("#{Issue.table_name}.created_on")).to_date
-      @date_to = options[:date_to] || Date.today
-      super data_scope, options
-      @line_colors = { 0 => '0,153,0', 1 => '80,122,170', 2 => '102,102,102', 3 => '255,204,0', 4 => '154,167,208', 5 => '224,112,235',
-                       6 => '235,17,142', 7 => '167,40,6', 8 => '108,97,58', 9 => '33,147,155', 10 => '43,237,59' }
-    end
-
-    def data
-      datasets = []
-      Tracker.where(:id => @data_scope.group("#{Issue.table_name}.tracker_id").count.keys).sorted.reverse.each_with_index do |tracker, index|
-        created_by_date = @data_scope.where(:tracker_id => tracker.id).
-                          where("#{Issue.table_name}.created_on >= ?", @date_from).
-                          where("#{Issue.table_name}.created_on <= ?", @date_to).
-                          where("#{Issue.table_name}.created_on IS NOT NULL").
-                          group("#{Issue.table_name}.created_on").
-                          count
-        created_by_period = issues_count_by_period(created_by_date)
-        total_issues = @data_scope.where(:tracker_id => tracker.id).
-                       where("#{Issue.table_name}.created_on < ?", @date_from).count
-        cumulative_created_by_period = created_by_period.map { |x| total_issues += x }
-
-        datasets << dataset(cumulative_created_by_period, tracker.name, :color => line_colors[index], :fill => true, :nopoints => true) if created_by_period.any?
-      end
-
-      {
-        :title    => l(:label_agile_charts_cumulative_flow),
-        :y_title  => l(:label_agile_charts_number_of_issues),
-        :stacked  => true,
-        :labels   => @fields,
-        :datasets => datasets
-      }
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/charts/velocity_chart.rb b/plugins/redmine_agile/lib/redmine_agile/charts/velocity_chart.rb
deleted file mode 100644
index f21688b..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/charts/velocity_chart.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  class VelocityChart < AgileChart
-    def initialize(data_scope, options = {})
-      @date_from = (options[:date_from] || data_scope.minimum("#{Issue.table_name}.created_on")).to_date
-      @date_to = options[:date_to] || Date.today
-      super data_scope, options
-      @line_colors = { :closed => '102,102,102', :created => '80,122,170' }
-    end
-
-    def data
-      created_by_period = issues_avg_count_by_period(scope_by_created_date)
-      closed_by_period = issues_avg_count_by_period(scope_by_closed_date)
-
-      if @scale_division > 1
-        y_title = l(:label_agile_charts_avarate_number_of_issues)
-        graph_title = l(:label_agile_charts_average_velocity)
-      else
-        y_title = l(:label_agile_charts_number_of_issues)
-        graph_title = l(:label_agile_charts_issues_velocity)
-      end
-
-      datasets = []
-      if closed_by_period.any?
-        datasets << dataset(closed_by_period, l(:field_closed_on), :type => 'bar', :fill => true, :color => line_colors[:closed])
-        datasets << dataset(trendline(closed_by_period), l(:field_closed_on_trendline), :nopoints => true, :dashed => true, :color => line_colors[:closed])
-      end
-
-      if created_by_period.any?
-        datasets << dataset(created_by_period, l(:field_created_on), :type => 'bar', :fill => true, :color => line_colors[:created])
-        datasets << dataset(trendline(created_by_period), l(:field_created_on_trendline), :nopoints => true, :dashed => true, :color => line_colors[:created])
-      end
-
-      {
-        :title    => graph_title,
-        :y_title  => y_title,
-        :labels   => @fields,
-        :datasets => datasets
-      }
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/charts/work_burndown_chart.rb b/plugins/redmine_agile/lib/redmine_agile/charts/work_burndown_chart.rb
index 27408d4..0c8019e 100644
--- a/plugins/redmine_agile/lib/redmine_agile/charts/work_burndown_chart.rb
+++ b/plugins/redmine_agile/lib/redmine_agile/charts/work_burndown_chart.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -29,7 +29,7 @@ module RedmineAgile
         @graph_title = l(:label_agile_charts_work_burndown_sp)
       end
 
-      @line_colors = { :work => '0,153,0', :ideal => '102,102,102', :total => '0,153,0' }
+      @line_colors = { work: '0,153,0', ideal: '102,102,102', total: '0,153,0' }
     end
 
     protected
@@ -40,11 +40,11 @@ module RedmineAgile
 
       if @estimated_unit == 'hours'
         all_issues = data_scope.where("#{Issue.table_name}.estimated_hours IS NOT NULL").
-                     eager_load([:journals, :status, { :journals => { :details => :journal } }])
+                     eager_load([:journals, :status, { journals: { details: :journal } }])
         cumulative_total_hours = data_scope.sum("#{Issue.table_name}.estimated_hours").to_f
       else
         all_issues = data_scope.where("#{AgileData.table_name}.story_points IS NOT NULL").
-                     joins(:agile_data).eager_load([:journals, :status, { :journals => { :details => :journal } }])
+                     joins(:agile_data).eager_load([:journals, :status, { journals: { details: :journal } }])
         cumulative_total_hours = data_scope.joins(:agile_data).sum("#{AgileData.table_name}.story_points").to_f
       end
 
@@ -53,7 +53,8 @@ module RedmineAgile
         total_hours_left, cumulative_total_hours_left = date_effort(issues, date)
         [total_hours_left, cumulative_total_hours - cumulative_total_hours_left]
       end
-      data = first_period_effort(all_issues, chart_dates_by_period.first, cumulative_total_hours) + data
+      tail_values = data.last ? [data.last] * (current_date_period - data.size) : []
+      data = first_period_effort(all_issues, chart_dates_by_period.first, cumulative_total_hours) + data + tail_values
       @burndown_data, @cumulative_burndown_data = data.transpose
     end
 
diff --git a/plugins/redmine_agile/lib/redmine_agile/charts/work_burnup_chart.rb b/plugins/redmine_agile/lib/redmine_agile/charts/work_burnup_chart.rb
deleted file mode 100644
index b72262f..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/charts/work_burnup_chart.rb
+++ /dev/null
@@ -1,112 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  class WorkBurnupChart < BurnupChart
-    def initialize(data_scope, options = {})
-      super data_scope, options
-      if @estimated_unit == 'hours'
-        @y_title = l(:label_agile_charts_number_of_hours)
-        @graph_title = l(:label_agile_charts_work_burnup_hours)
-      else
-        @y_title = l(:label_agile_charts_number_of_story_points)
-        @graph_title = l(:label_agile_charts_work_burnup_sp)
-      end
-      @line_colors = { :created => '102,102,102', :closed => '0,153,0', :ideal => '102,102,102' }
-    end
-
-    protected
-
-    def calculate_data
-      data_scope = @data_scope
-      data_scope = data_scope.where("#{Issue.table_name}.rgt - #{Issue.table_name}.lft = 1") if use_subissue_done_ratio && @estimated_unit == 'hours'
-      if @estimated_unit == 'hours'
-        all_issues = data_scope.where("#{Issue.table_name}.estimated_hours IS NOT NULL").
-                     eager_load([:journals, :status, { :journals => { :details => :journal } }])
-        @cumulative_data = cumulative_hours_by_period(data_scope)
-      else
-        all_issues = data_scope.where("#{AgileData.table_name}.story_points IS NOT NULL").
-                     joins(:agile_data).eager_load([:journals, :status, { :journals => { :details => :journal } }])
-        @cumulative_data = cumulative_story_points_by_period(data_scope)
-      end
-
-      data = chart_dates_by_period.select { |d| d <= Date.today }.map do |date|
-        issues = all_issues.select do |issue|
-          issue.created_on.localtime.to_date <= date
-        end
-        cumulative_total_hours_left, total_hours_done = date_effort(issues, date)[1..2]
-        total_hours_done
-      end
-      @data = [first_period_effort(all_issues, chart_dates_by_period.first)[0][2]] + data
-      @cumulative_data = @cumulative_data.first(@data.count) if @cumulative_data.count > @data.count
-      @data
-    end
-
-    private
-
-    def cumulative_hours_by_period(data_scope)
-      data = [0] * chart_dates_by_period.count
-      data_scope.
-        where("#{Issue.table_name}.created_on >= ?", @date_from).
-        where("#{Issue.table_name}.created_on < ?", @date_to.to_date + 1).
-        where("#{Issue.table_name}.created_on IS NOT NULL").
-        group("#{Issue.table_name}.created_on").
-        sum(:estimated_hours).each do |c|
-          next if c.first.localtime.to_date > @date_to.to_date
-          period_num = ((@date_to.to_date - c.first.localtime.to_date).to_i / @scale_division).to_i
-          data[period_num] += c.last unless data[period_num].blank?
-        end
-
-      total_estimated_hours = data_scope.where("#{Issue.table_name}.created_on < ?", @date_from).sum(:estimated_hours)
-      first_date_estimated_hours = data_scope.where("#{Issue.table_name}.created_on < ?", @date_from).sum(:estimated_hours)
-      ([first_date_estimated_hours] + data.reverse.map { |x| total_estimated_hours += x })
-    end
-
-    def cumulative_story_points_by_period(data_scope)
-      data = [0] * @period_count
-      data_scope.
-        where("#{Issue.table_name}.created_on >= ?", @date_from).
-        where("#{Issue.table_name}.created_on < ?", @date_to.to_date + 1).
-        where("#{Issue.table_name}.created_on IS NOT NULL").
-        group("#{Issue.table_name}.created_on").
-        joins(:agile_data).
-        sum("#{AgileData.table_name}.story_points").each do |c|
-          next if c.first.localtime.to_date > @date_to.to_date
-          period_num = ((@date_to.to_date - c.first.localtime.to_date).to_i / @scale_division).to_i
-          data[period_num] += c.last.to_i unless data[period_num].blank?
-        end
-
-      total_estimated_hours = data_scope.where("#{Issue.table_name}.created_on < ?", @date_from).
-                              joins(:agile_data).
-                              sum("#{AgileData.table_name}.story_points").to_i
-      first_date_estimated_hours = data_scope.where("#{Issue.table_name}.created_on < ?", @date_from).
-                                   joins(:agile_data).
-                                   sum("#{AgileData.table_name}.story_points").to_i
-      [first_date_estimated_hours] +  data.reverse.map { |x| total_estimated_hours += x }
-    end
-
-    def first_period_effort(issues_scope, start_date)
-      issues = issues_scope.select do |issue|
-        issue.created_on.localtime.to_date <= start_date
-      end
-      total_left, cumulative_left, total_done = date_effort(issues, start_date - 1)
-      [[total_left, cumulative_left, total_done]]
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/helpers/agile_helper.rb b/plugins/redmine_agile/lib/redmine_agile/helpers/agile_helper.rb
index 9b77fbd..cdc4be9 100644
--- a/plugins/redmine_agile/lib/redmine_agile/helpers/agile_helper.rb
+++ b/plugins/redmine_agile/lib/redmine_agile/helpers/agile_helper.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -51,76 +51,27 @@ module RedmineAgile
         session[:agile_query] = {:id => @query.id, :project_id => @query.project_id}
         sort_clear
       elsif api_request? || params[:set_filter] || session[:agile_query].nil? || session[:agile_query][:project_id] != (@project ? @project.id : nil)
-        @query = AgileQuery.default_query(@project) || AgileQuery.default_query unless params[:set_filter]
         unless @query
           @query = AgileQuery.new(:name => "_", :project => @project)
           @query.build_from_params(params)
         else
           @query.project = @project if @project
         end
-        save_qeury_attribures_to_session(@query)
+        save_query_attribures_to_session(@query)
       else
         # retrieve from session
         @query = nil
         if session[:agile_query] && !session[:agile_query][:id] && !params[:project_id]
           @query = AgileQuery.new(get_query_attributes_from_session)
         end
-        @query ||= AgileQuery.default_query(@project) || AgileQuery.default_query
 
         @query ||= AgileQuery.find_by_id(session[:agile_query][:id]) if session[:agile_query][:id]
         @query ||= AgileQuery.new(get_query_attributes_from_session)
         @query.project = @project
-        save_qeury_attribures_to_session(@query)
+        save_query_attribures_to_session(@query)
       end
     end
 
-    def retrieve_versions_query
-      if api_request? || params[:set_filter] || session[:versions_query].nil? || session[:versions_query][:project_id] != (@project ? @project.id : nil)
-        @query = AgileVersionsQuery.new(:name => "_")
-        @query.project = @project if @project
-        @query.build_from_params(params)
-
-        session[:versions_query] = {:project_id => @query.project_id,
-                                 :filters => @query.filters}
-      else
-        @query = AgileVersionsQuery.new(:name => "_", :filters => session[:versions_query][:filters])
-        @query.project = @project if @project
-      end
-                            end
-    def agile_query_links(title, queries)
-      return '' if queries.empty?
-      # links to #index on issues/show
-      url_params = {:controller => 'agile_boards', :action => 'index', :project_id => @project}
-
-      content_tag('h3', title) + "\n" +
-        content_tag('ul',
-          queries.collect {|query|
-              css = 'query'
-              css << ' selected' if query == @query
-              content_tag('li', link_to(query.name, url_params.merge(:query_id => query), :class => css))
-            }.join("\n").html_safe,
-          :class => 'queries'
-        ) + "\n"
-    end
-
-
-    def sidebar_agile_queries
-      unless @sidebar_agile_queries
-        @sidebar_agile_queries = AgileQuery.visible.
-          order("#{Query.table_name}.name ASC").
-          # Project specific queries and global queries
-          where(@project.nil? ? ["project_id IS NULL"] : ["project_id IS NULL OR project_id = ?", @project.id]).
-          all
-      end
-      @sidebar_agile_queries
-    end
-
-    def render_sidebar_agile_queries
-      out = ''.html_safe
-      out << agile_query_links(l(:label_agile_my_boards), sidebar_agile_queries.select {|q| !q.is_public?})
-      out << agile_query_links(l(:label_agile_board_plural), sidebar_agile_queries.reject {|q| !q.is_public?})
-      out
-    end
     def options_card_colors_for_select(selected, options={})
       color_base = [[l(:label_agile_color_no_colors), "none"],
         [l(:label_issue), "issue"],
@@ -128,56 +79,74 @@ module RedmineAgile
         [l(:field_priority), "priority"],
         [l(:label_spent_time), "spent_time"],
         [l(:field_assigned_to), "user"]]
-      if (@project && @project.children.any?) || !@project
-        color_base << [l(:field_project), 'project']
+      color_base << [l(:field_project), 'project'] if (@project && @project.children.any?) || !@project
+      options_for_select(color_base.compact, selected)
+    end
+
+    def options_charts_for_select(selected)
+      container = []
+      RedmineAgile::Charts::AGILE_CHARTS.each { |k, v| container << [l(v[:name]), k] }
+      selected_chart = RedmineAgile::Charts.chart_by_alias(selected) || selected
+      options_for_select(container, selected_chart)
+    end
+
+    def grouped_options_charts_for_select(selected)
+      grouped_options = {}
+      container = []
+
+      RedmineAgile::Charts::AGILE_CHARTS.each do |chart, value|
+        if RedmineAgile::Charts::CHARTS_WITH_UNITS.include?(chart)
+          group = l(value[:name])
+          grouped_options[group] = []
+          value[:aliases].each do |alias_name|
+            grouped_options[group] << ["#{group} (#{l(RedmineAgile::Charts.chart_unit_label_by(alias_name))})", alias_name]
+          end
+        else
+          container << [l(value[:name]), chart]
+        end
       end
-      options_for_select(color_base.compact,
-        selected)
+
+      grouped_options_for_select(grouped_options, selected) + options_for_select(container, selected)
     end
 
-    def options_charts_for_select(selected, options={})
-      options_for_select([[l(:label_agile_charts_issues_burndown), "issues_burndown"],
-        [l(:label_agile_charts_work_burndown_sp), "work_burndown_sp"],
-        [l(:label_agile_charts_work_burndown_hours), "work_burndown_hours"],
-        [l(:label_agile_charts_burnup), "burnup"],
-        [l(:label_agile_charts_work_burnup_sp), "work_burnup_sp"],
-        [l(:label_agile_charts_work_burnup_hours), "work_burnup_hours"],
-        [l(:label_agile_charts_cumulative_flow), "cumulative_flow"],
-        [l(:label_agile_charts_issues_velocity), "issues_velocity"],
-        [l(:label_agile_charts_lead_time), "lead_time"],
-        [l(:label_agile_charts_average_lead_time), "average_lead_time"],
-        [l(:label_agile_charts_trackers_cumulative_flow), "trackers_cumulative_flow"],
-        nil].compact,
-        selected)
+    def options_chart_units_for_select(selected = nil)
+      container = []
+      RedmineAgile::Charts::CHART_UNITS.each { |k, v| container << [l(v), k] }
+      selected_unit = RedmineAgile::Charts::CHART_UNIT_BY_ALIAS[selected] || selected
+      options_for_select(container, selected_unit)
     end
 
     def render_agile_chart(chart_name, issues_scope)
-      render :partial => "agile_charts/chart", :locals => {:chart => chart_name, :issues_scope => issues_scope}
+      render partial: "agile_charts/chart",
+             locals: { chart: chart_name, issues_scope: issues_scope, chart_unit: params[:chart_unit] }
+    end
+
+    def upgrade_to_pro_agile_chart_link(chart_name, query = @query, current_chart = @chart)
+      link_to l("label_agile_charts_#{chart_name}"), '#',
+              onclick: "showModal('upgrade-to-pro', '557px');",
+              class: ('selected' if query.is_a?(AgileChartsQuery) && query.new_record? && current_chart == chart_name)
     end
 
     private
 
     def get_query_attributes_from_session
-      attributes = {
-        :name => "_",
-        :filters => session[:agile_query][:filters],
-        :group_by => session[:agile_query][:group_by],
-        :column_names => session[:agile_query][:column_names],
-        :color_base => session[:agile_query][:color_base]
-      }
+      attributes = { name: '_',
+                     filters: session[:agile_query][:filters],
+                     group_by: session[:agile_query][:group_by],
+                     column_names: session[:agile_query][:column_names],
+                     color_base: session[:agile_query][:color_base] }
       (attributes[:options] = session[:agile_query][:options] || {}) if Redmine::VERSION.to_s > '2.4'
       attributes
     end
 
-    def save_qeury_attribures_to_session(query)
-      session[:agile_query] = {:project_id => query.project_id,
-                                 :filters => query.filters,
-                                 :group_by => query.group_by,
-                                 :color_base => (query.respond_to?(:color_base) && query.color_base),
-                                 :column_names => query.column_names}
+    def save_query_attribures_to_session(query)
+      session[:agile_query] = { project_id: query.project_id,
+                                filters: query.filters,
+                                group_by: query.group_by,
+                                color_base: (query.respond_to?(:color_base) && query.color_base),
+                                column_names: query.column_names }
       (session[:agile_query][:options] = query.options) if Redmine::VERSION.to_s > '2.4'
     end
-
   end
 end
 
diff --git a/plugins/redmine_agile/lib/redmine_agile/hooks/controller_issue_hook.rb b/plugins/redmine_agile/lib/redmine_agile/hooks/controller_issue_hook.rb
index 1905c47..06cc964 100644
--- a/plugins/redmine_agile/lib/redmine_agile/hooks/controller_issue_hook.rb
+++ b/plugins/redmine_agile/lib/redmine_agile/hooks/controller_issue_hook.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -22,18 +22,19 @@ module RedmineAgile
     class ControllerIssueHook < Redmine::Hook::ViewListener
 
       def controller_issues_edit_before_save(context={})
+        add_agile_journal_details(context)
+      end
+
+      def controller_issues_bulk_edit_before_save(context={})
+        add_agile_journal_details(context)
+      end
+
+      private
+
+      def add_agile_journal_details(context)
         return false unless context[:issue].project.module_enabled?(:agile)
         # return false unless context[:issue].color
-        old_value = Issue.find(context[:issue].id)
-        old_issue_color = old_value.color.to_s
-        new_issue_color = context[:issue].color.to_s
-
-        if new_issue_color && !((new_issue_color == old_issue_color) || context[:issue].current_journal.blank?)
-          context[:issue].current_journal.details << JournalDetail.new(:property => 'attr',
-                                                                       :prop_key => 'color',
-                                                                       :old_value => old_issue_color,
-                                                                       :value => new_issue_color)
-        end
+        old_value = Issue.where(id: context[:issue].id).first || context[:issue]
         # save changes for story points to journal
         old_sp = old_value.story_points
         new_sp = context[:issue].story_points
diff --git a/plugins/redmine_agile/lib/redmine_agile/hooks/helper_issues_hook.rb b/plugins/redmine_agile/lib/redmine_agile/hooks/helper_issues_hook.rb
deleted file mode 100644
index 6057ba7..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/hooks/helper_issues_hook.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  module Hooks
-    class HelperIssuesHook < Redmine::Hook::ViewListener
-
-      def helper_issues_show_detail_after_setting(context={})
-        if context[:detail].prop_key == 'color'
-          detail = context[:detail]
-          context[:detail].value = detail.value.blank? ? nil : l(("label_agile_color_" + detail.value.to_s).to_sym)
-          context[:detail].old_value = detail.old_value.blank? ? nil : l(("label_agile_color_" + detail.old_value.to_s).to_sym)
-        end
-      end
-
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/hooks/views_context_menus_hook.rb b/plugins/redmine_agile/lib/redmine_agile/hooks/views_context_menus_hook.rb
deleted file mode 100644
index b701bd4..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/hooks/views_context_menus_hook.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  module Hooks
-    class ViewsContextMenuesHook < Redmine::Hook::ViewListener
-      render_on :view_issues_context_menu_end, :partial => "context_menus/agile_colors"
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/hooks/views_issues_hook.rb b/plugins/redmine_agile/lib/redmine_agile/hooks/views_issues_hook.rb
old mode 100755
new mode 100644
index dab96ad..d4d7ab2
--- a/plugins/redmine_agile/lib/redmine_agile/hooks/views_issues_hook.rb
+++ b/plugins/redmine_agile/lib/redmine_agile/hooks/views_issues_hook.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -20,16 +20,12 @@
 module RedmineAgile
   module Hooks
     class ViewsIssuesHook < Redmine::Hook::ViewListener
-      def view_issues_sidebar_issues_bottom(context={})
-        context[:controller].send(:render_to_string, {
-          :partial => 'agile_boards/issues_sidebar',
-          :locals => context }) +
-        context[:controller].send(:render_to_string, {
-          :partial => "agile_charts/agile_charts",
-          :locals => context })
+      def view_issues_sidebar_issues_bottom(context = {})
+        context[:controller].send(:render_to_string, partial: 'agile_charts/agile_charts', locals: context)
       end
-      render_on :view_issues_form_details_bottom, :partial => "issues/agile_data_fields"
-      render_on :view_issues_show_details_bottom, :partial => "issues/issue_story_points"
+
+      render_on :view_issues_form_details_bottom, :partial => 'issues/agile_data_fields'
+      render_on :view_issues_show_details_bottom, :partial => 'issues/agile_data_labels'
     end
   end
 end
diff --git a/plugins/redmine_agile/lib/redmine_agile/hooks/views_layouts_hook.rb b/plugins/redmine_agile/lib/redmine_agile/hooks/views_layouts_hook.rb
index 0ac141b..ef723bc 100644
--- a/plugins/redmine_agile/lib/redmine_agile/hooks/views_layouts_hook.rb
+++ b/plugins/redmine_agile/lib/redmine_agile/hooks/views_layouts_hook.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -21,7 +21,7 @@ module RedmineAgile
   module Hooks
     class ViewsLayoutsHook < Redmine::Hook::ViewListener
       def view_layouts_base_html_head(context={})
-        return stylesheet_link_tag(:redmine_agile, :plugin => 'redmine_agile') 
+        return stylesheet_link_tag(:redmine_agile, :plugin => 'redmine_agile')
       end
     end
   end
diff --git a/plugins/redmine_agile/lib/redmine_agile/hooks/views_projects_form_hook.rb b/plugins/redmine_agile/lib/redmine_agile/hooks/views_projects_form_hook.rb
deleted file mode 100644
index 94864ce..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/hooks/views_projects_form_hook.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  module Hooks
-    class ViewsProjectsForm < Redmine::Hook::ViewListener
-      render_on :view_projects_form, :partial => "projects/project_color_form"
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/hooks/views_users_form_hook.rb b/plugins/redmine_agile/lib/redmine_agile/hooks/views_users_form_hook.rb
deleted file mode 100644
index 42d228c..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/hooks/views_users_form_hook.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  module Hooks
-    class ViewsUsersHook < Redmine::Hook::ViewListener
-      preferences_hook = Redmine::VERSION.to_s > '2.5' ? :view_users_form_preferences : :view_users_form
-      render_on preferences_hook, :partial => 'users/user_color_form'
-    end
-  end
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/hooks/views_versions_hook.rb b/plugins/redmine_agile/lib/redmine_agile/hooks/views_versions_hook.rb
index deae5de..67af1e1 100644
--- a/plugins/redmine_agile/lib/redmine_agile/hooks/views_versions_hook.rb
+++ b/plugins/redmine_agile/lib/redmine_agile/hooks/views_versions_hook.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_agile/lib/redmine_agile/patches/compatibility/application_controller_patch.rb b/plugins/redmine_agile/lib/redmine_agile/patches/compatibility/application_controller_patch.rb
deleted file mode 100644
index 633e1a4..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/patches/compatibility/application_controller_patch.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  module Patches
-    module ApplicationControllerPatch
-      def self.included(base) # :nodoc:
-        base.extend(ClassMethods)
-        base.class_eval do
-          unloadable # Send unloadable so it will not be unloaded in development
-        end
-      end
-
-      module ClassMethods
-        def before_action(*filters, &block)
-          before_filter(*filters, &block)
-        end
-      end
-    end
-  end
-end
-
-unless ApplicationController.included_modules.include?(RedmineAgile::Patches::ApplicationControllerPatch)
-  ApplicationController.send(:include, RedmineAgile::Patches::ApplicationControllerPatch)
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/patches/compatibility_patch.rb b/plugins/redmine_agile/lib/redmine_agile/patches/compatibility_patch.rb
deleted file mode 100644
index e4b67d3..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/patches/compatibility_patch.rb
+++ /dev/null
@@ -1,351 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-class AgileQuery < Query
-  unloadable
-
-  VISIBILITY_PRIVATE = 0
-  VISIBILITY_ROLES   = 1
-  VISIBILITY_PUBLIC  = 2
-
-  attr_reader :truncated
-
-  self.queried_class = Issue
-
-  self.available_columns = [
-    QueryColumn.new(:id, :sortable => "#{Issue.table_name}.id", :default_order => 'desc', :caption => :label_agile_issue_id),
-    QueryColumn.new(:project, :groupable => "#{Issue.table_name}.project_id"),
-    QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
-    QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
-    QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio"),
-    QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true),
-    QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement("users")}, :groupable => true),
-    QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => "#{Issue.table_name}.category_id"),
-    QueryColumn.new(:fixed_version, :sortable => lambda {Version.fields_for_order_statement}, :groupable => true),
-    QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
-    QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
-    QueryColumn.new(:thumbnails, :caption => :label_agile_board_thumbnails),
-    QueryColumn.new(:description),
-    QueryColumn.new(:parent, :groupable => "#{Issue.table_name}.parent_id", :caption => :field_parent_issue),
-    QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => "#{Issue.table_name}.assigned_to_id")
-  ]
-
-  scope :visible, lambda {|*args|
-    user = args.shift || User.current
-    base = Project.allowed_to_condition(user, :view_issues, *args)
-    user_id = user.logged? ? user.id : 0
-
-    eager_load(:project).where("(#{table_name}.project_id IS NULL OR (#{base})) AND (#{table_name}.is_public = ? OR #{table_name}.user_id = ?)", true, user_id)
-  }
-
-  def initialize(attributes=nil, *args)
-    attributes.delete(:color_base) if attributes
-    super attributes
-    self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
-    @truncated = false
-  end
-
-  def card_columns
-    self.inline_columns.select{|c| !%w(tracker thumbnails description assigned_to done_ratio spent_hours estimated_hours project id day_in_state last_comment story_points).include?(c.name.to_s)}
-  end
-
-  def visible?(user=User.current)
-    (project.nil? || user.allowed_to?(:view_issues, project)) && (self.is_public? || self.user_id == user.id)
-  end
-
-  def is_private?
-    visibility == VISIBILITY_PRIVATE
-  end
-
-  def is_public?
-    !is_private?
-  end
-
-  def self.default_query(project=nil)
-    false
-  end
-
-  def visibility=(value)
-    self.is_public = value == VISIBILITY_PUBLIC
-  end
-
-  def visibility
-    self.is_public ? VISIBILITY_PUBLIC : VISIBILITY_PRIVATE
-  end
-
-  def build_from_params(params)
-    if params[:fields] || params[:f]
-      self.filters = {}
-      add_filters(params[:fields] || params[:f], params[:operators] || params[:op], params[:values] || params[:v])
-    else
-      available_filters.keys.each do |field|
-        add_short_filter(field, params[field]) if params[field]
-      end
-    end
-    self.group_by = params[:group_by] || (params[:query] && params[:query][:group_by])
-    self.column_names = params[:c] || (params[:query] && params[:query][:column_names])
-    self
-  end
-
-  # Builds a new query from the given params and attributes
-  def self.build_from_params(params, attributes={})
-    new(attributes).build_from_params(params)
-  end
-
-  def initialize_available_filters
-    principals = []
-    subprojects = []
-    versions = []
-    categories = []
-    issue_custom_fields = []
-
-    if project
-      principals += project.principals.sort
-      unless project.leaf?
-        subprojects = project.descendants.visible.all
-        principals += Principal.member_of(subprojects)
-      end
-      versions = project.shared_versions.all
-      categories = project.issue_categories.all
-      issue_custom_fields = project.all_issue_custom_fields
-    else
-      if all_projects.any?
-        principals += Principal.member_of(all_projects)
-      end
-      versions = Version.visible.where(:sharing => 'system').all
-      issue_custom_fields = IssueCustomField.where(:is_for_all => true)
-    end
-    principals.uniq!
-    principals.sort!
-    users = principals.select {|p| p.is_a?(User)}
-
-    add_available_filter "status_id",
-      :type => :list_status, :values => IssueStatus.sorted.collect{|s| [s.name, s.id.to_s] }
-
-    if project.nil?
-      project_values = []
-      if User.current.logged? && User.current.memberships.any?
-        project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"]
-      end
-      project_values += all_projects_values
-      add_available_filter("project_id",
-        :type => :list, :values => project_values
-      ) unless project_values.empty?
-    end
-
-    add_available_filter "tracker_id",
-      :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] }
-    add_available_filter "priority_id",
-      :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
-
-    author_values = []
-    author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
-    author_values += users.collect{|s| [s.name, s.id.to_s] }
-    add_available_filter("author_id",
-      :type => :list, :values => author_values
-    ) unless author_values.empty?
-
-    assigned_to_values = []
-    assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
-    assigned_to_values += (Setting.issue_group_assignment? ?
-                              principals : users).collect{|s| [s.name, s.id.to_s] }
-    add_available_filter("assigned_to_id",
-      :type => :list_optional, :values => assigned_to_values
-    ) unless assigned_to_values.empty?
-
-    if versions.any?
-      add_available_filter "fixed_version_id",
-        :type => :list_optional,
-        :values => versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] }
-    end
-
-    if categories.any?
-      add_available_filter "category_id",
-        :type => :list_optional,
-        :values => categories.collect{|s| [s.name, s.id.to_s] }
-    end
-
-    add_available_filter "subject", :type => :text
-    add_available_filter "created_on", :type => :date_past
-    add_available_filter "updated_on", :type => :date_past
-    add_available_filter "closed_on", :type => :date_past
-    add_available_filter "start_date", :type => :date
-    add_available_filter "due_date", :type => :date
-    add_available_filter "estimated_hours", :type => :float
-    add_available_filter "done_ratio", :type => :integer
-
-    if subprojects.any?
-      add_available_filter "subproject_id",
-        :type => :list_subprojects,
-        :values => subprojects.collect{|s| [s.name, s.id.to_s] }
-    end
-
-    add_custom_fields_filters(issue_custom_fields)
-
-    add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
-
-    Tracker.disabled_core_fields(trackers).each {|field|
-      delete_available_filter field
-    }
-  end
-
-  def available_columns
-    return @available_columns if @available_columns
-    @available_columns = self.class.available_columns.dup
-    @available_columns += (project ?
-                            project.all_issue_custom_fields :
-                            IssueCustomField.all
-                           ).collect {|cf| QueryCustomFieldColumn.new(cf) }
-
-    if User.current.allowed_to?(:view_time_entries, project, :global => true)
-      index = nil
-      @available_columns.each_with_index {|column, i| index = i if column.name == :estimated_hours}
-      index = (index ? index + 1 : -1)
-      # insert the column after estimated_hours or at the end
-      @available_columns.insert index, QueryColumn.new(:spent_hours,
-        :sortable => "COALESCE((SELECT SUM(hours) FROM #{TimeEntry.table_name} WHERE #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id), 0)",
-        :default_order => 'desc',
-        :caption => :label_spent_time
-      )
-    end
-
-    disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')}
-    @available_columns.reject! {|column|
-      disabled_fields.include?(column.name.to_s)
-    }
-
-    @available_columns.reject! {|column| column.name == :done_ratio} unless Issue.use_field_for_done_ratio?
-
-    @available_columns
-  end
-
-  def editable_by?(user)
-    return false unless user
-    # Admin can edit them all and regular users can edit their private queries
-    return true if user.admin? || (is_private? && self.user_id == user.id)
-    # Members can not edit public queries that are for all project (only admin is allowed to)
-    is_public? && !@is_for_all && user.allowed_to?(:manage_public_agile_queries, project, global: true)
-  end
-
-  def default_columns_names
-    @default_columns_names = RedmineAgile.default_columns.map(&:to_sym)
-  end
-
-  def has_column_name?(name)
-    columns.detect{|c| c.name == name}
-  end
-
-  def groupable_columns
-    available_columns.select {|c| c.groupable && !c.is_a?(QueryCustomFieldColumn)}
-  end
-
-  def issues(options={})
-    order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?)
-
-    scope = issue_scope.
-      joins(:status, :project).
-      includes((options[:include] || []).uniq).
-      where(options[:conditions]).
-      order(order_option).
-      joins(joins_for_order_statement(order_option.join(','))).
-      limit(options[:limit]).
-      offset(options[:offset])
-
-    scope = scope.preload(:custom_values)
-    if has_column?(:author)
-      scope = scope.preload(:author)
-    end
-
-    # if has_column?(:spent_hours)
-    #   Issue.load_visible_spent_hours(issues)
-    # end
-
-    scope
-  rescue ::ActiveRecord::StatementInvalid => e
-    raise StatementInvalid.new(e.message)
-  end
-
-  # for compatibility with upper versions
-  def issue_last_comment(issue_id, options = {})
-    nil
-  end
-
-  def journals_for_state
-    nil
-  end
-
-  def board_statuses
-    status_filter_operator = filters.fetch("status_id", {}).fetch(:operator, nil)
-    status_filter_values = filters.fetch("status_id", {}).fetch(:values, [])
-    statuses = IssueStatus.where(:id => Tracker.includes(:issues => [:status, :project, :fixed_version]).where(statement).map(&:issue_statuses).flatten.uniq.map(&:id))
-    result_statuses = case status_filter_operator
-    when "o"
-      statuses.where(:is_closed => false).sorted
-    when "c"
-      statuses.where(:is_closed => true).sorted
-    when "="
-      statuses.where(:id => status_filter_values).sorted
-    when "!"
-      statuses.where("#{IssueStatus.table_name}.id NOT IN (" + status_filter_values.map{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")").sorted
-    else
-      statuses.sorted
-    end
-    result_statuses.map do |s|
-      s.instance_variable_set "@issue_count", self.issue_count_by_status[s.id]
-      s
-    end
-  end
-  def swimlanes
-    return [] unless self.grouped?
-    lane_ids = issue_scope.group(self.group_by_column.groupable).count.keys
-    lanes = Issue.reflect_on_association(self.group_by_column.name).klass.where(:id => lane_ids).reorder(group_by_sort_order)
-    lanes << nil if lane_ids.include?(nil)
-    lanes
-  end
-
-  def issue_count_by_swimlane
-    @issue_count_by_swimlane ||= issue_scope.group("#{Issue.table_name}.#{self.group_by_column.name}_id").count if self.grouped?
-  end
-
-  def issue_count_by_status
-    @issue_count_by_status ||= issue_scope.group("#{Issue.table_name}.status_id").count
-  end
-
-  def issue_board(options={})
-    @truncated = RedmineAgile.board_items_limit <= issue_scope.count
-    all_issues = self.issues.limit(RedmineAgile.board_items_limit).sorted_by_rank
-    grouped_issues = grouped? ? all_issues.group_by{|i| [i.status_id, i.send("#{self.group_by_column.name}_id")]} : all_issues.group_by{|i| [i.status_id]}
-    grouped_issues.values.each{|x|x.sort!{|a,b| a.position.to_i <=> b.position.to_i}} if grouped?
-    grouped_issues
-          end
-
-private
-  def issue_scope
-    Issue.visible.
-      includes(:status,
-               :project,
-               :assigned_to,
-               :tracker,
-               :priority,
-               :category,
-               :fixed_version).
-      where(statement)
-  end
-
-end if Redmine::VERSION.to_s < '2.4'
diff --git a/plugins/redmine_agile/lib/redmine_agile/patches/issue_patch.rb b/plugins/redmine_agile/lib/redmine_agile/patches/issue_patch.rb
old mode 100755
new mode 100644
index 3161f86..8caca54
--- a/plugins/redmine_agile/lib/redmine_agile/patches/issue_patch.rb
+++ b/plugins/redmine_agile/lib/redmine_agile/patches/issue_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -31,15 +31,8 @@ module RedmineAgile
           has_one :agile_data, :dependent => :destroy
           delegate :position, :to => :agile_data, :allow_nil => true
           scope :sorted_by_rank, lambda { eager_load(:agile_data).
-                                          order("COALESCE(#{AgileData.table_name}.position, 999999)") }
-          alias_method :css_classes_without_agile, :css_classes
-          alias_method :css_classes, :css_classes_with_agile
-
-          acts_as_colored
-
-          safe_attributes 'agile_color_attributes',
-            :if => lambda { |issue, user| user.allowed_to?(:edit_issues, issue.project) && user.allowed_to?(:view_agile_queries, issue.project) && RedmineAgile.issue_colors? }
-          safe_attributes 'agile_data_attributes', :if => lambda { |issue, user| issue.new_record? || user.allowed_to?(:edit_issues, issue.project) && RedmineAgile.use_story_points? }
+                                          order(Arel.sql("COALESCE(#{AgileData.table_name}.position, 999999  )")) }
+          safe_attributes 'agile_data_attributes', :if => lambda { |issue, user| issue.new_record? || user.allowed_to?(:edit_issues, issue.project) }
           accepts_nested_attributes_for :agile_data, :allow_destroy => true
 
           alias_method :agile_data_without_default, :agile_data
@@ -67,15 +60,6 @@ module RedmineAgile
         def story_points
           @story_points ||= agile_data.story_points
         end
-        def css_classes_with_agile(user = User.current)
-          s = if Redmine::VERSION.to_s < '2.4'
-                css_classes_without_agile
-              else
-                css_classes_without_agile(user)
-              end
-          s << " #{RedmineAgile.color_prefix}-#{color}" if RedmineAgile.issue_colors? && color
-          s
-        end
 
         def sub_issues
           descendants
diff --git a/plugins/redmine_agile/lib/redmine_agile/patches/issue_priority_patch.rb b/plugins/redmine_agile/lib/redmine_agile/patches/issue_priority_patch.rb
deleted file mode 100644
index 5ffcc76..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/patches/issue_priority_patch.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  module Patches
-
-    module IssuePriorityPatch
-      def self.included(base)
-        base.class_eval do
-          acts_as_colored
-        end
-      end
-    end
-
-  end
-end
-
-unless IssuePriority.included_modules.include?(RedmineAgile::Patches::IssuePriorityPatch)
-  IssuePriority.send(:include, RedmineAgile::Patches::IssuePriorityPatch)
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/patches/issue_query_patch.rb b/plugins/redmine_agile/lib/redmine_agile/patches/issue_query_patch.rb
deleted file mode 100644
index 6fe8c90..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/patches/issue_query_patch.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-require_dependency 'issue'
-
-module RedmineAgile
-  module Patches
-
-    module IssueQueryPatch
-      def self.included(base)
-        base.send(:include, InstanceMethods)
-        base.class_eval do
-          alias_method :issues_without_agile, :issues
-          alias_method :issues, :issues_with_agile
-          alias_method :issue_ids_without_agile, :issue_ids
-          alias_method :issue_ids, :issue_ids_with_agile
-
-          available_columns << QueryColumn.new(:story_points, :caption => :label_agile_story_points, :sortable => "#{AgileData.table_name}.story_points")
-        end
-      end
-
-      module InstanceMethods
-        def issues_with_agile(options = {})
-          options[:include] = (options[:include] || []) | [:agile_data]
-          issues = issues_without_agile(options)
-          if RedmineAgile.color_base == AgileColor::COLOR_GROUPS[:issue]
-            agile_colors = AgileColor.where(container_id: issues, container_type: 'Issue').group_by { |ac| ac[:container_id] }
-            issues.each { |issue| issue.color = agile_colors[issue.id].try(:first).try(:color) }
-          end
-          issues
-        end
-
-        def issue_ids_with_agile(options = {})
-          options[:include] = (options[:include] || []) | [:agile_data]
-          options[:include] = (options[:include] || []) | [:agile_color] if RedmineAgile.color_base == AgileColor::COLOR_GROUPS[:issue]
-          issue_ids_without_agile(options)
-        end
-      end
-    end
-
-  end
-end
-
-unless IssueQuery.included_modules.include?(RedmineAgile::Patches::IssueQueryPatch)
-  IssueQuery.send(:include, RedmineAgile::Patches::IssueQueryPatch)
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/patches/project_patch.rb b/plugins/redmine_agile/lib/redmine_agile/patches/project_patch.rb
index d03c328..2f4a628 100644
--- a/plugins/redmine_agile/lib/redmine_agile/patches/project_patch.rb
+++ b/plugins/redmine_agile/lib/redmine_agile/patches/project_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -19,16 +19,18 @@
 
 module RedmineAgile
   module Patches
-
     module ProjectPatch
+
       def self.included(base)
         base.class_eval do
-          unloadable
-          acts_as_colored
+          base.send(:include, InstanceMethods)
           safe_attributes 'agile_color_attributes',
-            :if => lambda { |project, user| user.allowed_to?(:edit_project, project) && user.allowed_to?(:view_agile_queries, project) && RedmineAgile.use_colors? }
+            if: lambda { |project, user| user.allowed_to?(:edit_project, project) && user.allowed_to?(:view_agile_queries, project) && RedmineAgile.use_colors? }
         end
       end
+
+      module InstanceMethods
+      end
     end
 
   end
diff --git a/plugins/redmine_agile/lib/redmine_agile/patches/queries_controller_patch.rb b/plugins/redmine_agile/lib/redmine_agile/patches/queries_controller_patch.rb
index d60e504..085b7d6 100644
--- a/plugins/redmine_agile/lib/redmine_agile/patches/queries_controller_patch.rb
+++ b/plugins/redmine_agile/lib/redmine_agile/patches/queries_controller_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -31,6 +31,7 @@ module RedmineAgile
       module InstanceMethods
         def query_class_with_agile
           return AgileChartsQuery if params[:type] == 'AgileChartsQuery'
+          return AgileVersionsQuery if params[:type] == 'AgileVersionsQuery'
           query_class_without_agile
         end
       end
diff --git a/plugins/redmine_agile/lib/redmine_agile/patches/tracker_patch.rb b/plugins/redmine_agile/lib/redmine_agile/patches/tracker_patch.rb
deleted file mode 100644
index 6e32677..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/patches/tracker_patch.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  module Patches
-
-    module TrackerPatch
-      def self.included(base)
-        base.class_eval do
-          acts_as_colored
-        end
-      end
-    end
-
-  end
-end
-
-unless Tracker.included_modules.include?(RedmineAgile::Patches::TrackerPatch)
-  Tracker.send(:include, RedmineAgile::Patches::TrackerPatch)
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/patches/user_patch.rb b/plugins/redmine_agile/lib/redmine_agile/patches/user_patch.rb
deleted file mode 100644
index 8422507..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/patches/user_patch.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  module Patches
-
-    module UserPatch
-      def self.included(base)
-        base.class_eval do
-          unloadable
-          acts_as_colored
-          safe_attributes 'agile_color_attributes',
-            :if => lambda { |user, current_user| (current_user.admin? || (user.new_record? && current_user.anonymous? && Setting.self_registration?)) && RedmineAgile.use_colors? }
-        end
-      end
-    end
-
-  end
-end
-
-unless User.included_modules.include?(RedmineAgile::Patches::UserPatch)
-  User.send(:include, RedmineAgile::Patches::UserPatch)
-end
diff --git a/plugins/redmine_agile/lib/redmine_agile/utils/header_tree.rb b/plugins/redmine_agile/lib/redmine_agile/utils/header_tree.rb
deleted file mode 100644
index eee599e..0000000
--- a/plugins/redmine_agile/lib/redmine_agile/utils/header_tree.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineAgile
-  module HeaderTree
-    class HeaderTree
-      class HeaderTreeNode
-        attr_accessor :leaf
-
-        def initialize(name = nil, level = 0)
-          @name = name
-          @level = level
-          @children = ActiveSupport::OrderedHash.new
-        end
-
-        def has_child?(name, has_leaf)
-          @children.keys.last == [name, has_leaf, @children.keys.size - 1]
-        end
-
-        def get_child(name, has_leaf)
-          k = @children.keys.select{ |x| x.first(2) == [name, has_leaf] }.last
-          @children[k]
-        end
-
-        def add_child(name, has_leaf)
-          @children[[name, has_leaf, @children.keys.size]] = HeaderTreeNode.new(name, @level + 1)
-        end
-
-        def branch_width
-          if @leaf
-            1
-          else
-            @children.values.map(&:branch_width).sum
-          end
-        end
-
-        def depth
-          if @leaf
-            1
-          else
-            1 + @children.values.map(&:depth).max
-          end
-        end
-
-        def render(levels)
-          height = @children.values.any? ? 1 : levels.size - @level
-          levels[@level] ||= []
-          levels[@level] << [@name, height, branch_width, @leaf]
-          @children.values.each do |child|
-            child.render(levels)
-          end
-          levels
-        end
-
-        def to_s
-          "#{"\t" * @level}#{@name || 'ROOT'} depth: #{depth}, width: #{branch_width}, level: #{@level}, leaf: #{!!@leaf}\n" +
-            @children.values.map(&:to_s).join
-        end
-      end
-
-      def initialize
-        @root = HeaderTreeNode.new
-      end
-
-      def put(path, leaf, node = nil)
-        node_to_put = node || @root
-        child_node_name = path.first
-        path_left = path[1..-1]
-        has_leaf = path_left.empty?
-        child =
-          if node_to_put.has_child? child_node_name, has_leaf
-            node_to_put.get_child(child_node_name, has_leaf)
-          else
-            node_to_put.add_child(child_node_name, has_leaf)
-          end
-        if has_leaf
-          child.leaf = leaf
-        else
-          put path_left, leaf, child
-        end
-      end
-
-      def depth
-        @root.depth - 1 # Because root itself is not treat as node
-      end
-
-      def render
-        maxdepth = depth
-        levels = Array.new maxdepth + 1
-        @root.render(levels)
-      end
-
-      def to_s
-        @root.to_s
-      end
-    end
-  end
-end
-
-unless AgileBoardsHelper.included_modules.include?(RedmineAgile::HeaderTree)
-  AgileBoardsHelper.send(:include, RedmineAgile::HeaderTree)
-end
diff --git a/plugins/redmine_agile/test/functional/agile_board_controller_test.rb b/plugins/redmine_agile/test/functional/agile_board_controller_test.rb
deleted file mode 100755
index 094e635..0000000
--- a/plugins/redmine_agile/test/functional/agile_board_controller_test.rb
+++ /dev/null
@@ -1,963 +0,0 @@
-# encoding: utf-8
-#
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-require File.expand_path('../../test_helper', __FILE__)
-
-module RedmineAgile
-  module AgileBoardsControllerTest
-    module SpecificTestCase
-      def test_get_index_with_filter_issue_id
-        compatible_request :get, :index, {
-          project_id: 'ecookbook',
-          set_filter: '1',
-          f: %w(issue_id),
-          op: { 'issue_id' => '<=' },
-          v: { 'issue_id' => ['2'] },
-          c: %w(tracker assigned_to)
-        }
-
-        assert_response :success
-        assert_equal [1, 2], agile_issues_in_list.map(&:id).sort
-      end
-
-      def test_get_index_with_filter_is_private
-        compatible_request :get, :index, {
-          project_id: 'ecookbook',
-          set_filter: '1',
-          f: %w(is_private),
-          op: { 'is_private' => '=' },
-          v: { 'is_private' => ['1'] },
-          c: %w(tracker assigned_to)
-        }
-
-        assert_response :success
-        assert_equal [14], agile_issues_in_list.map(&:id)
-      end
-      def test_get_index_with_filter_description
-        compatible_request :get, :index, {
-          project_id: 'ecookbook',
-          set_filter: '1',
-          f: %w(description),
-          op: { 'description' => '=' },
-          v: { 'description' => ['Unable to print recipes'] },
-          c: %w(tracker assigned_to)
-        }
-
-        assert_response :success
-        assert_equal [1], agile_issues_in_list.map(&:id)
-      end
-
-      def test_get_index_with_filter_watcher_id
-        issues(:issues_001).set_watcher(users(:users_002))
-
-        compatible_request :get, :index, {
-          project_id: 'ecookbook',
-          set_filter: '1',
-          f: %w(watcher_id),
-          op: { 'watcher_id' => '=' },
-          v: { 'watcher_id' => ['2'] },
-          c: %w(tracker assigned_to)
-        }
-
-        assert_response :success
-        assert_equal [1], agile_issues_in_list.map(&:id)
-      end
-    end
-  end
-end
-
-
-class AgileBoardsControllerTest < ActionController::TestCase
-  fixtures :projects,
-           :users,
-           :roles,
-           :members,
-           :member_roles,
-           :issues,
-           :issue_statuses,
-           :issue_relations,
-           :versions,
-           :trackers,
-           :projects_trackers,
-           :issue_categories,
-           :enabled_modules,
-           :enumerations,
-           :attachments,
-           :workflows,
-           :custom_fields,
-           :custom_values,
-           :custom_fields_projects,
-           :custom_fields_trackers,
-           :time_entries,
-           :journals,
-           :journal_details,
-           :queries
-  fixtures :email_addresses if Redmine::VERSION.to_s > '3.0'
-
-  include(RedmineAgile::AgileBoardsControllerTest::SpecificTestCase) if Redmine::VERSION.to_s > '2.4'
-
-  def setup
-    @project_1 = Project.find(1)
-    @project_2 = Project.find(5)
-    EnabledModule.create(:project => @project_1, :name => 'agile')
-    EnabledModule.create(:project => @project_2, :name => 'agile')
-    @request.session[:user_id] = 1
-  end
-
-  def test_get_index
-    # global board
-    compatible_request :get, :index
-    assert_response :success
-    assert_equal Issue.open.map(&:id).sort, agile_issues_in_list.map(&:id).sort
-    assert_select '.issue-card', Issue.open.count
-  end
-
-  def test_get_index_with_project
-    compatible_request :get, :index, :project_id => @project_1
-    issues = Issue.where(:project_id => [@project_1] + Project.where(:parent_id => @project_1.id).to_a,
-                         :status_id => IssueStatus.where(:is_closed => false))
-    assert_equal issues.map(&:id).sort, agile_issues_in_list.map(&:id).sort
-    assert_select '.issue-card', issues.count
-    assert_select '.issue-card span.fields p.issue-id strong', issues.count
-    assert_select '.issue-card span.fields p.name a', issues.count
-  end
-
-  def test_get_index_truncated
-    with_agile_settings 'board_items_limit' => 1 do
-      compatible_request :get, :index, agile_query_params
-      assert_response :success
-      assert_select 'div#content p.warning', 1
-      assert_select 'td.issue-status-col .issue-card', 1
-    end
-  end
-
-  def test_limit_for_truncated
-    expected_issues = Issue.where(:project_id => [@project_1] + Project.where(:parent_id => @project_1.id).to_a,
-                                  :status_id => IssueStatus.where(:is_closed => false))
-    with_agile_settings 'board_items_limit' => (expected_issues.count + 1) do
-      compatible_request :get, :index, agile_query_params.merge(:f_status => IssueStatus.where(:is_closed => false).pluck(:id))
-      assert_response :success
-      assert_select 'div#content p.warning', 0
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_get_index_with_filters
-    if Redmine::VERSION.to_s > '2.4'
-      compatible_request :get, :index, agile_query_params.merge(:f_status => IssueStatus.where('id != 1').pluck(:id))
-    else
-      compatible_request :get, :index, agile_query_params.merge(:op => { :status_id => '!' }, :v => { :status_id => ['1'] })
-    end
-    assert_response :success
-    expected_issues = Issue.where(:project_id => [@project_1] + Project.where(:parent_id => @project_1.id).to_a,
-                                  :status_id => IssueStatus.where('id != 1'))
-    assert_equal expected_issues.map(&:id).sort, agile_issues_in_list.map(&:id).sort
-  end
-
-  def test_get_index_with_filter_on_assignee_role
-    assignee_params = { :set_filter => '1',
-                        :f => ['assigned_to_role', ''],
-                        :op => { :assigned_to_role => '=' },
-                        :v => { 'assigned_to_role' => ['2'] },
-                        :c => ['assigned_to'],
-                        :project_id => 'ecookbook' }
-    compatible_request :get, :index, assignee_params
-    assert_response :success
-    members = Member.joins(:roles).where(:project_id => [@project_1]).where('member_roles.role_id = 2').map(&:user_id).uniq
-    expected_issues = Issue.where(:project_id => [@project_1]).where(:assigned_to_id => members)
-    assert_equal expected_issues.map(&:id).sort, agile_issues_in_list.map(&:id).sort
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def create_subissue
-    @issue1 = Issue.find(1)
-    @subissue = Issue.create!(
-      :subject         => 'Sub issue',
-      :project         => @issue1.project,
-      :tracker         => @issue1.tracker,
-      :author          => @issue1.author,
-      :parent_issue_id => @issue1.id,
-      :fixed_version   => Version.last
-    )
-  end
-
-  def test_get_index_with_filter_on_parent_tracker
-    create_subissue
-    compatible_request :get, :index, agile_query_params.merge(
-      :op => { :parent_issue_tracker_id => '=' },
-      :v => { :parent_issue_tracker_id => [Tracker.find(1).name] },
-      :f => [:parent_issue_tracker_id],
-      :project_id => Project.order(:id).first.id
-    )
-    assert_response :success
-    assert_equal [@subissue.id], agile_issues_in_list.map(&:id)
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_get_index_with_filter_on_two_parent_id
-    create_subissue
-    issue2 = Issue.generate!
-    child2 = Issue.generate!(:parent_issue_id => issue2.id)
-
-    compatible_request :get, :index, agile_query_params.merge(
-      :op => { :parent_issue_id => '=' },
-      :v => { :parent_issue_id => ["#{@issue1.id}, #{issue2.id}"] },
-      :f => [:parent_issue_id],
-      :project_id => Project.order(:id).first.id
-    )
-    assert_response :success
-    assert_equal [@subissue.id, child2.id].sort, agile_issues_in_list.map(&:id).sort
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_get_index_with_filter_on_parent_tracker_inversed
-    create_subissue
-    compatible_request :get, :index, agile_query_params.merge(
-      :op => { :parent_issue_tracker_id => '!' },
-      :v => { :parent_issue_tracker_id => [Tracker.find(1).name] },
-      :f => [:parent_issue_tracker_id],
-      :project_id => Project.order(:id).first.id
-    )
-    assert_response :success
-    assert_not_include @subissue.id, agile_issues_in_list.map(&:id)
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_get_index_with_filter_on_has_subissues
-    create_subissue
-    compatible_request :get, :index, agile_query_params.merge(
-      :op => { :has_sub_issues => '=' },
-      :v => { :has_sub_issues => ['yes'] },
-      :f => [:has_sub_issues],
-      :project_id => Project.order(:id).first.id
-    )
-    assert_response :success
-    assert_equal [@issue1.id], agile_issues_in_list.map(&:id)
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_get_index_with_filter_and_field_time_in_state
-    create_subissue
-    columns_group_by = AgileQuery.new.groupable_columns
-    columns_group_by.each do |col|
-      compatible_request :get, :index, agile_query_params.merge(
-        :project_id => Project.order(:id).first.id,
-        :group_by => col.name.to_s
-      )
-      assert_response :success, "Error with group by #{col.name}"
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_put_update_status
-    status_id = 1
-    first_issue_id = 1
-    second_issue_id = 3
-    first_pos = 1
-    second_pos = 2
-    positions = { first_issue_id.to_s => { 'position' => first_pos }, second_issue_id.to_s => { 'position' => second_pos } }
-    compatible_xhr_request :put, :update, :id => first_issue_id, :issue => { :status_id => status_id }, :positions => positions
-    assert_response :success
-    assert_equal status_id, Issue.find(first_issue_id).status_id
-    assert_equal first_pos, Issue.find(first_issue_id).agile_data.position
-    assert_equal second_pos, Issue.find(second_issue_id).agile_data.position
-    # check js code for update board header
-    assert_match '$("table.issues-board thead").html(', @response.body
-  end
-
-  def test_put_update_version
-    fixed_version_id = 3
-    first_issue_id = 1
-    second_issue_id = 3
-    first_pos = 1
-    second_pos = 2
-    positions = { first_issue_id.to_s => { 'position' => first_pos }, second_issue_id.to_s => { 'position' => second_pos } }
-    compatible_xhr_request :put, :update, :id => first_issue_id, :issue => { :fixed_version_id => fixed_version_id }, :positions => positions
-    assert_response :success
-    assert_equal fixed_version_id, Issue.find(first_issue_id).fixed_version_id
-    assert_equal first_pos, Issue.find(first_issue_id).agile_data.position
-    assert_equal second_pos, Issue.find(second_issue_id).agile_data.position
-  end
-
-  def test_put_update_assigned
-    assigned_to_id = 3
-    issue_id = 1
-    compatible_xhr_request :put, :update, :id => issue_id, :issue => { :assigned_to_id => assigned_to_id }
-    assert_response :success
-    assert_equal assigned_to_id, Issue.find(issue_id).assigned_to_id
-  end
-
-  def test_get_index_with_all_fields
-    compatible_request :get, :index, agile_query_params.merge(:f => AgileQuery.available_columns.map(&:name))
-    assert_response :success
-  end
-  def test_get_index_with_colors
-    with_agile_settings 'color_on' => 'issue' do
-      issue = Issue.find(1)
-      issue.agile_color.color = 'red'
-      issue.save
-      compatible_request :get, :index, agile_query_params.merge(:color_base => 'issue')
-      assert_response :success
-      assert_select 'td.issue-status-col .issue-card.bk-red', 1
-    end
-  end
-
-  def test_get_index_with_colors_for_spent_time
-    with_agile_settings 'color_on' => 'spent_time' do
-      issue = Issue.find(1)
-      est_time = issue.estimated_hours
-      spent_time = issue.spent_hours
-      compatible_request :get, :index, agile_query_params.merge(:color_base => 'spent_time')
-      assert_response :success
-      assert_select "td.issue-status-col .issue-card.bk-#{AgileColor.for_spent_time(est_time, spent_time)}"
-    end
-  end
-
-  def test_get_index_with_colors_for_project
-    with_agile_settings 'color_on' => 'project' do
-      @project_1.color = AgileColor::AGILE_COLORS[:red]
-      @project_1.save
-      compatible_request :get, :index, :color_base => 'project'
-      assert_response :success
-      assert_select 'td.issue-status-col .issue-card.bk-red', @project_1.issues.open.count
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_get_index_with_colors_for_user
-    with_settings :gravatar_enabled => '1' do
-      with_agile_settings 'color_on' => 'user' do
-        issue = Issue.find(2)
-        user = issue.assigned_to
-        user.color = AgileColor::AGILE_COLORS[:red]
-        user.save
-        user_color = user.reload.color
-        compatible_request :get, :index, agile_query_params.merge(:color_base => 'user')
-        assert_response :success
-        assert_select 'td.issue-status-col .issue-card[data-id="2"]'
-        assert_select "img.gravatar[style='#{"border-left: 5px solid #{user_color}".html_safe}']"
-        assert response.body.include? user_color
-      end
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_get_index_with_colors_for_user_with_allow_issue_assignment_to_groups_settings
-    with_settings :gravatar_enabled => '1', :issue_group_assignment => '1' do
-      with_agile_settings 'color_on' => 'user' do
-        issue = Issue.find(2)
-        user = issue.assigned_to
-        user.color = AgileColor::AGILE_COLORS[:red]
-        user.save
-        user_color = user.reload.color
-        compatible_request :get, :index, agile_query_params.merge(:color_base => 'user')
-        assert_response :success
-        assert_select 'td.issue-status-col .issue-card[data-id="2"]'
-        assert_select "img.gravatar[style='#{"border-left: 5px solid #{user_color}".html_safe}']"
-        assert response.body.include? user_color
-      end
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_get_index_with_colors_by_user
-    with_settings :gravatar_enabled => '1' do
-      with_agile_settings 'color_on' => 'user' do
-        issue = Issue.find(2)
-        user_color = AgileColor.for_user(issue.assigned_to.login)
-        compatible_request :get, :index, agile_query_params.merge(:color_base => 'user')
-        assert_response :success
-        assert_select 'td.issue-status-col .issue-card[data-id="2"]'
-        assert_select "img.gravatar[style='#{"border-left: 5px solid #{user_color}".html_safe}']"
-        assert response.body.include? user_color
-      end
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_get_index_with_none_colors_by_user
-    with_settings :gravatar_enabled => '1' do
-      with_agile_settings 'color_on' => 'user' do
-        issue = Issue.find(2)
-        user_color = AgileColor.for_user(issue.assigned_to.login)
-        compatible_request :get, :index, agile_query_params.merge(:color_base => 'none')
-        assert_response :success
-        assert_select 'td.issue-status-col .issue-card[data-id="2"]'
-        assert_select "img.gravatar[style='#{"border-left: 5px solid #{user_color}".html_safe}']", false
-        assert !(response.body.include? user_color), 'Found color for user'
-      end
-    end
-  end
-
-  def test_get_index_with_swimlanes
-    compatible_request :get, :index, agile_query_params.merge(:group_by => 'priority')
-    assert_response :success
-    assert_select 'tr.group.open.swimlane[data-id="4"] td', /Low/
-    assert_select 'tr.group.open.swimlane[data-id="4"] td span.count', { :text => '5' }
-
-    AgileQuery.available_columns.select(&:groupable).each do |column|
-      compatible_request :get, :index, agile_query_params.merge(:group_by => column.name.to_s)
-      assert_response :success
-    end
-  end
-
-  def test_count_for_swimlanes
-    issues_count = Issue.where(:project_id => [@project_1.id] + @project_1.children.pluck(:id)).open.group('project_id').count
-    compatible_request :get, :index, agile_query_params.merge(:group_by => 'project')
-    assert_response :success
-    issues_count.each do |c|
-      assert_select "tr.group.open.swimlane[data-id='#{c[0]}'] td span.count", :text => c[1].to_s
-    end
-  end
-
-  def test_get_index_with_sub_issues
-    issue1 = Issue.find(1)
-    issue2 = Issue.create!(
-      :subject         => 'Sub issue',
-      :project         => issue1.project,
-      :tracker         => issue1.tracker,
-      :author          => issue1.author,
-      :parent_issue_id => issue1.id,
-      :fixed_version   => Version.last
-    )
-
-    compatible_request :get, :index, agile_query_params.merge(:c => ['sub_issues'])
-    assert_response :success
-
-    assert_select 'div.sub-issues', 1
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_proper_columns_for_default_board
-    # In Redmine 2.3 Query model doesn't have options attribute
-    # So it can't be default and so 'default' behaviour cannot be broken
-    # As it does not work at all
-    if AgileQuery.new.has_attribute?(:options)
-      AgileQuery.destroy_all
-      project1 = Project.create!(:name => 'project1', :identifier => 'project1')
-      board_params = {
-        :project => project1,
-        :name => 'board1',
-        :column_names => [:id, :author, :description],
-        :options => { :is_default => true },
-        :visibility => 2
-      }
-
-      AgileQuery.create! board_params
-      EnabledModule.create(:project => project1, :name => 'agile')
-      compatible_request :get, :index, :project_id => 'project1'
-      assert_response :success
-    end
-  end
-
-  def test_proper_columns_for_default_board_with_update
-    if AgileQuery.new.has_attribute?(:options)
-      board_params = {
-        :project => @project_1,
-        :name => 'board_for_project_1',
-        :column_names => [:id, :author, :description, :project, :tracker, :assignee],
-        :options => { :is_default => true },
-        :visibility => 2
-      }
-
-      board = AgileQuery.create! board_params
-      issues = Issue.where(:project_id => [@project_1] + Project.where(:parent_id => @project_1.id).to_a).open(true)
-      compatible_request :get, :index, :project_id => @project_1, :query_id => board.id
-      assert_select '.issue-card span.fields p.issue-id strong', issues.count
-      assert_select '.issue-card span.fields p.project', issues.count
-      assert_select '.issue-card span.fields p.name', issues.count
-      assert_select '.issue-card span.fields p.attributes a', issues.count
-      compatible_request :put, :update, :issue => { :status_id => (issues.first.status_id + 1) }, :id => issues.first.id
-      # update action return html for one card but with same fields
-      assert_select '.issue-card span.fields p.issue-id strong', 1
-      assert_select '.issue-card span.fields p.project', 1
-      assert_select '.issue-card span.fields p.name', 1
-      assert_select '.issue-card span.fields p.attributes a', 1
-    end
-  end
-
-  def test_short_card_for_closed_issue
-    with_agile_settings 'hide_closed_issues_data' => '1' do
-      closed_status = IssueStatus.where(:is_closed => true).pluck(:id)
-      closed_issues = Issue.where(:status_id => closed_status)
-      project = closed_issues.first.project
-
-      # get :index, agile_query_params.merge(:f_status => closed_status)
-      if Redmine::VERSION.to_s > '2.4'
-        compatible_request :get, :index, agile_query_params.merge(:f_status => closed_status)
-      else
-        compatible_request :get, :index, agile_query_params.merge('f' => [''])
-      end
-
-      assert_response :success
-      assert_select '.closed-issue', project.issues.where(:status_id => IssueStatus.where(:is_closed => true)).count
-    end
-  end
-
-  def test_get_tooltip_for_issue
-    issue = Issue.where(:status_id => IssueStatus.where(:is_closed => true)).first
-    compatible_request :get, :issue_tooltip, :id => issue.id
-    assert_response :success
-    assert_select 'a.issue', 1
-    assert_select 'strong', 6
-    assert_match issue.status.name, @response.body
-  end
-
-  def test_empty_node_for_tooltip
-    with_agile_settings 'hide_closed_issues_data' => '1' do
-      closed_status = IssueStatus.where(:is_closed => true).pluck(:id)
-      if Redmine::VERSION.to_s > '2.4'
-        compatible_request :get, :index, agile_query_params.merge(:f_status => closed_status)
-      else
-        compatible_request :get, :index, agile_query_params.merge('f' => [''])
-      end
-      assert_select 'span.tip', { :text => '' }
-    end
-  end
-
-  def test_setting_for_closed_issues
-    with_agile_settings 'hide_closed_issues_data' => '0' do
-      closed_issues = Issue.where(:status_id => IssueStatus.where(:is_closed => true))
-      project = closed_issues.first.project
-      compatible_request :get, :index, agile_query_params.merge('f' => [''])
-      assert_response :success
-      assert_select '.closed-issue', 0
-    end
-  end
-
-  def test_index_with_js_format
-    with_agile_settings 'hide_closed_issues_data' => '1' do
-      closed_issues = Issue.where(:status_id => IssueStatus.where(:is_closed => true))
-      project = closed_issues.first.project
-      compatible_xhr_request :get, :index, agile_query_params.merge('f' => [''], :format => :js)
-      assert_response :success
-      assert_match "$('.tooltip').mouseenter(callGetToolTipInfo)", @response.body
-    end
-  end
-
-  def test_get_index_with_day_in_state_and_parent_group
-    compatible_request :get, :index, agile_query_params.merge(:c => ['day_in_state'], :group_by => 'parent')
-    assert_response :success
-  end
-
-  def test_assinged_to_and_in_state_in_index
-    issue1 = Issue.find(1)
-    issue2 = Issue.create!(
-      :subject         => 'Test assigned_to with day in state',
-      :project         => issue1.project,
-      :tracker         => issue1.tracker,
-      :author          => issue1.author,
-      :fixed_version   => Version.last
-    )
-    compatible_request :get, :index, agile_query_params.merge(:c => ['day_in_state'], :group_by => 'parent')
-    assigned_to_id = 3
-    issue_id = issue2.id
-    compatible_xhr_request :put, :update, :id => issue_id, :issue => { :assigned_to_id => assigned_to_id }
-    assert_response :success
-    issue2.reload
-    assert_equal assigned_to_id, issue2.assigned_to_id
-  end
-
-  def test_index_with_relations_relates
-    create_issue_relation
-    compatible_request :get, :index, agile_query_params.merge(:f_status => IssueStatus.pluck(:id), :op => { :status_id => '*', :relates => '*' }, :f => ['relates'])
-    assert_response :success
-    assert_equal [1, 7, 8], agile_issues_in_list.map(&:id).sort
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_index_with_relations_blocked
-    create_issue_relation
-
-    compatible_request :get, :index, agile_query_params.merge(:f_status => IssueStatus.pluck(:id), :op => { :status_id => '*', :blocked => '*' }, :f => ['blocked'])
-    assert_response :success
-    assert_equal [2, 11], agile_issues_in_list.map(&:id).sort
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_list_of_attributes_on_update_status
-    params = agile_query_params.merge(:c => ['project', 'day_in_state'], :group_by => 'parent')
-    params.delete(:project_id)
-    compatible_request :get, :index, params
-    assert_response :success
-    assert_select 'p.project'
-    compatible_request :put, :update, :issue => { :status_id => 2 }, :id => 1
-    assert_select 'p.project', { :text => @project_1.to_s }
-  end
-
-  def test_day_in_state_when_change_status
-    @request.session[:agile_query] = {}
-    @request.session[:agile_query][:column_names] = ['project', 'day_in_state']
-    issue = Issue.find(1)
-    compatible_request :put, :update, :issue => { :status_id => 2 }, :id => 1
-    issue.reload
-    assert_response :success
-    assert_equal 2, issue.status_id
-    assert_select 'p.attributes', /0.hours/
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_global_query_and_project_board
-    query = AgileQuery.create(:name => 'global', :column_names => ['project'], :visibility => 2, :options => { :is_default => true })
-    compatible_request :get, :index, :project_id => 1
-    assert_select 'p.project', { :count => 0, :text => Project.find(2).to_s }
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_total_estimated_hours_for_status
-    [1, 2, 3].each do |id|
-      issue = Issue.find(id)
-      issue.estimated_hours = 10
-      issue.save
-    end
-    compatible_request :get, :index, agile_query_params.merge(:c => ['estimated_hours'])
-    assert_response :success
-    assert_select 'thead tr th span.hours', { :count => 1, :text => '20.00h' } # status_id == 1
-    assert_select 'thead tr th span.hours', { :count => 1, :text => '10.00h' } # status_id == 2
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_get_index_with_checklist
-    # global board
-    issue1 = issue_with_checklist
-
-    compatible_request :get, :index, agile_query_params.merge(:c => ['checklists'], :project_id => nil)
-    assert_response :success
-    # check checklist nodes
-    assert_select "#checklist_#{issue1.id}"
-    issue1.checklists.each do |checklist|
-      assert_select "#checklist_item_#{checklist.id}", :text => checklist.subject
-    end
-  end if RedmineAgile.use_checklist?
-
-  def test_get_index_without_checklist
-    issue1 = issue_with_checklist
-    compatible_request :get, :index, :c => []
-    assert_response :success
-    # check checklist nodes
-    assert_select "#checklist_#{issue1.id}", :count => 0
-    issue1.checklists.each do |checklist|
-      assert_select "#checklist_item_#{checklist.id}", :count => 0
-    end
-  end if RedmineAgile.use_checklist?
-
-  def test_get_index_with_project_with_checklist
-    issue1 = issue_with_checklist
-    compatible_request :get, :index, agile_query_params.merge(:c => ['checklists'], :project_id => issue1.project)
-    assert_response :success
-    # check checklist nodes
-    assert_select "#checklist_#{issue1.id}"
-    issue1.checklists.each do |checklist|
-      assert_select "#checklist_item_#{checklist.id}", :text => checklist.subject
-    end
-  end if RedmineAgile.use_checklist?
-
-  def test_get_index_with_project_without_checklist
-    issue1 = issue_with_checklist
-    compatible_request :get, :index, agile_query_params.merge(:project_id => issue1.project, :c => [])
-    assert_response :success
-    # check checklist nodes
-    assert_select "#checklist_#{issue1.id}", :count => 0
-    issue1.checklists.each do |checklist|
-      assert_select "#checklist_item_#{checklist.id}", :count => 0
-    end
-  end if RedmineAgile.use_checklist?
-
-  def test_get_index_check_quick_edit_without_permission
-    role = Role.find(2)
-    role.permissions << :veiw_issues
-    role.permissions << :view_agile_queries
-    role.permissions.delete(:edit_issues)
-    role.save
-    @request.session[:user_id] = 3
-
-    compatible_request :get, :index, :project_id => @project_1
-    issues = Issue.where(:project_id => [@project_1], :status_id => IssueStatus.where(:is_closed => false))
-    assert_response :success
-    assert_select '.issue-card', agile_issues_in_list.count
-    assert_select '.issue-card .quick-edit-card a', :count => 0
-  end
-
-  def test_last_comment_on_issue_cart
-    issue = @project_1.issues.open.first
-    text_comment = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus in blandit ex. Donec vitae urna quis tortor tempor mollis.'
-    comment = Journal.new(:notes => text_comment, :user_id => 1)
-    issue.journals << comment
-    params = agile_query_params.merge(:c => ['project', 'last_comment'], :group_by => 'parent')
-    compatible_request :get, :index, params
-    assert_response :success
-    assert_select 'span.last-comment', :text => text_comment.truncate(100)
-    compatible_request :put, :update, :issue => { :status_id => 2 }, :id => issue.id
-    assert_select 'span.last-comment', :text => text_comment.truncate(100)
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_show_sp_value_on_issue_cart
-    with_agile_settings 'estimate_units' => 'story_points' do
-      issues = @project_1.issues.open.first(3)
-      issues.each do |issue|
-        issue.agile_data.story_points = issue.id * 10
-        issue.save
-      end
-      params = agile_query_params.merge(:c => ['story_points'])
-      compatible_request :get, :index, params
-      IssueStatus.where(:id => @project_1.issues.open.joins(:status).pluck(:status_id).uniq).each do |status|
-        sp_sum = @project_1.issues.eager_load(:agile_data).where(:status_id => status.id).sum("#{AgileData.table_name}.story_points")
-        next unless sp_sum.to_i > 0
-        assert_select "th[data-column-id='#{status.id}'] span.hours", :text =>"#{sp_sum}sp"
-      end
-      issues.each do |issue|
-        assert_select ".issue-card[data-id='#{issue.id}'] span.fields p.issue-id span.hours", :text =>"(#{issue.story_points}sp)"
-      end
-      # check story_points in available_columns for query
-      assert_select 'input[value="story_points"]'
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_show_sp_with_estimated_hours_on_issue_cart
-    with_agile_settings 'estimate_units' => 'story_points' do
-      issues = @project_1.issues.open.first(3)
-      issues.each do |issue|
-        issue.agile_data.story_points = issue.id * 10
-        issue.estimated_hours = issue.id * 2
-        issue.save
-      end
-      params = agile_query_params.merge(:c => ['story_points', 'estimated_hours'])
-      compatible_request :get, :index, params
-      # in a header show only story_points!
-      IssueStatus.where(:id => @project_1.issues.open.joins(:status).pluck(:status_id).uniq).each do |status|
-        sp_sum = @project_1.issues.eager_load(:agile_data).where(:status_id => status.id).sum("#{AgileData.table_name}.story_points")
-        next unless sp_sum.to_i > 0
-        assert_select "th[data-column-id='#{status.id}'] span.hours", :text =>"#{sp_sum}sp"
-      end
-      # in a card show and estimated hours and story points
-      issues.each do |issue|
-        assert_select ".issue-card[data-id='#{issue.id}'] span.fields p.issue-id span.hours",
-                      :text => "(#{"%.2fh" % issue.estimated_hours.to_f}/#{issue.story_points}sp)"
-      end
-    end if Redmine::VERSION.to_s > '2.4'
-  end
-
-  def test_quick_add_comment_button
-    with_agile_settings 'allow_inline_comments' => 1 do
-      compatible_request :get, :index, agile_query_params
-      assert_response :success
-      assert_select '.quick-edit-card img[alt="Edit"]'
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_quick_add_comment_form
-    compatible_request :get, :inline_comment, :id => @project_1.issues.open.first
-    assert_response :success
-    assert_select 'textarea'
-    assert_select 'button'
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_quick_add_comment_update
-    issue = @project_1.issues.open.first
-    compatible_request :put, :update, :issue => { :notes => 'new comment!!!' }, :id => issue
-    assert_response :success
-    assert_select '.last_comment', :text => 'new comment!!!'
-  end if Redmine::VERSION.to_s > '2.4'
-  def test_wp_max_count_and_style
-    statuses = IssueStatus.pluck(:id)
-    compatible_request :get, :index, 'set_filter' => '1', :project_id => @project_1, :f_status => statuses, :wp => statuses.inject({}){ |r,s| r.merge!(s.to_s => '5') }
-    issues_for_status = Issue.where(:project_id => [@project_1] + Project.where(:parent_id => @project_1.id).to_a).group(:status_id).count
-    issues_for_status.each do |status_id, issues_count|
-      if issues_count > 5
-        assert_select "th.over_wp_limit[data-column-id='#{status_id}']"
-        assert_select "th.over_wp_limit[data-column-id='#{status_id}'] span.count span.over_wp_limit", :text => issues_count.to_s
-      end
-      assert_select "th[data-column-id='#{status_id}'] span.count", :text => "#{issues_count}/5"
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_wp_min_count_and_style
-    statuses = IssueStatus.pluck(:id)
-    issues_for_status = Issue.where(:project_id => [@project_1] + Project.where(:parent_id => @project_1.id).to_a).group(:status_id).count
-    wp_limits = issues_for_status.inject({}) do |h, (k, v)|
-      h.merge!(k.to_s => "#{v.to_i + 1}-100")
-    end
-
-    compatible_request :get, :index, 'set_filter' => '1', :project_id => @project_1, :f_status => statuses, :wp => wp_limits
-    issues_for_status.each do | status_id, issues_count|
-      if issues_count > 0
-        assert_select "th.under_wp_limit[data-column-id='#{status_id}']"
-        assert_select "th.under_wp_limit[data-column-id='#{status_id}'] span.count span.under_wp_limit", :text => issues_count.to_s
-      end
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_card_for_new_issue
-    with_agile_settings 'allow_create_card' => 1 do
-      statuses = IssueStatus.all
-      compatible_request :get, :index, 'set_filter' => '1', :project_id => @project_1, :f_status => statuses.map(&:id)
-      assert_select '.add-issue input.new-card__input', IssueStatus.where(:is_closed => false).count
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_not_show_card_for_new_issue_without_permission
-    with_agile_settings 'allow_create_card' => 1 do
-      role = Role.find(2)
-      role.permissions << :veiw_issues
-      role.permissions << :view_agile_queries
-      role.permissions.delete(:add_issues)
-      role.save
-      @request.session[:user_id] = 3
-      statuses = IssueStatus.all
-      compatible_request :get, :index, 'set_filter' => '1', :project_id => @project_1, :f_status => statuses.map(&:id)
-      assert_select '.add-issue input.new-card__input', 0
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_create_issue_from_board
-    with_agile_settings 'allow_create_card' => 1 do
-      assert_difference 'Issue.count' do
-        compatible_request :post, :create_issue, :project_id => @project_1, :subject => 'new issue from board', :status_id => 1
-      end
-      assert_response :success
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_create_issue_from_board_with_empty_subject
-    with_agile_settings 'allow_create_card' => 1 do
-      assert_no_difference 'Issue.count' do
-        compatible_request :post, :create_issue, :project_id => @project_1, :subject => '', :status_id => 1
-      end
-      assert_response 500
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_create_issue_from_board_when_subject_consists_of_only_spaces
-    with_agile_settings "allow_create_card" => 1 do
-      assert_no_difference 'Issue.count' do
-        compatible_request :post, :create_issue, :project_id => @project_1, :subject => '   ', :status_id => 1
-      end
-      assert_response 500
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_create_issue_from_board_without_permission
-    with_agile_settings 'allow_create_card' => 1 do
-      role = Role.find(2)
-      role.permissions << :veiw_issues
-      role.permissions << :view_agile_queries
-      role.permissions.delete(:add_issues)
-      role.save
-      @request.session[:user_id] = 3
-      assert_no_difference 'Issue.count' do
-        compatible_request :post, :create_issue, :project_id => @project_1, :subject => 'new issue from board', :status_id => 1
-      end
-      assert_response 500
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_on_auto_assign_on_move
-    with_agile_settings 'auto_assign_on_move' => '1' do
-      @request.session[:user_id] = 2
-      issue = Issue.find(1)
-
-      user = issue.project.users.first
-      @request.session[:user_id] = user.id
-
-      assert_nil issue.assigned_to
-      compatible_request :put, :update, :issue => { :status_id => 2 }, :id => 1
-      issue.reload
-      assert_response :success
-      assert_equal 2, issue.status_id
-      assert_equal user, issue.assigned_to
-    end
-  end
-
-  def test_off_auto_assign_on_move
-    with_agile_settings 'auto_assign_on_move' => '0' do
-      issue = Issue.find(1)
-
-      user = issue.project.users.first
-      @request.session[:user_id] = user.id
-
-      assert_nil issue.assigned_to
-      compatible_request :put, :update, :issue => { :status_id => 2 }, :id => 1
-      issue.reload
-      assert_response :success
-      assert_equal 2, issue.status_id
-      assert_nil issue.assigned_to
-    end
-  end
-
-  def test_off_auto_assign_on_move_by_sorting
-    with_agile_settings 'auto_assign_on_move' => '1' do
-      @request.session[:user_id] = 2
-      issue = Issue.find(1)
-
-      user = issue.project.users.first
-      @request.session[:user_id] = user.id
-
-      assert_nil issue.assigned_to
-      compatible_request :put, :update, :issue => { :status_id => issue.status_id }, :id => 1
-      issue.reload
-      assert_response :success
-      assert_nil issue.assigned_to
-    end
-  end
-
-  def test_option_for_select_current_version
-    @request.session[:user_id] = 1
-    compatible_request :get, :index, :project_id => @project_1
-    assert_response :success
-    assert_match 'current_version', response.body
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_filter_by_current_version
-    @request.session[:user_id] = 1
-    compatible_request :get, :index, :project_id => @project_1, 'v' => { 'fixed_version_id' => ['current_version'] },
-                                                                'set_filter' => '1', 'f' => ['fixed_version_id'],
-                                                                'op' => { 'fixed_version_id' => '=' }
-    assert_response :success
-    current_version = @project_1.shared_versions.order(:effective_date).first
-    issue = @project_1.issues.first
-    issue.fixed_version = current_version
-    issue.save
-    assert agile_issues_in_list.all? { |i| i.fixed_version == current_version }
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_filter_by_current_version_closed
-    @request.session[:user_id] = 1
-    current_version = @project_1.shared_versions.order(:effective_date).first
-    current_version.status = 'closed'
-    current_version.save
-
-    compatible_request :get, :index, :project_id => @project_1, 'v' => { 'fixed_version_id' => ['current_version'] },
-                                                                'set_filter' => '1', 'f' => ['fixed_version_id'],
-                                                                'op' => { 'fixed_version_id' => '=' }
-    assert_response :success
-    issue = @project_1.issues.first
-    issue.fixed_version = current_version
-    issue.save
-    assert agile_issues_in_list.all?{ |i| i.fixed_version != current_version }
-    # check set filter for current version
-    assert_match 'addFilter("fixed_version_id", "=", ["current_version"])', response.body
-  end if Redmine::VERSION.to_s > '2.4'
-
-  private
-
-  def agile_query_params
-    { :set_filter => '1', :f => ['status_id', ''], :op => { :status_id => 'o' }, :c => ['tracker', 'assigned_to'], :project_id => 'ecookbook' }
-  end
-
-  def create_issue_relation
-    IssueRelation.delete_all
-    IssueRelation.create!(:relation_type => 'relates', :issue_from => Issue.find(1), :issue_to => Issue.find(7))
-    IssueRelation.create!(:relation_type => 'relates', :issue_from => Issue.find(8), :issue_to => Issue.find(1))
-    IssueRelation.create!(:relation_type => 'blocks', :issue_from => Issue.find(1), :issue_to => Issue.find(11))
-    IssueRelation.create!(:relation_type => 'blocks', :issue_from => Issue.find(12), :issue_to => Issue.find(2))
-  end
-
-  def issue_with_checklist
-    issue1 = Issue.find(1)
-    chk1 = issue1.checklists.create(:subject => 'TEST1', :position => 1)
-    chk2 = issue1.checklists.create(:subject => 'TEST2', :position => 2)
-    issue1
-  end if RedmineAgile.use_checklist?
-
-end
diff --git a/plugins/redmine_agile/test/functional/agile_charts_controller_test.rb b/plugins/redmine_agile/test/functional/agile_charts_controller_test.rb
index 02b51f7..0752633 100644
--- a/plugins/redmine_agile/test/functional/agile_charts_controller_test.rb
+++ b/plugins/redmine_agile/test/functional/agile_charts_controller_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -46,105 +46,150 @@ class AgileChartsControllerTest < ActionController::TestCase
            :journal_details,
            :queries
 
-  def test_get_show
+  def setup
     @request.session[:user_id] = 1
-    compatible_request :get, :show
-    assert_response :success
-    assert_select 'canvas#agile-chart', 1
+    @project = Project.find(1)
+    @issue = @project.issues.first
+
+    EnabledModule.create(project: @project, name: 'agile')
+
+    @charts = RedmineAgile::Charts::AGILE_CHARTS.keys
+    @charts_with_units = RedmineAgile::Charts::CHARTS_WITH_UNITS
   end
 
-  def test_get_render_chart_issues_burndown_with_version
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'issues_burndown', :version_id => 2
-    assert_response :success
-    assert_equal 'application/json', @response.content_type
+  def test_get_show
+    should_get_show
+    should_get_show project_id: @project.identifier
   end
 
-  def test_get_render_chart_issues_burndown
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'issues_burndown'
-    assert_response :success
-    assert_equal 'application/json', @response.content_type
+  def test_get_show_with_period
+    should_get_show({ f: ['issue_id', ''], op: { 'issue_id' => '*' } })
+    should_get_show({ f: ['issue_id', ''], op: { 'issue_id' => '*' }, project_id: @project.identifier })
   end
 
-  def test_get_render_chart_work_burndown_sp
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'work_burndown_sp'
-    assert_response :success
-    assert_equal 'application/json', @response.content_type
+  def test_charts_by_default_params
+    @charts.each { |chart| check_chart(chart: chart, project_id: @project.identifier) }
   end
 
-  def test_get_render_chart_work_burndown_hours
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'work_burndown_hours'
-    assert_response :success
-    assert_equal 'application/json', @response.content_type
+  def test_charts_with_chart_unit
+    @charts_with_units.each do |chart|
+      RedmineAgile::Charts::CHART_UNITS.each do |chart_unit, label|
+        check_chart chart: chart, project_id: @project.identifier, chart_unit: chart_unit
+      end
+    end
   end
 
-  def test_get_render_chart_burnup
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'burnup'
-    assert_response :success
-    assert_equal 'application/json', @response.content_type
+  def test_charts_by_different_time_intervals
+    @charts.each do |chart|
+      RedmineAgile::AgileChart::TIME_INTERVALS.each do |interval|
+        check_chart chart: chart, project_id: @project.identifier, interval_size: interval
+      end
+    end
   end
 
-  def test_get_render_chart_work_burnup_sp
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'work_burnup_sp'
-    assert_response :success
-    assert_equal 'application/json', @response.content_type
+  def test_charts_by_different_periods_and_time_intervals
+    @charts.each do |chart|
+      RedmineAgile::AgileChart::TIME_INTERVALS.each do |interval|
+        params = {
+          chart: chart,
+          project_id: @project.identifier,
+          interval_size: interval,
+          set_filter: 1,
+          f: ['chart_period']
+        }
+
+        check_chart params.merge(op: { chart_period: '=' }, v: { chart_period: ['2014-01-01'] })
+        check_chart params.merge(op: { chart_period: '>=' }, v: { chart_period: ['2014-01-01'] })
+        check_chart params.merge(op: { chart_period: '<=' }, v: { chart_period: ['2019-01-01'] })
+        check_chart params.merge(op: { chart_period: '><' }, v: { chart_period: ['2014-01-01', '2018-12-31'] })
+        check_chart params.merge(op: { chart_period: '>t-' }, v: { chart_period: [99] })
+        check_chart params.merge(op: { chart_period: '<t-' }, v: { chart_period: [99] })
+        check_chart params.merge(op: { chart_period: '><t-' }, v: { chart_period: [99] })
+        check_chart params.merge(op: { chart_period: 't-' }, v: { chart_period: [99] })
+        check_chart params.merge(op: { chart_period: 't' })
+        check_chart params.merge(op: { chart_period: 'ld' })
+        check_chart params.merge(op: { chart_period: 'w' })
+        check_chart params.merge(op: { chart_period: 'lw' })
+        check_chart params.merge(op: { chart_period: 'l2w' })
+        check_chart params.merge(op: { chart_period: 'm' })
+        check_chart params.merge(op: { chart_period: 'lm' })
+        check_chart params.merge(op: { chart_period: 'y' })
+        check_chart params.merge(op: { chart_period: '!*' })
+        check_chart params.merge(op: { chart_period: '*' })
+      end
+    end
   end
 
-  def test_get_render_chart_work_burnup_hours
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'work_burnup_hours'
-    assert_response :success
-    assert_equal 'application/json', @response.content_type
+  def test_render_charts
+    @charts.each do |chart|
+      should_get_render_chart chart: chart, chart_unit: 'issues'
+    end
   end
 
-  def test_get_render_chart_trackers_cumulative_flow
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'trackers_cumulative_flow'
-    assert_response :success
-    assert_equal 'application/json', @response.content_type
+  def test_charts_with_version
+    @charts.each do |chart|
+      should_get_render_chart chart: chart, version_id: 2
+      should_get_render_chart chart: chart, version_id: 2, project_id: @project.identifier
+    end
   end
 
-  def test_get_render_chart_cumulative_flow
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'cumulative_flow'
-    assert_response :success
-    assert_equal 'application/json', @response.content_type
+  def test_charts_with_version_and_chart_unit
+    @charts_with_units.each do |chart|
+      RedmineAgile::Charts::CHART_UNITS.each do |chart_unit, label|
+        should_get_render_chart chart: chart, version_id: 2, chart_unit: chart_unit
+      end
+    end
   end
 
-  def test_get_render_chart_issues_velocity
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'issues_velocity'
-    assert_response :success
-    assert_equal 'application/json', @response.content_type
+  def test_issues_burndown_chart_when_first_issue_later_then_due_date
+    new_version = Version.create!(name: 'Some new vesion', effective_date: (Date.today - 10.days), project_id: @project.id)
+    issue = Issue.create!(
+      project_id: @project.id,
+      tracker_id: 1,
+      subject: 'test_issues_burndown_chart_when_first_issue_later_then_due_date',
+      author_id: 2,
+      start_date: Date.today
+    )
+    new_version.fixed_issues << issue.reload
+
+    should_get_render_chart chart: RedmineAgile::Charts::BURNDOWN_CHART, project_id: @project.identifier, version_id: new_version.id
   end
 
-  def test_get_render_chart_lead_time
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'lead_time'
-    assert_response :success
-    assert_equal 'application/json', @response.content_type
+  def test_get_show_chart_with_open_target_version
+    current_version = @issue.fixed_version
+    @issue.update(fixed_version: Version.open.first)
+
+    should_get_render_chart project_id: @project.identifier, chart: 'burndown_chart',
+                                                             f: ['version_status'],
+                                                             op: { 'version_status' => '=' },
+                                                             v: { 'version_status' => ['open'] }
+    ensure
+    @issue.update(fixed_version: current_version)
   end
 
-  def test_get_render_chart_average_lead_time
-    @request.session[:user_id] = 1
-    compatible_request :get, :render_chart, :chart => 'average_lead_time'
+  private
+
+  def should_get_show(parameters = {})
+    compatible_request :get, :show, parameters
     assert_response :success
-    assert_equal 'application/json', @response.content_type
+    assert_select 'canvas#agile-chart', 1
   end
 
-  def test_issues_burndown_chart_when_first_issue_later_then_due_date
-    @project_1 = Project.find(1)
-    EnabledModule.create(:project => @project_1, :name => 'agile')
-    @request.session[:user_id] = 1
-    new_version = Version.create!(:name => 'Some new vesion', :effective_date => (Date.today - 10.days), :project_id => @project_1.id)
-    issue = Issue.create!(:project_id => 1, :tracker_id => 1, :subject => 'test_issues_burndown_chart_when_first_issue_later_then_due_date', :author_id => 2, :start_date => Date.today)
-    new_version.fixed_issues << issue
-    compatible_request :get, :render_chart, :chart => 'issues_burndown', :project_id => 1, :version_id => new_version.id
+  def should_get_render_chart(parameters = {})
+    compatible_xhr_request :get, :render_chart, parameters
     assert_response :success
+    assert_match 'application/json', response.content_type
+
+    json = ActiveSupport::JSON.decode(response.body)
+    assert_kind_of Hash, json
+    assert_equal parameters[:chart], json['chart']
+    if parameters[:chart_unit]
+      assert_equal parameters[:chart_unit], json['chart_unit']
+    end
+  end
+
+  def check_chart(parameters = {})
+    should_get_show parameters
+    should_get_render_chart parameters.slice(:chart, :project_id)
   end
 end
diff --git a/plugins/redmine_agile/test/functional/agile_journal_details_controller_test.rb b/plugins/redmine_agile/test/functional/agile_journal_details_controller_test.rb
index d63aa5c..5d3db81 100644
--- a/plugins/redmine_agile/test/functional/agile_journal_details_controller_test.rb
+++ b/plugins/redmine_agile/test/functional/agile_journal_details_controller_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -55,7 +55,7 @@ class AgileJournalDetailsControllerTest < ActionController::TestCase
   end
 
   def test_get_done_ratio
-    compatible_request :get, :done_ratio, :issue_id => 1
+    compatible_request :get, :done_ratio, issue_id: 1
     assert_response :success
     assert_match /% Done/, @response.body
     assert_match /Bug #1/, @response.body
@@ -63,15 +63,30 @@ class AgileJournalDetailsControllerTest < ActionController::TestCase
   end
 
   def test_get_status
-    compatible_request :get, :status, :issue_id => 1
+    compatible_request :get, :status, issue_id: 1
     assert_response :success
     assert_match /Issue statuses/, @response.body
     assert_match /Bug #1/, @response.body
     assert_select '.list td.name', 2
   end
 
+  def test_get_status_with_group
+    compatible_request :get, :status, issue_id: 1, group_by: 'status'
+    assert_response :success
+    assert_match /Issue statuses/, @response.body
+    assert_match /Bug #1/, @response.body
+    assert_select 'tr.group'
+  end
+
+  def test_get_status_csv
+    compatible_request :get, :status, issue_id: 1, format: :csv
+    assert_response :success
+    assert_match 'text/csv', @response.content_type
+    assert_match /#,Created/, @response.body
+  end
+
   def test_get_done_assignee
-    compatible_request :get, :assignee, :issue_id => 1
+    compatible_request :get, :assignee, issue_id: 1
     assert_response :success
     assert_match /Assignee/, @response.body
     assert_match /Bug #1/, @response.body
diff --git a/plugins/redmine_agile/test/functional/agile_queries_controller_test.rb b/plugins/redmine_agile/test/functional/agile_queries_controller_test.rb
deleted file mode 100644
index 37edd27..0000000
--- a/plugins/redmine_agile/test/functional/agile_queries_controller_test.rb
+++ /dev/null
@@ -1,134 +0,0 @@
-# encoding: utf-8
-#
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-require File.expand_path('../../test_helper', __FILE__)
-class AgileQueriesControllerTest < ActionController::TestCase
-  fixtures :projects,
-           :users,
-           :roles,
-           :members,
-           :member_roles,
-           :issues,
-           :issue_statuses,
-           :versions,
-           :trackers,
-           :projects_trackers,
-           :issue_categories,
-           :enabled_modules,
-           :enumerations,
-           :attachments,
-           :workflows,
-           :custom_fields,
-           :custom_values,
-           :custom_fields_projects,
-           :custom_fields_trackers,
-           :time_entries,
-           :journals,
-           :journal_details,
-           :queries
-
-  RedmineAgile::TestCase.create_fixtures(Redmine::Plugin.find(:redmine_agile).directory + '/test/fixtures/', [:queries])
-
-  def setup
-    RedmineAgile::TestCase.prepare
-  end
-
-  def test_get_index
-    @request.session[:user_id] = 1
-    compatible_request :get, :index
-    assert_response :success
-    assert_match /Agile boards/, @response.body
-  end
-
-  def test_get_new
-    @request.session[:user_id] = 1
-    compatible_request :get, :new
-    assert_response :success
-    assert_match /New agile board/, @response.body
-  end
-
-  def test_get_edit
-    @request.session[:user_id] = 1
-    compatible_request :get, :edit, :id => create_agile_query.id
-    assert_response :success
-    assert_match /Edit agile board/, @response.body
-  end
-
-  def test_get_edit_for_public_board
-    @request.session[:user_id] = 2
-    compatible_request :get, :edit, id: AgileQuery.find(102).id # Public board query
-    assert_response :success
-    assert_match /Edit agile board/, @response.body
-  end
-
-  def test_post_create
-    @request.session[:user_id] = 1
-    params = { :query => { :name => 'Test', :group_by => '' },
-               :query_is_for_all => '1', :default_columns => '1', :f => ['status_id', ''],
-               :op => { 'status_id' => 'o' }, :c => ['tracker', 'assigned_to'] }
-    if Redmine::VERSION.to_s < '2.4'
-      params[:query][:is_public] = true
-    else
-      params[:query][:visibility] = '0'
-    end
-    assert_difference 'AgileQuery.count' do
-      compatible_request :post, :create, params
-      assert_response :redirect
-    end
-  end
-
-  def test_put_update
-    @request.session[:user_id] = 1
-    params = { :query => { :name => 'Test changed', :group_by => ''}, :id => create_agile_query.id}
-    Redmine::VERSION.to_s < '2.4' ? params[:query][:is_public] = true : params[:query][:visibility] = '0'
-    compatible_request :put, :update, params
-    assert_response :redirect
-  end
-  def test_save_wip_options
-    @request.session[:user_id] = 1
-    wp_params = { '1' => '5', '2' => '5', '3' => '5', '4' => '5', '5' => '', '6' => '' }
-    params = { :query => { :name => 'Test', :group_by => '', :is_default => '1' },
-               :query_is_for_all => '1',
-               :default_columns => '1',
-               :f_status => ['1', '2', '3', '4'],
-               :wp => wp_params,
-               :op => { 'status_id' => 'o' },
-               :c => ['tracker', 'assigned_to'] }
-    compatible_request :post, :create, params
-    query = Query.last
-    assert_equal true, query.is_default?
-    assert_equal wp_params, query.options[:wp]
-    assert_equal ['1', '2', '3', '4'], query.options[:f_status]
-  end if Redmine::VERSION.to_s > '2.4'
-
-private
-
-  def create_agile_query
-    query = AgileQuery.new(:name => 'Board for specific project',
-                           :user_id => 1,
-                           :project_id => 1,
-                           :filters => { :tracker_id => { :values => ['3'], :operator => '=' } })
-    Redmine::VERSION.to_s < '2.4' ? query.is_public = false : query.visibility = 2
-    query.save
-    query
-  end
-
-end
diff --git a/plugins/redmine_agile/test/functional/agile_versions_controller_test.rb b/plugins/redmine_agile/test/functional/agile_versions_controller_test.rb
deleted file mode 100644
index 5c19385..0000000
--- a/plugins/redmine_agile/test/functional/agile_versions_controller_test.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-# encoding: utf-8
-#
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-require File.expand_path('../../test_helper', __FILE__)
-
-class AgileVersionsControllerTest < ActionController::TestCase
-  fixtures :projects,
-           :users,
-           :roles,
-           :members,
-           :member_roles,
-           :issues,
-           :issue_statuses,
-           :versions,
-           :trackers,
-           :projects_trackers,
-           :issue_categories,
-           :enabled_modules,
-           :enumerations,
-           :attachments,
-           :workflows,
-           :custom_fields,
-           :custom_values,
-           :custom_fields_projects,
-           :custom_fields_trackers,
-           :time_entries,
-           :journals,
-           :journal_details,
-           :queries
-
-  def setup
-    RedmineAgile.create_issues
-
-    @project_1 = Project.find(1)
-    @project_2 = Project.find(2)
-    @project_3 = Project.find(5)
-
-    EnabledModule.create(:project => @project_1, :name => 'agile')
-    EnabledModule.create(:project => @project_2, :name => 'agile')
-    EnabledModule.create(:project => @project_3, :name => 'agile')
-
-    @request.session[:user_id] = 1
-  end
-
-  def test_get_index
-    compatible_request :get, :index, :project_id => @project_1
-    assert_response :success
-    assert_match /Version planning/, @response.body
-  end
-
-  def test_get_load
-    compatible_xhr_request :get, :load, :version_type => 'backlog', :version_id => '3', :project_id => 'ecookbook'
-    assert_response :success
-  end
-
-  def test_get_autocomplete_id
-    compatible_xhr_request :get, :autocomplete, :project_id => 'ecookbook', :q => '#3'
-    assert_response :success
-    assert_match "Error 281",  @response.body
-  end
-
-  def test_get_autocomplete_text
-    compatible_xhr_request :get, :autocomplete, :project_id => 'ecookbook', :q => 'error'
-    assert_response :success
-    assert_match "Error 281",  @response.body
-  end
-  def test_get_index_with_filter
-    compatible_request :get, :index, version_params
-
-    assert_response :success
-
-    assert_match /Issue 108/, @response.body
-    assert_match /Blaaa/, @response.body
-    assert_match /Issue 105/, @response.body
-    assert_match /Issue 106/, @response.body
-
-    assert_no_match /(Issue 100)|(Issue 101)|(Issue 102)|(Issue 103)/, @response.body
-  end
-
-  def test_get_index_with_filter_and_search_query
-    compatible_request :get, :index, version_params.merge({ :q => 'Blaaa' })
-
-    assert_no_match /Issue 108/,  @response.body
-    assert_match /Blaaa/,  @response.body
-  end
-
-  def test_get_index_with_sp
-    with_agile_settings "estimate_units" => "story_points" do
-      compatible_request :get, :index, :project_id => @project_2
-      assert_response :success
-      assert_select '.issue-card[data-id="100"] span.hours', :text => '10.00sp'
-    end
-  end
-
-  private
-
-  def version_params
-    {
-      :f =>['status_id'],
-      :op => { 'status_id' => '=' },
-      :v => { 'status_id' => ['5'] },
-      :project_id => @project_2
-    }
-  end
-
-end
diff --git a/plugins/redmine_agile/test/functional/issues_controller_test.rb b/plugins/redmine_agile/test/functional/issues_controller_test.rb
index 5ea9766..f124644 100644
--- a/plugins/redmine_agile/test/functional/issues_controller_test.rb
+++ b/plugins/redmine_agile/test/functional/issues_controller_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -53,30 +53,9 @@ class IssuesControllerTest < ActionController::TestCase
     EnabledModule.create(:project => @project_2, :name => 'agile')
     @request.session[:user_id] = 1
   end
-  def test_get_index_with_colors
-    with_agile_settings "color_on" => "issue" do
-      issue = Issue.find(1)
-      issue.color = AgileColor::AGILE_COLORS[:red]
-      issue.save
-      compatible_request :get, :index
-      assert_response :success
-      assert_select 'tr#issue-1.issue.bk-red', 1
-    end
-  end
-
-  def test_post_issue_journal_color
-    with_agile_settings 'color_on' => 'issue' do
-      compatible_request :put, :update, :id => 1, :issue => { :agile_color_attributes => { :color => AgileColor::AGILE_COLORS[:red] } }
-      issue = Issue.find(1)
-      details = issue.journals.order(:id).last.details.last
-      assert issue.color
-      assert_equal 'color', details.prop_key
-      assert_equal AgileColor::AGILE_COLORS[:red], details.value
-    end
-  end
 
   def test_new_issue_with_sp_value
-    with_agile_settings 'estimate_units' => 'story_points' do
+    with_agile_settings 'estimate_units' => 'story_points', 'story_points_on' => '1' do
       compatible_request :get, :new, :project_id => 1
       assert_response :success
       assert_select 'input#issue_agile_data_attributes_story_points'
@@ -92,7 +71,7 @@ class IssuesControllerTest < ActionController::TestCase
   end
 
   def test_create_issue_with_sp_value
-    with_agile_settings 'estimate_units' => 'story_points' do
+    with_agile_settings 'estimate_units' => 'story_points', 'story_points_on' => '1' do
       assert_difference 'Issue.count' do
         compatible_request :post, :create, :project_id => 1, :issue => {
           :subject => 'issue with sp',
@@ -109,7 +88,7 @@ class IssuesControllerTest < ActionController::TestCase
   end
 
   def test_post_issue_journal_story_points
-    with_agile_settings 'estimate_units' => 'story_points' do
+    with_agile_settings 'estimate_units' => 'story_points', 'story_points_on' => '1' do
       compatible_request :put, :update, :id => 1, :issue => { :agile_data_attributes => { :story_points => 100 } }
       issue = Issue.find(1)
       assert_equal 100, issue.story_points
@@ -120,10 +99,10 @@ class IssuesControllerTest < ActionController::TestCase
   end
 
   def test_show_issue_with_story_points
-    with_agile_settings 'estimate_units' => 'story_points' do
+    with_agile_settings 'estimate_units' => 'story_points', 'story_points_on' => '1' do
       compatible_request :get, :show, :id => 1
       assert_response :success
-      assert_select '.attributes', :text => /Story points/, :count => 1
+      assert_select '#issue-form .attributes', :text => /Story points/, :count => 1
     end
   end
 
@@ -135,29 +114,12 @@ class IssuesControllerTest < ActionController::TestCase
                               :totalable_names => [],
                               :sort => [['story_points', 'asc'], ['id', 'desc']]
                             }
-    with_agile_settings 'estimate_units' => 'story_points' do
+    with_agile_settings 'estimate_units' => 'story_points', 'story_points_on' => '1' do
       compatible_request :get, :show, :id => 1
       assert_response :success
-      assert_select '.attributes', :text => /Story points/, :count => 1
+      assert_select '#issue-form .attributes', :text => /Story points/, :count => 1
     end
   ensure
     session[:issue_query] = {}
   end
-  def test_show_issue_form_with_story_points_select
-    with_agile_settings('sp_values' => [1,2,3],
-      'estimate_units' => 'story_points') do
-        compatible_request :get, :new, :project_id => 1
-        assert_response :success
-        assert_select 'select#issue_agile_data_attributes_story_points'
-    end
-  end
-
-  def test_dont_show_story_points_select_when_no_sp_values
-    with_agile_settings('sp_values' => [],
-      'estimate_units' => 'story_points') do
-        compatible_request :get, :new, :project_id => 1
-        assert_response :success
-        assert_select 'select#issue_agile_data_attributes_story_points', :count => 0
-    end
-  end
 end
diff --git a/plugins/redmine_agile/test/functional/projects_controller_test.rb b/plugins/redmine_agile/test/functional/projects_controller_test.rb
index da7e3f2..67d3a32 100644
--- a/plugins/redmine_agile/test/functional/projects_controller_test.rb
+++ b/plugins/redmine_agile/test/functional/projects_controller_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -36,22 +36,4 @@ class ProjectsControllerTest < ActionController::TestCase
     EnabledModule.create(:project => @project_2, :name => 'agile')
     @request.session[:user_id] = 1
   end
-  def test_get_index_with_colors
-    with_agile_settings 'color_on' => 'project' do
-      compatible_request :get, :settings, :id => @project_1
-      assert_response :success
-      assert_select '#project_agile_color_attributes_color', 1
-    end
-  end
-
-
-  def test_save_project_with_color
-    with_agile_settings 'color_on' => 'project' do
-      compatible_request :post, :update, :id => @project_1, :project => { :name => 'Test changed name',
-        :agile_color_attributes => { :color => AgileColor::AGILE_COLORS[:red] } }
-      @project_1.reload
-      assert_equal 'Test changed name', @project_1.name
-      assert_equal AgileColor::AGILE_COLORS[:red], @project_1.color
-    end
-  end
 end
diff --git a/plugins/redmine_agile/test/functional/users_controller_test.rb b/plugins/redmine_agile/test/functional/users_controller_test.rb
index 213f427..71aa90c 100644
--- a/plugins/redmine_agile/test/functional/users_controller_test.rb
+++ b/plugins/redmine_agile/test/functional/users_controller_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -33,19 +33,4 @@ class UsersControllerTest < ActionController::TestCase
     @user = User.find(1)
     @request.session[:user_id] = @user.id
   end
-  def test_get_index_with_colors
-    with_agile_settings 'color_on' => 'user' do
-      compatible_request :get, :edit, :id => @user.id
-      assert_response :success
-      assert_select '#user_agile_color_attributes_color', 1
-    end
-  end if Redmine::VERSION.to_s > '2.4'
-
-  def test_save_user_with_color
-    with_agile_settings 'color_on' => 'user' do
-      compatible_request :post, :update, :id => @user.id,
-        :user => { :agile_color_attributes => { :color => AgileColor::AGILE_COLORS[:red] } }
-      assert_equal AgileColor::AGILE_COLORS[:red], @user.reload.color
-    end
-  end if Redmine::VERSION.to_s > '2.4'
 end
diff --git a/plugins/redmine_agile/test/integration/common_views_test.rb b/plugins/redmine_agile/test/integration/common_views_test.rb
index c595266..eb3936c 100644
--- a/plugins/redmine_agile/test/integration/common_views_test.rb
+++ b/plugins/redmine_agile/test/integration/common_views_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_agile/test/test_helper.rb b/plugins/redmine_agile/test/test_helper.rb
index efa1661..73e3b83 100644
--- a/plugins/redmine_agile/test/test_helper.rb
+++ b/plugins/redmine_agile/test/test_helper.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -73,6 +73,11 @@ module RedmineAgile
       send(type, action, :params => parameters, :xhr => true)
     end
 
+    def compatible_api_request(type, action, parameters = {}, headers = {})
+      return send(type, action, :params => parameters, :headers => headers) if Rails.version >= '5.1'
+      send(type, action, parameters, headers)
+    end
+
     def agile_issues_in_list
       ids = css_select('p.issue-id input').map { |tag| tag.to_s.to_s[/.*?value=\"(\d+)".*?/, 1] }.map(&:to_i)
       Issue.where(:id => ids).sort_by { |issue| ids.index(issue.id) }
@@ -105,8 +110,8 @@ module RedmineAgile
         # journal = issue.init_journal(User.current)
         issue.done_ratio = value
         issue.save
-        issue.update_attributes(:updated_on => change_date)
-        Journal.last.update_attributes(:created_on => change_date)
+        issue.update(:updated_on => change_date)
+        Journal.last.update(:created_on => change_date)
       end
 
       def close_issue(issue, closed_on)
@@ -116,11 +121,11 @@ module RedmineAgile
         days_count = (issue.due_date - issue.created_on).to_i
         change_date = issue.created_on
         change_date = change_date + rand((issue.due_date - days_count).to_i)
-        issue.update_attributes(:status_id => in_status.id, :updated_on => change_date)
-        journal.update_attributes(:created_on => change_date)
+        issue.update(:status_id => in_status.id, :updated_on => change_date)
+        journal.update(:created_on => change_date)
         change_date = change_date + rand((issue.due_date - days_count).to_i)
-        issue.update_attributes(:status_id => done_status.id, :updated_on => change_date)
-        journal.update_attributes(:created_on => change_date)
+        issue.update(:status_id => done_status.id, :updated_on => change_date)
+        journal.update(:created_on => change_date)
         issue
       end
 
@@ -354,6 +359,7 @@ class RedmineAgile::TestCase
       r.permissions << :manage_public_agile_queries
       r.permissions << :add_agile_queries
       r.permissions << :view_agile_queries
+      r.permissions << :agile_versions
       r.save
     end
   end
diff --git a/plugins/redmine_agile/test/ui/agile_board_ui_test.rb b/plugins/redmine_agile/test/ui/agile_board_ui_test.rb
index 3c99997..b27389f 100644
--- a/plugins/redmine_agile/test/ui/agile_board_ui_test.rb
+++ b/plugins/redmine_agile/test/ui/agile_board_ui_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_agile/test/unit/agile_color_test.rb b/plugins/redmine_agile/test/unit/agile_color_test.rb
deleted file mode 100644
index 3466977..0000000
--- a/plugins/redmine_agile/test/unit/agile_color_test.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# encoding: utf-8
-#
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-require File.expand_path('../../test_helper', __FILE__)
-
-class AgileColorTest < ActiveSupport::TestCase
-  fixtures :projects,
-           :users,
-           :roles,
-           :members,
-           :member_roles,
-           :issues,
-           :issue_statuses,
-           :versions,
-           :trackers,
-           :projects_trackers,
-           :issue_categories,
-           :enabled_modules,
-           :enumerations,
-           :attachments,
-           :workflows,
-           :custom_fields,
-           :custom_values,
-           :custom_fields_projects,
-           :custom_fields_trackers,
-           :time_entries,
-           :journals,
-           :journal_details,
-           :queries
-
-  # Replace this with your real tests.
-  def test_save_color
-    issue = Issue.find(1)
-    assert_nil issue.color
-    issue.agile_color.color = AgileColor::AGILE_COLORS[:red]
-    assert issue.save
-    issue.reload
-    assert_equal AgileColor::AGILE_COLORS[:red], issue.color
-  end
-
-  def test_delete_color
-    issue = Issue.find(1)
-    assert_nil issue.color
-    issue.agile_color.color = AgileColor::AGILE_COLORS[:red]
-    assert issue.save
-    issue.reload
-    color = issue.agile_color
-    assert issue.destroy
-    assert !AgileColor.exists?(color.id)
-  end
-
-  def test_color_for_spent_time
-    assert_equal 'gray', AgileColor.for_spent_time(nil, nil)
-    assert_equal 'gray', AgileColor.for_spent_time(nil, 10)
-    assert_equal 'green', AgileColor.for_spent_time(20, 10)
-    assert_equal 'yellow', AgileColor.for_spent_time(20, 19)
-    assert_equal 'red', AgileColor.for_spent_time(20, 30)
-    assert_equal 'purple', AgileColor.for_spent_time(20, 42)
-    assert_equal 'gray', AgileColor.for_spent_time(0.0, 0.0)
-    assert_equal 'gray', AgileColor.for_spent_time(0.0, 8.8)
-  end
-end
diff --git a/plugins/redmine_agile/test/unit/agile_data_test.rb b/plugins/redmine_agile/test/unit/agile_data_test.rb
index cbec736..13bf20a 100644
--- a/plugins/redmine_agile/test/unit/agile_data_test.rb
+++ b/plugins/redmine_agile/test/unit/agile_data_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_agile/test/unit/agile_versions_query_test.rb b/plugins/redmine_agile/test/unit/agile_versions_query_test.rb
deleted file mode 100644
index c473fcc..0000000
--- a/plugins/redmine_agile/test/unit/agile_versions_query_test.rb
+++ /dev/null
@@ -1,138 +0,0 @@
-# encoding: utf-8
-#
-# This file is a part of Redmin Agile (redmine_agile) plugin,
-# Agile board plugin for redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_agile is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_agile is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_agile.  If not, see <http://www.gnu.org/licenses/>.
-
-require File.expand_path('../../test_helper', __FILE__)
-
-class AgileVersionsQueryTest < ActiveSupport::TestCase
-  fixtures :projects,
-           :users,
-           :roles,
-           :members,
-           :member_roles,
-           :issue_statuses,
-           :issues,
-           :versions,
-           :trackers,
-           :projects_trackers,
-           :issue_categories,
-           :enabled_modules,
-           :enumerations,
-           :attachments,
-           :workflows,
-           :custom_fields,
-           :custom_values,
-           :custom_fields_projects,
-           :custom_fields_trackers,
-           :time_entries,
-           :journals,
-           :journal_details,
-           :queries
-  def filter_fields
-    ['assigned_to_id', 'tracker_id', 'status_id', 'author_id', 'category_id'] # estimated_hours
-  end
-
-  def setup
-    super
-    RedmineAgile.create_issues
-    @query = AgileVersionsQuery.new(:name => '_')
-            @query.project = Project.find(2)
-    @backlog_version = Version.find(7)
-    @current_version = Version.find(5)
-    User.current = User.find(1) # because issues selected according permissions
-  end
-
-  def test_backlog_version
-    assert_equal @backlog_version, @query.backlog_version
-  end
-
-  def test_current_version
-    assert_equal @current_version, @query.current_version
-  end
-
-  def test_backlog_issues
-    assert_equal [100, 101, 102, 103], @query.backlog_version_issues.map(&:id).sort
-  end
-
-  def test_current_issues
-    assert_equal [104], @query.current_version_issues.map(&:id).sort
-  end
-  def test_current_version_issues
-    assert_equal 1, @query.current_version_issues.count
-
-    @query.build_from_params(:f => ['status_id'], :o => ['status_id' => '*'], :v => ['status_id' => IssueStatus.all.map(&:id)])
-    assert_equal [104, 105, 106], @query.current_version_issues.map(&:id).sort
-  end
-
-  def test_filters_backlog_issues_in
-    filter_fields.each do |filter|
-      m = "Error in the #{filter} filter"
-      hash = {
-        :f =>[filter],
-        :op => { filter => '='},
-        :v => { filter => ['1', '3'] } }
-
-      @query.build_from_params(hash)
-      assert_equal [1, 3], @query.backlog_version_issues.collect { |issue| issue.send(filter.to_sym).to_i }.uniq.sort, m
-    end
-  end
-
-  def test_filters_backlog_issues_not_in
-    filter_fields.each do |filter|
-      m = "Error in the #{filter} filter"
-      hash = {
-        :f => [filter],
-        :op => { filter => '!' },
-        :v => { filter => ['1', '3'] } }
-
-      @query.build_from_params(hash)
-      issues = @query.backlog_version_issues.collect { |issue| issue.send(filter.to_sym) }.uniq.sort
-      assert_equal [], [1, 3] & issues, m
-    end
-  end
-
-  def test_with_few_filters
-    hash = {
-        :f => ['assigned_to_id', 'priority_id', 'tracker_id', 'estimated_hours'],
-        :op => { 'assigned_to_id' => '*', 'priority_id' => '!', 'tracker_id' => '=', 'estimated_hours' => '><' },
-        :v => { 'priority_id' => ['3'], 'tracker_id' => ['1', '2', '3'], 'estimated_hours' => ['2', '7'] } }
-    @query.build_from_params(hash)
-    assert_equal [100], @query.backlog_version_issues.map(&:id)
-    assert_equal [105], @query.current_version_issues.map(&:id).sort
-  end
-
-  def test_no_assigned_to
-    hash = {
-        :f => ['assigned_to_id'], :op => { 'assigned_to_id' => '!*' }
-    }
-    @query.build_from_params(hash)
-    assert_equal [104], @query.current_version_issues.map(&:id).sort
-  end
-
-  def test_no_version_issues
-    hash = {
-        :f =>['tracker_id'],
-        :op => { 'tracker_id' => '=' },
-        :v => { 'tracker_id' => ['1', '2', '3'] } }
-    @query.build_from_params(hash)
-    assert_equal [107, 109], @query.no_version_issues(:q => 'bla').map(&:id).sort
-    assert_equal [109], @query.no_version_issues(:q => '#109').map(&:id)
-  end
-end
diff --git a/plugins/redmine_agile/test/unit/helpers/agile_boards_helper_test.rb b/plugins/redmine_agile/test/unit/helpers/agile_boards_helper_test.rb
index 3dc35ff..accb368 100644
--- a/plugins/redmine_agile/test/unit/helpers/agile_boards_helper_test.rb
+++ b/plugins/redmine_agile/test/unit/helpers/agile_boards_helper_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmin Agile (redmine_agile) plugin,
 # Agile board plugin for redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_agile is free software: you can redistribute it and/or modify
@@ -38,135 +38,6 @@ class AgileBoardsHelperTest < ActiveSupport::TestCase
     EnabledModule.create(:project => Project.find(1), :name => 'issue_tracking') if RedmineAgile.use_checklist?
   end
 
-  def test_render_board_headers_flat
-    columns = [
-      status_1 = IssueStatus.create(:name => "ToDo"),
-      status_2 = IssueStatus.create(:name => "Doing"),
-      status_3 = IssueStatus.create(:name => "Done"),
-    ]
-
-    html = %{
-      <tr><th data-column-id="#{status_1.id}">ToDo (<span class="count">0</span>)</th><th data-column-id="#{status_2.id}">Doing (<span class="count">0</span>)</th><th data-column-id="#{status_3.id}">Done (<span class="count">0</span>)</th></tr>
-    }.strip.gsub(/\r/, "")
-
-    headers = render_board_headers(columns)
-
-    assert_equal html, headers
-  end
-
-  def test_render_board_headers_rowspan
-    # | One  | Two    | Three |
-    # |      | Doing  | Done  |
-    columns = [
-      status_1 = IssueStatus.create(:name => "One"),
-      status_2 = IssueStatus.create(:name => "Two:Doing"),
-      status_3 = IssueStatus.create(:name => "Three:Done"),
-    ]
-
-    html = %{
-       <tr><th data-column-id="#{status_1.id}" rowspan="2">One (<span class="count">0</span>)</th><th>Two</th><th>Three</th></tr><tr><th data-column-id="#{status_2.id}">Doing (<span class="count">0</span>)</th><th data-column-id="#{status_3.id}">Done (<span class="count">0</span>)</th></tr>
-    }.strip.gsub(/\r/, "")
-
-    headers = render_board_headers(columns)
-
-    assert_equal html, headers
-  end
-
-  def test_render_board_headers_colspan
-    # | One  |       Two      |
-    # |      | Doing  | Done  |
-    columns = [
-      status_1 = IssueStatus.create(:name => "One"),
-      status_2 = IssueStatus.create(:name => "Two:Doing"),
-      status_3 = IssueStatus.create(:name => "Two:Done"),
-    ]
-
-    html = %{
-       <tr><th data-column-id="#{status_1.id}" rowspan="2">One (<span class="count">0</span>)</th><th colspan="2">Two</th></tr><tr><th data-column-id="#{status_2.id}">Doing (<span class="count">0</span>)</th><th data-column-id="#{status_3.id}">Done (<span class="count">0</span>)</th></tr>
-    }.strip.gsub(/\r/, "")
-
-    headers = render_board_headers(columns)
-
-    assert_equal html, headers
-  end
-
-  def test_render_board_headers_colspan_first
-    # |      One      | Two  |
-    # | ToDo  | Doing | Done |
-    columns = [
-      status_1 = IssueStatus.create(:name => "One:ToDo"),
-      status_2 = IssueStatus.create(:name => "One:Doing"),
-      status_3 = IssueStatus.create(:name => "Two:Done"),
-    ]
-
-    html = %{
-       <tr><th colspan="2">One</th><th>Two</th></tr><tr><th data-column-id="#{status_1.id}">ToDo (<span class="count">0</span>)</th><th data-column-id="#{status_2.id}">Doing (<span class="count">0</span>)</th><th data-column-id="#{status_3.id}">Done (<span class="count">0</span>)</th></tr>
-    }.strip.gsub(/\r/, "")
-
-    headers = render_board_headers(columns)
-
-    assert_equal html, headers
-  end
-
-  def test_render_board_headers_three_levels
-    # |         Mega One     |            |
-    # |      One      | Two  |  Mega Two  |
-    # | ToDo  | Doing | Done |            |
-    columns = [
-      status_1 = IssueStatus.create(:name => "Mega One:One:ToDo"),
-      status_2 = IssueStatus.create(:name => "Mega One:One:Doing"),
-      status_3 = IssueStatus.create(:name => "Mega One:Two:Done"),
-      status_4 = IssueStatus.create(:name => "Mega Two"),
-    ]
-
-    html = %{
-       <tr><th colspan="3">Mega One</th><th data-column-id="#{status_4.id}" rowspan="3">Mega Two (<span class="count">0</span>)</th></tr><tr><th colspan="2">One</th><th>Two</th></tr><tr><th data-column-id="#{status_1.id}">ToDo (<span class="count">0</span>)</th><th data-column-id="#{status_2.id}">Doing (<span class="count">0</span>)</th><th data-column-id="#{status_3.id}">Done (<span class="count">0</span>)</th></tr>
-    }.strip.gsub(/\r/, "")
-
-    headers = render_board_headers(columns)
-
-    assert_equal html, headers
-  end
-
-  def test_render_board_headers_same_name_levels
-    # |         Mega One     |            | Mega Two |
-    # |      One      | Two  |  Mega Two  |    One   |
-    # | ToDo  | Doing | Done |            |          |
-    columns = [
-      status_1 = IssueStatus.create(:name => "Mega One:One:ToDo"),
-      status_2 = IssueStatus.create(:name => "Mega One:One:Doing"),
-      status_3 = IssueStatus.create(:name => "Mega One:Two:Done"),
-      status_4 = IssueStatus.create(:name => "Mega Two"),
-      status_5 = IssueStatus.create(:name => "Mega Two:One"),
-    ]
-
-    html = %{
-       <tr><th colspan="3">Mega One</th><th data-column-id="#{status_4.id}" rowspan="3">Mega Two (<span class="count">0</span>)</th><th>Mega Two</th></tr><tr><th colspan="2">One</th><th>Two</th><th data-column-id="#{status_5.id}" rowspan="2">One (<span class="count">0</span>)</th></tr><tr><th data-column-id="#{status_1.id}">ToDo (<span class="count">0</span>)</th><th data-column-id="#{status_2.id}">Doing (<span class="count">0</span>)</th><th data-column-id="#{status_3.id}">Done (<span class="count">0</span>)</th></tr>
-    }.strip.gsub(/\r/, "")
-
-    headers = render_board_headers(columns)
-
-    assert_equal html, headers
-  end
-
-  def test_render_board_headers_keep_order
-    # | Dev   |  Testing     | Dev  |
-    # | ToDo  |              | Done |
-    columns = [
-      status_1 = IssueStatus.create(:name => "Dev:ToDo"),
-      status_2 = IssueStatus.create(:name => "Testing"),
-      status_3 = IssueStatus.create(:name => "Dev:Done")
-    ]
-
-    html = %{
-       <tr><th>Dev</th><th data-column-id="#{status_2.id}" rowspan="2">Testing (<span class="count">0</span>)</th><th>Dev</th></tr><tr><th data-column-id="#{status_1.id}">ToDo (<span class="count">0</span>)</th><th data-column-id="#{status_3.id}">Done (<span class="count">0</span>)</th></tr>
-    }.strip.gsub(/\r/, "")
-
-    headers = render_board_headers(columns)
-
-    assert_equal html, headers
-  end
-
   def test_time_in_state
     hour10 = Time.now - 10.hours
     assert_equal "#{I18n.t('datetime.distance_in_words.x_hours', :count => 10)}", time_in_state(hour10)
diff --git a/plugins/redmine_checklists/app/controllers/checklist_template_categories_controller.rb b/plugins/redmine_checklists/app/controllers/checklist_template_categories_controller.rb
deleted file mode 100644
index 03893bc..0000000
--- a/plugins/redmine_checklists/app/controllers/checklist_template_categories_controller.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-class ChecklistTemplateCategoriesController < ApplicationController
-  unloadable
-
-  before_action :find_category, :only => [:destroy, :update, :edit]
-
-  def create
-    @category = ChecklistTemplateCategory.new
-    @category.safe_attributes = params[:category]
-    if @category.save
-      flash[:notice] = l(:notice_successful_create)
-      redirect_to_list
-    else
-      render :action => 'new'
-    end
-  end
-
-  def destroy
-    @category.destroy
-    redirect_to_list
-  rescue
-    flash[:error] = l(:label_finance_can_not_delete_category)
-    redirect_to_list
-  end
-
-  def update
-    @category.safe_attributes = params[:category]
-    @category.insert_at(@category.position) if @category.position_changed?
-    if @category.save
-      respond_to do |format|
-        format.html do
-          flash[:notice] = l(:notice_successful_update)
-          redirect_to_list
-        end
-        format.js { head 200 }
-      end
-    else
-      respond_to do |format|
-        format.html { render :action => 'edit' }
-        format.js { head 422 }
-      end
-    end
-  end
-
-private
-
-  def find_category
-    @category = ChecklistTemplateCategory.find(params[:id])
-  end
-
-  def redirect_to_list
-    redirect_to :action =>"plugin", :id => "redmine_checklists", :controller => "settings", :tab => 'checklist_template_categories'
-  end
-end
diff --git a/plugins/redmine_checklists/app/controllers/checklist_templates_controller.rb b/plugins/redmine_checklists/app/controllers/checklist_templates_controller.rb
deleted file mode 100644
index 651d907..0000000
--- a/plugins/redmine_checklists/app/controllers/checklist_templates_controller.rb
+++ /dev/null
@@ -1,94 +0,0 @@
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-class ChecklistTemplatesController < ApplicationController
-  unloadable
-
-  before_action :find_checklist_template, :except => [:new, :create, :index]
-  before_action :find_optional_project, :only => [:new, :create, :add, :destroy, :edit, :update]
-  before_action :require_admin, :only => [:index]
-
-  def new
-    @checklist_template = ChecklistTemplate.new
-    @checklist_template.user = User.current
-    @checklist_template.project = @project
-    @checklist_template.is_public = false unless User.current.allowed_to?(:manage_checklist_templates, @project) || User.current.admin?
-  end
-
-  def create
-    @checklist_template = ChecklistTemplate.new
-    @checklist_template.safe_attributes = params[:checklist_template]
-    @checklist_template.user = User.current
-    @checklist_template.project = params[:checklist_template_is_for_all] && User.current.admin? ? nil : @project
-    @checklist_template.is_public = false unless User.current.allowed_to?(:manage_checklist_templates, @project) || User.current.admin?
-
-    if @checklist_template.save
-      flash[:notice] = l(:notice_successful_create)
-      redirect_to_project_or_global
-    else
-      render :action => 'new', :layout => !request.xhr?
-    end
-  end
-
-  def edit
-  end
-
-  def update
-    @checklist_template.safe_attributes = params[:checklist_template]
-    @checklist_template.project = nil if params[:checklist_template_is_for_all]
-    @checklist_template.project = @project if params[:checklist_template][:is_public] == '1' && !User.current.admin?
-    @checklist_template.project = (params[:checklist_template_is_for_all] && User.current.admin?) ? nil : @project
-    @checklist_template.is_public = false unless User.current.allowed_to?(:manage_checklist_templates, @project) || User.current.admin?
-
-    if @checklist_template.save
-      flash[:notice] = l(:notice_successful_update)
-      redirect_to_project_or_global
-    else
-      render :action => 'edit'
-    end
-  end
-
-  def destroy
-    @checklist_template.destroy
-    redirect_to_project_or_global
-  end
-
-private
-  def redirect_to_project_or_global
-    redirect_to @project ? settings_project_path(@project, :tab => 'checklist_template') : {:action => "plugin", :id => "redmine_checklists", :controller => "settings", :tab => 'checklist_templates'}
-  end
-
-  def find_issue
-    @issue = Issue.find(params[:issue_id])
-    @project = @issue.project
-  rescue ActiveRecord::RecordNotFound
-    render_404
-  end
-
-  def find_checklist_template
-    template_scope = ChecklistTemplate.where(:id => params[:id].to_i)
-    template_scope = template_scope.where('user_id = ? OR is_public = ?', User.current.id, true) unless User.current.admin?
-    @checklist_template = template_scope.first
-    raise ActiveRecord::RecordNotFound unless @checklist_template.present?
-    @project = @checklist_template.project
-  rescue ActiveRecord::RecordNotFound
-    render_404
-  end
-
-end
diff --git a/plugins/redmine_checklists/app/controllers/checklists_controller.rb b/plugins/redmine_checklists/app/controllers/checklists_controller.rb
index 5cf9545..e760c09 100644
--- a/plugins/redmine_checklists/app/controllers/checklists_controller.rb
+++ b/plugins/redmine_checklists/app/controllers/checklists_controller.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -54,6 +54,7 @@ class ChecklistsController < ApplicationController
     respond_to do |format|
       format.api {
         if @checklist_item.save
+          recalculate_issue_ratio(@checklist_item)
           render :action => 'show', :status => :created, :location => checklist_url(@checklist_item)
         else
           render_validation_errors(@checklist_item)
@@ -66,7 +67,8 @@ class ChecklistsController < ApplicationController
     @checklist_item.safe_attributes = params[:checklist]
     respond_to do |format|
       format.api {
-        if @checklist_item.save
+        if with_issue_journal { @checklist_item.save }
+          recalculate_issue_ratio(@checklist_item)
           render_api_ok
         else
           render_validation_errors(@checklist_item)
@@ -77,27 +79,12 @@ class ChecklistsController < ApplicationController
 
   def done
     (render_403; return false) unless User.current.allowed_to?(:done_checklists, @checklist_item.issue.project)
-    old_checklist_items = @checklist_item.issue.checklists.to_a
-    @checklist_item.is_done = params[:is_done] == 'true'
 
-    if @checklist_item.save
-      journal = Journal.new(:journalized => @checklist_item.issue, :user => User.current)
-      checklist_items = Checklist.where(:issue_id => @checklist_item.issue.id).to_a
-      detail = JournalDetail.new( :property => 'attr',
-                                  :prop_key => 'checklist',
-                                  :old_value => old_checklist_items.to_json.to_s,
-                                  :value => checklist_items.to_json.to_s,
-                                  :journal => journal
-                                  )
-      if JournalChecklistHistory.can_fixup?(detail)
-        JournalChecklistHistory.fixup(detail)
-      else
-        journal.details << detail
-      end
-      journal.save unless journal.destroyed?
-      if (Setting.issue_done_ratio == "issue_field") && RedmineChecklists.settings["issue_done_ratio"].to_i > 0
-        Checklist.recalc_issue_done_ratio(@checklist_item.issue.id)
-        @checklist_item.issue.reload
+    with_issue_journal do
+      @checklist_item.is_done = params[:is_done] == 'true'
+      if @checklist_item.save
+        recalculate_issue_ratio(@checklist_item)
+        true
       end
     end
     respond_to do |format|
@@ -121,4 +108,16 @@ class ChecklistsController < ApplicationController
   rescue ActiveRecord::RecordNotFound
     render_404
   end
+
+  def with_issue_journal(&block)
+    return unless yield
+    true
+  end
+
+  def recalculate_issue_ratio(checklist_item)
+    if (Setting.issue_done_ratio == 'issue_field') && RedmineChecklists.issue_done_ratio?
+      Checklist.recalc_issue_done_ratio(checklist_item.issue.id)
+      checklist_item.issue.reload
+    end
+  end
 end
diff --git a/plugins/redmine_checklists/app/helpers/checklists_helper.rb b/plugins/redmine_checklists/app/helpers/checklists_helper.rb
index e1459db..16c73cb 100644
--- a/plugins/redmine_checklists/app/helpers/checklists_helper.rb
+++ b/plugins/redmine_checklists/app/helpers/checklists_helper.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -54,20 +54,5 @@ module ChecklistsHelper
       ""
     end
   end
-  def template_options_for_select(project = nil, tracker = nil)
-    scoped = ChecklistTemplate.visible
-    scoped = scoped.in_project_and_global(project) if project.present?
-    scoped = scoped.for_tracker_and_global(tracker) if tracker.present?
-    templates = scoped.eager_load(:category).to_a
-    without_category = templates.select{ |x| x.category.nil? }.map{ |x| [x.name, x.id, {'data-template-items' => x.template_items}] }
-    with_category = templates.select{ |x| x.category }
-    options_for_select(
-      [[l(:label_select_template), '']] + without_category
-    ) +
-    grouped_options_for_select(
-        with_category.group_by{ |x| x.category.try(:name) }.
-        map{ |k,v| [ k, v.map{ |x| [ x.name, x.id, {'data-template-items' => x.template_items} ] } ] }
-    )
-  end
 
 end
diff --git a/plugins/redmine_checklists/app/models/checklist.rb b/plugins/redmine_checklists/app/models/checklist.rb
index 763a2d2..97b964a 100644
--- a/plugins/redmine_checklists/app/models/checklist.rb
+++ b/plugins/redmine_checklists/app/models/checklist.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -53,16 +53,16 @@ class Checklist < ActiveRecord::Base
   rcrm_acts_as_list
 
   validates_presence_of :subject
-  validates_length_of :subject, :maximum => 512
+  validates_length_of :subject, maximum: 512
   validates_presence_of :position
   validates_numericality_of :position
 
   def self.recalc_issue_done_ratio(issue_id)
     issue = Issue.find(issue_id)
-    return false if (Setting.issue_done_ratio != "issue_field") || RedmineChecklists.settings["issue_done_ratio"].to_i < 1 || issue.checklists.empty?
-    done_checklist = issue.checklists.map{|c| c.is_done ? 1 : 0}
+    return false if (Setting.issue_done_ratio != 'issue_field') || !RedmineChecklists.issue_done_ratio? || issue.checklists.reject(&:is_section).empty?
+    done_checklist = issue.checklists.reject(&:is_section).map { |c| c.is_done ? 1 : 0 }
     done_ratio = (done_checklist.count(1) * 10) / done_checklist.count * 10
-    issue.update_attribute(:done_ratio, done_ratio)
+    issue.update(done_ratio: done_ratio)
   end
 
   def self.old_format?(detail)
@@ -70,7 +70,7 @@ class Checklist < ActiveRecord::Base
       (detail.value.is_a?(String) && detail.value.match(/^\[[ |x]\] .+$/).present?)
   end
 
-  safe_attributes 'subject', 'position', 'issue_id', 'is_done'
+  safe_attributes 'subject', 'position', 'issue_id', 'is_done', 'is_section'
 
   def editable_by?(usr = User.current)
     usr && (usr.allowed_to?(:edit_checklists, project) || (author == usr && usr.allowed_to?(:edit_own_checklists, project)))
diff --git a/plugins/redmine_checklists/app/models/checklist_template.rb b/plugins/redmine_checklists/app/models/checklist_template.rb
deleted file mode 100644
index 873ab64..0000000
--- a/plugins/redmine_checklists/app/models/checklist_template.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-class ChecklistTemplate < ActiveRecord::Base
-  unloadable
-  include Redmine::SafeAttributes
-  belongs_to :project
-  belongs_to :tracker
-  belongs_to :user
-  belongs_to :category, :class_name => "ChecklistTemplateCategory", :foreign_key => "category_id"
-
-  validates_presence_of :name, :template_items
-  validates_length_of :name, :maximum => 255
-
-  attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
-  safe_attributes 'name', 'template_items', 'project', 'user', 'category_id', 'is_public', 'is_default', 'tracker_id'
-
-  scope :visible, lambda {|*args|
-    user = args.shift || User.current
-    base = Project.allowed_to_condition(user, :manage_checklist_templates, *args)
-    user_id = user.logged? ? user.id : 0
-
-    eager_load(:project).where("(#{table_name}.project_id IS NULL OR (#{base})) AND (#{table_name}.is_public = ? OR #{table_name}.user_id = ?)", true, user_id)
-  }
-
-  scope :in_project_and_global, lambda {|project|
-    where("#{table_name}.project_id IS NULL OR #{table_name}.project_id = 0 OR #{table_name}.project_id = ?", project)
-  }
-
-  scope :for_tracker_and_global, lambda { |tracker|
-    where("#{table_name}.tracker_id IS NULL OR #{table_name}.tracker_id = 0 OR #{table_name}.tracker_id = ?", tracker)
-  }
-
-  scope :for_tracker_id, lambda { |tracker_id| where(:tracker_id => tracker_id) }
-
-  scope :default, lambda { where(:is_default => true) }
-
-  def to_s
-    name
-  end
-
-  def checklists
-    template_items.split("\r\n").map { |subject| Checklist.new(:subject => subject) }
-  end
-
-end
diff --git a/plugins/redmine_checklists/app/models/checklist_template_category.rb b/plugins/redmine_checklists/app/models/checklist_template_category.rb
deleted file mode 100644
index 1059afa..0000000
--- a/plugins/redmine_checklists/app/models/checklist_template_category.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-class ChecklistTemplateCategory < ActiveRecord::Base
-  unloadable
-  include Redmine::SafeAttributes
-
-  attr_protected :id if ActiveRecord::VERSION::MAJOR <= 4
-  scope :ordered, lambda { order(:position) }
-  safe_attributes 'name', 'position'
-
-  rcrm_acts_as_list
-
-  def to_s
-    name
-  end
-end
diff --git a/plugins/redmine_checklists/app/models/journal_checklist_history.rb b/plugins/redmine_checklists/app/models/journal_checklist_history.rb
index ca66b33..76278ff 100644
--- a/plugins/redmine_checklists/app/models/journal_checklist_history.rb
+++ b/plugins/redmine_checklists/app/models/journal_checklist_history.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -19,27 +19,24 @@
 
 class JournalChecklistHistory
   def self.can_fixup?(journal_details)
-    unless journal_details.journal
-      return false
-    end
+    return false if journal_details.journal.nil?
+
     issue = journal_details.journal.journalized
-    unless issue.is_a?(Issue)
-      return false
-    end
+    return false unless issue.is_a?(Issue)
+
     prev_journal_scope = issue.journals.order('id DESC')
     prev_journal_scope = prev_journal_scope.where('id <> ?', journal_details.journal_id) if journal_details.journal_id
     prev_journal = prev_journal_scope.first
-    unless prev_journal
-      return false
-    end
+    return false unless prev_journal
 
+    return false if prev_journal.user_id != journal_details.journal.user_id
     return false if Time.zone.now > prev_journal.created_on + 1.minute
 
-    prev_journal.details.all?{ |x| x.prop_key == 'checklist'} &&
-      journal_details.journal.details.all?{ |x| x.prop_key == 'checklist'} &&
+    prev_journal.details.all? { |x| x.prop_key == 'checklist' } &&
+      journal_details.journal.details.all? { |x| x.prop_key == 'checklist' } &&
       journal_details.journal.notes.blank? &&
       prev_journal.notes.blank? &&
-      prev_journal.details.select{ |x| x.prop_key == 'checklist' }.size == 1
+      prev_journal.details.select { |x| x.prop_key == 'checklist' }.size == 1
   end
 
   def self.fixup(journal_details)
@@ -50,11 +47,9 @@ class JournalChecklistHistory
     checklist_details = prev_journal.details.find{ |x| x.prop_key == 'checklist'}
     if new(checklist_details.old_value, journal_details.value).empty_diff?
       prev_journal.destroy
-      journal_details.journal.send(:send_checklist_notification)
     else
-      checklist_details.update_attribute(:value, journal_details.value)
+      checklist_details.update(value: journal_details.value)
       journal_details.journal.destroy unless journal_details.journal.new_record? && journal_details.journal.details.any?{ |x| x.prop_key != 'checklist'}
-      prev_journal.send(:send_checklist_notification)
     end
   end
 
@@ -63,16 +58,11 @@ class JournalChecklistHistory
     @become = force_object(become)
     @was_ids = @was.map(&:id)
     @become_ids = @become.map(&:id)
-    @added_ids = @become_ids - @was_ids
-    @removed_ids = @was_ids - @become_ids
     @both_ids = @become_ids & @was_ids
   end
 
   def diff
     {
-      :added => @become.select{ |x| @added_ids.include? x.id },
-      :removed => @was.select{ |x| @removed_ids.include? x.id },
-      :renamed => renamed,
       :undone => undone,
       :done => done
     }
@@ -116,17 +106,6 @@ class JournalChecklistHistory
       end
     end.compact
   end
-  def renamed
-    Hash[@both_ids.map do |id|
-      was_subject = was_by_id(id).subject
-      become_subject = become_by_id(id).subject
-      if was_subject != become_subject
-        [was_subject, become_subject]
-      else
-        nil
-      end
-    end.compact]
-  end
 
   def was_by_id(id)
     @was.find{ |x| x.id == id }
diff --git a/plugins/redmine_checklists/app/views/checklist_template_categories/_form.html.erb b/plugins/redmine_checklists/app/views/checklist_template_categories/_form.html.erb
deleted file mode 100644
index 1ae5418..0000000
--- a/plugins/redmine_checklists/app/views/checklist_template_categories/_form.html.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-<%= error_messages_for 'category' %>
-
-<div class="box tabular">
-<p><label for="category_name"><%=l(:field_name)%><span class="required"> *</span></label>
-<%= text_field 'category', 'name'  %></p>
-</div>
diff --git a/plugins/redmine_checklists/app/views/checklist_template_categories/edit.html.erb b/plugins/redmine_checklists/app/views/checklist_template_categories/edit.html.erb
deleted file mode 100644
index d8b7f0f..0000000
--- a/plugins/redmine_checklists/app/views/checklist_template_categories/edit.html.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-<h2><%= link_to l(:label_checklist_template_category_plural), :action =>"plugin", :id => "redmine_checklists", :controller => "settings", :tab => 'checklist_template_categories' %> &#187; <%= @category.name %></h2>
-
-<%= form_tag({:action => 'update', :id => @category}, :class => "tabular edit_checklist_template_category", :method => :put) do %>
-  <%= render :partial => 'form' %>
-  <%= submit_tag l(:button_save) %>
-<% end %>
diff --git a/plugins/redmine_checklists/app/views/checklist_template_categories/new.html.erb b/plugins/redmine_checklists/app/views/checklist_template_categories/new.html.erb
deleted file mode 100644
index 0a30b9d..0000000
--- a/plugins/redmine_checklists/app/views/checklist_template_categories/new.html.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-<h2><%= link_to l(:label_checklist_template_category_plural), :action =>"plugin", :id => "redmine_checklists", :controller => "settings", :tab => 'checklist_template_categories' %> &#187; <%=l(:label_checklist_template_category_new)%></h2>
-
-<%= form_tag({:action => 'create'}, :class => "tabular new_checklist_template_category") do %>
-  <%= render :partial => 'form' %>
-  <%= submit_tag l(:button_create) %>
-<% end %>
diff --git a/plugins/redmine_checklists/app/views/checklist_templates/_form.html.erb b/plugins/redmine_checklists/app/views/checklist_templates/_form.html.erb
deleted file mode 100644
index dc1e998..0000000
--- a/plugins/redmine_checklists/app/views/checklist_templates/_form.html.erb
+++ /dev/null
@@ -1,59 +0,0 @@
-<%= error_messages_for 'checklist_template' %>
-
-<div class="box tabular">
-  <p><%= f.text_field :name, :size => 80, :required => true %></p>
-  <% if ChecklistTemplateCategory.any? %>
-    <p>
-      <%= f.select :category_id, options_for_select([['', nil]] + ChecklistTemplateCategory.all.map{ |x| [x.name, x.id]}, f.object.category_id) %>
-    </p>
-  <% end %>
-
-  <% if User.current.admin? || User.current.allowed_to?(:manage_checklist_templates, @project) %>
-    <p><label><%=l(:field_visible)%></label>
-      <label class="block">
-        <%= f.radio_button :is_public, 0, :checked => !@checklist_template.is_public?,
-                           :onchange => (User.current.admin? ? nil : '$("#checklist_template_is_for_all").removeAttr("disabled");') %>
-        <%= l(:label_visibility_private) %>
-      </label>
-      <label class="block">
-        <%= f.radio_button :is_public, 1, :checked => @checklist_template.is_public?,
-                           :onchange => (User.current.admin? ? nil : '$("#checklist_template_is_for_all").removeAttr("checked"); $("#checklist_template_is_for_all").attr("disabled", true);') %>
-        <%= l(:label_visibility_public) %>
-      </label>
-    </p>
-  <% end %>
-
-  <p><label for="checklist_template_is_for_all"><%=l(:field_is_for_all)%></label>
-  <%= check_box_tag 'checklist_template_is_for_all', 1, @checklist_template.project.nil?,
-        :disabled => (!@checklist_template.new_record? && (@checklist_template.project.nil? || (@checklist_template.is_public? && !User.current.admin?)) || @project.nil? ) %></p>
-
-  <p id='is_default_block'><%= f.check_box :is_default, :label => l(:label_checklist_is_default) %></p>
-
-  <p>
-    <%= f.select :tracker_id, options_from_collection_for_select(@project ? @project.trackers : Tracker.all, :id, :name, f.object.tracker_id), :include_blank => true, :label => l(:label_tracker) %>
-  </p>
-
-  <p><%= f.text_area :template_items, :required => true, :rows => 5 %></p>
-  <p>
-    <em class='info'>
-      <%= l(:label_checklists_description) %>
-    </em>
-  </p>
-</div>
-
-<%= javascript_tag do %>
-  function isDefaultToggle(element){
-    if (element.prop('checked') == true) {
-      $('#is_default_block').hide();
-      $('#checklist_template_is_default').prop('checked', false);
-    } else {
-      $('#is_default_block').show();
-    }
-  }
-
-  $('#checklist_template_is_for_all').change(function(){
-    isDefaultToggle($(this));
-  });
-
-  isDefaultToggle($('#checklist_template_is_for_all'));
-<% end%>
diff --git a/plugins/redmine_checklists/app/views/checklist_templates/edit.html.erb b/plugins/redmine_checklists/app/views/checklist_templates/edit.html.erb
deleted file mode 100644
index a7e2e7a..0000000
--- a/plugins/redmine_checklists/app/views/checklist_templates/edit.html.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-<h2><%=l(:label_checklist_template)%></h2>
-
-<%= labelled_form_for :checklist_template, @checklist_template, :url => { :action => 'update', :project_id => @project } do |f| %>
-<%= render :partial => 'checklist_templates/form', :locals => { :f => f } %>
-<%= submit_tag l(:button_save) %>
-<% end %>
diff --git a/plugins/redmine_checklists/app/views/checklist_templates/new.html.erb b/plugins/redmine_checklists/app/views/checklist_templates/new.html.erb
deleted file mode 100644
index 6c646f9..0000000
--- a/plugins/redmine_checklists/app/views/checklist_templates/new.html.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-<h2><%=l(:label_checklist_new_checklist_template)%></h2>
-
-<%= labelled_form_for :checklist_template, @checklist_template, :url => { :action => 'create', :project_id => @project } do |f| %>
-<%= render :partial => 'checklist_templates/form', :locals => { :f => f } %>
-<%= submit_tag l(:button_create) %>
-<% end %>
diff --git a/plugins/redmine_checklists/app/views/checklists/_checklist_item.html.erb b/plugins/redmine_checklists/app/views/checklists/_checklist_item.html.erb
index 19d094a..0855cdb 100644
--- a/plugins/redmine_checklists/app/views/checklists/_checklist_item.html.erb
+++ b/plugins/redmine_checklists/app/views/checklists/_checklist_item.html.erb
@@ -1,8 +1,11 @@
-<li id="checklist_item_<%= checklist_item.id %>" <%= "class=is-done-checklist-item" if checklist_item.is_done %> >
-  <%= check_box_tag 'checklist_item', "", checklist_item.is_done,
-                    :disabled => !User.current.allowed_to?(:done_checklists, checklist_item.issue.project) && !User.current.allowed_to?(:edit_checklists, checklist_item.issue.project),
-                    :data_url => url_for( {:controller => "checklists", :action => "done", :id => checklist_item.id} ), :class => 'checklist-checkbox'
-  %>
+<li id="checklist_item_<%= checklist_item.id %>" class="<%= 'is-done-checklist-item' if checklist_item.is_done %> <%= 'checklist-section' if checklist_item.is_section %>">
+  <% unless checklist_item.is_section %>
+    <%= check_box_tag 'checklist_item', '', checklist_item.is_done,
+                      disabled: !User.current.allowed_to?(:done_checklists, checklist_item.issue.project) && !User.current.allowed_to?(:edit_checklists, checklist_item.issue.project),
+                      data_url: url_for({ controller: 'checklists', action: 'done', id: checklist_item.id }),
+                      class: 'checklist-checkbox',
+                      id: "checklist-checkbox-#{checklist_item.id}"
+    %>
+  <% end %>
   <%= textilizable(checklist_item, :subject).gsub(/<\/?(p|h\d+|li|ul)>/, '').strip.html_safe %>
-
 </li>
diff --git a/plugins/redmine_checklists/app/views/checklists/done.js.erb b/plugins/redmine_checklists/app/views/checklists/done.js.erb
index 9fc91bd..ac8b878 100644
--- a/plugins/redmine_checklists/app/views/checklists/done.js.erb
+++ b/plugins/redmine_checklists/app/views/checklists/done.js.erb
@@ -2,3 +2,16 @@ $("#checklist_item_<%= @checklist_item.id %>").toggleClass('is-done-checklist-it
 $('#checklist_form .checklist-item#<%= @checklist_item.id %> input[type=checkbox]').trigger('click');
 $('.issue .attributes table.progress').parent().html('<%= j(progress_bar(@checklist_item.issue.done_ratio, :width => '80px', :legend => "#{@checklist_item.issue.done_ratio}%")) %>');
 $('#issue_done_ratio').val('<%= @checklist_item.issue.done_ratio %>');
+
+var checkedCheckboxes = $('#checklist_items .checklist-checkbox:checkbox:checked');
+
+if(localStorage.getItem("hide_closed_checklists") === '0' && checkedCheckboxes.length > 0){
+  $("#checklist_item_<%= @checklist_item.id %>").fadeOut(1000).hide(15);
+  $('#switch_link').text('<%= l("label_checklist_show_closed") %>' + '(' + checkedCheckboxes.length + ')');
+}
+if(checkedCheckboxes.length < 1 && localStorage.getItem("hide_closed_checklists") === '1'){
+  $('#switch_link').hide();
+}
+if(checkedCheckboxes.length > 0){
+  $('#switch_link').show();
+}
diff --git a/plugins/redmine_checklists/app/views/issues/_checklist.html.erb b/plugins/redmine_checklists/app/views/issues/_checklist.html.erb
index edf30ee..9ef810f 100644
--- a/plugins/redmine_checklists/app/views/issues/_checklist.html.erb
+++ b/plugins/redmine_checklists/app/views/issues/_checklist.html.erb
@@ -1,7 +1,9 @@
 <%  if !@issue.blank? &&  @issue.checklists.any? && User.current.allowed_to?(:view_checklists, @project) %>
 <hr />
 <div id="checklist">
-
+  <div class="contextual">
+    <%= link_to l("label_checklist_hide_closed"), '#', id: 'switch_link' %>
+  </div>
   <p><strong><%=l(:label_checklist_plural)%></strong></p>
 
   <ul id="checklist_items">
@@ -11,6 +13,8 @@
   <% end %>
   </ul>
 </div>
-
-
+  <%= javascript_tag do %>
+    new Redmine.ChecklistToggle('<%= l("label_checklist_show_closed") %>', '<%= l("label_checklist_hide_closed") %>');
+    $("#checklist_items").checklist();
+  <% end %>
 <% end %>
diff --git a/plugins/redmine_checklists/app/views/issues/_checklist_fields.html.erb b/plugins/redmine_checklists/app/views/issues/_checklist_fields.html.erb
index 92dd220..f0daba9 100644
--- a/plugins/redmine_checklists/app/views/issues/_checklist_fields.html.erb
+++ b/plugins/redmine_checklists/app/views/issues/_checklist_fields.html.erb
@@ -1,21 +1,26 @@
-<span class="checklist-item <%= new_or_show(f) %>" id = "<%=f.object.id%>">
-  <span class = "checklist-show-only checklist-checkbox"><%= f.check_box :is_done %></span>
-  <span class = "checklist-show checklist-subject <%= done_css(f) %>">
-    <%= f.object.subject %>
+<span class="checklist-item <%= new_or_show(f) %> <%= 'checklist-section' if f.object.is_section %> existing" id="<%= f.object.id %>">
+  <% unless f.object.is_section %>
+    <span class="checklist-show-only checklist-checkbox"><%= f.check_box :is_done %></span>
+  <% end %>
+
+  <span class="checklist-show checklist-subject <%= done_css(f) %>">
+    <%= textilizable(f.object, :subject).gsub(/<\/?(p|h\d+|li|ul)>/, '').strip.html_safe %>
   </span>
-  <span class = "checklist-edit checklist-new checklist-edit-box">
-    <%= text_field_tag nil, f.object.subject, :class => 'edit-box'%>
-    <%= f.hidden_field :subject, :class => 'checklist-subject-hidden' %>
+
+  <span class="checklist-edit checklist-new checklist-edit-box">
+    <%= text_field_tag nil, f.object.subject, class: 'edit-box' %>
+    <%= f.hidden_field :subject, class: 'checklist-subject-hidden' %>
   </span>
-  <span class= "checklist-edit-only checklist-edit-save-button"><%= submit_tag l(:button_save), :type => "button", :class => "item item-save small"%> </span>
-  <span class= "checklist-edit-only checklist-edit-reset-button"><% concat l(:button_cancel)
-  %> </span>
-  <span class = "checklist-show-only checklist-remove"><%= link_to_remove_checklist_fields "", f,
-                                                                            :class => "icon icon-del"  %></span>
-  <%= f.hidden_field :position, :class => 'checklist-item-position' %>
-  <%= f.hidden_field :id, :class => 'checklist-item-id' %>
-  <span class = "icon icon-add checklist-new-only save-new-by-button"></span>
+
+  <span class="checklist-edit-only checklist-edit-save-button"><%= submit_tag l(:button_save), type: 'button', class: 'item item-save small' %> </span>
+  <span class="checklist-edit-only checklist-edit-reset-button"><% concat l(:button_cancel) %> </span>
+  <span class="checklist-show-only checklist-remove"><%= link_to_remove_checklist_fields "", f, class: 'icon icon-del' %></span>
+
+  <%= f.hidden_field :position, class: 'checklist-item-position' %>
+  <%= f.hidden_field :is_section, class: 'checklist-item-is_section' %>
+  <%= f.hidden_field :id, class: 'checklist-item-id' %>
+
+  <span class="icon icon-add checklist-new-only save-new-by-button"></span>
 
  <br>
 </span>
-
diff --git a/plugins/redmine_checklists/app/views/issues/_checklist_form.html.erb b/plugins/redmine_checklists/app/views/issues/_checklist_form.html.erb
index e7f8546..0b5ab22 100644
--- a/plugins/redmine_checklists/app/views/issues/_checklist_form.html.erb
+++ b/plugins/redmine_checklists/app/views/issues/_checklist_form.html.erb
@@ -4,12 +4,10 @@
       <label><%=l(:label_checklist_plural)%></label>
       <% @issue.checklists.build if @issue.checklists.blank? || @issue.checklists.last.subject.present? %>
       <%= fields_for :issue, issue do |f| -%>
-        <%= hidden_field_tag 'issue[checklist_template_id]', params[:issue][:checklist_template_id] if params[:issue] %>
         <span id="checklist_form_items" data-checklist-fields='<%= fields(f, :checklists) %>'>
           <%= f.fields_for :checklists do |builder| %>
             <%= render :partial => 'checklist_fields', :locals => {:f => builder, :checklist => @checklist} %>
           <% end %>
-          <%= render 'checklist_templates' if ChecklistTemplate.visible.in_project_and_global(@project).for_tracker_and_global(@issue.tracker).any? %>
         </span>
       <% end %>
     </p>
@@ -22,5 +20,4 @@
   <% end %>
 
   $("span#checklist_form_items").checklist();
-  $("#checklist_items").checklist();
 <% end %>
diff --git a/plugins/redmine_checklists/app/views/issues/_checklist_templates.html.erb b/plugins/redmine_checklists/app/views/issues/_checklist_templates.html.erb
deleted file mode 100644
index fe35508..0000000
--- a/plugins/redmine_checklists/app/views/issues/_checklist_templates.html.erb
+++ /dev/null
@@ -1,4 +0,0 @@
-<span class='search_for_watchers template-wrapper'>
-  <%= link_to l(:label_add_checklists_from_template), 'javascript:void(0)', :id => 'template-link' %>
-</span>
-<%= select :checklist, :template, template_options_for_select(@project, @issue.tracker), {}, :style => 'display: none' %>
diff --git a/plugins/redmine_checklists/app/views/projects/settings/_checklist_templates.html.erb b/plugins/redmine_checklists/app/views/projects/settings/_checklist_templates.html.erb
deleted file mode 100644
index 19ba0c0..0000000
--- a/plugins/redmine_checklists/app/views/projects/settings/_checklist_templates.html.erb
+++ /dev/null
@@ -1,34 +0,0 @@
-<% @checklist_templates = ChecklistTemplate.visible.in_project_and_global(@project).order("#{ChecklistTemplate.table_name}.name") %>
-<% if @checklist_templates %>
-  <table class="list">
-    <thead>
-      <tr>
-        <th><%= l(:field_name) %></th>
-        <th><%= "#{l(:field_visible)} #{l(:label_visibility_public)}" %></th>
-        <th><%= l(:field_is_for_all) %></th>
-        <th><%= l(:field_is_for_tracker) %></th>
-        <th></th>
-        </tr>
-    </thead>
-    <tbody>
-      <% @checklist_templates.each do |checklist_template| %>
-        <tr class="checklist-template <%= cycle 'odd', 'even' %>">
-          <td class="name"><%= checklist_template.name  %></td>
-          <td class="tick"><%= checked_image checklist_template.is_public? %></td>
-          <td class="tick"><%= checked_image checklist_template.project.blank? %></td>
-          <td><%= checklist_template.tracker  %></td>
-          <td class="buttons">
-            <% if User.current.admin? || User.current == checklist_template.user || User.current.allowed_to?(:manage_checklist_templates, checklist_template.project) %>
-              <%= link_to l(:button_edit), edit_project_checklist_template_path(@project, checklist_template), :class => 'icon icon-edit' %>
-              <%= delete_link checklist_template_path(checklist_template, :project_id => @project) %>
-            <% end %>
-          </td>
-        </tr>
-      <% end %>
-    </tbody>
-  </table>
-<% else %>
-  <p class="nodata"><%= l(:label_no_data) %></p>
-<% end %>
-
-<p><%= link_to l(:label_checklist_new_checklist_template), new_project_checklist_template_path(@project), :class => 'icon icon-add' if User.current.allowed_to?(:manage_checklist_templates, @project) %></p>
diff --git a/plugins/redmine_checklists/app/views/settings/checklists/_checklists.html.erb b/plugins/redmine_checklists/app/views/settings/checklists/_checklists.html.erb
index f59936b..d4307f4 100644
--- a/plugins/redmine_checklists/app/views/settings/checklists/_checklists.html.erb
+++ b/plugins/redmine_checklists/app/views/settings/checklists/_checklists.html.erb
@@ -1,11 +1,5 @@
 <% checklist_tabs = [
   {:name => 'general', :partial => 'settings/checklists/general', :label => :label_general}]
-  checklist_tabs.push({:name => 'checklist_templates',
-                       :partial => 'settings/checklists/templates',
-                       :label => :label_checklist_templates})
-  checklist_tabs.push({:name => 'checklist_template_categories',
-                       :partial => 'settings/checklists/template_categories',
-                       :label => :label_checklist_template_category_plural})
 
  %>
 
diff --git a/plugins/redmine_checklists/app/views/settings/checklists/_template_categories.html.erb b/plugins/redmine_checklists/app/views/settings/checklists/_template_categories.html.erb
deleted file mode 100644
index 5853c1c..0000000
--- a/plugins/redmine_checklists/app/views/settings/checklists/_template_categories.html.erb
+++ /dev/null
@@ -1,29 +0,0 @@
-<div class="contextual">
-<%= link_to l(:label_checklist_template_category_new), {:controller => "checklist_template_categories", :action => 'new'}, :class => 'icon icon-add' %>
-</div>
-
-<h3><%=l(:label_checklist_template_category_plural)%></h3>
-
-<table class="list">
-  <thead><tr>
-  <th><%=l(:field_checklist_template_category)%></th>
-  <th><%=l(:button_sort)%></th>
-  <th></th>
-  </tr></thead>
-  <tbody>
-    <% for category in ChecklistTemplateCategory.ordered  %>
-      <tr class="<%= cycle("odd", "even") %>">
-      <td class="name"><%= category.name %></td>
-      <td align="center" style="width:15%;"><%= stocked_reorder_link(category, 'category', {:controller => "checklist_template_categories", :action => 'update', :id => category}, :put) %></td>
-      <td class="buttons">
-        <%= link_to l(:button_edit), edit_checklist_template_category_path(category), :class => 'icon icon-edit' %>
-        <%= delete_link checklist_template_category_path(category) %>
-      </td>
-      </tr>
-    <% end %>
-  </tbody>
-</table>
-
-<%= javascript_tag do %>
-  $(function() { $("table.list tbody").positionedItems(); });
-<% end %>
diff --git a/plugins/redmine_checklists/app/views/settings/checklists/_templates.html.erb b/plugins/redmine_checklists/app/views/settings/checklists/_templates.html.erb
deleted file mode 100644
index 8a1b487..0000000
--- a/plugins/redmine_checklists/app/views/settings/checklists/_templates.html.erb
+++ /dev/null
@@ -1,44 +0,0 @@
-<div class='contextual'>
-  <%= link_to l(:label_checklist_new_checklist_template), new_checklist_template_path, :class => 'icon icon-add' if User.current.allowed_to?(:manage_checklist_templates, nil, { :global => true }) %>
-</div>
-
-<h3><%=l(:label_checklist_templates)%></h3>
-
-<% if ChecklistTemplate.any? %>
-  <table class="list">
-    <thead><tr>
-    <th><%= l(:field_name) %></th>
-    <th><%= "#{l(:field_visible)} #{l(:label_visibility_public)}" %></th>
-    <th><%= l(:field_project) %></th>
-    <th></th>
-    </tr></thead>
-    <tbody>
-    <% previous_group = false %>
-    <% ChecklistTemplate.eager_load(:category).each do |checklist_template| %>
-    <% if checklist_template.category != previous_group %>
-      <% reset_cycle %>
-      <% unless checklist_template.category.blank? %>
-      <tr class="group open">
-        <td colspan="4">
-          <span class="expander" onclick="toggleRowGroup(this);">&nbsp;</span>
-          <%= checklist_template.category.name %>
-        </td>
-      </tr>
-      <% end %>
-      <% previous_group = checklist_template.category %>
-    <% end %>
-    <tr class="checklist-template <%= cycle 'odd', 'even' %>">
-      <td class="name"><%= checklist_template.name  %></td>
-      <td class="tick"><%= checked_image checklist_template.is_public? %></td>
-      <td class="project"><%= checklist_template.project ? checklist_template.project.name : l(:field_is_for_all) %></td>
-      <td class="buttons">
-        <%= link_to l(:button_edit), edit_checklist_template_path(checklist_template), :class => 'icon icon-edit' %>
-        <%= delete_link checklist_template_path(checklist_template, :project_id => checklist_template.project) %>
-      </td>
-    </tr>
-    <% end %>
-    </tbody>
-  </table>
-<% else %>
-  <p class="nodata"><%= l(:label_no_data) %></p>
-<% end %>
diff --git a/plugins/redmine_checklists/assets/javascripts/checklists.js b/plugins/redmine_checklists/assets/javascripts/checklists.js
index 1bdb224..828ac52 100644
--- a/plugins/redmine_checklists/assets/javascripts/checklists.js
+++ b/plugins/redmine_checklists/assets/javascripts/checklists.js
@@ -99,6 +99,12 @@ if(typeof(String.prototype.trim) === "undefined")
 
 })( jQuery );
 
+var updateChecklistPositions =  function() {
+  $(".checklist-item.existing").each(function(index, element){
+    $(element).children('.checklist-item-position').val(index);
+  });
+}
+
 var Redmine = Redmine || {};
 
 Redmine.Checklist = $.klass({
@@ -110,15 +116,12 @@ Redmine.Checklist = $.klass({
       event.returnValue = false
   },
 
-  addChecklistFields: function(templateDiv) {
+  addChecklistFields: function() {
     var new_id = new Date().getTime();
     var regexp = new RegExp("new_checklist", "g");
-    if (templateDiv) {
-      appended = $(this.content.replace(regexp, new_id)).insertBefore(templateDiv)
-    } else {
-      appended = $(this.content.replace(regexp, new_id)).appendTo(this.root)
-    }
-    appended.find('.edit-box').focus()
+    appended = $(this.content.replace(regexp, new_id)).appendTo(this.root);
+    updateChecklistPositions();
+    appended.find('.edit-box').focus();
   },
 
   findSpan: function(event) {
@@ -129,13 +132,16 @@ Redmine.Checklist = $.klass({
     return elem.prevAll('span.checklist-item.new')
   },
 
-  transformItem: function(event, elem, valueToSet) {
+  transformItem: function(event, elem, valueToSet, isSection) {
     var checklistItem;
     if (event) {
       checklistItem = this.findSpan(event)
-    } else {
+    } else if (elem) {
       checklistItem = this.findSpanBefore(elem)
+    } else {
+      checklistItem = this.root.find('span.checklist-item.new')
     }
+
     var val;
     if (valueToSet) {
       val = valueToSet
@@ -143,11 +149,17 @@ Redmine.Checklist = $.klass({
     } else {
       val = checklistItem.find('input.edit-box').val()
     }
+
     checklistItem.find('.checklist-subject').text(val)
     checklistItem.find('.checklist-subject-hidden').val(val)
     checklistItem.removeClass('edit')
     checklistItem.removeClass('new')
     checklistItem.addClass('show')
+
+    if (isSection) {
+      checklistItem.addClass('checklist-section');
+      checklistItem.children('.checklist-item-is_section').val(true);
+    }
   },
 
   resetItem: function(item) {
@@ -157,12 +169,9 @@ Redmine.Checklist = $.klass({
   },
 
   addChecklistItem: function(event) {
-    this.preventEvent(event)
-    this.transformItem(event)
-    if ($('.template-wrapper').length)
-      this.addChecklistFields($('.template-wrapper'))
-    else
-      this.addChecklistFields()
+    this.preventEvent(event);
+    this.transformItem(event);
+    this.addChecklistFields();
   },
 
   canSave: function(span) {
@@ -193,6 +202,57 @@ Redmine.Checklist = $.klass({
     }, this))
   },
 
+  onClickAddChecklistItemMenuButton: function() {
+    $('#checklist-menu .add-checklist-item').on('click', $.proxy(function(event) {
+      this.preventEvent(event);
+      var span = $('#checklist_form_items > span.checklist-item.new');
+      if (this.canSave(span)) {
+        this.transformItem();
+        this.addChecklistFields();
+        this.$plusButtonMenu.hide();
+      }
+    }, this))
+  },
+
+  onClickNewSectionMenuButton: function() {
+    $('#checklist-menu .add-checklist-section').on('click', $.proxy(function(event) {
+      this.preventEvent(event);
+      var span = $('#checklist_form_items > span.checklist-item.new');
+      if (this.canSave(span)) {
+        this.transformItem(null, null, null, true);
+        this.addChecklistFields();
+        this.$plusButtonMenu.hide();
+      }
+    }, this))
+  },
+
+  onMouseEnterLeavePlusButton: function() {
+    var hideMenuTimer;
+    var $menu = this.$plusButtonMenu;
+
+    this.root.on('mouseenter', '.save-new-by-button', function() {
+      var $plusButton = $(this);
+      var position = $plusButton.position();
+      $menu.css('left', (position.left + 'px'));
+      $menu.css('top', (position.top + $plusButton.height() + 'px'));
+      $menu.show();
+    });
+
+    this.root.on('mouseleave', '.save-new-by-button', function() {
+      hideMenuTimer = setTimeout(function() {
+        $menu.hide();
+      }, 500);
+    });
+
+    $('#checklist-menu').on('mouseenter', function() {
+      clearTimeout(hideMenuTimer);
+    });
+
+    $('#checklist-menu').on('mouseleave', function() {
+      $menu.hide();
+    });
+  },
+
   onIssueFormSubmitRemoveEmptyChecklistItems: function() {
     $('body').on('submit', '#issue-form', function(){
       $('.checklist-subject-hidden').each(function(i, elem) {
@@ -212,14 +272,16 @@ Redmine.Checklist = $.klass({
 
       if (checkbox.val() === "false") {
         checkbox.val("1");
+        itemToRemove.removeClass('existing')
         itemToRemove.fadeOut(200);
       }
+
+      updateChecklistPositions();
     }, this));
   },
 
   makeChecklistsSortable: function() {
     $('#checklist_form_items').sortable({
-      revert: true,
       items: '.checklist-item.show',
       helper: "clone",
       stop: function (event, ui) {
@@ -229,9 +291,7 @@ Redmine.Checklist = $.klass({
         if (ui.item.hasClass("edit-active")) {
           $( this ).sortable( "cancel" );
         }
-        $(".checklist-item").each(function(index, element){
-          $(element).children('.checklist-item-position').val(index);
-        });
+        updateChecklistPositions();
       }
     });
   },
@@ -269,13 +329,33 @@ Redmine.Checklist = $.klass({
   },
 
   onChangeCheckbox: function(){
-    this.root.on('change', 'input.checklist-checkbox', $.proxy(function(event){
+    this.root.on('change', 'input.checklist-checkbox', $.proxy(function(event) {
+      this.darkenCompletedSections();
       checkbox = $(event.target)
       url = checkbox.attr('data_url')
       $.ajax({type: "PUT", url: url, data: { is_done: checkbox.prop('checked') }, dataType: 'script'})
     }, this))
   },
 
+  darkenCompletedSections: function() {
+    var isCompletedSection = true;
+    var reversedChecklistItems = $('#checklist_items li').get().reverse();
+
+    $(reversedChecklistItems).each(function(index, element) {
+      var $element = $(element);
+      if ($element.hasClass('checklist-section')) {
+        if (isCompletedSection) {
+          $element.addClass('completed-section')
+        } else {
+          $element.removeClass('completed-section')
+        }
+        isCompletedSection = true;
+      } else {
+        isCompletedSection = isCompletedSection && $element.children('.checklist-checkbox').is(':checked')
+      }
+    })
+  },
+
   enableUniquenessValidation: function() {
     this.root.on('keyup', 'input.edit-box', $.proxy(function(event) {
       value = $(event.target).val()
@@ -307,39 +387,39 @@ Redmine.Checklist = $.klass({
   },
 
   assignTemplateSelectedEvent: function() {
-    var item;
-    this.root.on('change', '#checklist_template', $.proxy(function(){
-      value = $('#checklist_template').val()
-      selected = $('#checklist_template option[value='+value+']').data('template-items')
-      items = selected.split(/\n/)
-      for(i = 0; i<items.length; i++)
-      {
-        item = items[i]
-        if (!this.hasAlreadyChecklistWithName(item))
-        {
-          this.transformItem(null, $('#checklist_template'), item)
-          this.addChecklistFields($('#template-link').closest('span'))
+    this.$plusButtonMenu.on('click', 'li a.checklist-template', $.proxy(function(event) {
+      this.preventEvent(event);
+      items = $(event.target).data('template-items').split(/\n/);
+      for(var i = 0; i < items.length; i++) {
+        var item = items[i];
+        var isSection = item.slice(0, 2) === '--';
+        if (isSection) { item = item.slice(2) }
+        if (!this.hasAlreadyChecklistWithName(item)) {
+          this.transformItem(null, null, item, isSection);
+          this.addChecklistFields();
         }
       }
-      $('#checklist_template').val('')
-      $('#template-link').show()
-      $('#checklist_template').hide()
-
     }, this))
   },
 
-  clickSelectTemplateLink: function() {
-    this.root.on('click', '#template-link', function(){
-      $('#template-link').hide()
-      $('#checklist_template').show()
-    })
-  },
-
   init: function(element) {
     this.root = element
     this.content = element.data('checklist-fields')
     this.onEnterInNewChecklistItemForm()
     this.onClickPlusInNewChecklistItem()
+
+    if (this.content) {
+      this.$plusButtonMenu = $('#checklist-menu').menu();
+      if (this.$plusButtonMenu.length > 0) {
+        this.onMouseEnterLeavePlusButton();
+        this.onClickAddChecklistItemMenuButton();
+        this.assignTemplateSelectedEvent();
+        this.onClickNewSectionMenuButton();
+      }
+    } else {
+      this.darkenCompletedSections()
+    }
+
     this.onIssueFormSubmitRemoveEmptyChecklistItems()
     this.onChecklistRemove()
     this.makeChecklistsSortable()
@@ -347,12 +427,59 @@ Redmine.Checklist = $.klass({
     this.onCheckboxChanged()
     this.onChangeCheckbox()
     this.enableUniquenessValidation()
-    this.assignTemplateSelectedEvent()
-    this.clickSelectTemplateLink()
   }
 
 })
 
 $.fn.checklist = function(element){
   new Redmine.Checklist(this);
-}
+};
+
+Redmine.ChecklistToggle = $.klass({
+  manageToggling: function (t_val) {
+    var checkedCheckboxes = $('#checklist_items .checklist-checkbox:checkbox:checked');
+
+    if(localStorage.getItem("hide_closed_checklists") === t_val){
+      $($(checkedCheckboxes).closest('li')).hide();
+      $(this.switch_link).text(this.show_text + '(' + checkedCheckboxes.length + ')');
+    } else {
+      $($(checkedCheckboxes).closest('li')).show();
+      $(this.switch_link).text(this.hide_text);
+    }
+  },
+  switch_link_click: function(){
+    var th = $(this)[0];
+    this.switch_link.click(function (e) {
+      e.preventDefault();
+      th.manageToggling("1");
+      var setVal = (localStorage.getItem("hide_closed_checklists") === "1") ? "0" : "1";
+      localStorage.setItem("hide_closed_checklists", setVal);
+    });
+  },
+  hide_switch_link: function(){
+    if($('.checklist-checkbox:checkbox:checked').length < 1){
+      this.switch_link.hide();
+    }
+  },
+  init: function(show_text, hide_text) {
+    this.show_text = show_text;
+    this.hide_text = hide_text;
+    this.switch_link = $('#switch_link');
+    this.manageToggling("0");
+    this.switch_link_click();
+    this.hide_switch_link();
+  }
+});
+
+
+$(document).ready(function () {
+  if (typeof(contextMenuCheckSelectionBox) === 'function') {
+    var originContextMenuCheckSelectionBox = contextMenuCheckSelectionBox;
+    contextMenuCheckSelectionBox = function (tr, checked) {
+      var $td = tr.find('td.checklist_relations');
+      var $checklist = $td.find('.checklist').detach();
+      originContextMenuCheckSelectionBox(tr, checked);
+      $checklist.appendTo($td);
+    };
+  }
+});
diff --git a/plugins/redmine_checklists/assets/stylesheets/checklists.css b/plugins/redmine_checklists/assets/stylesheets/checklists.css
index 030318f..a254bda 100644
--- a/plugins/redmine_checklists/assets/stylesheets/checklists.css
+++ b/plugins/redmine_checklists/assets/stylesheets/checklists.css
@@ -1,3 +1,7 @@
+#checklist_form_items input[type="checkbox"], 
+#checklist_items input[type="checkbox"] {
+  height: initial;
+}
 
 div#checklist ul {
 	list-style: none;
@@ -10,6 +14,8 @@ div#checklist li {
   margin-left: 10px;
 }
 
+.checklist-checkbox {height: inherit}
+
 #checklist li:hover a.delete {opacity: 1;}
 
 #checklist a.delete {opacity: 0.4;}
@@ -74,4 +80,65 @@ span.checklist-item.edit .checklist-show-only {
 div#checklist ol {
   display: inline-block;
   padding-left: 0;
-}
\ No newline at end of file
+}
+
+.checklist-section {
+  padding-top: 10px;
+  font-weight: bold;
+  border-bottom-width: 1px;
+  border-bottom-style: solid;
+  border-bottom-color: #eee;
+}
+
+.checklist-item.checklist-section > .checklist-checkbox { display: none; }
+
+#checklist_items li.checklist-section {
+  padding-bottom: 5px;
+  margin-bottom: 5px;
+}
+
+.completed-section { color: #999; }
+
+.save-new-by-button { cursor: pointer; }
+
+table.list td.checklist_relations { text-align: left }
+
+/* ========================================================================= */
+/*                        Checklist context menu                             */
+/* ========================================================================= */
+
+#checklist-menu ul, #checklist-menu li, #checklist-menu a {
+  display:block;
+  margin:0;
+  padding:0;
+  border:0;
+}
+
+#checklist-menu {
+  display: none;
+  position: absolute;
+  font-size: 0.9em;
+}
+
+#checklist-menu, #checklist-menu ul {
+  width: 150px;
+  border: 1px solid #ccc;
+  background: white;
+  list-style: none;
+  padding: 2px;
+  border-radius: 2px;
+}
+
+#checklist-menu li {
+  position: relative;
+  padding: 1px;
+  border: 1px solid white;
+}
+
+#checklist-menu a {
+  text-decoration: none !important;
+  padding: 2px 0px 2px 20px;
+}
+
+#checklist-menu a:hover { color:#2A5685; }
+#checklist-menu li:hover { border:1px solid #628db6; background-color:#eef5fd; border-radius:3px; }
diff --git a/plugins/redmine_checklists/config/locales/de.yml b/plugins/redmine_checklists/config/locales/de.yml
index 4a90806..ac85201 100644
--- a/plugins/redmine_checklists/config/locales/de.yml
+++ b/plugins/redmine_checklists/config/locales/de.yml
@@ -25,5 +25,15 @@ de:
   label_checklist_changed_to: zu
   label_checklist_added: hinzugefĂĽgt
   label_checklist_done: als Erledigt markiert
-  label_checklist_undone: als Nicht Erledigt markiert
-  label_checklist_updated: Checklisten-Eintrag editiert
\ No newline at end of file
+  label_checklist_undone: als Nicht erledigt markiert
+  label_checklist_updated: Checklisten-Eintrag editiert
+  label_checklist_status: Checklisten-Status
+  label_checklist_status_done: Erledigt
+  label_checklist_status_undone: Nicht erledigt
+  label_checklist_is_default: Standard
+  field_is_for_tracker: Tracker
+  label_checklists_must_be_completed: "Alle Checklisten-Einträge eines Tickets müssen vor dem Schließen erledigt werden."
+  label_checklist_block_issue_closing: "SchlieĂźen des Tickets blockieren"
+  label_checklist_show_closed: Zeige alle
+  label_checklist_hide_closed: Verberge geschlossene
+  label_checklist_new_section: Neue Unterteilung
diff --git a/plugins/redmine_checklists/config/locales/en.yml b/plugins/redmine_checklists/config/locales/en.yml
index 4052f03..b60cd28 100644
--- a/plugins/redmine_checklists/config/locales/en.yml
+++ b/plugins/redmine_checklists/config/locales/en.yml
@@ -1,5 +1,9 @@
 # English strings go here for Rails i18n
 en:
+  activerecord:
+    attributes:
+      checklists:
+        subject: Checklist subject
   label_checklist_plural: Checklist
   field_checklist: Checklist
   label_checklist_save_log: Save changes to issue log
@@ -15,10 +19,12 @@ en:
   field_template_items: Template items
   label_checklist_template: Checklist template
   label_add_checklists_from_template: Add from template
+  label_checklists_from_template: From template
   label_select_template: "-- Select template --"
   label_checklist_category_not_specified: "-- Not specified --"
   label_checklists_description: 'Multiple values allowed (one line for each value)'
   label_checklist_item: Checklist item
+  label_checklist_section: Checklist section
   label_checklist_deleted: deleted
   label_checklist_changed_from: changed from
   label_checklist_changed_to: to
@@ -29,8 +35,10 @@ en:
   label_checklist_status: Checklist status
   label_checklist_status_done: Done
   label_checklist_status_undone: Undone
-  label_checklist_status: Checklist status
   label_checklist_is_default: Default
   field_is_for_tracker: Tracker
   label_checklists_must_be_completed: All checklists of an issue must be done before closing
-  label_checklist_block_issue_closing: Block issue closing
\ No newline at end of file
+  label_checklist_block_issue_closing: Block issue closing
+  label_checklist_show_closed: Show closed
+  label_checklist_hide_closed: Hide closed
+  label_checklist_new_section: New section
diff --git a/plugins/redmine_checklists/config/locales/fr.yml b/plugins/redmine_checklists/config/locales/fr.yml
old mode 100755
new mode 100644
index 6a1ed72..5c1fe44
--- a/plugins/redmine_checklists/config/locales/fr.yml
+++ b/plugins/redmine_checklists/config/locales/fr.yml
@@ -2,3 +2,8 @@
 fr:
   label_checklist_plural: Liste de Tâches
   field_checklist: Tâche
+  label_checklist_templates: Template de checklists
+  label_checklist_new_checklist_template: Ajouter un template
+  field_is_for_tracker:
+  label_checklist_is_default: Checklist par d  faut
+  field_template_items: Eléments
diff --git a/plugins/redmine_checklists/config/locales/pt-BR.yml b/plugins/redmine_checklists/config/locales/pt-BR.yml
index 0991c43..b185ee7 100644
--- a/plugins/redmine_checklists/config/locales/pt-BR.yml
+++ b/plugins/redmine_checklists/config/locales/pt-BR.yml
@@ -1,9 +1,44 @@
 #Portuguese Brazilian strings go here for Rails i18n
 pt-BR:
+  activerecord:
+    attributes:
+      checklists:
+        subject: Checklist conteĂşdo
   label_checklist_plural: Checklist
   field_checklist: Checklist
-  label_checklist_save_log: Salvar alterações nos logs das tarefas
-  label_checklist_done_ratio: Definir % terminando quando desmarcar o item
+  label_checklist_save_log: Salvar alterações nas notas
+  label_checklist_done_ratio: Atribuir % conclusĂŁo
   permission_view_checklists: Ver checklist
-  permission_done_checklists: Remover itens do checklist
-  permission_edit_checklists: Alterar itens do checklist
+  permission_done_checklists: Prontos do checklist
+  permission_edit_checklists: Editar checklist
+  label_checklist_template_category_plural: Categoria de templates
+  label_checklist_template_category_new: Nova categoria
+  field_checklist_template_category: Categoria
+  label_checklist_templates: Checklist templates
+  label_checklist_new_checklist_template: Novo checklist template
+  field_template_items: Template items
+  label_checklist_template: Checklist template
+  label_add_checklists_from_template: Adicionar do template
+  label_checklists_from_template: Do template
+  label_select_template: "-- Selecione o template --"
+  label_checklist_category_not_specified: "-- NĂŁo especificado --"
+  label_checklists_description: 'Permitir multiplos valores'
+  label_checklist_item: Checklist item
+  label_checklist_section: Checklist sessĂŁo
+  label_checklist_deleted: deletado
+  label_checklist_changed_from: alterado por
+  label_checklist_changed_to: para
+  label_checklist_added: adicionado
+  label_checklist_done: alterar para concluĂ­do
+  label_checklist_undone: alterar para nĂŁo concluĂ­do
+  label_checklist_updated: Checklist item editado
+  label_checklist_status: Checklist status
+  label_checklist_status_done: Pronto
+  label_checklist_status_undone: NĂŁo pronto
+  label_checklist_is_default: PadrĂŁo
+  field_is_for_tracker: Tipo
+  label_checklists_must_be_completed: Para concluir a tarefa, todos os checklists devem estar prontos
+  label_checklist_block_issue_closing: Bloquear tarefa ao concluir
+  label_checklist_show_closed: Mostrar concluĂ­dos
+  label_checklist_hide_closed: Esconder concluĂ­dos
+  label_checklist_new_section: Nova sessĂŁo
diff --git a/plugins/redmine_checklists/config/locales/ru.yml b/plugins/redmine_checklists/config/locales/ru.yml
index 7e3d5d6..8c7463f 100644
--- a/plugins/redmine_checklists/config/locales/ru.yml
+++ b/plugins/redmine_checklists/config/locales/ru.yml
@@ -1,5 +1,9 @@
 # encoding: utf-8
 ru:
+  activerecord:
+    attributes:
+      checklists:
+        subject: Заголовок чеклиста
   label_checklist_plural: Чеклист
   field_checklist: Чеклист
   label_checklist_save_log: Сохранять изменения в истории
@@ -15,10 +19,12 @@ ru:
   field_template_items: Элементы шаблона
   label_checklist_template: Шаблон чеклистов
   label_add_checklists_from_template: Добавить из шаблона
+  label_checklists_from_template: Из шаблона
   label_select_template: "-- Выберите шаблон --"
   label_checklist_category_not_specified: "-- Без категории --"
   label_checklists_description: 'Для ввода нескольких значений вводите по одному на строку'
   label_checklist_item: Пункт чеклиста
+  label_checklist_section: Раздел чеклиста
   label_checklist_deleted: удалён
   label_checklist_changed_from: изменён с
   label_checklist_changed_to: на
@@ -31,3 +37,10 @@ ru:
   label_checklist_status_undone: Не выполнен
   label_checklist_is_default: По умолчанию
   field_is_for_tracker: Трекер
+  label_checklist_show_closed: Показать закрытые
+  label_checklist_hide_closed: Скрыть закрытые
+  label_checklist_new_section: Новая секция
+  label_checklist_block_issue_closing: Запретить закрытие заявки
+  label_checklists: Чеклист
+  label_checklists_must_be_completed: Чтобы заявка закрылась, должны быть выполнены все пункты чеклиста
+  field_checklists: Чеклист
diff --git a/plugins/redmine_checklists/config/routes.rb b/plugins/redmine_checklists/config/routes.rb
index 98d51b4..dab2482 100644
--- a/plugins/redmine_checklists/config/routes.rb
+++ b/plugins/redmine_checklists/config/routes.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -26,10 +26,3 @@ resources :checklists, :only => [:destroy, :update, :show] do
     put :done
   end
 end
-resources :projects do
-  resources :checklist_templates, :only => [:new, :create, :update, :edit]
-end
-
-resources :checklist_templates
-
-resources :checklist_template_categories, :only => [:edit, :update, :new, :create, :destroy]
diff --git a/plugins/redmine_checklists/db/migrate/001_create_checklists.rb b/plugins/redmine_checklists/db/migrate/001_create_checklists.rb
index f5a56c3..2408934 100644
--- a/plugins/redmine_checklists/db/migrate/001_create_checklists.rb
+++ b/plugins/redmine_checklists/db/migrate/001_create_checklists.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/db/migrate/002_add_time_stamps_to_checklists.rb b/plugins/redmine_checklists/db/migrate/002_add_time_stamps_to_checklists.rb
index db3f1a4..03e3c99 100644
--- a/plugins/redmine_checklists/db/migrate/002_add_time_stamps_to_checklists.rb
+++ b/plugins/redmine_checklists/db/migrate/002_add_time_stamps_to_checklists.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/db/migrate/003_create_checklist_template_category.rb b/plugins/redmine_checklists/db/migrate/003_create_checklist_template_category.rb
index 700930e..ebb7987 100644
--- a/plugins/redmine_checklists/db/migrate/003_create_checklist_template_category.rb
+++ b/plugins/redmine_checklists/db/migrate/003_create_checklist_template_category.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/db/migrate/004_create_checklist_templates.rb b/plugins/redmine_checklists/db/migrate/004_create_checklist_templates.rb
index 89b7a07..8de0692 100644
--- a/plugins/redmine_checklists/db/migrate/004_create_checklist_templates.rb
+++ b/plugins/redmine_checklists/db/migrate/004_create_checklist_templates.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/db/migrate/005_modify_checklist_subject_length.rb b/plugins/redmine_checklists/db/migrate/005_modify_checklist_subject_length.rb
index 7707d7c..5538b2b 100644
--- a/plugins/redmine_checklists/db/migrate/005_modify_checklist_subject_length.rb
+++ b/plugins/redmine_checklists/db/migrate/005_modify_checklist_subject_length.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/db/migrate/006_add_fields_to_checklist_template.rb b/plugins/redmine_checklists/db/migrate/006_add_fields_to_checklist_template.rb
index f5a75fa..ce38cb1 100644
--- a/plugins/redmine_checklists/db/migrate/006_add_fields_to_checklist_template.rb
+++ b/plugins/redmine_checklists/db/migrate/006_add_fields_to_checklist_template.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/doc/CHANGELOG b/plugins/redmine_checklists/doc/CHANGELOG
index c86364d..dec076c 100644
--- a/plugins/redmine_checklists/doc/CHANGELOG
+++ b/plugins/redmine_checklists/doc/CHANGELOG
@@ -1,12 +1,64 @@
 == Redmine Checklists plugin changelog
 
 Redmine Checklists plugin - managing issue checklists plugin for Redmine
-Copyright (C) 2011-2018 RedmineUP
+Copyright (C) 2011-2021 RedmineUP
 http://www.redmineup.com/
 
+== 2021-05-21 v3.1.19
+
+* Added Redmine 4.2 compatibility
+* Fixed empty project errors
+* Fixed notification on journal fixup
+* Fixed checklist positions bug
+
+== 2020-08-17 v3.1.18
+
+* Added italian locale
+* Updated zh-tw locale
+* Fixed size() method error
+* Fixed initial install error
+* Fixed checklist ration recalculate
+* Fixed display checklist element with Markdown syntax
+* Fixed done ratio recalculate on checklist API update
+* Fixed API call journalizing
+
+== 2020-01-31 v3.1.17
+
+* Redmine 4.1 compatibility fixes
+* Fixed view permission bug
+* Fixed template permissions bug
+* Fixed context menu conflicts
+* Fixed Agile support
+* Fixed checklist copy bug
+* Fixed locale bug
+* Fixed project copy bug
+
+== 2019-04-29 v3.1.16
+
+* Checklists sections
+
+== 2019-04-15 v3.1.15
+
+* Redmine 4.0.3 support
+* Added Hide link for closed items
+
+== 2018-12-20 v3.1.14
+
+* Hotfix for Redmine 4
+
+== 2018-12-18 v3.1.13
+
+* Redmine 4 saving issue fixes
+
+== 2018-11-26 v3.1.12
+
+* German translation update from Tobias Fischer
+* Fixed diferent authors changes bug
+* Fixed sortable animation bug
+
 == 2018-03-23 v3.1.11
 
-* Rails 4 support
+* Redmine 4 support
 * Setting for block issues with undone checklists
 * Fixed bug with default template
 * Fixed email notification bug
@@ -36,10 +88,10 @@ http://www.redmineup.com/
 
 * Redmine 3.4 support
 * New checklists filters for issues table
-* Save log by default 
+* Save log by default
 * Chinese translation update
 * Polish translation update
-* Fixed bug with template editing 
+* Fixed bug with template editing
 
 == 2016-08-15 v3.1.5
 
diff --git a/plugins/redmine_checklists/init.rb b/plugins/redmine_checklists/init.rb
index 174d9d7..3250729 100644
--- a/plugins/redmine_checklists/init.rb
+++ b/plugins/redmine_checklists/init.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -20,9 +20,8 @@
 require 'redmine'
 require 'redmine_checklists/redmine_checklists'
 
-CHECKLISTS_VERSION_NUMBER = '3.1.11'.freeze
-CHECKLISTS_VERSION_TYPE = 'PRO version'.freeze
-
+CHECKLISTS_VERSION_NUMBER = '3.1.19'.freeze
+CHECKLISTS_VERSION_TYPE = "Light version"
 
 Redmine::Plugin.register :redmine_checklists do
   name "Redmine Checklists plugin (#{CHECKLISTS_VERSION_TYPE})"
@@ -32,7 +31,7 @@ Redmine::Plugin.register :redmine_checklists do
   url 'https://www.redmineup.com/pages/plugins/checklists'
   author_url 'mailto:support@redmineup.com'
 
-  requires_redmine :version_or_higher => '2.3'
+  requires_redmine :version_or_higher => '3.0'
 
   settings :default => {
     :save_log => true,
@@ -44,7 +43,6 @@ Redmine::Plugin.register :redmine_checklists do
       map.permission :view_checklists, { :checklists => [:show, :index] }
       map.permission :done_checklists, { :checklists => :done }
       map.permission :edit_checklists, { :checklists => [:done, :create, :destroy, :update] }
-      map.permission :manage_checklist_templates, { :checklist_templates => [:new, :create, :destroy, :edit, :update] }
     end
   end
 
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/hooks/controller_issues_hook.rb b/plugins/redmine_checklists/lib/redmine_checklists/hooks/controller_issues_hook.rb
index ef97797..2a8cde0 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/hooks/controller_issues_hook.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/hooks/controller_issues_hook.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -21,20 +21,8 @@ module RedmineChecklists
   module Hooks
     class ControllerIssuesHook < Redmine::Hook::ViewListener
       def controller_issues_edit_after_save(context = {})
-        old_checklists = context[:issue].old_checklists
-        new_checklists = context[:issue].checklists.to_json
-        journal = context[:journal]
-        details = JournalChecklistHistory.new(old_checklists, new_checklists).journal_details
-        if JournalChecklistHistory.can_fixup?(details)
-          JournalChecklistHistory.fixup(details)
-        elsif details.old_value != details.value
-          journal.details << details
-          journal.save
-        else
-          journal.save
-        end
 
-        if (Setting.issue_done_ratio == "issue_field") && RedmineChecklists.settings["issue_done_ratio"].to_i > 0
+        if (Setting.issue_done_ratio == 'issue_field') && RedmineChecklists.issue_done_ratio?
           Checklist.recalc_issue_done_ratio(context[:issue].id)
         end
       end
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/hooks/views_issues_hook.rb b/plugins/redmine_checklists/lib/redmine_checklists/hooks/views_issues_hook.rb
index 3923d25..79c2294 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/hooks/views_issues_hook.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/hooks/views_issues_hook.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/hooks/views_layouts_hook.rb b/plugins/redmine_checklists/lib/redmine_checklists/hooks/views_layouts_hook.rb
index 7631101..852f30a 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/hooks/views_layouts_hook.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/hooks/views_layouts_hook.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/add_helpers_for_checklists_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/add_helpers_for_checklists_patch.rb
index 750e2d0..660da28 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/add_helpers_for_checklists_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/add_helpers_for_checklists_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/2.1/redmine_api_test_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/2.1/redmine_api_test_patch.rb
index d7ad48c..d15bcfd 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/2.1/redmine_api_test_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/2.1/redmine_api_test_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/application_controller_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/application_controller_patch.rb
deleted file mode 100644
index 22a5595..0000000
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/application_controller_patch.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineChecklists
-  module Patches
-    module ApplicationControllerPatch
-      def self.included(base) # :nodoc:
-        base.extend(ClassMethods)
-        base.class_eval do
-          unloadable # Send unloadable so it will not be unloaded in development
-        end
-      end
-
-      module ClassMethods
-        def before_action(*filters, &block)
-          before_filter(*filters, &block)
-        end
-      end
-    end
-  end
-end
-
-unless ApplicationController.included_modules.include?(RedmineChecklists::Patches::ApplicationControllerPatch)
-  ApplicationController.send(:include, RedmineChecklists::Patches::ApplicationControllerPatch)
-end
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/application_helper_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/application_helper_patch.rb
index a627386..47ed55e 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/application_helper_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/application_helper_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/journal_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/journal_patch.rb
index 4e48af9..9892dd1 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/journal_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/journal_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -17,28 +17,18 @@
 # You should have received a copy of the GNU General Public License
 # along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
 
-
 require_dependency 'journal'
 
 module RedmineChecklists
   module Patches
     module JournalPatch
-
       def self.included(base) # :nodoc:
         base.send(:include, InstanceMethods)
         base.class_eval do
-          after_create :send_checklist_notification
         end
       end
 
       module InstanceMethods
-        def send_checklist_notification
-          detail = detail_for_attribute('checklist')
-          checklist_email_nootification(self).deliver if !Setting.notified_events.include?('issue_updated') &&
-                                                         Setting.notified_events.include?('checklist_updated') &&
-                                                         detail.present? &&
-                                                         !JournalChecklistHistory.new(detail.old_value, detail.value).empty_diff?
-        end
 
         if Redmine::VERSION.to_s < '2.6'
           def send_notification
@@ -48,7 +38,7 @@ module RedmineChecklists
                   (Setting.notified_events.include?('issue_status_updated') && new_status.present?) ||
                   (Setting.notified_events.include?('issue_priority_updated') && new_value_for('priority_id').present?)
                 )
-              checklist_email_nootification(self).deliver
+              deliver_checklist_notification
             end
           end
 
@@ -57,7 +47,17 @@ module RedmineChecklists
           end
         end
 
-        def checklist_email_nootification(journal)
+        def deliver_checklist_notification
+          if Redmine::VERSION.to_s >= '4.0'
+            (notified_watchers | notified_users).each do |user|
+              Mailer.issue_edit(user, self).deliver
+            end
+          else
+            checklist_email_notification(self).deliver
+          end
+        end
+
+        def checklist_email_notification(journal)
           if Redmine::VERSION.to_s < '2.4'
             Mailer.issue_edit(journal)
           else
@@ -65,7 +65,6 @@ module RedmineChecklists
           end
         end
       end
-
     end
   end
 end
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/open_struct_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/open_struct_patch.rb
index d0d27e1..36c0395 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/open_struct_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility/open_struct_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility_patch.rb
index 3e1460e..c347ab2 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/compatibility_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/issue_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/issue_patch.rb
index ab57df5..8cfc0c6 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/issue_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/issue_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -46,23 +46,21 @@ module RedmineChecklists
 
           safe_attributes 'checklists_attributes',
             :if => lambda { |issue, user| (user.allowed_to?(:done_checklists, issue.project) || user.allowed_to?(:edit_checklists, issue.project)) }
+        end
+      end
 
-          def copy_checklists(arg)
-            issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg)
-            issue.checklists.each{ |checklist| Checklist.create(checklist.attributes.except('id', 'issue_id').merge(:issue => self)) } if issue
-          end
-
-          def block_issue_closing_if_checklists_unclosed
-            if RedmineChecklistSetting.block_issue_closing? && checklists.any? && status.is_closed?
-              errors.add(:checklists, l(:label_checklists_must_be_completed)) unless (checklists - checklists.where(:id => removed_checklist_ids)).all?(&:is_done)
+      module InstanceMethods
+        def copy_checklists(arg)
+          issue = arg.is_a?(Issue) ? arg : Issue.visible.find(arg)
+          if issue
+            issue.checklists.each do |checklist|
+              Checklist.create(checklist.attributes.except('id', 'issue_id').merge(issue: self))
             end
           end
         end
-      end
 
-      module InstanceMethods
         def copy_subtask_checklists
-          return if !copy? || parent_id.nil? || checklists.any?
+          return if !copy? || parent_id.nil? || checklists.reload.any?
           copy_checklists(@copied_from)
         end
 
@@ -71,6 +69,23 @@ module RedmineChecklists
           copy.copy_checklists(self)
           copy
         end
+
+        def all_checklist_items_is_done?
+          (checklists - checklists.where(id: removed_checklist_ids)).reject(&:is_section).all?(&:is_done)
+        end
+
+        def need_to_block_issue_closing?
+          RedmineChecklists.block_issue_closing? &&
+            checklists.reject(&:is_section).any? &&
+            status.is_closed? &&
+            !all_checklist_items_is_done?
+        end
+
+        def block_issue_closing_if_checklists_unclosed
+          if need_to_block_issue_closing?
+            errors.add(:checklists, l(:label_checklists_must_be_completed))
+          end
+        end
       end
     end
   end
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/issue_query_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/issue_query_patch.rb
index d6b2de3..4e613f9 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/issue_query_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/issue_query_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -24,67 +24,15 @@ module RedmineChecklists
     module IssueQueryPatch
       def self.included(base)
         base.send(:include, InstanceMethods)
-        base.class_eval do
-          unloadable
-          alias_method :available_filters_without_checklists, :available_filters
-          alias_method :available_filters, :available_filters_with_checklists
-        end
       end
 
       module InstanceMethods
-
-        def available_filters_with_checklists
-          if @available_filters.blank?
-            add_available_filter('checklists_status', :type => :list, :name => l(:label_checklist_status),
-                                             :values => [[l(:label_checklist_status_done), '1'], [l(:label_checklist_status_undone), '0']]) unless available_filters_without_checklists.key?('checklists_status') && !User.current.allowed_to?(:view_checklists, project, :global => true)
-
-            add_available_filter('checklists_item', :type => :string, :name => l(:label_checklist_item)) unless available_filters_without_checklists.key?('checklists_item') && !User.current.allowed_to?(:view_checklists, project, :global => true)
-          else
-            available_filters_without_checklists
-          end
-          @available_filters
-        end
-
-        def sql_for_checklists_status_field(_field, operator, value)
-          case operator
-          when '='
-            compare = '='
-          when '!'
-            compare = '!='
-          end
-          condition =
-            if value.size > 1
-              '1=1'
-            else
-              "is_done #{compare} #{value.join == '1' ? self.class.connection.quoted_true : self.class.connection.quoted_false}"
-            end
-          issue_ids = "SELECT DISTINCT(#{Checklist.table_name}.issue_id) FROM #{Checklist.table_name} WHERE #{condition}"
-          "(#{Issue.table_name}.id IN (#{issue_ids}))"
-        end
-
-        def sql_for_checklists_item_field(_field, operator, value)
-          case operator
-          when '=', '!'
-            condition = "#{Checklist.table_name}.subject = ?"
-          when '~', '!~'
-            condition = "LOWER(#{Checklist.table_name}.subject) LIKE LOWER(?)"
-            value = "%#{value.join}%"
-          when '*', '!*'
-            condition = '1=1'
-          end
-          issue_ids = Checklist.where(condition, value).pluck(:issue_id).uniq
-          if ['!', '!~'].include?(operator)
-            all_issue_ids = Checklist.pluck(:issue_id).uniq
-            issue_ids = all_issue_ids - issue_ids
-          end
-          return '1=0' if issue_ids.empty?
-          "(#{Issue.table_name}.id #{'NOT' if operator == '!*'} IN (#{issue_ids.join(',')}))"
-        end
       end
     end
   end
 end
 
-unless IssueQuery.included_modules.include?(RedmineChecklists::Patches::IssueQueryPatch)
+if (ActiveRecord::Base.connection.tables.include?('queries') rescue false) &&
+   IssueQuery.included_modules.exclude?(RedmineChecklists::Patches::IssueQueryPatch)
   IssueQuery.send(:include, RedmineChecklists::Patches::IssueQueryPatch)
 end
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/issues_controller_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/issues_controller_patch.rb
index af244e6..f927fd4 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/issues_controller_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/issues_controller_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -35,10 +35,14 @@ module RedmineChecklists
       module InstanceMethods
         def build_new_issue_from_params_with_checklist
           if params[:id].blank?
-            if params[:copy_from].blank?
-              fill_default_checklist
-            else
-              fill_checklist_attributes
+            begin
+              if params[:copy_from].blank?
+              else
+                fill_checklist_attributes
+              end
+            rescue ActiveRecord::RecordNotFound
+              render_404
+              return
             end
           end
           build_new_issue_from_params_without_checklist
@@ -58,51 +62,22 @@ module RedmineChecklists
 
         def fill_checklist_attributes
           return unless params[:issue].blank?
-          begin
-            @copy_from = Issue.visible.find(params[:copy_from])
-            add_checklists_to_params(@copy_from.checklists)
-          rescue ActiveRecord::RecordNotFound
-            render_404
-            return
-          end
-        end
-        def fill_default_checklist
-          return if custom_checklists_included?(params[:issue])
-          params[:issue] ||= {}
-          tracker_id = params[:issue].try(:[], :tracker_id) || issue_project.trackers.first.try(:id)
-          default_template = issue_project.default_checklist_template(tracker_id)
-          return params[:issue][:checklists_attributes] = {} if default_template.nil?
-          params[:issue][:checklist_template_id] = default_template.id
-          add_checklists_to_params(default_template.checklists)
+
+          @copy_from = Issue.visible.find(params[:copy_from])
+          add_checklists_to_params(@copy_from.checklists)
         end
 
         def add_checklists_to_params(checklists)
           params[:issue].blank? ? params[:issue] = { :checklists_attributes => {} } : params[:issue][:checklists_attributes] = {}
           checklists.each_with_index do |checklist_item, index|
-            params[:issue][:checklists_attributes][index.to_s] = { :is_done => checklist_item.is_done,
-                                                                   :subject => checklist_item.subject,
-                                                                   :position => checklist_item.position }
-          end
-        end
-        def custom_checklists_included?(issue_attr)
-          return if issue_attr.blank? || issue_attr[:checklists_attributes].blank? || checklist_subjects(issue_attr[:checklists_attributes]).empty?
-          default_template = issue_project.checklist_templates.visible.where(:id => issue_attr[:checklist_template_id]).first
-          return true unless default_template
-          checklist_subjects(issue_attr[:checklists_attributes]) != default_template.checklists.map(&:subject)
-        end
-
-        def checklist_subjects(attrs)
-          if attrs.is_a?(Array) # JSON/XML
-            attrs.map { |checklist_attr| checklist_attr[:subject] }.compact
-          else
-            attrs = attrs.permit!.to_h if Rails::VERSION::MAJOR >= 5
-            attrs.map { |_k, v| v[:subject] if v[:subject].present? }.compact
+            params[:issue][:checklists_attributes][index.to_s] = {
+              is_done: checklist_item.is_done,
+              subject: checklist_item.subject,
+              position: checklist_item.position,
+              is_section: checklist_item.is_section
+            }
           end
         end
-
-        def issue_project
-          @project || Project.where(:id => Issue.new.allowed_target_projects.map(&:id)).order(:id).first
-        end
       end
     end
   end
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/issues_helper_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/issues_helper_patch.rb
index f010f4e..07333cf 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/issues_helper_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/issues_helper_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -28,28 +28,18 @@ module RedmineChecklists
 
           alias_method :details_to_strings_without_checklists, :details_to_strings
           alias_method :details_to_strings, :details_to_strings_with_checklists
-          if Redmine::VERSION.to_s >= '2.2' && Redmine::VERSION.to_s <= '2.4'
-            alias_method :render_email_issue_attributes_without_checklists, :render_email_issue_attributes
-            alias_method :render_email_issue_attributes, :render_email_issue_attributes_with_checklists
-          end
         end
       end
 
 
       module InstanceMethods
 
-        def render_email_issue_attributes_with_checklists(issue, html = false)
-          journal = issue.journals.order(:id).last
-          return render_email_issue_attributes_without_checklists(issue, html) unless journal
-          details = journal.details
-          return render_email_issue_attributes_without_checklists(issue, html) unless details
-          checklist_details = details.select{ |x| x.prop_key == 'checklist'}
-          return render_email_issue_attributes_without_checklists(issue, html) unless checklist_details.any?
-          return render_email_issue_attributes_without_checklists(issue, html) + details_to_strings_with_checklists(checklist_details, !html).join(html ? "<br/>".html_safe : "\n")
-        end
-
         def details_to_strings_with_checklists(details, no_html = false, options = {})
           details_checklist, details_other = details.partition{ |x| x.prop_key == 'checklist' }
+          if @issue.nil? || !User.current.allowed_to?(:view_checklists, @issue.try(:project), global: @issue.present?)
+            return details_to_strings_without_checklists(details_other, no_html, options)
+          end
+
           details_checklist.map do |detail|
             result = []
             diff = Hash.new([])
@@ -59,33 +49,20 @@ module RedmineChecklists
             else
               diff = JournalChecklistHistory.new(detail.old_value, detail.value).diff
             end
-            if diff[:removed].any?
-              diff[:removed].each do |item|
-                result << "<b>#{ERB::Util.h l(:label_checklist_item)}</b> #{ERB::Util.h l(:label_checklist_deleted)} (<strike><i>#{ERB::Util.h item[:subject]}</i></strike>)"
-              end
-            end
 
-            if diff[:renamed].any?
-              diff[:renamed].each do |was, became|
-                result << "<b>#{ERB::Util.h l(:label_checklist_item)}</b> #{ERB::Util.h l(:label_checklist_changed_from)} <i>#{ERB::Util.h was}</i> #{ERB::Util.h l(:label_checklist_changed_to)} <i>#{ERB::Util.h  became}</i>"
-              end
-            end
-
-            if diff[:added].any?
-              diff[:added].each do |item|
-                result << "<b>#{ERB::Util.h l(:label_checklist_item)}</b> <input type='checkbox' #{item.is_done ? 'checked' : '' } disabled> <i>#{ERB::Util.h item[:subject]}</i> #{ERB::Util.h l(:label_checklist_added)}"
-              end
+            checklist_item_label = lambda do |item|
+              item[:is_section] ? l(:label_checklist_section) : l(:label_checklist_item)
             end
 
             if diff[:done].any?
               diff[:done].each do |item|
-                result << "<b>#{ERB::Util.h l(:label_checklist_item)}</b> <input type='checkbox' #{item.is_done ? 'checked' : '' } disabled> <i>#{ERB::Util.h item[:subject]}</i> #{ERB::Util.h l(:label_checklist_done)}"
+                result << "<b>#{ERB::Util.h l(:label_checklist_item)}</b> <input type='checkbox' class='checklist-checkbox' #{item.is_done ? 'checked' : '' } disabled> <i>#{ERB::Util.h item[:subject]}</i> #{ERB::Util.h l(:label_checklist_done)}"
               end
             end
 
             if diff[:undone].any?
               diff[:undone].each do |item|
-                result << "<b>#{ERB::Util.h l(:label_checklist_item)}</b> <input type='checkbox' #{item.is_done ? 'checked' : '' } disabled> <i>#{ERB::Util.h item[:subject]}</i> #{ERB::Util.h l(:label_checklist_undone)}"
+                result << "<b>#{ERB::Util.h l(:label_checklist_item)}</b> <input type='checkbox' class='checklist-checkbox' #{item.is_done ? 'checked' : '' } disabled> <i>#{ERB::Util.h item[:subject]}</i> #{ERB::Util.h l(:label_checklist_undone)}"
               end
             end
 
@@ -93,8 +70,8 @@ module RedmineChecklists
             result = nil if result.blank?
             if result && no_html
               result = result.gsub /<\/li><li>/, "\n"
-              result = result.gsub /<input type='checkbox'[^c^>]*checked[^>]*>/, '[x]'
-              result = result.gsub /<input type='checkbox'[^c^>]*>/, '[ ]'
+              result = result.gsub /<input type='checkbox' class='checklist-checkbox'[^c^>]*checked[^>]*>/, '[x]'
+              result = result.gsub /<input type='checkbox' class='checklist-checkbox'[^c^>]*>/, '[ ]'
               result = result.gsub /<[^>]*>/, ''
               result = CGI.unescapeHTML(result)
             end
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/notifiable_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/notifiable_patch.rb
index 911a3fe..c0fa95b 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/notifiable_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/notifiable_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -18,32 +18,3 @@
 # along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
 
 
-module RedmineChecklists
-  module Patches
-    module NotifiablePatch
-      def self.included(base)
-        base.extend(ClassMethods)
-        base.class_eval do
-          unloadable
-          class << self
-            alias_method :all_without_checklists, :all
-            alias_method :all, :all_with_checklists
-          end
-        end
-      end
-
-      module ClassMethods
-        def all_with_checklists
-          notifications = all_without_checklists
-          last_issue_child_index = notifications.find_index(notifications.select{ |element| element.parent == 'issue_updated' }.last)
-          notifications.insert(last_issue_child_index + 1, Redmine::Notifiable.new('checklist_updated', 'issue_updated'))
-          notifications
-        end
-      end
-    end
-  end
-end
-
-unless Redmine::Notifiable.included_modules.include?(RedmineChecklists::Patches::NotifiablePatch)
-  Redmine::Notifiable.send(:include, RedmineChecklists::Patches::NotifiablePatch)
-end
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/project_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/project_patch.rb
index 0c374f3..d2985db 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/project_patch.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/patches/project_patch.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -28,20 +28,14 @@ module RedmineChecklists
           unloadable # Send unloadable so it will not be unloaded in development
           alias_method :copy_issues_without_checklist, :copy_issues
           alias_method :copy_issues, :copy_issues_with_checklist
-          has_many :checklist_templates
         end
       end
 
       module InstanceMethods
-        def default_checklist_template(tracker_id = nil)
-          default_templates = checklist_templates.visible.default
-          default_by_tracker = default_templates.for_tracker_id(tracker_id).first
-          default_by_tracker || default_templates.for_tracker_id(nil).first
-        end
 
         def copy_issues_with_checklist(project)
           copy_issues_without_checklist(project)
-          issues.each{ |issue| issue.copy_checklists(issue.copied_from)}
+          issues.each{ |issue| issue.copy_checklists(issue.copied_from) if issue.reload.checklists.empty? }
         end
       end
     end
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/patches/projects_helper_patch.rb b/plugins/redmine_checklists/lib/redmine_checklists/patches/projects_helper_patch.rb
deleted file mode 100644
index 51b6fe2..0000000
--- a/plugins/redmine_checklists/lib/redmine_checklists/patches/projects_helper_patch.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-
-module RedmineChecklists
-  module Patches
-    module ProjectsHelperPatch
-      def self.included(base)
-        base.send(:include, InstanceMethods)
-
-        base.class_eval do
-          unloadable
-
-          alias_method :project_settings_tabs_without_checklists, :project_settings_tabs
-          alias_method :project_settings_tabs, :project_settings_tabs_with_checklists
-        end
-      end
-
-      module InstanceMethods
-        def project_settings_tabs_with_checklists
-          tabs = project_settings_tabs_without_checklists
-          tab = { :name => 'checklist_template',
-                  :action => :manage_checklist_templates,
-                  :partial => 'projects/settings/checklist_templates',
-                  :label => :label_checklist_templates }
-          tabs << tab if User.current.allowed_to?(:edit_issues, @project) && User.current.allowed_to?(tab[:action], @project)
-          tabs
-        end
-      end
-    end
-  end
-end
-
-unless ProjectsHelper.included_modules.include?(RedmineChecklists::Patches::ProjectsHelperPatch)
-  ProjectsHelper.send(:include, RedmineChecklists::Patches::ProjectsHelperPatch)
-end
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/redmine_checklist_setting.rb b/plugins/redmine_checklists/lib/redmine_checklists/redmine_checklist_setting.rb
deleted file mode 100644
index a876100..0000000
--- a/plugins/redmine_checklists/lib/redmine_checklists/redmine_checklist_setting.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineChecklists
-  class RedmineChecklistSetting
-    def self.block_issue_closing?
-      Setting.plugin_redmine_checklists['block_issue_closing'].to_i > 0
-    end
-  end
-end
diff --git a/plugins/redmine_checklists/lib/redmine_checklists/redmine_checklists.rb b/plugins/redmine_checklists/lib/redmine_checklists/redmine_checklists.rb
index 966a59b..6db0db4 100644
--- a/plugins/redmine_checklists/lib/redmine_checklists/redmine_checklists.rb
+++ b/plugins/redmine_checklists/lib/redmine_checklists/redmine_checklists.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -19,7 +19,6 @@
 
 Rails.configuration.to_prepare do
   require 'redmine_checklists/patches/compatibility/application_helper_patch'
-  require 'redmine_checklists/patches/compatibility/application_controller_patch' if Rails::VERSION::MAJOR < 4
 
   require 'redmine_checklists/hooks/views_issues_hook'
   require 'redmine_checklists/hooks/views_layouts_hook'
@@ -30,14 +29,19 @@ Rails.configuration.to_prepare do
   require 'redmine_checklists/patches/issues_controller_patch'
   require 'redmine_checklists/patches/add_helpers_for_checklists_patch'
   require 'redmine_checklists/patches/compatibility_patch'
-  require 'redmine_checklists/patches/projects_helper_patch'
-  require 'redmine_checklists/patches/notifiable_patch'
-  require 'redmine_checklists/patches/issue_query_patch'
   require 'redmine_checklists/patches/issues_helper_patch'
   require 'redmine_checklists/patches/compatibility/open_struct_patch'
   require 'redmine_checklists/patches/compatibility/journal_patch'
 end
 
 module RedmineChecklists
-  def self.settings() Setting[:plugin_redmine_checklists].blank? ? {} : Setting[:plugin_redmine_checklists] end
+  def self.settings() Setting.plugin_redmine_checklists.blank? ? {} : Setting.plugin_redmine_checklists end
+
+  def self.block_issue_closing?
+    settings['block_issue_closing'].to_i > 0
+  end
+
+  def self.issue_done_ratio?
+    settings['issue_done_ratio'].to_i > 0
+  end
 end
diff --git a/plugins/redmine_checklists/scripts/run_local.sh b/plugins/redmine_checklists/scripts/run_local.sh
deleted file mode 100755
index dc77740..0000000
--- a/plugins/redmine_checklists/scripts/run_local.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-RUBY=$1
-DB=$2
-REDMINE=$3
-
-docker run -t -i -v `pwd`:/var/www/$RUBY/$DB/$REDMINE/plugins/redmine_checklists \
-  --env RUBY=$1 \
-  --env DB=$2 \
-  --env REDMINE=$3 \
-  --env PLUGIN=redmine_checklists \
-  redmineup/redmine_checklists \
-  /root/run_local.sh
diff --git a/plugins/redmine_checklists/test/fixtures/checklists.yml b/plugins/redmine_checklists/test/fixtures/checklists.yml
index 573b24a..399383b 100644
--- a/plugins/redmine_checklists/test/fixtures/checklists.yml
+++ b/plugins/redmine_checklists/test/fixtures/checklists.yml
@@ -1,14 +1,25 @@
-# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+---
+# === Checklist for Issue(1) ===
 one:
   id: 1
   is_done: false
   subject: First todo
   issue_id: 1
+
 two:
   id: 2
   is_done: true
   subject: Second todo
   issue_id: 1
+
+# === Checklist for Issue(2) ===
+section_one:
+  id: 4
+  is_done: false
+  subject: New section
+  is_section: true
+  issue_id: 2
+
 three:
   id: 3
   is_done: true
diff --git a/plugins/redmine_checklists/test/functional/checklist_template_categories_controller_test.rb b/plugins/redmine_checklists/test/functional/checklist_template_categories_controller_test.rb
deleted file mode 100644
index 6c242b2..0000000
--- a/plugins/redmine_checklists/test/functional/checklist_template_categories_controller_test.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-# encoding: utf-8
-#
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-require File.expand_path('../../test_helper', __FILE__)
-class ChecklistTemplateCategoriesControllerTest < ActionController::TestCase
-  fixtures :projects,
-           :users,
-           :roles,
-           :members,
-           :member_roles,
-           :issues,
-           :issue_statuses,
-           :versions,
-           :trackers,
-           :projects_trackers,
-           :issue_categories,
-           :enabled_modules,
-           :enumerations,
-           :attachments,
-           :workflows,
-           :custom_fields,
-           :custom_values,
-           :custom_fields_projects,
-           :custom_fields_trackers,
-           :time_entries,
-           :journals,
-           :journal_details,
-           :queries
-  RedmineChecklists::TestCase.create_fixtures(Redmine::Plugin.find(:redmine_checklists).directory + '/test/fixtures/', [:checklists])
-
-  def setup
-    RedmineChecklists::TestCase.prepare
-    Setting.default_language = 'en'
-    Project.find(1).enable_module!(:checklists)
-    Project.find(1).enable_module!(:issue_tracking)
-    User.current = nil
-    @project_1 = Project.find(1)
-    @issue_1 = Issue.find(1)
-    @checklist_1 = Checklist.find(1)
-  end
-
-  test 'should show new form' do
-    @request.session[:user_id] = 1
-    compatible_request :get, :new
-    assert_select 'form.new_checklist_template_category div.box.tabular'
-  end
-
-  test 'creates new checklist template category' do
-    @request.session[:user_id] = 1
-    compatible_request :post, :create, :checklist_template_category => { :name => 'test1' }
-    assert_equal 1, ChecklistTemplateCategory.count
-  end
-
-  test 'should show edit form' do
-    @request.session[:user_id] = 1
-    @template = ChecklistTemplateCategory.create!(:name => 'category1')
-    compatible_request :get, :edit, :id => @template.to_param
-    assert_select 'form.edit_checklist_template_category div.box.tabular'
-  end
-
-  test 'should update checklist template category' do
-    @request.session[:user_id] = 1
-    @template = ChecklistTemplateCategory.create!(:name => 'category1')
-    compatible_request :put, :update, :id => @template.to_param, :category => { :name => 'category2' }
-    assert_equal 'category2', ChecklistTemplateCategory.last.name
-  end
-
-  test 'should delete checklist template' do
-    @request.session[:user_id] = 1
-    @template = ChecklistTemplateCategory.create!(:name => 'category1')
-    compatible_request :delete, :destroy, :id => @template.to_param
-    assert_equal 0, ChecklistTemplateCategory.count
-  end
-end
diff --git a/plugins/redmine_checklists/test/functional/checklist_templates_controller_test.rb b/plugins/redmine_checklists/test/functional/checklist_templates_controller_test.rb
deleted file mode 100644
index 73a6374..0000000
--- a/plugins/redmine_checklists/test/functional/checklist_templates_controller_test.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-# encoding: utf-8
-#
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-require File.expand_path('../../test_helper', __FILE__)
-
-class ChecklistTemplatesControllerTest < ActionController::TestCase
-  fixtures :projects,
-           :users,
-           :roles,
-           :members,
-           :member_roles,
-           :issues,
-           :issue_statuses,
-           :versions,
-           :trackers,
-           :projects_trackers,
-           :issue_categories,
-           :enabled_modules,
-           :enumerations,
-           :attachments,
-           :workflows,
-           :custom_fields,
-           :custom_values,
-           :custom_fields_projects,
-           :custom_fields_trackers,
-           :time_entries,
-           :journals,
-           :journal_details,
-           :queries
-  RedmineChecklists::TestCase.create_fixtures(Redmine::Plugin.find(:redmine_checklists).directory + '/test/fixtures/', [:checklists])
-
-  def setup
-    RedmineChecklists::TestCase.prepare
-    Setting.default_language = 'en'
-    Project.find(1).enable_module!(:checklists)
-    Project.find(1).enable_module!(:issue_tracking)
-    User.current = nil
-    @second_user = User.find(2)
-    @project_1 = Project.find(1)
-    @issue_1 = Issue.find(1)
-    @checklist_1 = Checklist.find(1)
-    MemberRole.create(:member_id => 1, :role_id => 2)
-  end
-
-  test 'should show new form' do
-    @request.session[:user_id] = 1
-    compatible_request :get, :new
-    assert_select 'form.new_checklist_template div.box.tabular'
-  end
-
-  test 'creates new checklist template' do
-    @request.session[:user_id] = 1
-    compatible_request :post, :create, :checklist_template => { :name => 'test1', :template_items => 'item1 item2' }, :checklist_template_is_for_all => true
-    assert_equal 'test1', ChecklistTemplate.last.name
-    assert_equal 1, ChecklistTemplate.last.user_id
-  end
-
-  test 'user with right can create template' do
-    @request.session[:user_id] = @second_user.id
-    compatible_request :post, :create, :checklist_template => { :name => 'user_test', :template_items => 'item1 item2' }, :project_id => @project_1
-    assert_equal 'user_test', ChecklistTemplate.last.name
-    assert_equal 2, ChecklistTemplate.last.user_id
-    assert_equal @project_1, ChecklistTemplate.last.project
-  end
-
-  test 'user cant create public template for all projects' do
-    @request.session[:user_id] = @second_user.id
-    compatible_request :post, :create, :checklist_template => { :name => 'public_template', :template_items => 'item1 item2', :is_public => '1' },
-                                       :checklist_template_is_for_all => '1',
-                                       :project_id => @project_1
-    assert_equal 'public_template', ChecklistTemplate.last.name
-    assert_equal 2, ChecklistTemplate.last.user_id
-    assert_equal true, ChecklistTemplate.last.is_public
-    assert_equal @project_1, ChecklistTemplate.last.project
-  end
-
-  test 'nobody cant edit user template if it not public, except admins' do
-    @request.session[:user_id] = @second_user.id
-    compatible_request :post, :create, :checklist_template => { :name => 'not_public_template', :template_items => 'item1 item2', :is_public => '0' },
-                                       :checklist_template_is_for_all => '1',
-                                       :project_id => @project_1
-    assert_equal 'not_public_template', ChecklistTemplate.last.name
-    @request.session[:user_id] = 3
-    compatible_request :get, :edit, :project_id => @project_1, :id => ChecklistTemplate.last.id
-    assert_response :missing
-  end
-
-  test 'should show edit form' do
-    @request.session[:user_id] = 1
-    @template = ChecklistTemplate.create!(:name => 'template1', :template_items => 'item1 item2', :is_public => true)
-    compatible_request :get, :edit, :id => @template.to_param
-    assert_select 'form.edit_checklist_template div.box.tabular'
-  end
-
-  test 'should update checklist template' do
-    @request.session[:user_id] = 1
-    @template = ChecklistTemplate.create!(:name => 'template1', :template_items => 'item1 item2', :is_public => true)
-    compatible_request :put, :update, :id => @template.to_param, :checklist_template => { :name => 'test2' }
-    assert_equal 'test2', ChecklistTemplate.last.name
-  end
-
-  test 'should delete checklist template' do
-    @request.session[:user_id] = 1
-    @template = ChecklistTemplate.create!(:name => 'template1', :template_items => 'item1 item2', :is_public => true)
-    compatible_request :delete, :destroy, :id => @template.to_param
-    assert_equal 0, ChecklistTemplate.count
-  end
-
-  def test_should_created_default_template_for_tracker
-    @request.session[:user_id] = 1
-    compatible_request :post, :create, :checklist_template => { :name => 'default', :template_items => 'default1 default2', :is_default => 1, :tracker_id => 1 }
-    assert_equal 'default', ChecklistTemplate.last.name
-    assert_equal 1, ChecklistTemplate.last.user_id
-    assert_equal true, ChecklistTemplate.last.is_default?
-    assert_equal Tracker.find(1), ChecklistTemplate.last.tracker
-  end
-end
diff --git a/plugins/redmine_checklists/test/functional/checklists_controller_test.rb b/plugins/redmine_checklists/test/functional/checklists_controller_test.rb
index 3ad4aaf..29d7a59 100644
--- a/plugins/redmine_checklists/test/functional/checklists_controller_test.rb
+++ b/plugins/redmine_checklists/test/functional/checklists_controller_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -64,19 +64,6 @@ class ChecklistsControllerTest < ActionController::TestCase
     assert_response :success, 'Post done not working'
     assert_equal true, Checklist.find(1).is_done, 'Post done not working'
   end
-  test 'sends email about checklists' do
-    @request.session[:user_id] = 1
-    Setting[:plugin_redmine_checklists] = { :save_log => 1, :issue_done_ratio => 0 }
-    EmailAddress.create!(:user_id => 2, :address =>  'test@example.com') if Redmine::VERSION.to_s >= '3.0'
-    compatible_xhr_request :put, :done, :is_done => 'true', :id => '1'
-    assert ActionMailer::Base.deliveries.last
-    email = ActionMailer::Base.deliveries.last
-    assert_include 'Checklist item [x] First todo set to Done', email.text_part.body.to_s
-    # Test changes fixup
-    compatible_xhr_request :put, :done, :is_done => 'false', :id => '2'
-    email = ActionMailer::Base.deliveries.last
-    assert_include 'Checklist item [x] First todo set to Done', email.text_part.body.to_s
-  end
 
   test "should not post done by deny user" do
     # log_user('admin', 'admin')
diff --git a/plugins/redmine_checklists/test/functional/issues_controller_test.rb b/plugins/redmine_checklists/test/functional/issues_controller_test.rb
index 3cf565a..ad83748 100644
--- a/plugins/redmine_checklists/test/functional/issues_controller_test.rb
+++ b/plugins/redmine_checklists/test/functional/issues_controller_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -53,28 +53,7 @@ class IssuesControllerTest < ActionController::TestCase
 
   def setup
     @request.session[:user_id] = 1
-  end
-  def test_show_index_with_checklists_filter
-    compatible_request :get, :index, :project_id => 1, :set_filter => '1', :f => ['checklists_status', ''], :op => { :checklists_status => '=' }, :v => { :checklists_status => ['1'] }
-    assert_response :success
-    assert_equal issues_in_list.map(&:id), [2, 1]
-
-    compatible_request :get, :index, :project_id => 1, :set_filter => '1', :f => ['checklists_status', ''], :op => { :checklists_status => '=' }, :v => { :checklists_status => ['0'] }
-    assert_response :success
-    assert_equal issues_in_list.map(&:id), [1]
-
-    compatible_request :get, :index, :project_id => 1, :set_filter => '1', :f => ['checklists_item', ''], :op => { :checklists_item => '~' }, :v => { :checklists_item => ['todo'] }
-    assert_response :success
-    assert_equal issues_in_list.map(&:id), [2, 1]
-
-    compatible_request :get, :index, :project_id => 1, :set_filter => '1', :f => ['checklists_item', ''], :op => { :checklists_item => '~' }, :v => { :checklists_item => ['Third'] }
-    assert_response :success
-    assert_equal issues_in_list.map(&:id), [2]
-
-    compatible_request :get, :index, :project_id => 1, :set_filter => '1', :f => ['checklists_item', ''], :op => { :checklists_item => '!*' }
-    assert_response :success
-    assert_equal issues_in_list.map(&:id).include?(1), false
-    assert_equal issues_in_list.map(&:id).include?(2), false
+    RedmineChecklists::TestCase.prepare
   end
 
   def test_new_issue_without_project
@@ -118,64 +97,9 @@ class IssuesControllerTest < ActionController::TestCase
       compatible_xhr_request :put, :new, :issue => parameters, :project_id => issue.project
     end
     assert_response :success
-    assert_equal 'text/javascript', response.content_type
+    assert_match 'text/javascript', response.content_type
     assert_match 'FirstChecklist', response.body
   end
-  def test_update_sends_email
-    Setting[:plugin_redmine_checklists] = { :save_log => 1, :issue_done_ratio => 0 }
-    parameters = { :checklists_attributes => { '0' => { 'is_done' => '0', 'subject' => 'Third' },
-                                               '1' => { 'is_done' => '1', 'subject' => 'Fourth' },
-                                               '2' => { 'id' => 2, '_destroy' => '1', 'subject' => 'Second todo' }
-                    } }
-
-    @request.session[:user_id] = 1
-    issue = Issue.find(1)
-    EmailAddress.create!(:user_id => 2, :address => 'test@example.com') if Redmine::VERSION.to_s >= '3.0'
-
-    compatible_xhr_request :put, :update, :issue => parameters, :project_id => issue.project, :id => issue.to_param
-    assert ActionMailer::Base.deliveries.last
-    email = ActionMailer::Base.deliveries.last
-    assert_include 'Checklist item [ ] Third added', email.text_part.body.to_s
-    assert_include 'Checklist item [x] Fourth added', email.text_part.body.to_s
-    assert_include 'Checklist item deleted (Second todo)', email.text_part.body.to_s
-  end
-
-  def test_update_send_notification_email
-    old_events = Setting.notified_events
-    Setting.notified_events = ['checklist_updated']
-    Setting[:plugin_redmine_checklists] = { :save_log => 1, :issue_done_ratio => 0 }
-    parameters = { 'checklists_attributes' => { '0' => { 'is_done' => '0', 'subject' => 'Third' },
-                                                '1' => { 'is_done' => '1', 'subject' => 'Fourth' },
-                                                '2' => { 'id' => '2', '_destroy' => '1', 'subject' => 'Second todo' } } }
-    @request.session[:user_id] = 1
-    issue = Issue.find(1)
-    EmailAddress.create!(:user_id => 2, :address => 'test@example.com', :is_default => true) if Redmine::VERSION.to_s >= '3.0'
-
-    compatible_xhr_request :put, :update, :issue => parameters, :project_id => issue.project, :id => issue.to_param
-    assert ActionMailer::Base.deliveries.last
-    email = ActionMailer::Base.deliveries.last
-    assert_include 'Checklist item [ ] Third added', email.text_part.body.to_s
-  ensure
-    Setting.notified_events = old_events
-  end
-
-  def test_update_status_with_checklist_destroy
-    issue = Issue.find(1)
-    initial_status = issue.status_id
-    closed_status = IssueStatus.where(:is_closed => true).first
-    with_checklists_settings('block_issue_closing' => '1') do
-      parameters = { :status_id => closed_status.id,
-                     :checklists_attributes => { '0' => { 'id' => 1, 'is_done' => '1', 'subject' => 'First todo' },
-                                                 '1' => { 'id' => 2, '_destroy' => '1', 'subject' => 'Second todo' } } }
-
-      compatible_xhr_request :put, :update, :issue => parameters, :project_id => issue.project, :id => issue.to_param
-      issue.reload
-      assert_equal issue.status, closed_status
-      assert_equal issue.checklists.count, 1
-    end
-  ensure
-    issue.update_attributes(:status_id => initial_status)
-  end
 
   def test_added_attachment_shows_in_log_once
     Setting[:plugin_redmine_checklists] = { :save_log => 1, :issue_done_ratio => 0 }
@@ -193,31 +117,6 @@ class IssuesControllerTest < ActionController::TestCase
     assert_response :redirect
     assert_equal 1, Journal.last.details.where(:property => 'attachment').count
   end
-  def test_update_with_delete_write_to_journal
-    Setting[:plugin_redmine_checklists] = { :save_log => 1, :issue_done_ratio => 0 }
-    @request.session[:user_id] = 1
-    issue = Issue.find(1)
-    EmailAddress.create!(:user_id => 2, :address => 'test@example.com') if Redmine::VERSION.to_s >= '3.0'
-
-    # Create new checklist
-    compatible_xhr_request :put, :update,
-                           :issue => { :notes => 'fix me',
-                                       :checklists_attributes => { '0' => { 'is_done' => '0', 'subject' => 'Five' } } },
-                           :project_id => issue.project,
-                           :id => issue.to_param
-    assert_response :redirect
-    issue.reload
-    # Delete new checklist
-    compatible_xhr_request :put, :update,
-                           :issue => { :checklists_attributes => { '0' => { 'id' => issue.checklists.max.id, '_destroy' => '1', 'subject' => 'First todo' } } },
-                           :project_id => issue.project,
-                           :id => issue.to_param
-    assert_response :redirect
-
-    compatible_request :get, :show, :id => issue.id
-    assert_response :success
-    assert_select "#change-#{issue.journals.last.id} .details li", 'Checklist item deleted (Five)'
-  end
 
   def test_history_dont_show_old_format_checklists
     Setting[:plugin_redmine_checklists] = { :save_log => 1, :issue_done_ratio => 0 }
@@ -261,6 +160,24 @@ class IssuesControllerTest < ActionController::TestCase
     assert_not_nil issue
   end
 
+  def test_create_issue_with_checklists
+    @request.session[:user_id] = 1
+    assert_difference 'Issue.count' do
+      compatible_request :post, :create, :project_id => 1, :issue => { :tracker_id => 3,
+                                                                       :status_id => 2,
+                                                                       :subject => 'NEW issue with checklists',
+                                                                       :description => 'This is the description',
+                                                                       :checklists_attributes => { '0' => { 'is_done' => '0', 'subject' => 'item 001', 'position' => '1' } }
+                                                                     }
+    end
+    assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
+
+    issue = Issue.find_by_subject('NEW issue with checklists')
+    assert_equal 1, issue.checklists.count
+    assert_equal 'item 001', issue.checklists.last.subject
+    assert_not_nil issue
+  end
+
   def test_create_issue_using_json
     old_value = Setting.rest_api_enabled
     Setting.rest_api_enabled = '1'
@@ -282,89 +199,28 @@ class IssuesControllerTest < ActionController::TestCase
   ensure
     Setting.rest_api_enabled = old_value
   end
-  def test_save_order_position
-    parameters = { :checklists_attributes => { '0' => { 'id' => 1, 'is_done' => '0', 'subject' => 'First todo', 'position' => '0' },
-                                               '1' => { 'is_done' => '1', 'subject' => 'New checklist', 'position' => '1' },
-                                               '2' => { 'id' => 2, 'is_done' => '0', 'subject' => 'Second todo', 'position' => '2' } } }
 
-    issue = Issue.find(1)
+  def test_history_displaying_for_checklist
     @request.session[:user_id] = 1
-    compatible_xhr_request :put, :update, :issue => parameters, :project_id => issue.project, :id => issue.to_param
-    assert_equal 0, Checklist.find(1).position
-    assert_equal 2, Checklist.find(2).position
-    assert_equal 1, Checklist.last.position
-  end
+    Setting[:plugin_redmine_checklists] = { save_log: 1, issue_done_ratio: 0 }
 
-  def test_add_default_project_template_to_issue
-    @request.session[:user_id] = 1
-    @project = Project.find(1)
-    @template = ChecklistTemplate.create!(:name => 'Default', :template_items => 'def 1', :is_default => true, :user => User.find(1), :project => @project)
-    compatible_request :get, :new, :project_id => @project.id
-    assert_response :success
-    assert_select 'span.checklist-subject', 'def 1'
-  ensure
-    @template.destroy
-  end
+    issue = Issue.find(1)
+    journal = issue.journals.create!(user_id: 1)
+    journal.details.create!(:property =>  'attr',
+                            :prop_key =>  'checklist',
+                            :old_value => '[ ] TEST',
+                            :value =>     '[x] TEST')
 
-  def test_add_default_tracker_project_template_to_issue
+    # With permissions
     @request.session[:user_id] = 1
-    @project = Project.find(1)
-    @tracker = @project.trackers.first
-    @p_template = ChecklistTemplate.create!(:name => 'Default P', :template_items => 'project 1', :is_default => true, :user => User.find(1), :project => @project)
-    @t_template = ChecklistTemplate.create!(:name => 'Default T', :template_items => 'tracker 1', :is_default => true,
-                                            :tracker_id => @tracker.id, :user => User.find(1), :project => @project)
-    compatible_request :get, :new, :project_id => @project.id
+    compatible_request :get, :show, id: issue.id
     assert_response :success
-    assert_select 'span.checklist-subject', 'tracker 1'
-  ensure
-    @p_template.destroy
-    @t_template.destroy
-  end
+    assert_include 'changed from [ ] TEST to [x] TEST', response.body
 
-  def test_apply_default_tracker_template_on_tracker_change
-    @request.session[:user_id] = 1
-    @project = Project.find(1)
-    @tracker = @project.trackers.first
-    @t_template = ChecklistTemplate.create!(:name => 'Default T', :template_items => 'tracker-1', :is_default => true,
-                                            :tracker_id => @tracker.id, :user => User.find(1), :project => @project)
-
-    parameters = { :tracker_id => @tracker.id + 1, :checklists_attributes => { '0' => { 'is_done' => '0', 'subject' => '', '_destroy' => 'false', 'position' => '1', 'id' => '' } } }
-
-    # Tracker without default list
-    compatible_xhr_request :post, :new, :issue => parameters, :project_id => @project
+    # Without permissions
+    @request.session[:user_id] = 5
+    compatible_request :get, :show, id: issue.id
     assert_response :success
-    assert_no_match %r{tracker-1}, response.body
-
-    # Tracker with default list
-    compatible_xhr_request :post, :new, :issue => parameters.merge(:tracker_id => @tracker.id), :project_id => @project
-    assert_response :success
-    assert_match 'tracker-1', response.body
-
-    # Tracker with custom checklist
-    compatible_xhr_request :post, :new, :issue => { :tracker_id => @tracker.id, :checklists_attributes => { '0' => { 'is_done' => '0', 'subject' => 'CUSTOM', '_destroy' => 'false', 'position' => '1', 'id' => '' } } }, :project_id => @project
-    assert_response :success
-    assert_match 'CUSTOM', response.body
-  ensure
-    @t_template.destroy
-  end
-
-  def test_copy_subtask_with_checklits
-    parent = Issue.find(1)
-    child  = Issue.find(2)
-    child.parent_issue_id = parent.id
-    child.fixed_version_id = nil
-    child.save
-
-    check_attrs = { :checklists_attributes => { '0' => { 'is_done' => '0', 'subject' => 'First todo', '_destroy' => 'false', 'position' => '1', 'id' => '' },
-                                                '1' => { 'is_done' => '1', 'subject' => 'Second todo', '_destroy' => 'false', 'position' => '2', 'id' => '' } } }
-    compatible_request :post, :create, :copy_from => parent.id, :link_copy => 1, :copy_subtasks => 1, :issue => parent.attributes.except(:created_on, :updated_on).merge(check_attrs)
-    parent_copy = parent.reload.relations.first.issue_to
-    assert_not_nil parent_copy
-    assert_equal 2, parent_copy.checklists.count
-    child_copy = parent_copy.children.first
-    assert_not_nil child_copy
-    assert_equal 1, child_copy.checklists.count
-  ensure
-    Issue.find(2).update_attributes(:parent_id => nil, :fixed_version_id => 2)
+    assert_not_include 'changed from [ ] TEST to [x] TEST', response.body
   end
 end
diff --git a/plugins/redmine_checklists/test/integration/api_test/checklists_test.rb b/plugins/redmine_checklists/test/integration/api_test/checklists_test.rb
index 613b7b5..9ad8c35 100644
--- a/plugins/redmine_checklists/test/integration/api_test/checklists_test.rb
+++ b/plugins/redmine_checklists/test/integration/api_test/checklists_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -84,12 +84,12 @@ class Redmine::ApiTest::ChecklistsTest < Redmine::ApiTest::Base
     assert_equal parameters[:checklist][:subject], checklist.subject
 
     assert_response :created
-    assert_equal 'application/xml', @response.content_type
+    assert_match 'application/xml', @response.content_type
     assert_select 'checklist id', :text => checklist.id.to_s
   end
 
   def test_put_checklists_1_xml
-    parameters = { :checklist => { :subject => 'Item_UPDATED' } }
+    parameters = { :checklist => { subject: 'Item_UPDATED', is_done: '1' } }
 
     assert_no_difference('Checklist.count') do
       compatible_api_request :put, '/checklists/1.xml', parameters, credentials('admin')
@@ -99,12 +99,35 @@ class Redmine::ApiTest::ChecklistsTest < Redmine::ApiTest::Base
     assert_equal parameters[:checklist][:subject], checklist.subject
   end
 
+  def test_recalculate_ratio_after_multirequests
+    issue = Issue.find(1)
+    with_checklists_settings('issue_done_ratio' => '1') do
+      assert_equal 0, issue.reload.done_ratio
+
+      parameters_array = [
+        [1, { :checklist => { subject: 'Item 1', is_done: '1' } }],
+        [2, { :checklist => { subject: 'Item 2', is_done: '1' } }],
+        [1, { :checklist => { subject: 'Item 1', is_done: '0' } }],
+        [2, { :checklist => { subject: 'Item 2', is_done: '1' } }]
+      ]
+
+      assert_no_difference('Checklist.count') do
+        parameters_array.each do |params|
+          compatible_api_request :put, "/checklists/#{params[0]}.xml", params[1], credentials('admin')
+          assert ['200', '204'].include?(response.code)
+        end
+      end
+
+      assert_equal 50, issue.reload.done_ratio
+    end
+  end
+
   def test_delete_1_xml
     assert_difference 'Checklist.count', -1 do
       compatible_api_request :delete, '/checklists/1.xml', {}, credentials('admin')
     end
 
-    assert_response :ok
+    assert ['200', '204'].include?(response.code)
     assert_equal '', @response.body
     assert_nil Checklist.find_by_id(1)
   end
diff --git a/plugins/redmine_checklists/test/integration/common_issue_test.rb b/plugins/redmine_checklists/test/integration/common_issue_test.rb
index c43526a..47520a1 100644
--- a/plugins/redmine_checklists/test/integration/common_issue_test.rb
+++ b/plugins/redmine_checklists/test/integration/common_issue_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -53,23 +53,6 @@ class CommonIssueTest < RedmineChecklists::IntegrationTest
     @issue_1     = Issue.find(1)
     @checklist_1 = Checklist.find(1)
   end
-  def test_checklist_settings
-    log_user('admin', 'admin')
-    ChecklistTemplate.create!(:name => 'template1', :template_items => 'item1 item2', :is_public => true, :project => @project_1)
-    ChecklistTemplate.create!(:name => 'template2', :template_items => 'item1 item2', :is_public => true)
-    compatible_request :get, '/settings/plugin/redmine_checklists'
-    assert_response :success
-    assert_select 'tr.checklist-template', 2
-  end
-
-  def test_checklist_project_settings
-    log_user('admin', 'admin')
-    ChecklistTemplate.create!(:name => 'template1', :template_items => 'item1 item2', :is_public => true, :project => @project_1)
-    ChecklistTemplate.create!(:name => 'template2', :template_items => 'item1 item2', :is_public => true)
-    compatible_request :get, '/projects/ecookbook/settings/checklist_template'
-    assert_response :success
-    assert_select 'tr.checklist-template', 2
-  end
 
   def test_global_search_with_checklist
     log_user('admin', 'admin')
diff --git a/plugins/redmine_checklists/test/test_helper.rb b/plugins/redmine_checklists/test/test_helper.rb
index c457c3c..0d949e7 100644
--- a/plugins/redmine_checklists/test/test_helper.rb
+++ b/plugins/redmine_checklists/test/test_helper.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -44,11 +44,11 @@ module RedmineChecklists
     end
 
     def with_checklists_settings(options, &block)
-      Setting.plugin_redmine_checklists.stubs(:[]).returns(nil)
-      options.each { |k, v| Setting.plugin_redmine_checklists.stubs(:[]).with(k).returns(v) }
+      original_settings = Setting.plugin_redmine_checklists
+      Setting.plugin_redmine_checklists = original_settings.merge(Hash[options.map {|k,v| [k, v]}])
       yield
     ensure
-      options.each { |_k, _v| Setting.plugin_redmine_checklists.unstub(:[]) }
+      Setting.plugin_redmine_checklists = original_settings
     end
   end
 end
@@ -71,6 +71,11 @@ class RedmineChecklists::TestCase
   end
 
   def self.prepare
+    Role.find([1,2]).each do |r| # For anonymous
+      r.permissions << :view_checklists
+      r.save
+    end
+
     Role.find(1, 2, 3, 4).each do |r|
       r.permissions << :edit_checklists
       r.save
diff --git a/plugins/redmine_checklists/test/unit/checklist_template_category_test.rb b/plugins/redmine_checklists/test/unit/checklist_template_category_test.rb
deleted file mode 100644
index 17ab54b..0000000
--- a/plugins/redmine_checklists/test/unit/checklist_template_category_test.rb
+++ /dev/null
@@ -1,80 +0,0 @@
-# encoding: utf-8
-#
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-require File.expand_path('../../test_helper', __FILE__)
-
-class ChecklistTemplateCategoryTest < ActiveSupport::TestCase
-  fixtures :projects,
-           :users,
-           :roles,
-           :members,
-           :member_roles,
-           :issues,
-           :issue_statuses,
-           :versions,
-           :trackers,
-           :projects_trackers,
-           :issue_categories,
-           :enabled_modules,
-           :enumerations,
-           :attachments,
-           :workflows,
-           :custom_fields,
-           :custom_values,
-           :custom_fields_projects,
-           :custom_fields_trackers,
-           :time_entries,
-           :journals,
-           :journal_details,
-           :queries
-
-  RedmineChecklists::TestCase.create_fixtures(Redmine::Plugin.find(:redmine_checklists).directory + '/test/fixtures/', [:checklists])
-
-  def setup
-    RedmineChecklists::TestCase.prepare
-    Setting.default_language = 'en'
-    @project_1 = Project.find(1)
-    @issue_1 = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 1,
-                            :status_id => 1, :priority => IssuePriority.first,
-                            :subject => 'Invoice Issue 1')
-    @checklist_1 = Checklist.create(:subject => 'TEST1', :position => 1, :issue => @issue_1)
-  end
-
-  test "should exist" do
-    assert ChecklistTemplateCategory, "Checklist template category is not defined"
-  end
-
-  test "has ordered scope" do
-    assert ChecklistTemplateCategory.ordered, "Ordered scope not found for checklist template category model"
-  end
-
-  test "should act as list" do
-    cat1 = ChecklistTemplateCategory.create!(:name => 'Category 1', :position => 1)
-    cat2 = ChecklistTemplateCategory.create!(:name => 'Category 2', :position => 2)
-    assert_equal 'Category 2', ChecklistTemplateCategory.ordered.last.name
-
-    cat2.move_higher
-    assert_equal ChecklistTemplateCategory.ordered.last.name, 'Category 1'
-
-    cat2.move_to_bottom
-    assert_equal ChecklistTemplateCategory.ordered.last.name, 'Category 2'
-  end
-end
diff --git a/plugins/redmine_checklists/test/unit/checklist_template_test.rb b/plugins/redmine_checklists/test/unit/checklist_template_test.rb
deleted file mode 100644
index d26101b..0000000
--- a/plugins/redmine_checklists/test/unit/checklist_template_test.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-# encoding: utf-8
-#
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-require File.expand_path('../../test_helper', __FILE__)
-
-class ChecklistTemplateTest < ActiveSupport::TestCase
-
-  def test_save_with_category
-    ch_temp_cat = ChecklistTemplateCategory.create(:name => 'Category 1', :position => 1)
-    check_list_template = ChecklistTemplate.new(:name => 'name', :category_id => ch_temp_cat.id, :template_items => 's')
-    check_list_template.save
-    assert_equal ch_temp_cat.id, check_list_template.reload.category.id
-  end
-end
diff --git a/plugins/redmine_checklists/test/unit/checklist_test.rb b/plugins/redmine_checklists/test/unit/checklist_test.rb
index 75d7c92..63e498f 100644
--- a/plugins/redmine_checklists/test/unit/checklist_test.rb
+++ b/plugins/redmine_checklists/test/unit/checklist_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -86,4 +86,53 @@ class ChecklistTest < ActiveSupport::TestCase
     assert_equal "[x] #{@checklist_1.subject}", @checklist_1.info, "Helper info broken"
   end
 
+  def test_should_correct_recalculate_rate
+    issues = [
+      [Issue.create(project_id: 1, tracker_id: 1, author_id: 1, status_id: 1, priority: IssuePriority.first, subject: "TI #1", done_ratio: 0,
+                    checklists_attributes: {
+                      '0' => { subject: 'item 1', is_done: false },
+                      '1' => { subject: 'item 2', is_done: false },
+                      '2' => { subject: 'item 3', is_done: true },
+                    }),
+       30],
+      [Issue.create(project_id: 1, tracker_id: 1, author_id: 1, status_id: 1, priority: IssuePriority.first, subject: "TI #2", done_ratio: 0,
+                    checklists_attributes: {
+                      '0' => { subject: 'item 1', is_done: false },
+                      '1' => { subject: 'item 2', is_done: true },
+                      '2' => { subject: 'item 3', is_done: true },
+                    }),
+       60],
+       [Issue.create(project_id: 1, tracker_id: 1, author_id: 1, status_id: 1, priority: IssuePriority.first, subject: "TI #3", done_ratio: 0,
+                     checklists_attributes: {
+                       '0' => { subject: 'item 1', is_done: true },
+                       '1' => { subject: 'item 2', is_done: true },
+                       '2' => { subject: 'item 3', is_done: true },
+                     }),
+       100],
+       [Issue.create(project_id: 1, tracker_id: 1, author_id: 1, status_id: 1, priority: IssuePriority.first, subject: "TI #4", done_ratio: 0,
+                     checklists_attributes: {
+                       '0' => { subject: 'item 1', is_done: false },
+                       '1' => { subject: 'item 2', is_done: false },
+                       '2' => { subject: 'section 1', is_done: true, is_section: true },
+                       '3' => { subject: 'item 3', is_done: true },
+                     }),
+       30],
+       [Issue.create(project_id: 1, tracker_id: 1, author_id: 1, status_id: 1, priority: IssuePriority.first, subject: "TI #5", done_ratio: 0,
+        checklists_attributes: {
+          '0' => { subject: 'section 1', is_done: true, is_section: true }
+        }),
+       0]
+    ]
+
+    with_checklists_settings('issue_done_ratio' => '1') do
+      issues.each do |issue, after_ratio|
+        assert_equal 0, issue.done_ratio
+        Checklist.recalc_issue_done_ratio(issue.id)
+        issue.reload
+        assert_equal after_ratio, issue.done_ratio
+      end
+    end
+  ensure
+    issues.each { |issue, ratio| issue.destroy }
+  end
 end
diff --git a/plugins/redmine_checklists/test/unit/issue_test.rb b/plugins/redmine_checklists/test/unit/issue_test.rb
index cbf31d8..8ea4c05 100644
--- a/plugins/redmine_checklists/test/unit/issue_test.rb
+++ b/plugins/redmine_checklists/test/unit/issue_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
@@ -76,10 +76,10 @@ class IssueTest < ActiveSupport::TestCase
 
   def test_issue_should_close_when_all_checklists_finished
     with_checklists_settings('block_issue_closing' => '1') do
-      @checklist_1.update_attributes(:is_done => true)
+      @checklist_1.update(is_done: true)
       assert @issue.valid?
     end
   ensure
-    @checklist_1.update_attributes(:is_done => false)
+    @checklist_1.update(is_done: false)
   end
 end
diff --git a/plugins/redmine_checklists/test/unit/journal_checklist_history_test.rb b/plugins/redmine_checklists/test/unit/journal_checklist_history_test.rb
deleted file mode 100644
index 3e505af..0000000
--- a/plugins/redmine_checklists/test/unit/journal_checklist_history_test.rb
+++ /dev/null
@@ -1,251 +0,0 @@
-# encoding: utf-8
-#
-# This file is a part of Redmine Checklists (redmine_checklists) plugin,
-# issue checklists management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_checklists is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_checklists is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_checklists.  If not, see <http://www.gnu.org/licenses/>.
-
-require File.expand_path('../../test_helper', __FILE__)
-
-class JournalChecklistHistoryTest < ActiveSupport::TestCase
-  fixtures :projects,
-           :users,
-           :roles,
-           :members,
-           :member_roles,
-           :issues,
-           :issue_statuses,
-           :versions,
-           :trackers,
-           :projects_trackers,
-           :issue_categories,
-           :enabled_modules,
-           :enumerations,
-           :attachments,
-           :workflows,
-           :custom_fields,
-           :custom_values,
-           :custom_fields_projects,
-           :custom_fields_trackers,
-           :time_entries,
-           :journals,
-           :journal_details,
-           :queries
-
-  RedmineChecklists::TestCase.create_fixtures(Redmine::Plugin.find(:redmine_checklists).directory + '/test/fixtures/', [:checklists])
-
-  def setup
-    RedmineChecklists::TestCase.prepare
-    Setting.default_language = 'en'
-    @project_1 = Project.find(1)
-    @issue_1 = Issue.create(:project => @project_1, :tracker_id => 1, :author_id => 1,
-                            :status_id => 1, :priority => IssuePriority.first,
-                            :subject => 'TestIssue')
-
-    @checklist_1 = Checklist.create(:subject => 'TEST1', :position => 1, :issue => @issue_1)
-    @checklist_2 = Checklist.create(:subject => 'TEST2', :position => 2, :issue => @issue_1, :is_done => true)
-    @checklist_3 = Checklist.create(:subject => 'TEST3', :position => 3, :issue => @issue_1, :is_done => true)
-  end
-
-  test 'JournalChecklistHistory exists' do
-    assert JournalChecklistHistory
-  end
-
-  test 'JournalChecklistHistory can discover that checklist has been added' do
-    checklist_set_1 = [@checklist_1, @checklist_2]
-    checklist_set_2 = [@checklist_1, @checklist_2, @checklist_3]
-    diff = JournalChecklistHistory.new(checklist_set_1, checklist_set_2).diff
-
-    assert(diff[:added])
-    assert_equal(@checklist_3.id, diff[:added].map(&:to_h)[0]['id'])
-  end
-
-  test 'JournalChecklistHistory can discover that checklist has been removed' do
-    checklist_set_1 = [@checklist_1, @checklist_2]
-    checklist_set_2 = [@checklist_1, @checklist_2, @checklist_3]
-
-    diff = JournalChecklistHistory.new(checklist_set_2, checklist_set_1).diff
-
-    assert(diff[:removed])
-    assert_equal(@checklist_3.id, diff[:removed].map(&:to_h)[0]['id'])
-  end
-
-  test 'JournalChecklistHistory can discover that checklist has been renamed' do
-    checklist_set_1 = [@checklist_1, @checklist_2]
-
-    @checklist_2_dup = @checklist_2.dup
-    @checklist_2_dup.subject = 'TEST2_CHANGED'
-    # dup is redefined in ActiveRecord so it nullifies id in return value
-    # But we are imitating situation when user has modified same instance
-    @checklist_2_dup.id = @checklist_2.id
-
-    checklist_set_2 = [@checklist_1, @checklist_2_dup]
-
-    diff = JournalChecklistHistory.new(checklist_set_1, checklist_set_2).diff
-
-    assert(diff[:renamed])
-    assert_equal({ @checklist_2.subject => @checklist_2_dup.subject }, diff[:renamed])
-  end
-
-  test 'is able to work with JSON representations' do
-    checklist_set_1 = [@checklist_1, @checklist_2].to_json
-    checklist_set_2 = [@checklist_1, @checklist_2, @checklist_3].to_json
-
-    diff = JournalChecklistHistory.new(checklist_set_2, checklist_set_1).diff
-
-    assert(diff[:removed])
-    # In fact diff can contain OpenStruct instances instead of Checklists
-    # But they should have same attributes
-    assert_equal(@checklist_3.id, diff[:removed].map(&:to_h)[0]['id'])
-    assert_equal(@checklist_3.subject, diff[:removed].map(&:to_h)[0]['subject'])
-    assert_equal(@checklist_3.is_done, diff[:removed].map(&:to_h)[0]['is_done'])
-  end
-
-  test 'JournalChecklistHistory able to create JournalDetail' do
-    checklist_set_1 = [@checklist_1, @checklist_2]
-
-    @checklist_2_dup = @checklist_2.dup
-    @checklist_2_dup.subject = 'TEST2_CHANGED'
-    # dup is redefined in ActiveRecord so it nullifies id in return value
-    # But we are imitating situation when user has modified same instance
-    @checklist_2_dup.id = @checklist_2.id
-    checklist_set_2 = [@checklist_2_dup, @checklist_3]
-    details = JournalChecklistHistory.new(checklist_set_1, checklist_set_2).journal_details(:journal => @issue_1.current_journal)
-    assert_equal details.prop_key, 'checklist'
-    assert_equal JSON.parse(details.old_value)[1]['subject'], 'TEST2'
-    assert_equal JSON.parse(details.value)[0]['subject'], 'TEST2_CHANGED'
-  end
-
-  test 'can define that previous Journal contains only checklist changes' do
-    checklist_set_1 = [@checklist_1, @checklist_2]
-
-    @checklist_2_dup = @checklist_2.dup
-    @checklist_2_dup.subject = 'TEST2_CHANGED'
-    # dup is redefined in ActiveRecord so it nullifies id in return value
-    # But we are imitating situation when user has modified same instance
-    @checklist_2_dup.id = @checklist_2.id
-    checklist_set_2 = [@checklist_2_dup, @checklist_3]
-    @issue_1.init_journal(User.find(1))
-    @old_journal = @issue_1.current_journal
-    @issue_1.current_journal.save!
-    JournalChecklistHistory.new(checklist_set_1, checklist_set_2).journal_details(:journal => @issue_1.current_journal).save!
-    assert_equal(1, @issue_1.journals.size)
-
-    @issue_1 = Issue.find(@issue_1.id)
-    @issue_1.init_journal(User.find(1))
-    @issue_1.current_journal.save!
-    journal_detail = JournalChecklistHistory.new(checklist_set_1, checklist_set_2).journal_details(:journal => @issue_1.current_journal)
-    assert(JournalChecklistHistory.can_fixup?(journal_detail))
-
-    JournalDetail.new({
-      :property  => 'attr',
-      :prop_key  => 'color',
-      :old_value => 'blue',
-      :value     => 'red',
-      :journal   => @old_journal
-    }).save!
-
-    assert(!JournalChecklistHistory.can_fixup?(journal_detail))
-  end
-
-  test 'is able to fixup JournalDetail if previous Journal contains only checklist changes' do
-    checklist_set_1 = [@checklist_1, @checklist_2]
-
-    @checklist_2_dup = @checklist_2.dup
-    @checklist_2_dup.subject = 'TEST2_CHANGED'
-    # dup is redefined in ActiveRecord so it nullifies id in return value
-    # But we are imitating situation when user has modified same instance
-    @checklist_2_dup.id = @checklist_2.id
-    checklist_set_2 = [@checklist_2_dup, @checklist_3]
-    @issue_1.init_journal(User.find(1))
-    @old_journal = @issue_1.current_journal
-    @issue_1.current_journal.save!
-    JournalChecklistHistory.new(checklist_set_1, checklist_set_2).journal_details(:journal => @issue_1.current_journal).save!
-    assert_equal(1, @issue_1.journals.size)
-
-    @issue_1 = Issue.find(@issue_1.id)
-    @issue_1.init_journal(User.find(1))
-    @issue_1.current_journal.save!
-    checklist_set_2 = [@checklist_1, @checklist_3]
-    journal_detail = JournalChecklistHistory.new(checklist_set_1, checklist_set_2).journal_details(:journal => @issue_1.current_journal)
-    JournalChecklistHistory.fixup(journal_detail)
-    diff = JournalChecklistHistory.new(JournalDetail.last.old_value, JournalDetail.last.value).diff
-
-    assert_equal(@checklist_3.id, diff[:added].map(&:to_h)[0]['id'])
-    assert_equal(@checklist_2.id, diff[:removed].map(&:to_h)[0]['id'])
-    assert_equal(0, diff[:renamed].size)
-  end
-
-  test 'JournalChecklistHistory detects is_done change to undone' do
-    checklist_set_1 = [@checklist_1, @checklist_2]
-
-    @checklist_2_dup = @checklist_2.dup
-    @checklist_2_dup.is_done = false
-    # dup is redefined in ActiveRecord so it nullifies id in return value
-    # But we are imitating situation when user has modified same instance
-    @checklist_2_dup.id = @checklist_2.id
-    checklist_set_2 = [@checklist_1, @checklist_2_dup]
-    diff = JournalChecklistHistory.new(checklist_set_1, checklist_set_2).diff
-
-    assert(diff[:undone])
-    assert_equal(@checklist_2_dup.id, diff[:undone].map(&:to_h)[0]['id'])
-  end
-
-  test 'JournalChecklistHistory detects is_done change to done' do
-    checklist_set_1 = [@checklist_1, @checklist_2]
-
-    @checklist_1_dup = @checklist_1.dup
-    @checklist_1_dup.is_done = true
-    # dup is redefined in ActiveRecord so it nullifies id in return value
-    # But we are imitating situation when user has modified same instance
-    @checklist_1_dup.id = @checklist_1.id
-    checklist_set_2 = [@checklist_1_dup, @checklist_2]
-    diff = JournalChecklistHistory.new(checklist_set_1, checklist_set_2).diff
-
-    assert(diff[:done])
-    assert_equal(@checklist_1_dup.id, diff[:done].map(&:to_h)[0]['id'])
-  end
-
-  test 'it not fixup after one minute details' do
-    checklist_set_1 = [@checklist_1, @checklist_2]
-
-    @checklist_2_dup = @checklist_2.dup
-    @checklist_2_dup.is_done = false
-    @checklist_2_dup.id = @checklist_2.id
-    checklist_set_2 = [@checklist_2_dup]
-
-    @issue_1.init_journal(User.find(1))
-    @old_journal = @issue_1.current_journal
-    @issue_1.current_journal.save!
-    JournalChecklistHistory.new(checklist_set_1, checklist_set_2).journal_details(:journal => @issue_1.current_journal).save!
-    assert_equal(1, @issue_1.journals.size)
-
-    # Try fixup for fresh data
-    journal = Journal.new(:journalized => @issue_1, :user => User.find(1))
-    detail = JournalDetail.new(:property  => 'attr',
-                               :prop_key  => 'checklist',
-                               :old_value => checklist_set_1.to_json.to_s,
-                               :value     => checklist_set_2.to_json.to_s,
-                               :journal   => journal
-                              )
-    assert_equal true, JournalChecklistHistory.can_fixup?(detail)
-
-    # Try fixup old data
-    @issue_1.journals.last.update_attribute(:created_on, Time.zone.now - 10.minutes)
-    assert_equal false, JournalChecklistHistory.can_fixup?(detail)
-  end
-end
diff --git a/plugins/redmine_checklists/test/unit/project_test.rb b/plugins/redmine_checklists/test/unit/project_test.rb
index baf52b1..5e8775c 100644
--- a/plugins/redmine_checklists/test/unit/project_test.rb
+++ b/plugins/redmine_checklists/test/unit/project_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Checklists (redmine_checklists) plugin,
 # issue checklists management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_checklists is free software: you can redistribute it and/or modify
diff --git a/plugins/redmine_monitoring_controlling b/plugins/redmine_monitoring_controlling
deleted file mode 160000
index 3b2d055..0000000
--- a/plugins/redmine_monitoring_controlling
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 3b2d055a0d24c97e39f8ae45720ad4c4f81b04c4
diff --git a/plugins/redmine_user_specific_theme b/plugins/redmine_user_specific_theme
deleted file mode 160000
index 9e3cdc3..0000000
--- a/plugins/redmine_user_specific_theme
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 9e3cdc38b8c14ec9550938d4ccc42d86ab8cb12e
diff --git a/plugins/redmine_work_time/.hgignore b/plugins/redmine_work_time/.hgignore
deleted file mode 100644
index ca4b11c..0000000
--- a/plugins/redmine_work_time/.hgignore
+++ /dev/null
@@ -1,3 +0,0 @@
-syntax: regexp
-.svn
-.git
diff --git a/plugins/redmine_work_time/README.md b/plugins/redmine_work_time/README.md
index 1f37251..00f6186 100644
--- a/plugins/redmine_work_time/README.md
+++ b/plugins/redmine_work_time/README.md
@@ -1,11 +1,12 @@
-WorkTime is a plugin of Redmine to view and update Spent time by each user.
+WorkTime is a Redmine plugin to edit spent time by each user.
 
 ### Installation notes ###
 
 0. Setup Redmine
-1. Download redmine_work_time-*.zip from https://bitbucket.org/tkusukawa/redmine_work_time/downloads
+1. Download redmine_work_time-*.zip from https://github.com/tkusukawa/redmine_work_time/releases
 2. Expand the plugin into the plugins directory
-3. Migrate plugin: rake redmine:plugins:migrate RAILS_ENV=production
+3. Migrate plugin:  
+  $ RAILS_ENV=production bundle exec rake redmine:plugins:migrate
 4. Restart Redmine
 5. Enable the module on the project setting page.
 6. Check the permissions on the Roles and permissions(Administration)
@@ -13,5 +14,5 @@ WorkTime is a plugin of Redmine to view and update Spent time by each user.
 ### Links ###
 
 * http://www.redmine.org/plugins/redmine_work_time
-* https://bitbucket.org/tkusukawa/redmine_work_time
-* http://www.r-labs.org/projects/worktime/
\ No newline at end of file
+* https://github.com/tkusukawa/redmine_work_time
+* http://www.r-labs.org/projects/worktime/
diff --git a/plugins/redmine_work_time/app/controllers/work_time_controller.rb b/plugins/redmine_work_time/app/controllers/work_time_controller.rb
old mode 100755
new mode 100644
index 3a407d1..a706803
--- a/plugins/redmine_work_time/app/controllers/work_time_controller.rb
+++ b/plugins/redmine_work_time/app/controllers/work_time_controller.rb
@@ -83,6 +83,43 @@ class WorkTimeController < ApplicationController
     send_data Redmine::CodesetUtil.from_utf8(csv_data, l(:general_csv_encoding)), :type=>"text/csv", :filename=>"member_monthly.csv"
   end
 
+  def member_monthly_data_table
+    require_login || return
+    if params.key?(:id) then
+      find_project
+    end
+    prepare_values
+    make_pack
+
+    csv_data = %Q|""|
+    (@first_date..@last_date).each do |date|
+      csv_data << %Q|,"#{date}"|
+    end
+    csv_data << "\n"
+    
+    @month_pack[:odr_prjs].each do |prj_pack|
+      next if prj_pack[:count_issues] == 0
+      prj_pack[:odr_issues].each do |issue_pack|
+        next if issue_pack[:count_hours] == 0
+        issue = issue_pack[:issue]
+        
+        csv_data << %Q|"##{issue.id} #{issue.subject}"|
+        
+        (@first_date..@last_date).each do |date|
+          if issue_pack[:total_by_day].has_key?(date) then
+            csv_data << %Q|,"#{issue_pack[:total_by_day][date]}"|
+          else
+            csv_data << %Q|,""|
+          end
+        end
+        
+        csv_data << "\n"
+      end
+    end
+
+    send_data Redmine::CodesetUtil.from_utf8(csv_data, l(:general_csv_encoding)), :type=>"text/csv", :filename=>"member_monthly_table.csv"
+  end
+
   def total
     @message = ""
     find_project
@@ -727,8 +764,12 @@ private
         next if issue.nil? || !issue.visible?
         next if !User.current.allowed_to?(:log_time, issue.project)
         valss.each do |count, vals|
-          tm_vals = vals.slice! "remaining_hours", "status_id"
-          tm_vals.merge!(params["new_time_entry_#{issue_id}_#{count}"]) if params.has_key?("new_time_entry_#{issue_id}_#{count}")
+          tm_vals = vals.except "remaining_hours", "status_id"
+          if params.has_key?("new_time_entry_#{issue_id}_#{count}")
+            params["new_time_entry_#{issue_id}_#{count}"].each do |k, v|
+              tm_vals[k] = v
+            end
+          end
           next if tm_vals["hours"].blank? && vals["remaining_hours"].blank? && vals["status_id"].blank?
           if tm_vals["hours"].present? then
             if !tm_vals[:activity_id] then
@@ -740,7 +781,7 @@ private
               append_text += " add time entry of ##{issue.id.to_s}: #{tm_vals[:hours].to_f}h"
               update_daily_memo(append_text, true)
             end
-            new_entry = TimeEntry.new(:project => issue.project, :issue => issue, :user => @this_user, :spent_on => @this_date)
+            new_entry = TimeEntry.new(:project => issue.project, :issue => issue, :author => User.current, :user => @this_user, :spent_on => @this_date)
             new_entry.safe_attributes = tm_vals
             new_entry.save
             append_error_message_html(@message, hour_update_check_error(new_entry, issue_id))
@@ -757,8 +798,12 @@ private
       params["time_entry"].each do |id, vals|
         tm = TimeEntry.find_by_id(id)
         issue_id = tm.issue.id
-        tm_vals = vals.slice! "remaining_hours", "status_id"
-        tm_vals.merge!(params["time_entry_"+id.to_s]) if params.has_key?("time_entry_"+id.to_s)
+        tm_vals = vals.except "remaining_hours", "status_id"
+        if params.has_key?("time_entry_"+id.to_s)
+          params["time_entry_"+id.to_s].each do |k,v|
+            tm_vals[k] = v
+          end
+        end
         if tm_vals["hours"].blank? then
           # 工数指定が空文字の場合は工数項目を削除
           if by_other
@@ -1278,20 +1323,10 @@ private
     next_date = @this_date+1
     t1 = Time.local(@this_date.year, @this_date.month, @this_date.day)
     t2 = Time.local(next_date.year, next_date.month, next_date.day)
-    isu = Issue.arel_table
-    jnl = Journal.arel_table
-    union_sql = Issue.where((isu[:author_id].eq(@this_uid))
-      .and(  isu[:created_on].gteq(t1))
-      .and(  isu[:created_on].lt(t2)))
-    .union(
-      Issue.joins(isu.join(jnl).on(isu[:id].eq(jnl[:journalized_id])).join_sources.first)
-      .where(jnl[:journalized_type].eq('Issue')
-        .and(  jnl[:user_id].eq(@this_uid))
-        .and(  jnl[:created_on].gteq(t1))
-        .and(  jnl[:created_on].lt(t2))
-      ).uniq
-    )
-    issues = Issue.from("#{union_sql.to_sql} issues" )
+    issues = Issue.where(["(author_id = :u and created_on >= :t1 and created_on < :t2) or "+
+                              "id in (select journalized_id from journals where journalized_type = 'Issue' and "+
+                              "user_id = :u and created_on >= :t1 and created_on < :t2 group by journalized_id)",
+                          {:u => @this_user, :t1 => t1, :t2 => t2}]).all
 
     issues.each do |issue|
       next if @restrict_project && @restrict_project!=issue.project.id
diff --git a/plugins/redmine_work_time/app/models/user_issue_month.rb b/plugins/redmine_work_time/app/models/user_issue_month.rb
index b9788f7..3543e37 100644
--- a/plugins/redmine_work_time/app/models/user_issue_month.rb
+++ b/plugins/redmine_work_time/app/models/user_issue_month.rb
@@ -1,3 +1,8 @@
 class UserIssueMonth < ActiveRecord::Base
-  attr_accessible :uid, :issue, :odr
+  #attr_accessible :uid, :issue, :odr
+
+  #private
+  #def user_issue_month_params
+  #  params.require(:user_issue_month).permit(:uid, :issue, :odr)
+  #end
 end
diff --git a/plugins/redmine_work_time/app/models/wt_daily_memo.rb b/plugins/redmine_work_time/app/models/wt_daily_memo.rb
index f44b38e..a421394 100644
--- a/plugins/redmine_work_time/app/models/wt_daily_memo.rb
+++ b/plugins/redmine_work_time/app/models/wt_daily_memo.rb
@@ -1,3 +1,8 @@
 class WtDailyMemo < ActiveRecord::Base
-  attr_accessible :user_id, :day, :created_on, :updated_on, :description
+  #attr_accessible :user_id, :day, :created_on, :updated_on, :description
+
+  # private
+  # def wt_daily_memo_params
+  #   params.require(:wt_daily_memo).permit(:user_id, :day, :created_on, :updated_on, :description)
+  # end
 end
diff --git a/plugins/redmine_work_time/app/models/wt_holidays.rb b/plugins/redmine_work_time/app/models/wt_holidays.rb
index 99ac609..0ea5d0c 100644
--- a/plugins/redmine_work_time/app/models/wt_holidays.rb
+++ b/plugins/redmine_work_time/app/models/wt_holidays.rb
@@ -1,3 +1,8 @@
 class WtHolidays < ActiveRecord::Base
-  attr_accessible :holiday, :created_on, :created_by
+  #attr_accessible :holiday, :created_on, :created_by
+
+  #private
+  # def wt_holidays_params
+  #   params.require(:wt_holidays).permit(:holiday, :created_on, :created_by)
+  # end
 end
diff --git a/plugins/redmine_work_time/app/models/wt_member_order.rb b/plugins/redmine_work_time/app/models/wt_member_order.rb
index 7de57bb..9804e6f 100644
--- a/plugins/redmine_work_time/app/models/wt_member_order.rb
+++ b/plugins/redmine_work_time/app/models/wt_member_order.rb
@@ -1,3 +1,8 @@
 class WtMemberOrder < ActiveRecord::Base
-  attr_accessible :user_id, :position, :prj_id
+  #attr_accessible :user_id, :position, :prj_id
+
+  # private
+  # def wt_memver_order_params
+  #   params.require(:wt_memver_order).permit(:user_id, :position, :prj_id)
+  # end
 end
diff --git a/plugins/redmine_work_time/app/models/wt_project_orders.rb b/plugins/redmine_work_time/app/models/wt_project_orders.rb
index 84312c0..da7d2ed 100644
--- a/plugins/redmine_work_time/app/models/wt_project_orders.rb
+++ b/plugins/redmine_work_time/app/models/wt_project_orders.rb
@@ -1,3 +1,8 @@
 class WtProjectOrders < ActiveRecord::Base
-  attr_accessible :uid, :dsp_prj, :dsp_pos
+  #attr_accessible :uid, :dsp_prj, :dsp_pos
+
+  # private
+  # def wt_project_orders_params
+  #   params.require(:wt_project_orders).permit(:uid, :dsp_prj, :dsp_pos)
+  # end
 end
diff --git a/plugins/redmine_work_time/app/models/wt_ticket_relay.rb b/plugins/redmine_work_time/app/models/wt_ticket_relay.rb
index e150b44..f578249 100644
--- a/plugins/redmine_work_time/app/models/wt_ticket_relay.rb
+++ b/plugins/redmine_work_time/app/models/wt_ticket_relay.rb
@@ -1,3 +1,8 @@
 class WtTicketRelay < ActiveRecord::Base
-  attr_accessible :issue_id, :position, :parent
+  #attr_accessible :issue_id, :position, :parent
+
+  # private
+  # def wt_ticket_relay_params
+  #   params.require(:wt_ticket_relay).permit(:issue_id, :position, :parent)
+  # end
 end
diff --git a/plugins/redmine_work_time/app/views/work_time/_user_month_table.html.erb b/plugins/redmine_work_time/app/views/work_time/_user_month_table.html.erb
index 974f4df..0788a31 100644
--- a/plugins/redmine_work_time/app/views/work_time/_user_month_table.html.erb
+++ b/plugins/redmine_work_time/app/views/work_time/_user_month_table.html.erb
@@ -249,6 +249,10 @@ end
        onclick="location.href='<%=url_for(@link_params.merge(:action=>"member_monthly_data"))%>'"
        value="<%=l(:wt_data_download)%>"
 />
+<input type="button"
+       onclick="location.href='<%=url_for(@link_params.merge(:action=>"member_monthly_data_table"))%>'"
+       value="<%=l(:wt_data_download_table)%>"
+/>
 <br/>
 
 <script type="text/javascript">
diff --git a/plugins/redmine_work_time/config/locales/ca.yml b/plugins/redmine_work_time/config/locales/ca.yml
index 4188bd5..dee1fcd 100644
--- a/plugins/redmine_work_time/config/locales/ca.yml
+++ b/plugins/redmine_work_time/config/locales/ca.yml
@@ -1,48 +1,49 @@
 ca:
-  work_time: "WorkTime"
-  wt_update: "Update"
-  wt_month_names: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec"
-  wt_week_day_names: "Sun,Mon,Tue,Wed,Thu,Fri,Sat"
-  wt_monthly_report: "Monthly Report"
-  wt_daily_report: "Daily Report"
-  wt_each_member_report: "Member Report"
-  wt_raw_total: "Monthly Report(not relayed)"
-  wt_relay_total: "Monthly Report(relayed)"
-  wt_edit_relay: "Ticket Relay Editor"
-  wt_ticket: "Ticket"
-  wt_add_ticket: "Add ticket"
-  wt_no_permission: "No permission"
-  wt_loop_relay: "Ticket relation looped"
-  wt_set_holiday: "set holiday on this date"
-  wt_del_holiday: "del holiday on this date"
-  wt_apply_checked: "apply all checked"
-  wt_select_project: "Restrict project..."
-  wt_select_user: "Select user..."
-  wt_edit_memo: "Edit memo"
-  wt_pre_memo: "pre. memo"
-  wt_next_memo: "next memo"
-  wt_data_list: "Data list"
-  wt_input_ticket_numbers: "or input ticket numbers"
-  wt_delete_closed_tickets: "delete closed tickets"
-  wt_data_download: "data download"
-  wt_data_download_with_act: "data download(each activity)"
-  wt_opt_disp_ticket_with_closed: "[display closed ticket]"
-  wt_opt_disp_ticket_opened_only: "[display opened ticket only]"
-  wt_opt_disp_ticket_with_other_member: "[display other members ticket]"
-  wt_opt_disp_ticket_mine_only: "[display my ticket only]"
-  wt_bulkupdate_relations: "bulkupdate to parent"
-  permission_view_work_time_tab: "View Work time tab"
-  permission_view_work_time_other_member: "View other members' work time"
-  permission_edit_work_time_total: "Edit work time totals"
-  permission_edit_work_time_other_member: "Edit other members' work time"
-  wt_saved_value: "Saved timed: "
-  wt_legend: "Legend"
-  wt_style_default: "By default"
-  wt_style_assigned: "Assigned"
-  wt_style_worked: "Worked on"
-  wt_style__assigned_worked: "Assigned and worked on"
-  wt_style_overdue: "Overdue"
-  wt_style_assigned_overdue: "Assigned and overdue"
-  wt_style_overdue_worked: "Overdue and worked on"
-  wt_style_assigned_overdue_worked: "Assigned, overdue and worked on"
-  wt_account_start_day: "Company start day"
+  work_time: "Agenda"
+  wt_update: "Actualitzar"
+  wt_month_names: "Gen,Feb,Mar,Abr,Mai,Jun,Jul,Ago,Sep,Oct,Nov,Dic"
+  wt_week_day_names: "Diu,Dll,Dm,Dx,Di,Dv,Ds"
+  wt_monthly_report: "Informe mensual"
+  wt_daily_report: "Informe diari"
+  wt_each_member_report: "Informe de membre"
+  wt_raw_total: "Informe mensual(no se retransmet)"
+  wt_edit_relay: "Editor transmisiĂł de peticions"
+  wt_relay_total: "Informe mensual de peticions"
+  wt_ticket: "PeticiĂł"
+  wt_add_ticket: "Afegir peticiĂł"
+  wt_no_permission: "Sense permĂ­s"
+  wt_loop_relay: "TransmisiĂł de peticiĂł en bucle"
+  wt_set_holiday: "Estableix vacances en aquesta data"
+  wt_del_holiday: "Elimina vacances en aquesta data"
+  wt_apply_checked: "Aplica a tots els seleccionats"
+  wt_select_project: "Seleccione projecte..."
+  wt_select_user: "Selecciona usuari..."
+  wt_edit_memo: "Edita memorandum"
+  wt_pre_memo: "Anterior memorandum"
+  wt_next_memo: "SegĂĽent memorandum"
+  wt_data_list: "Llista de dades"
+  wt_input_ticket_numbers: "o introdueix nĂşmero de peticiĂł"
+  wt_delete_closed_tickets: "borrar tiquests tancats"
+  wt_data_download: "descarregar dades"
+  wt_data_download_table: "descarregar dades taula"
+  wt_data_download_with_act: "descarregar dades(de cada activitat)"
+  wt_opt_disp_ticket_with_closed: "[mostrar tiquets tancants]"
+  wt_opt_disp_ticket_opened_only: "[mostrar només tiquets oberts]"
+  wt_opt_disp_ticket_with_other_member: "[mostrar tiquets d'altres membres]"
+  wt_opt_disp_ticket_mine_only: "[mostrar només els meus tiquets]"
+  wt_bulkupdate_relations: "actualizar al pare"
+  permission_view_work_time_tab: "Veure pestanya temps treballat"
+  permission_view_work_time_other_member: "Veure el temps treballat d'altres membres"
+  permission_edit_work_time_total: "Editar temps total treballat"
+  permission_edit_work_time_other_member: "Editar el tiemps treballat d'altres membres"
+  wt_saved_value: "Guardar cronometrat: "
+  wt_legend: "Llegenda"
+  wt_style_default: "Per defecte"
+  wt_style_assigned: "Assignat"
+  wt_style_worked: "Treballat per"
+  wt_style__assigned_worked: "Assignat i treballat per"
+  wt_style_overdue: "Atrassat"
+  wt_style_assigned_overdue: "Assignat i atrassat"
+  wt_style_overdue_worked: "Atrassat i treballat per"
+  wt_style_assigned_overdue_worked: "Assignat, atrassat i treballat per"
+  wt_account_start_day: "Data inici companyia"
diff --git a/plugins/redmine_work_time/config/locales/de.yml b/plugins/redmine_work_time/config/locales/de.yml
index acdbb8a..396e4af 100644
--- a/plugins/redmine_work_time/config/locales/de.yml
+++ b/plugins/redmine_work_time/config/locales/de.yml
@@ -25,6 +25,7 @@ de:
   wt_input_ticket_numbers: "oder Ticket-Nr. direkt eingeben"
   wt_delete_closed_tickets: "delete closed tickets"
   wt_data_download: "Daten laden"
+  wt_data_download_table: "Tabelle Daten laden"
   wt_data_download_with_act: "Daten laden(each activity)"
   wt_opt_disp_ticket_with_closed: "[display closed ticket]"
   wt_opt_disp_ticket_opened_only: "[display opened ticket only]"
diff --git a/plugins/redmine_work_time/config/locales/en.yml b/plugins/redmine_work_time/config/locales/en.yml
index 020b5e6..4729cf2 100644
--- a/plugins/redmine_work_time/config/locales/en.yml
+++ b/plugins/redmine_work_time/config/locales/en.yml
@@ -25,6 +25,7 @@ en:
   wt_input_ticket_numbers: "or input ticket numbers"
   wt_delete_closed_tickets: "delete closed tickets"
   wt_data_download: "data download"
+  wt_data_download_table: "table data download"
   wt_data_download_with_act: "data download(each activity)"
   wt_opt_disp_ticket_with_closed: "[display closed ticket]"
   wt_opt_disp_ticket_opened_only: "[display opened ticket only]"
diff --git a/plugins/redmine_work_time/config/locales/es.yml b/plugins/redmine_work_time/config/locales/es.yml
index ff94584..840ee72 100644
--- a/plugins/redmine_work_time/config/locales/es.yml
+++ b/plugins/redmine_work_time/config/locales/es.yml
@@ -23,26 +23,27 @@ es:
   wt_next_memo: "Siguiente memorandum"
   wt_data_list: "Lista de datos"
   wt_input_ticket_numbers: "o introduzca numero de peticiĂłn"
-  wt_delete_closed_tickets: "delete closed tickets"
-  wt_data_download: "data download"
-  wt_data_download_with_act: "data download(each activity)"
-  wt_opt_disp_ticket_with_closed: "[display closed ticket]"
-  wt_opt_disp_ticket_opened_only: "[display opened ticket only]"
-  wt_opt_disp_ticket_with_other_member: "[display other members ticket]"
-  wt_opt_disp_ticket_mine_only: "[display my ticket only]"
-  wt_bulkupdate_relations: "bulkupdate to parent"
-  permission_view_work_time_tab: "View Work time tab"
-  permission_view_work_time_other_member: "View other members' work time"
-  permission_edit_work_time_total: "Edit work time totals"
-  permission_edit_work_time_other_member: "Edit other members' work time"
-  wt_saved_value: "Saved timed: "
-  wt_legend: "Legend"
-  wt_style_default: "By default"
-  wt_style_assigned: "Assigned"
-  wt_style_worked: "Worked on"
-  wt_style__assigned_worked: "Assigned and worked on"
-  wt_style_overdue: "Overdue"
-  wt_style_assigned_overdue: "Assigned and overdue"
-  wt_style_overdue_worked: "Overdue and worked on"
-  wt_style_assigned_overdue_worked: "Assigned, overdue and worked on"
-  wt_account_start_day: "Company start day"
+  wt_delete_closed_tickets: "borrar tiquests cerrados"
+  wt_data_download: "descargar datos"
+  wt_data_download_table: "descargar datos tabla"
+  wt_data_download_with_act: "descargar datos(de cada actividad)"
+  wt_opt_disp_ticket_with_closed: "[mostrar tiquets cerrados]"
+  wt_opt_disp_ticket_opened_only: "[mostrar solo tiquets abiertos]"
+  wt_opt_disp_ticket_with_other_member: "[mostrar tiquets de otros miembros]"
+  wt_opt_disp_ticket_mine_only: "[mostrar solo mis tiquets]"
+  wt_bulkupdate_relations: "actualizar al padre"
+  permission_view_work_time_tab: "Ver pestaña tiempo trabajado"
+  permission_view_work_time_other_member: "Ver el tiempo trabajado de otros miembros"
+  permission_edit_work_time_total: "Editar tiempo total trabajado"
+  permission_edit_work_time_other_member: "Editar el tiempo trabajado de otros miembros"
+  wt_saved_value: "Guardar cronometrado: "
+  wt_legend: "Legenda"
+  wt_style_default: "Por defecto"
+  wt_style_assigned: "Asignado"
+  wt_style_worked: "Trabajado por"
+  wt_style__assigned_worked: "Asignado y trabajado por"
+  wt_style_overdue: "Atrasado"
+  wt_style_assigned_overdue: "Asignado y atrasado"
+  wt_style_overdue_worked: "Atrasado y trabajado por"
+  wt_style_assigned_overdue_worked: "Asignado, atrasado y trabajado por"
+  wt_account_start_day: "Fecha inicio compañia"
diff --git a/plugins/redmine_work_time/config/locales/fr.yml b/plugins/redmine_work_time/config/locales/fr.yml
index 538f5f2..a7b5bcc 100644
--- a/plugins/redmine_work_time/config/locales/fr.yml
+++ b/plugins/redmine_work_time/config/locales/fr.yml
@@ -25,6 +25,7 @@ fr:
   wt_input_ticket_numbers: "ou entrez le numéro de la demande"
   wt_delete_closed_tickets: "Supprimer les demandes fermées"
   wt_data_download: "Téléchargement des données"
+  wt_data_download_table: "Tableau de données Téléchargement"
   wt_data_download_with_act: "Téléchargement des données(each activity)"
   wt_opt_disp_ticket_with_closed: "[afficher les demandes fermés]"
   wt_opt_disp_ticket_opened_only: "[n'afficher que les demandes ouvertes]"
diff --git a/plugins/redmine_work_time/config/locales/it.yml b/plugins/redmine_work_time/config/locales/it.yml
index a1a1b13..2ad1623 100644
--- a/plugins/redmine_work_time/config/locales/it.yml
+++ b/plugins/redmine_work_time/config/locales/it.yml
@@ -25,6 +25,7 @@ it:
   wt_input_ticket_numbers: "o inserisci i numeri delle segnalazioni"
   wt_delete_closed_tickets: "Elimina segnalazioni chiuse"
   wt_data_download: "download di dati"
+  wt_data_download_table: "tavolo download di dati"
   wt_data_download_with_act: "download di dati(each activity)"
   wt_opt_disp_ticket_with_closed: "[mostra segnalazioni chiuse]"
   wt_opt_disp_ticket_opened_only: "[mostra solo segnalazioni aperte]"
diff --git a/plugins/redmine_work_time/config/locales/ja.yml b/plugins/redmine_work_time/config/locales/ja.yml
index cfff85e..1837d0b 100644
--- a/plugins/redmine_work_time/config/locales/ja.yml
+++ b/plugins/redmine_work_time/config/locales/ja.yml
@@ -25,6 +25,7 @@ ja:
   wt_input_ticket_numbers: "またはチケット番号を入力"
   wt_delete_closed_tickets: "終了チケット削除"
   wt_data_download: "データダウンロード"
+  wt_data_download_table: "データダウンロード(月間工数表)"
   wt_data_download_with_act: "活動別データダウンロード"
   wt_opt_disp_ticket_with_closed: "[終了したチケットも表示]"
   wt_opt_disp_ticket_opened_only: "[オープン中のチケットのみ表示]"
diff --git a/plugins/redmine_work_time/config/locales/ko.yml b/plugins/redmine_work_time/config/locales/ko.yml
index 896436a..240f887 100644
--- a/plugins/redmine_work_time/config/locales/ko.yml
+++ b/plugins/redmine_work_time/config/locales/ko.yml
@@ -26,6 +26,7 @@ ko:
   wt_input_ticket_numbers: "또는 티켓 번호를 입력하세요."
   wt_delete_closed_tickets: "완료된 티켓 삭제"
   wt_data_download: "데이터 다운로드"
+  wt_data_download_table: "표 데이터 다운로드"
   wt_data_download_with_act: "데이터 다운로드(each activity)"
   wt_opt_disp_ticket_with_closed: "[display closed ticket]"
   wt_opt_disp_ticket_opened_only: "[display opened ticket only]"
diff --git a/plugins/redmine_work_time/config/locales/no.yml b/plugins/redmine_work_time/config/locales/no.yml
index 6ba4200..913806c 100644
--- a/plugins/redmine_work_time/config/locales/no.yml
+++ b/plugins/redmine_work_time/config/locales/no.yml
@@ -25,6 +25,7 @@
   wt_input_ticket_numbers: "eller legg inn saksnummer"
   wt_delete_closed_tickets: "Slett lukkede poster"
   wt_data_download: "Last ned data"
+  wt_data_download_table: "Tabell nedlasting av data"
   wt_data_download_with_act: "Last ned data(each activity)"
   wt_opt_disp_ticket_with_closed: "[vis lukkede saker]"
   wt_opt_disp_ticket_opened_only: "[vis bare ĂĄpne saker]"
diff --git a/plugins/redmine_work_time/config/locales/po.yml b/plugins/redmine_work_time/config/locales/po.yml
index 0fab351..8fdc5cb 100644
--- a/plugins/redmine_work_time/config/locales/po.yml
+++ b/plugins/redmine_work_time/config/locales/po.yml
@@ -25,6 +25,7 @@ po:
   wt_input_ticket_numbers: "or input ticket numbers"
   wt_delete_closed_tickets: "delete closed tickets"
   wt_data_download: "data download"
+  wt_data_download_table: "Tabela data download"
   wt_data_download_with_act: "data download(each activity)"
   wt_opt_disp_ticket_with_closed: "[display closed ticket]"
   wt_opt_disp_ticket_opened_only: "[display opened ticket only]"
diff --git a/plugins/redmine_work_time/config/locales/pt-BR.yml b/plugins/redmine_work_time/config/locales/pt-BR.yml
index b097f3d..15beeac 100644
--- a/plugins/redmine_work_time/config/locales/pt-BR.yml
+++ b/plugins/redmine_work_time/config/locales/pt-BR.yml
@@ -26,6 +26,7 @@ pt-BR:
   wt_input_ticket_numbers: "ou inclua o nĂşmero da tarefa"
   wt_delete_closed_tickets: "apagar tarefas fechadas"
   wt_data_download: "download de dados"
+  wt_data_download_table: "Tabela download de dados"
   wt_data_download_with_act: "download de dados(each activity)"
   wt_opt_disp_ticket_with_closed: "[exibe tarefa fechada]"
   wt_opt_disp_ticket_opened_only: "[exibe somente tafera aberta]"
diff --git a/plugins/redmine_work_time/config/locales/ru.yml b/plugins/redmine_work_time/config/locales/ru.yml
index 35dbb1f..39dc975 100644
--- a/plugins/redmine_work_time/config/locales/ru.yml
+++ b/plugins/redmine_work_time/config/locales/ru.yml
@@ -25,6 +25,7 @@ ru:
   wt_input_ticket_numbers: "или введите номера задач (через запятую)"
   wt_delete_closed_tickets: "удалить закрытые задачи"
   wt_data_download: "экспорт данных"
+  wt_data_download_table: "Экспорт данных таблицы"
   wt_data_download_with_act: "экспорт данных(each activity)"
   wt_opt_disp_ticket_with_closed: "[отобразить закрытые задачи]"
   wt_opt_disp_ticket_opened_only: "[отобразить только открытые задачи]"
diff --git a/plugins/redmine_work_time/config/locales/tr.yml b/plugins/redmine_work_time/config/locales/tr.yml
index bff0ae8..28b7408 100644
--- a/plugins/redmine_work_time/config/locales/tr.yml
+++ b/plugins/redmine_work_time/config/locales/tr.yml
@@ -25,6 +25,7 @@ tr:
   wt_input_ticket_numbers: "veya iş numarası gir"
   wt_delete_closed_tickets: "kapalı işleri sil"
   wt_data_download: "veri indir"
+  wt_data_download_table: "Tablo veri Ä°ndir"
   wt_data_download_with_act: "veri indir(each activity)"
   wt_opt_disp_ticket_with_closed: "[kapalı işleri göster]"
   wt_opt_disp_ticket_opened_only: "[sadece açık işleri göster]"
diff --git a/plugins/redmine_work_time/config/locales/zh.yml b/plugins/redmine_work_time/config/locales/zh.yml
old mode 100755
new mode 100644
index 6fc4f9c..7c01c58
--- a/plugins/redmine_work_time/config/locales/zh.yml
+++ b/plugins/redmine_work_time/config/locales/zh.yml
@@ -25,6 +25,7 @@ zh:
   wt_input_ticket_numbers: "请输入编号"
   wt_delete_closed_tickets: "删除已关闭的工作"
   wt_data_download: "数据下载"
+  wt_data_download_table: "表数据下载"
   wt_data_download_with_act: "数据下载(按活动)"
   wt_opt_disp_ticket_with_closed: "[显示已关闭的工作]"
   wt_opt_disp_ticket_opened_only: "[只显示打开的工作]"
diff --git a/plugins/redmine_work_time/db/migrate/001_create_user_issue_months.rb b/plugins/redmine_work_time/db/migrate/001_create_user_issue_months.rb
index 19780cb..4bc929c 100644
--- a/plugins/redmine_work_time/db/migrate/001_create_user_issue_months.rb
+++ b/plugins/redmine_work_time/db/migrate/001_create_user_issue_months.rb
@@ -1,4 +1,4 @@
-class CreateUserIssueMonths < ActiveRecord::Migration
+class CreateUserIssueMonths < ActiveRecord::Migration[4.2]
   def self.up
     create_table :user_issue_months do |t|
       t.column :uid, :integer
diff --git a/plugins/redmine_work_time/db/migrate/002_create_wt_member_orders.rb b/plugins/redmine_work_time/db/migrate/002_create_wt_member_orders.rb
index 9a4884c..4f45857 100644
--- a/plugins/redmine_work_time/db/migrate/002_create_wt_member_orders.rb
+++ b/plugins/redmine_work_time/db/migrate/002_create_wt_member_orders.rb
@@ -1,4 +1,4 @@
-class CreateWtMemberOrders < ActiveRecord::Migration
+class CreateWtMemberOrders < ActiveRecord::Migration[4.2]
   def self.up
     create_table :wt_member_orders do |t|
       t.column :user_id, :integer
diff --git a/plugins/redmine_work_time/db/migrate/003_create_wt_ticket_relays.rb b/plugins/redmine_work_time/db/migrate/003_create_wt_ticket_relays.rb
index 84099ad..b77ef11 100644
--- a/plugins/redmine_work_time/db/migrate/003_create_wt_ticket_relays.rb
+++ b/plugins/redmine_work_time/db/migrate/003_create_wt_ticket_relays.rb
@@ -1,4 +1,4 @@
-class CreateWtTicketRelays < ActiveRecord::Migration
+class CreateWtTicketRelays < ActiveRecord::Migration[4.2]
   def self.up
     create_table :wt_ticket_relays do |t|
       t.column :issue_id, :integer
diff --git a/plugins/redmine_work_time/db/migrate/004_add_prj_to_mem_odr.rb b/plugins/redmine_work_time/db/migrate/004_add_prj_to_mem_odr.rb
index fb14bee..431fe31 100644
--- a/plugins/redmine_work_time/db/migrate/004_add_prj_to_mem_odr.rb
+++ b/plugins/redmine_work_time/db/migrate/004_add_prj_to_mem_odr.rb
@@ -1,4 +1,4 @@
-class AddPrjToMemOdr < ActiveRecord::Migration
+class AddPrjToMemOdr < ActiveRecord::Migration[4.2]
   def self.up
     add_column :wt_member_orders, :prj_id, :integer, :default => nil
   end
diff --git a/plugins/redmine_work_time/db/migrate/005_create_wt_daily_memos.rb b/plugins/redmine_work_time/db/migrate/005_create_wt_daily_memos.rb
index db53882..e529289 100644
--- a/plugins/redmine_work_time/db/migrate/005_create_wt_daily_memos.rb
+++ b/plugins/redmine_work_time/db/migrate/005_create_wt_daily_memos.rb
@@ -1,4 +1,4 @@
-class CreateWtDailyMemos < ActiveRecord::Migration
+class CreateWtDailyMemos < ActiveRecord::Migration[4.2]
   def self.up
     create_table :wt_daily_memos do |t|
       t.column :day, :date
diff --git a/plugins/redmine_work_time/db/migrate/006_create_wt_project_orders.rb b/plugins/redmine_work_time/db/migrate/006_create_wt_project_orders.rb
index fe65846..a78996d 100644
--- a/plugins/redmine_work_time/db/migrate/006_create_wt_project_orders.rb
+++ b/plugins/redmine_work_time/db/migrate/006_create_wt_project_orders.rb
@@ -1,4 +1,4 @@
-class CreateWtProjectOrders < ActiveRecord::Migration
+class CreateWtProjectOrders < ActiveRecord::Migration[4.2]
   def self.up
     create_table :wt_project_orders do |t|
       t.column :prj, :integer
diff --git a/plugins/redmine_work_time/db/migrate/007_create_wt_holidays.rb b/plugins/redmine_work_time/db/migrate/007_create_wt_holidays.rb
index a9537f9..0043f09 100644
--- a/plugins/redmine_work_time/db/migrate/007_create_wt_holidays.rb
+++ b/plugins/redmine_work_time/db/migrate/007_create_wt_holidays.rb
@@ -1,4 +1,4 @@
-class CreateWtHolidays < ActiveRecord::Migration
+class CreateWtHolidays < ActiveRecord::Migration[4.2]
   def self.up
     create_table :wt_holidays do |t|
       t.column :holiday, :date
diff --git a/plugins/redmine_work_time/db/migrate/008_remove_month_from_user_issue_month.rb b/plugins/redmine_work_time/db/migrate/008_remove_month_from_user_issue_month.rb
index 33559ce..26e93f7 100644
--- a/plugins/redmine_work_time/db/migrate/008_remove_month_from_user_issue_month.rb
+++ b/plugins/redmine_work_time/db/migrate/008_remove_month_from_user_issue_month.rb
@@ -1,4 +1,4 @@
-class RemoveMonthFromUserIssueMonth < ActiveRecord::Migration
+class RemoveMonthFromUserIssueMonth < ActiveRecord::Migration[4.2]
   def self.up
     remove_column :user_issue_months, :month
   end
diff --git a/plugins/redmine_work_time/db/migrate/009_remove_prj_from_wt_project_orders.rb b/plugins/redmine_work_time/db/migrate/009_remove_prj_from_wt_project_orders.rb
index 318bffc..74a1ec8 100644
--- a/plugins/redmine_work_time/db/migrate/009_remove_prj_from_wt_project_orders.rb
+++ b/plugins/redmine_work_time/db/migrate/009_remove_prj_from_wt_project_orders.rb
@@ -1,4 +1,4 @@
-class RemovePrjFromWtProjectOrders < ActiveRecord::Migration
+class RemovePrjFromWtProjectOrders < ActiveRecord::Migration[4.2]
   def self.up
     remove_column :wt_project_orders, :prj
   end
diff --git a/plugins/redmine_work_time/init.rb b/plugins/redmine_work_time/init.rb
index 5d1caf6..ccb5271 100644
--- a/plugins/redmine_work_time/init.rb
+++ b/plugins/redmine_work_time/init.rb
@@ -4,7 +4,7 @@ Redmine::Plugin.register :redmine_work_time do
   name 'Redmine Work Time plugin'
   author 'Tomohisa Kusukawa'
   description 'A plugin to view and update TimeEntry by each user'
-  version '0.3.4'
+  version '0.4.1'
   url 'http://www.redmine.org/plugins/redmine_work_time'
   author_url 'http://about.me/tkusukawa'
   
diff --git a/plugins/redmine_work_time/lib/work_time_projects_helper_patch.rb b/plugins/redmine_work_time/lib/work_time_projects_helper_patch.rb
index 6157b27..5d2e14c 100644
--- a/plugins/redmine_work_time/lib/work_time_projects_helper_patch.rb
+++ b/plugins/redmine_work_time/lib/work_time_projects_helper_patch.rb
@@ -1,22 +1,17 @@
 require_dependency 'projects_helper'
 
 module WorkTimeProjectsHelperPatch
-  def self.included base # :nodoc:
-    base.send :include, ProjectsHelperMethodsWorkTime
-    base.class_eval do
-      alias_method_chain :project_settings_tabs, :work_time
+  module ProjectsHelperPatch
+    def project_settings_tabs
+      tabs = super
+      action = {:name => 'work_time',
+                :controller => 'work_time',
+                :action => :show,
+                :partial => 'settings/work_time_project_settings', :label => :work_time}
+      tabs << action if User.current.allowed_to?(:edit_work_time_total, @project)
+      tabs
     end
   end
 end
 
-module ProjectsHelperMethodsWorkTime
-  def project_settings_tabs_with_work_time
-    tabs = project_settings_tabs_without_work_time
-    action = {:name => 'work_time',
-      :controller => 'work_time',
-      :action => :show, 
-      :partial => 'settings/work_time_project_settings', :label => :work_time}
-    tabs << action if User.current.allowed_to?(:edit_work_time_total, @project)
-    tabs
-  end
-end
+ProjectsHelper.prepend WorkTimeProjectsHelperPatch::ProjectsHelperPatch
diff --git a/plugins/redmineup_tags/Gemfile.lock b/plugins/redmineup_tags/Gemfile.lock
deleted file mode 100644
index a328ca3..0000000
--- a/plugins/redmineup_tags/Gemfile.lock
+++ /dev/null
@@ -1,108 +0,0 @@
-GEM
-  specs:
-    actionmailer (4.2.8)
-      actionpack (= 4.2.8)
-      actionview (= 4.2.8)
-      activejob (= 4.2.8)
-      mail (~> 2.5, >= 2.5.4)
-      rails-dom-testing (~> 1.0, >= 1.0.5)
-    actionpack (4.2.8)
-      actionview (= 4.2.8)
-      activesupport (= 4.2.8)
-      rack (~> 1.6)
-      rack-test (~> 0.6.2)
-      rails-dom-testing (~> 1.0, >= 1.0.5)
-      rails-html-sanitizer (~> 1.0, >= 1.0.2)
-    actionview (4.2.8)
-      activesupport (= 4.2.8)
-      builder (~> 3.1)
-      erubis (~> 2.7.0)
-      rails-dom-testing (~> 1.0, >= 1.0.5)
-      rails-html-sanitizer (~> 1.0, >= 1.0.3)
-    activejob (4.2.8)
-      activesupport (= 4.2.8)
-      globalid (>= 0.3.0)
-    activemodel (4.2.8)
-      activesupport (= 4.2.8)
-      builder (~> 3.1)
-    activerecord (4.2.8)
-      activemodel (= 4.2.8)
-      activesupport (= 4.2.8)
-      arel (~> 6.0)
-    activesupport (4.2.8)
-      i18n (~> 0.7)
-      minitest (~> 5.1)
-      thread_safe (~> 0.3, >= 0.3.4)
-      tzinfo (~> 1.1)
-    arel (6.0.4)
-    builder (3.2.3)
-    concurrent-ruby (1.1.3)
-    crass (1.0.4)
-    erubis (2.7.0)
-    globalid (0.4.1)
-      activesupport (>= 4.2.0)
-    i18n (0.7.0)
-    liquid (2.6.3)
-    loofah (2.2.3)
-      crass (~> 1.0.2)
-      nokogiri (>= 1.5.9)
-    mail (2.6.6)
-      mime-types (>= 1.16, < 4)
-    mime-types (3.2.2)
-      mime-types-data (~> 3.2015)
-    mime-types-data (3.2018.0812)
-    mini_portile2 (2.3.0)
-    minitest (5.11.3)
-    nokogiri (1.8.5)
-      mini_portile2 (~> 2.3.0)
-    rack (1.6.11)
-    rack-test (0.6.3)
-      rack (>= 1.0)
-    rails (4.2.8)
-      actionmailer (= 4.2.8)
-      actionpack (= 4.2.8)
-      actionview (= 4.2.8)
-      activejob (= 4.2.8)
-      activemodel (= 4.2.8)
-      activerecord (= 4.2.8)
-      activesupport (= 4.2.8)
-      bundler (>= 1.3.0, < 2.0)
-      railties (= 4.2.8)
-      sprockets-rails
-    rails-deprecated_sanitizer (1.0.3)
-      activesupport (>= 4.2.0.alpha)
-    rails-dom-testing (1.0.9)
-      activesupport (>= 4.2.0, < 5.0)
-      nokogiri (~> 1.6)
-      rails-deprecated_sanitizer (>= 1.0.1)
-    rails-html-sanitizer (1.0.4)
-      loofah (~> 2.2, >= 2.2.2)
-    railties (4.2.8)
-      actionpack (= 4.2.8)
-      activesupport (= 4.2.8)
-      rake (>= 0.8.7)
-      thor (>= 0.18.1, < 2.0)
-    rake (12.3.1)
-    redmine_crm (0.0.42)
-      liquid (< 2.6.4)
-      rails
-    sprockets (3.7.2)
-      concurrent-ruby (~> 1.0)
-      rack (> 1, < 3)
-    sprockets-rails (3.2.1)
-      actionpack (>= 4.0)
-      activesupport (>= 4.0)
-      sprockets (>= 3.0.0)
-    thor (0.20.3)
-    thread_safe (0.3.6)
-    tzinfo (1.2.5)
-      thread_safe (~> 0.1)
-
-PLATFORMS
-  ruby
-
-DEPENDENCIES
-  redmine_crm
-
-BUNDLED WITH
-   1.17.1
diff --git a/plugins/redmineup_tags/app/controllers/issue_tags_controller.rb b/plugins/redmineup_tags/app/controllers/issue_tags_controller.rb
index 7ed8cc7..34bf00c 100644
--- a/plugins/redmineup_tags/app/controllers/issue_tags_controller.rb
+++ b/plugins/redmineup_tags/app/controllers/issue_tags_controller.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -20,14 +20,13 @@
 class IssueTagsController < ApplicationController
   unloadable
 
-  before_action :find_issues, :only => [:edit, :update]
+  before_action :find_issues, only: [:edit, :update]
 
   def edit
     return unless User.current.allowed_to?(:edit_tags, @projects.first)
     @issue_ids = params[:ids]
     @is_bulk_editing = @issue_ids.size > 1
-    @issue_tags = @is_bulk_editing ? [] : @issues.first.tag_list
-    @most_used_tags = Issue.all_tags(:sort_by => 'count', :order => 'DESC').limit(10)
+    @most_used_tags = Issue.all_tags(sort_by: 'count', order: 'DESC').limit(10)
   end
 
   def update
@@ -39,13 +38,11 @@ class IssueTagsController < ApplicationController
         return
       end
 
-      Issue.transaction do
-        @issues.each do |issue|
-          issue.tag_list = tags
-          issue.save!
-        end
+      if update_tags(@issues, tags)
+        flash[:notice] = t(:notice_tags_added)
+      else
+        flash[:error] = t(:notice_failed_to_add_tags)
       end
-      flash[:notice] = t(:notice_tags_added)
     else
       flash[:error] = t(:notice_failed_to_add_tags)
     end
@@ -53,6 +50,40 @@ class IssueTagsController < ApplicationController
     puts e
     flash[:error] = t(:notice_failed_to_add_tags)
   ensure
-    redirect_to_referer_or { render :text => 'Tags updated.', :layout => true }
+    redirect_to_referer_or { render text: 'Tags updated.', layout: true }
+  end
+
+  private
+
+  def update_tags(issues, tags)
+    if tags.present? && issues.size > 1
+      add_issues_tags(issues, tags)
+    else
+      update_issues_tags(issues, tags)
+    end
+  end
+
+  def add_issues_tags(issues, tags)
+    saved = true
+    Issue.transaction do
+      issues.each do |issue|
+        issue.tag_list = (issue.tag_list + tags).uniq
+        saved &&= issue.save
+        raise ActiveRecord::Rollback unless saved
+      end
+    end
+    saved
+  end
+
+  def update_issues_tags(issues, tags)
+    saved = true
+    Issue.transaction do
+      issues.each do |issue|
+        issue.tag_list = tags
+        saved &&= issue.save
+        raise ActiveRecord::Rollback unless saved
+      end
+    end
+    saved
   end
 end
diff --git a/plugins/redmineup_tags/app/controllers/tags_controller.rb b/plugins/redmineup_tags/app/controllers/tags_controller.rb
index 485fd05..9472886 100644
--- a/plugins/redmineup_tags/app/controllers/tags_controller.rb
+++ b/plugins/redmineup_tags/app/controllers/tags_controller.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -20,8 +20,8 @@
 class TagsController < ApplicationController
   unloadable
   before_action :require_admin
-  before_action :find_tag, :only => [:edit, :update]
-  before_action :bulk_find_tags, :only => [:context_menu, :merge, :destroy]
+  before_action :find_tag, only: [:edit, :update]
+  before_action :bulk_find_tags, only: [:context_menu, :merge, :destroy]
 
   helper :issues_tags
 
@@ -32,11 +32,11 @@ class TagsController < ApplicationController
     @tags.each do |tag|
       begin
         tag.reload.destroy
-      rescue ::ActiveRecord::RecordNotFound 
+      rescue ::ActiveRecord::RecordNotFound
       end
     end
 
-    redirect_back_or_default(:controller => 'settings', :action => 'plugin', :id => 'redmineup_tags', :tab => 'manage_tags')
+    redirect_back_or_default(controller: 'settings', action: 'plugin', id: 'redmineup_tags', tab: 'manage_tags')
   end
 
   def update
@@ -44,29 +44,29 @@ class TagsController < ApplicationController
     if @tag.save
       flash[:notice] = l(:notice_successful_update)
       respond_to do |format|
-        format.html { redirect_to :controller => 'settings', :action => 'plugin', :id => 'redmineup_tags', :tab => 'manage_tags' }
+        format.html { redirect_to controller: 'settings', action: 'plugin', id: 'redmineup_tags', tab: 'manage_tags' }
         format.xml  {}
       end
     else
       respond_to do |format|
-        format.html { render :action => 'edit' }
+        format.html { render action: 'edit' }
       end
     end
   end
 
   def context_menu
-    @tag = @tags.first if (@tags.size == 1)
+    @tag = @tags.first if @tags.size == 1
     @back = back_url
-    render :layout => false
+    render layout: false
   end
 
   def merge
     if request.post? && params[:tag] && params[:tag][:name]
       RedmineCrm::Tagging.transaction do
         tag = RedmineCrm::Tag.find_by_name(params[:tag][:name]) || RedmineCrm::Tag.create(params[:tag])
-        RedmineCrm::Tagging.where(:tag_id => @tags.map(&:id)).update_all(:tag_id => tag.id)
-        @tags.select{|t| t.id != tag.id}.each{|t| t.destroy }
-        redirect_to :controller => 'settings', :action => 'plugin', :id => 'redmineup_tags', :tab => 'manage_tags'
+        RedmineCrm::Tagging.where(tag_id: @tags.map(&:id)).update_all(tag_id: tag.id)
+        @tags.select { |t| t.id != tag.id }.each{ |t| t.destroy }
+        redirect_to controller: 'settings', action: 'plugin', id: 'redmineup_tags', tab: 'manage_tags'
       end
     end
   end
@@ -76,7 +76,7 @@ class TagsController < ApplicationController
   def bulk_find_tags
     @tags = RedmineCrm::Tag.joins("JOIN #{RedmineCrm::Tagging.table_name} ON #{RedmineCrm::Tagging.table_name}.tag_id = #{RedmineCrm::Tag.table_name}.id ").
             select("#{RedmineCrm::Tag.table_name}.*, COUNT(DISTINCT #{RedmineCrm::Tagging.table_name}.taggable_id) AS count").
-            where(:id => params[:id] ? [params[:id]] : params[:ids]).
+            where(id: params[:id] ? [params[:id]] : params[:ids]).
             group("#{RedmineCrm::Tag.table_name}.id, #{RedmineCrm::Tag.table_name}.name")
     raise ActiveRecord::RecordNotFound if @tags.empty?
   end
diff --git a/plugins/redmineup_tags/app/helpers/issues_tags_helper.rb b/plugins/redmineup_tags/app/helpers/issues_tags_helper.rb
index 83f526e..ddf543d 100644
--- a/plugins/redmineup_tags/app/helpers/issues_tags_helper.rb
+++ b/plugins/redmineup_tags/app/helpers/issues_tags_helper.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -23,21 +23,19 @@ module IssuesTagsHelper
   def sidebar_tags
     unless @sidebar_tags
       @sidebar_tags = []
-      if :none != RedmineTags.settings['issues_sidebar'].to_sym
-        @sidebar_tags = Issue.available_tags({
-          :project => @project,
-          :open_only => (RedmineTags.settings['issues_open_only'].to_i == 1)
-        })
+      projects = [@project] + (@project && Setting.display_subprojects_issues? ? @project.descendants : [])
+      if RedmineupTags.settings['issues_sidebar'].to_sym != :none
+        @sidebar_tags = Issue.available_tags(project: @project,
+                                             projects: projects,
+                                             open_only: (RedmineupTags.settings['issues_open_only'].to_i == 1))
       end
     end
     @sidebar_tags.to_a
   end
 
   def render_sidebar_tags
-    render_tags_list(sidebar_tags, {
-      :show_count => (RedmineTags.settings['issues_show_count'].to_i == 1),
-      :open_only => (RedmineTags.settings['issues_open_only'].to_i == 1),
-      :style => RedmineTags.settings['issues_sidebar'].to_sym
-    })
+    render_tags_list(sidebar_tags, show_count: (RedmineupTags.settings['issues_show_count'].to_i == 1),
+                                   open_only: (RedmineupTags.settings['issues_open_only'].to_i == 1),
+                                   style: RedmineupTags.settings['issues_sidebar'].to_sym)
   end
 end
diff --git a/plugins/redmineup_tags/app/helpers/tags_helper.rb b/plugins/redmineup_tags/app/helpers/tags_helper.rb
index fe09a64..c5d1b84 100644
--- a/plugins/redmineup_tags/app/helpers/tags_helper.rb
+++ b/plugins/redmineup_tags/app/helpers/tags_helper.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -27,16 +27,15 @@ module TagsHelper
   def render_issue_tag_link(tag, options = {})
     filters = [[:issue_tags, '=', tag.name]]
     filters << [:status_id, 'o'] if options[:open_only]
-    if options[:use_search]
-      content =  link_to(tag, {:controller => "search", :action => "index", :id => @project, :q => tag.name, :wiki_pages => true, :issues => true})
-    else
-      content = link_to_issue_filter tag.name, filters, :project_id => @project
-    end
-    if options[:show_count]
-      content << content_tag('span', "(#{tag.count})", :class => 'tag-count')
-    end
+    content =
+      if options[:use_search]
+        link_to(tag, controller: 'search', action: 'index', id: @project, q: tag.name, wiki_pages: true, issues: true)
+      else
+        link_to_issue_filter tag.name, filters, project_id: @project
+      end
+    content << content_tag('span', "(#{tag.count})", class: 'tag-count') if options[:show_count]
 
-    style = RedmineTags.settings['issues_use_colors'].to_i > 0 ? {:class => "tag-label-color", :style => "background-color: #{tag_color(tag)}"} : {:class => "tag-label"}
+    style = RedmineupTags.settings['issues_use_colors'].to_i > 0 ? { class: 'tag-label-color', style: "background-color: #{tag_color(tag)}" } : { class: 'tag-label' }
     content_tag('span', content, style)
   end
 
@@ -46,52 +45,51 @@ module TagsHelper
   end
 
   def render_tags_list(tags, options = {})
-    unless tags.nil? or tags.empty?
+    unless tags.nil? || tags.empty?
       content, style = '', options.delete(:style)
 
       tags = tags.all.to_a if tags.respond_to?(:all)
 
-      case sorting = "#{RedmineTags.settings['issues_sort_by']}:#{RedmineTags.settings['issues_sort_order']}"
-        when "name:asc";    tags.sort! { |a,b| a.name <=> b.name }
-        when "name:desc";   tags.sort! { |a,b| b.name <=> a.name }
-        when "count:asc";   tags.sort! { |a,b| a.count <=> b.count }
-        when "count:desc";  tags.sort! { |a,b| b.count <=> a.count }
-        # Unknown sorting option. Fallback to default one
-        else
-          logger.warn "[redmine_tags] Unknown sorting option: <#{sorting}>"
-          tags.sort! { |a,b| a.name <=> b.name }
+      case sorting = "#{RedmineupTags.settings['issues_sort_by']}:#{RedmineupTags.settings['issues_sort_order']}"
+      when 'name:asc' then   tags.sort! { |a, b| a.name <=> b.name }
+      when 'name:desc' then  tags.sort! { |a, b| b.name <=> a.name }
+      when 'count:asc' then  tags.sort! { |a, b| a.count <=> b.count }
+      when 'count:desc' then tags.sort! { |a, b| b.count <=> a.count }
+      # Unknown sorting option. Fallback to default one
+      else
+        logger.warn "[redmine_tags] Unknown sorting option: <#{sorting}>"
+        tags.sort! { |a, b| a.name <=> b.name }
       end
 
-      if :list == style
+      if style == :list
         list_el, item_el = 'ul', 'li'
-      elsif :simple_cloud == style
+      elsif style == :simple_cloud
         list_el, item_el = 'div', 'span'
-      elsif :cloud == style
+      elsif style == :cloud
         list_el, item_el = 'div', 'span'
       else
-        raise "Unknown list style"
+        raise 'Unknown list style'
       end
 
       content = content.html_safe
-      if :list == style && RedmineTags.settings['issues_sort_by'] == 'name'
+      if style == :list && RedmineupTags.settings['issues_sort_by'] == 'name'
         tags.group_by { |tag| tag.name.downcase.first }.each do |letter, grouped_tags|
-          content << content_tag(item_el, letter.upcase, :class => 'letter', :style => '')
+          content << content_tag(item_el, letter.upcase, class: 'letter', :style => '')
           add_tags(style, grouped_tags, content, item_el, options)
         end
       else
         add_tags(style, tags, content, item_el, options)
       end
 
-      content_tag(list_el, content, :class => 'tags-cloud', :style => (:simple_cloud == style ? "text-align: left;" : ""))
+      content_tag(list_el, content, class: 'tags-cloud', style: (style == :simple_cloud ? 'text-align: left;' : ''))
     end
   end
 
-def link_to_issue_filter(title, filters, options = {})
+  def link_to_issue_filter(title, filters, options = {})
     options.merge! link_to_issue_filter_options(filters)
     link_to title, options
   end
 
-
   # returns hash suitable for passing it to the <tt>to_link</tt>
   # === parameters
   # * <i>filters</i> = array of arrays. each child array is an array of strings:
@@ -103,12 +101,12 @@ def link_to_issue_filter(title, filters, options = {})
   # link_to 'bazbaz', link_to_issue_filter_options filters
   def link_to_issue_filter_options(filters)
     options = {
-      :controller => 'issues',
-      :action => 'index',
-      :set_filter => 1,
-      :fields => [],
-      :values => {},
-      :operators => {}
+      controller: 'issues',
+      action: 'index',
+      set_filter: 1,
+      fields: [],
+      values: {},
+      operators: {}
     }
 
     filters.each do |f|
@@ -125,8 +123,7 @@ def link_to_issue_filter(title, filters, options = {})
 
   def add_tags(style, tags, content, item_el, options)
     tag_cloud tags, (1..8).to_a do |tag, weight|
-      content << ' '.html_safe + content_tag(item_el, render_issue_tag_link(tag, options), :class => "tag-nube-#{weight}", :style => (:simple_cloud == style ? "font-size: 1em;" : "")) + " ".html_safe
+      content << ' '.html_safe + content_tag(item_el, render_issue_tag_link(tag, options), class: "tag-nube-#{weight}", style: (style == :simple_cloud ? 'font-size: 1em;' : '')) + ' '.html_safe
     end
   end
-
 end
diff --git a/plugins/redmineup_tags/app/views/context_menus/_issues_tags.html.erb b/plugins/redmineup_tags/app/views/context_menus/_issues_tags.html.erb
index 53ac386..9923f00 100644
--- a/plugins/redmineup_tags/app/views/context_menus/_issues_tags.html.erb
+++ b/plugins/redmineup_tags/app/views/context_menus/_issues_tags.html.erb
@@ -1,11 +1,6 @@
-<% if User.current.allowed_to?(:edit_tags, @project) %>
-  <li class="folder">
-    <a href="#" class="submenu"><%= l(:tags) %></a>
-    <ul>
-      <li><%= context_menu_link l(:button_add),
-                                edit_issue_tags_path(:ids => @issue_ids),
-                                :remote => true,
-                                :class => 'icon-add' %></li>
-    </ul>
-  </li>
+<% if User.current.allowed_to?(:edit_tags, @projects || @project) %>
+  <li><%= context_menu_link l(:label_add_tags),
+                            edit_issue_tags_path(:ids => @issue_ids),
+                            :remote => true,
+                            :class => 'icon icon-add-tags' %></li>
 <% end %>
diff --git a/plugins/redmineup_tags/app/views/issue_tags/_edit_modal.html.erb b/plugins/redmineup_tags/app/views/issue_tags/_edit_modal.html.erb
index 12c77a5..121a3cc 100644
--- a/plugins/redmineup_tags/app/views/issue_tags/_edit_modal.html.erb
+++ b/plugins/redmineup_tags/app/views/issue_tags/_edit_modal.html.erb
@@ -20,12 +20,12 @@
       <legend><%= l(:tags) %></legend>
       <div id="issue_tags">
         <%= select2_tag 'issue[tag_list]',
-                        options_for_select(@issue_tags.map{ |tag| [tag, tag] }, @issue_tags),
-                        :multiple => true,
-                        :style => 'width: 100%;',
-                        :url => auto_complete_redmine_tags_path,
-                        :placeholder => @is_bulk_editing ? t(:label_no_change_option) : '+ add tag',
-                        :tags => User.current.allowed_to?(:create_tags, @project) %>
+                        [],
+                        width: '100%',
+                        multiple: true,
+                        url: auto_complete_redmine_tags_url,
+                        placeholder: @is_bulk_editing ? t(:label_no_change_option) : '+ add tag',
+                        tags: User.current.allowed_to?(:create_tags, @project) %>
       </div>
 
       <p class="most_used_tags">
diff --git a/plugins/redmineup_tags/app/views/issues/._tags_sidebar.html.erb.swp b/plugins/redmineup_tags/app/views/issues/._tags_sidebar.html.erb.swp
deleted file mode 100644
index 11569ee85b5237b593006f876dc13fe98a470e1e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 12288
zcmeI&%}T>S5C`zBCoiJZ7g#F+JtSMLNRhM!Zz6gT1@TbQ?zUY_(<Pf!Jg8US!}stN
zyn58T58)&vP=(@I@?RJ>*_nL(Acv3{G_Ow1c-w0-8XJtge}3I;JapLGF=K%-{aDGV
zj#QRiI;2wOA_?N91x4a<;e^7(Gnoz(J&Qzf4Y%bTc!$D-=~zd$Tun_s3C32?v0|>(
zofX>JR9TTP_pO#{5X9ajnTDQ<2Y=lI@(`$`K$?u)_8M#L@8z%hc4Lcg)-Nkbfro|w
z1Rwwb2tWV=5P(2c1rk$ZPxSCB1;A=C*M7~#E+z;-00Izz00bZa0SG_<0uX=z1pc8w
zAQ`K#Gj>jq{r@k&|37H|mEwisnc|j0Qe0DHwWkyq5P$##AOHafKmY;|fB*y_0D&qB
z_>S*z6-n+oUADL>q@H&f=f2b7F%_t|>|4*9*g2I`a?<K%1>w$7*6_YdtG=K$=F`!9
u7zWmM8b!Oin^iROi=<JgtmQ=_KcA<jCb{pGx1mQ|su4Bu%ifQ&JpT>iCU!Fb

diff --git a/plugins/redmineup_tags/app/views/issues/_tags.html.erb b/plugins/redmineup_tags/app/views/issues/_tags.html.erb
index 7975133..15a2574 100644
--- a/plugins/redmineup_tags/app/views/issues/_tags.html.erb
+++ b/plugins/redmineup_tags/app/views/issues/_tags.html.erb
@@ -1,5 +1,5 @@
 <% if issue.tag_list.present? %>
   <%= issue_fields_rows do |rows|
-    rows.left l(:tags), safe_join(issue.tag_counts.collect{ |t| render_issue_tag_link(t, :show_count => false, :open_only => RedmineTags.settings['issues_open_only'].to_i > 0 ) }, RedmineTags.settings['issues_use_colors'].to_i > 0 ? ' ' : ', ')
+    rows.left l(:tags), safe_join(issue.tag_counts.collect{ |t| render_issue_tag_link(t, :show_count => false, :open_only => RedmineupTags.settings['issues_open_only'].to_i > 0 ) }, RedmineupTags.settings['issues_use_colors'].to_i > 0 ? ' ' : ', ')
   end %>
 <% end %>
diff --git a/plugins/redmineup_tags/app/views/issues/_tags_form.html.erb b/plugins/redmineup_tags/app/views/issues/_tags_form.html.erb
index 83730d6..865cbb2 100644
--- a/plugins/redmineup_tags/app/views/issues/_tags_form.html.erb
+++ b/plugins/redmineup_tags/app/views/issues/_tags_form.html.erb
@@ -1,4 +1,4 @@
-<% if User.current.allowed_to?(:edit_tags, @project) %>
+<% if User.current.allowed_to?(:edit_tags, (@issue && @issue.project) || @projects || @project) %>
   <div class="<%= 'splitcontentleft' if defined? form %>">
     <p id="issue_tags">
       <label><%= l(:tags) %></label>
@@ -10,9 +10,9 @@
                         :multiple => true,
                         :include_hidden => (params[:action] != 'bulk_edit'),
                         :style => 'width: 100%;',
-                        :url => auto_complete_redmine_tags_path,
+                        :url => auto_complete_redmine_tags_url,
                         :placeholder => params[:action] == 'bulk_edit' ? t(:label_no_change_option) : '+ add tag',
-                        :tags => User.current.allowed_to?(:create_tags, @project) %>
+                        :tags => User.current.allowed_to?(:create_tags, @projects || @project) %>
 
       <% else %>
         <%= label_tag :tags, nil, :for => :issue_tag_list %>
diff --git a/plugins/redmineup_tags/app/views/issues/_tags_sidebar.html.erb b/plugins/redmineup_tags/app/views/issues/_tags_sidebar.html.erb
index 7d20add..966a296 100644
--- a/plugins/redmineup_tags/app/views/issues/_tags_sidebar.html.erb
+++ b/plugins/redmineup_tags/app/views/issues/_tags_sidebar.html.erb
@@ -1,8 +1,6 @@
-<% if defined? sidebar_tags -%>
 <% unless sidebar_tags.empty? -%>
 <div class="sidebar-tags">
   <h3><%= l(:tags) %></h3>
   <%= render_sidebar_tags %>
 </div>
 <% end -%>
-<% end -%>
diff --git a/plugins/redmineup_tags/app/views/tags/context_menu.html.erb b/plugins/redmineup_tags/app/views/tags/context_menu.html.erb
index dec5445..31bf288 100644
--- a/plugins/redmineup_tags/app/views/tags/context_menu.html.erb
+++ b/plugins/redmineup_tags/app/views/tags/context_menu.html.erb
@@ -1,12 +1,12 @@
 <ul>
   <% if @tag -%>
     <li><%= link_to l(:button_edit), edit_tag_path(@tag),
-            :class => 'icon-edit' %></li>
+            :class => 'icon icon-edit' %></li>
   <% else %>
     <li><%= link_to l(:issue_tags_button_merge), merge_tags_path(:ids => @tags.collect(&:id)),
-            :class => 'icon-tags-merge' %></li>
+            :class => 'icon icon-tags-merge' %></li>
   <% end %>
 
     <li><%= link_to l(:button_delete), tags_path(:ids => @tags.collect(&:id), :back_url => @back),
-                            :method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => 'icon-del' %></li>
+                            :method => :delete, :data => {:confirm => l(:text_are_you_sure)}, :class => 'icon icon-del' %></li>
 </ul>
diff --git a/plugins/redmineup_tags/assets/stylesheets/redmine_tags.css b/plugins/redmineup_tags/assets/stylesheets/redmine_tags.css
index 9efaaf0..67be79b 100644
--- a/plugins/redmineup_tags/assets/stylesheets/redmine_tags.css
+++ b/plugins/redmineup_tags/assets/stylesheets/redmine_tags.css
@@ -38,6 +38,7 @@ div.tags-cloud .tag-nube-8 { font-size: 1.5em; }
 #tr_tags .select2-selection__rendered { padding-left: 8px; }
 
 .icon-tags { background-image: url(../images/tag-icon.png);}
+.icon-add-tags { background-image: url(../images/tag_blue_add.png);}
 
 .most_used_tag { cursor: pointer; color: #169; }
 .most_used_tag:hover { text-decoration: underline;}
diff --git a/plugins/redmineup_tags/config/locales/de.yml b/plugins/redmineup_tags/config/locales/de.yml
index e369536..2b57337 100644
--- a/plugins/redmineup_tags/config/locales/de.yml
+++ b/plugins/redmineup_tags/config/locales/de.yml
@@ -1,39 +1,21 @@
-# This file is a part of redmine_tags
-# redMine plugin, that adds tagging support.
-#
-# German translation for redmine_tags
-# by: Terence Miller AKA cforce, <cforce(at)gmx.de>,
-#     Jörg Jans
-#
-# Copyright (c) 2010 Terence Miller AKA cforce
-# Copyright (c) 2010 Jörg Jans
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
 de:
   tags: Tags
   field_tags: Tags
   field_tag_list: Tags
+  label_add_tags: Tags hinzufĂĽgen
+  notice_tags_added: Tags hinzugefĂĽgt
+  notice_failed_to_add_tags: Fehler beim hinzufĂĽgen der Tags
   setting_issue_tags: Ticket Tags
   issues_sidebar: Zeige die Tags auf der Sidebar
   issues_show_count: Zeige die Ticketanzahl an
   issues_open_only: Zeige nur noch offene Tickets
   issues_sort_by: Sortiere Tags nach
+  issues_use_colors: Farben verwenden
 
   issue_tags_sidebar_none: Keine
   issue_tags_sidebar_list: Liste
   issue_tags_sidebar_cloud: Cloud
+  issue_tags_sidebar_simple_cloud: Simple Cloud
 
   issues_sort_by_name: Name
   issues_sort_by_count: Anzahl tickets
@@ -41,3 +23,14 @@ de:
   issues_sort_order_desc: Absteigend
 
   auto_complete_new_tag: HinzufĂĽgen...
+
+  issue_tags_label_add_tag: + tag hinzufĂĽgen
+  issue_tags_manage_tags: Tags bearbeiten
+  issue_tags_tag: Tag
+  issue_tags_button_merge: zusammenfĂĽhren
+  issue_tags_label_merge: ausgwählte Tags zusammenführen
+
+  tags_suggestion_order: Reihenfolge der Vorschläge
+  tags_order_by_name: Name
+  tags_order_by_last_created: zuletzt angelegt
+  tags_order_by_most_used: am meisten benutzt
diff --git a/plugins/redmineup_tags/config/locales/ru.yml b/plugins/redmineup_tags/config/locales/ru.yml
index 2ae790e..d381ddc 100644
--- a/plugins/redmineup_tags/config/locales/ru.yml
+++ b/plugins/redmineup_tags/config/locales/ru.yml
@@ -1,36 +1,15 @@
-# This file is a part of redmine_tags
-# redMine plugin, that adds tagging support.
-#
-# Russian translation for redmine_tags
-# by Aleksey V Zapparov AKA ixti
-#
-# Copyright (c) 2010 Aleksey V Zapparov AKA ixti
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
 ru:
-  tags: Метки
-  field_tags: Метки
-  field_tag_list: Метки
+  tags: Теги
+  field_tags: Теги
+  field_tag_list: Теги
   label_add_tags: Добавить теги
   notice_tags_added: Теги добавлены
   notice_failed_to_add_tags: Не удалось добавить теги
-  setting_issue_tags: Метки задач
+  setting_issue_tags: Теги задач
   issues_sidebar: Боковую панель как
   issues_show_count: Показать кол-во задач
   issues_open_only: Только открытые задачи
-  issues_sort_by: Упорядочить метки по
+  issues_sort_by: Упорядочить теги по
   issues_use_colors: Использовать цвета
   
   issue_tags_sidebar_none: Не показывать
@@ -44,7 +23,7 @@ ru:
   issues_sort_order_desc: по убыванию
 
   auto_complete_new_tag: Добавить...
-  issue_tags_label_add_tag: + добавить метку
+  issue_tags_label_add_tag: + добавить тег
 
   tags_suggestion_order: Порядок поиска
   tags_order_by_name: По названию
diff --git a/plugins/redmineup_tags/config/locales/zh.yml b/plugins/redmineup_tags/config/locales/zh.yml
index 3e830c1..b83afbb 100644
--- a/plugins/redmineup_tags/config/locales/zh.yml
+++ b/plugins/redmineup_tags/config/locales/zh.yml
@@ -23,19 +23,35 @@ zh:
   tags: 标签
   field_tags: 标签
   field_tag_list: 标签
+  label_add_tags: 添加标签
+  notice_tags_added: 添加的标签
+  notice_failed_to_add_tags: 添加标签失败
   setting_issue_tags: 问题标签
   issues_sidebar: 在侧边栏显示标签
   issues_show_count: 显示问题计数
   issues_open_only: 仅显示打开的问题
   issues_sort_by: 标签按何种方式排序
+  issues_use_colors: 使用颜色
 
   issue_tags_sidebar_none: ć— 
   issue_tags_sidebar_list: 列表显示
   issue_tags_sidebar_cloud: 云显示
+  issue_tags_sidebar_simple_cloud: 简单云
 
   issues_sort_by_name: 名称
   issues_sort_by_count: 问题计数
   issues_sort_order_asc: 顺序
   issues_sort_order_desc: 倒序
 
-  auto_complete_new_tag: 添加新标签 ... ...
+  auto_complete_new_tag: 添加新标签 ...
+
+  issue_tags_label_add_tag: + 添加标签
+  issue_tags_manage_tags: 管理标签
+  issue_tags_tag: 标签
+  issue_tags_button_merge: 合并
+  issue_tags_label_merge: 合并选择的标签
+
+  tags_suggestion_order: 建议顺序
+  tags_order_by_name: 名称
+  tags_order_by_last_created: 最后创建的标签
+  tags_order_by_most_used: 最常用的标签
diff --git a/plugins/redmineup_tags/config/routes.rb b/plugins/redmineup_tags/config/routes.rb
index f8d54e8..9a4d198 100644
--- a/plugins/redmineup_tags/config/routes.rb
+++ b/plugins/redmineup_tags/config/routes.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -18,19 +18,19 @@
 # along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
 
 RedmineApp::Application.routes.draw do
-  match '/issue_tags/auto_complete/:project_id', :to => 'auto_completes#issue_tags', :via => :get, :as => 'auto_complete_issue_tags'
-  match '/wiki_tags/auto_complete/:project_id', :to => 'auto_completes#wiki_tags', :via => :get, :as => 'auto_complete_wiki_tags'
-  match 'auto_completes/redmine_tags' => 'auto_completes#redmine_tags', :via => :get, :as => 'auto_complete_redmine_tags'
-  match '/tags/context_menu', :to => 'tags#context_menu', :as => 'tags_context_menu', :via => [:get, :post]
-  match '/tags', :controller => 'tags', :action => 'destroy', :via => :delete
+  match '/issue_tags/auto_complete/:project_id', to: 'auto_completes#issue_tags', via: :get, as: 'auto_complete_issue_tags'
+  match '/wiki_tags/auto_complete/:project_id', to: 'auto_completes#wiki_tags', via: :get, as: 'auto_complete_wiki_tags'
+  match 'auto_completes/redmine_tags' => 'auto_completes#redmine_tags', via: :get, as: 'auto_complete_redmine_tags'
+  match '/tags/context_menu', to: 'tags#context_menu', as: 'tags_context_menu', via: [:get, :post]
+  match '/tags', controller: 'tags', action: 'destroy', via: :delete
 end
 
-resources :tags, :only => [:edit, :update] do
+resources :tags, only: [:edit, :update] do
   collection do
     post :merge
     get :context_menu, :merge
   end
 end
 
-get :edit_issue_tags, :to => 'issue_tags#edit'
-post :update_issue_tags, :to => 'issue_tags#update'
+get :edit_issue_tags, to: 'issue_tags#edit'
+post :update_issue_tags, to: 'issue_tags#update'
diff --git a/plugins/redmineup_tags/db/migrate/001_add_tags_and_taggings.rb b/plugins/redmineup_tags/db/migrate/001_add_tags_and_taggings.rb
index 8da3ff6..1b6540a 100644
--- a/plugins/redmineup_tags/db/migrate/001_add_tags_and_taggings.rb
+++ b/plugins/redmineup_tags/db/migrate/001_add_tags_and_taggings.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
diff --git a/plugins/redmineup_tags/db/migrate/002_create_tags.rb b/plugins/redmineup_tags/db/migrate/002_create_tags.rb
index db11635..900be33 100644
--- a/plugins/redmineup_tags/db/migrate/002_create_tags.rb
+++ b/plugins/redmineup_tags/db/migrate/002_create_tags.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
diff --git a/plugins/redmineup_tags/doc/CHANGELOG b/plugins/redmineup_tags/doc/CHANGELOG
old mode 100755
new mode 100644
index b1a81c0..ffea186
--- a/plugins/redmineup_tags/doc/CHANGELOG
+++ b/plugins/redmineup_tags/doc/CHANGELOG
@@ -1,9 +1,53 @@
 == Redmine Tags plugin changelog
 
 Redmine Tags plugin - Tags board plugin for redmine
-Copyright (C) 2011-2018 RedmineUP
+Copyright (C) 2011-2021 RedmineUP
 http://www.redmineup.com/
 
+== 2021-06-14 v2.0.11
+
+* Fixed global issue list bug
+
+== 2021-06-04 v2.0.10
+
+* Fixed patches typo
+
+== 2021-05-31 v2.0.9
+
+* Added Redmine 4.2 compatibility
+* Changed path to url for autocomplete
+* Updated br locale
+* Updated zh locale
+* Fixed production load error
+* Fixed tags column bug
+* Fixed copy tags bug
+* Fixed global spent time bug
+* Fixed tags count bug
+* Fixed bulk edit issue tags bug
+* Fixed tags link
+
+== 2019-06-14 v2.0.8
+
+* Tags report for issues summary
+
+== 2019-06-14 v2.0.7
+
+* Agile plugin compatibility fixes
+
+== 2019-05-16 v2.0.6
+
+* Added tags to Time entries report
+* Added tags to Agile Versions board 
+* Updated german translation (Werner Maier)
+* Fixed Agile plugin compatibility bug
+* Fixed adding tags context menu permissions
+* Fixed bulk edit issue tags form permissions
+
+== 2019-04-08 v2.0.5
+
+ * Changed tags filter by condition AND
+ * Fixed tags field on issues bulk edit form
+ 
 == 2018-09-25 v2.0.4
 
 * Fixed bug with Agile board view
diff --git a/plugins/redmineup_tags/init.rb b/plugins/redmineup_tags/init.rb
index be594ed..6f7810d 100644
--- a/plugins/redmineup_tags/init.rb
+++ b/plugins/redmineup_tags/init.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -17,34 +17,32 @@
 # You should have received a copy of the GNU General Public License
 # along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
 
-requires_redmine_crm :version_or_higher => '0.0.38' rescue raise "\n\033[31mRedmine requires newer redmine_crm gem version.\nPlease update with 'bundle update redmine_crm'.\033[0m"
+requires_redmine_crm version_or_higher: '0.0.55' rescue raise "\n\033[31mRedmine requires newer redmine_crm gem version.\nPlease update with 'bundle update redmine_crm'.\033[0m"
 
 require 'redmine'
-require 'redmine_tags'
 
-TAGS_VERSION_NUMBER = '2.0.4'
-TAGS_VERSION_TYPE = "Light version"
+TAGS_VERSION_NUMBER = '2.0.11'
+TAGS_VERSION_TYPE = 'Light version'
 
 Redmine::Plugin.register :redmineup_tags do
   name "Redmine Tags plugin (#{TAGS_VERSION_TYPE})"
   author 'RedmineUP'
   description 'Redmine issues tagging support'
   version TAGS_VERSION_NUMBER
-  url 'https://www.redmineup.com/pages/tags'
+  url 'https://www.redmineup.com/pages/plugins/tags/'
   author_url 'mailto:support@redmineup.com'
 
-  requires_redmine :version_or_higher => '2.4'
+  requires_redmine version_or_higher: '3.0'
 
-  settings :default => {
-    :issues_sidebar => 'none',
-    :issues_show_count => 0,
-    :issues_open_only => 0,
-    :issues_sort_by => 'name',
-    :issues_sort_order => 'asc',
-    :tags_suggestion_order => 'name',
-  }, :partial => 'tags/settings'
+  settings default: { issues_sidebar: 'none',
+                      issues_show_count: 0,
+                      issues_open_only: 0,
+                      issues_sort_by: 'name',
+                      issues_sort_order: 'asc',
+                      tags_suggestion_order: 'name'
+                    }, partial: 'tags/settings'
 
-  menu :admin_menu, :tags, {:controller => 'settings', :action => 'plugin', :id => "redmineup_tags"}, :caption => :tags, :html => {:class => 'icon'}
+  menu :admin_menu, :tags, { controller: 'settings', action: 'plugin', id: 'redmineup_tags' }, caption: :tags, html: { class: 'icon' }
 
   project_module :issue_tracking do
     permission :create_tags, {}
@@ -52,24 +50,4 @@ Redmine::Plugin.register :redmineup_tags do
   end
 end
 
-(Rails.version < '5.1' ? ActionDispatch::Callbacks : ActiveSupport::Reloader).to_prepare do
-  unless Issue.included_modules.include?(RedmineTags::Patches::IssuePatch)
-    Issue.send(:include, RedmineTags::Patches::IssuePatch)
-  end
-
-  [IssuesController, CalendarsController, GanttsController, SettingsController].each do |controller|
-    RedmineTags::Patches::AddHelpersForIssueTagsPatch.apply(controller)
-  end
-  RedmineTags::Patches::AddHelpersForIssueTagsPatch.apply(ImportsController) if Redmine::VERSION.to_s > '3.2'
-
-  base = ActiveSupport::Dependencies::search_for_file('issue_queries_helper') ? IssueQueriesHelper : QueriesHelper
-  unless base.included_modules.include?(RedmineTags::Patches::QueriesHelperPatch)
-    base.send(:include, RedmineTags::Patches::QueriesHelperPatch)
-  end
-end
-
-require 'redmine_tags/hooks/model_issue_hook'
-require 'redmine_tags/hooks/views_issues_hook'
-require 'redmine_tags/hooks/views_layouts_hook'
-require 'redmine_tags/hooks/views_context_menus_hook'
-require 'redmine_tags/patches/agile_query_patch' if Redmine::Plugin.installed?(:redmine_agile) && Redmine::Plugin.find(:redmine_agile).version >= '1.4.3'
+require 'redmineup_tags'
diff --git a/plugins/redmineup_tags/lib/query_tags_column.rb b/plugins/redmineup_tags/lib/query_tags_column.rb
index 95ca616..23b4544 100644
--- a/plugins/redmineup_tags/lib/query_tags_column.rb
+++ b/plugins/redmineup_tags/lib/query_tags_column.rb
@@ -1,7 +1,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
diff --git a/plugins/redmineup_tags/lib/redmine_tags.rb b/plugins/redmineup_tags/lib/redmine_tags.rb
deleted file mode 100644
index 1eacd1f..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-require 'redmine_tags/patches/auto_completes_controller_patch'
-require 'redmine_tags/patches/action_controller_patch'
-require 'redmine_tags/patches/issue_query_patch'
-require 'query_tags_column'
-
-module RedmineTags
-  def self.settings() Setting[:plugin_redmineup_tags].stringify_keys end
-end
diff --git a/plugins/redmineup_tags/lib/redmine_tags/hooks/model_issue_hook.rb b/plugins/redmineup_tags/lib/redmine_tags/hooks/model_issue_hook.rb
deleted file mode 100644
index 371b7d2..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags/hooks/model_issue_hook.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineTags
-  module Hooks
-    class ModelIssueHook < Redmine::Hook::ViewListener
-      def controller_issues_edit_before_save(context = {})
-        tags_log context
-      end
-
-      def controller_issues_bulk_edit_before_save(context = {})
-        tags_log context
-      end
-
-      def tags_log(context, create_journal = true)
-        issue = context[:issue]
-        params = context[:params]
-        if params && params[:issue] && !params[:issue][:tag_list].nil?
-          old_tags = Issue.find(issue.id).tag_list.to_s
-          new_tags = issue.tag_list.to_s
-          if create_journal && !(old_tags == new_tags || issue.current_journal.blank?)
-            issue.current_journal.details << JournalDetail.new(:property => 'attr',
-                                                               :prop_key => 'tag_list',
-                                                               :old_value => old_tags,
-                                                               :value => new_tags)
-          end
-        end
-      end
-    end
-  end
-end
diff --git a/plugins/redmineup_tags/lib/redmine_tags/hooks/views_context_menus_hook.rb b/plugins/redmineup_tags/lib/redmine_tags/hooks/views_context_menus_hook.rb
deleted file mode 100644
index 33f41b0..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags/hooks/views_context_menus_hook.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineTags
-  module Hooks
-    class ViewsContextMenuesHook < Redmine::Hook::ViewListener
-      render_on :view_issues_context_menu_end, :partial => 'context_menus/issues_tags'
-    end
-  end
-end
diff --git a/plugins/redmineup_tags/lib/redmine_tags/hooks/views_issues_hook.rb b/plugins/redmineup_tags/lib/redmine_tags/hooks/views_issues_hook.rb
deleted file mode 100644
index 7fa1fec..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags/hooks/views_issues_hook.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineTags
-  module Hooks
-    class ViewsIssuesHook < Redmine::Hook::ViewListener
-      render_on :view_issues_show_details_bottom, :partial => 'issues/tags'
-      render_on :view_issues_form_details_bottom, :partial => 'issues/tags_form'
-      render_on :view_issues_sidebar_planning_bottom, :partial => 'issues/tags_sidebar'
-      render_on :view_issues_bulk_edit_details_bottom, :partial => 'issues/tags_form'
-    end
-  end
-end
-
diff --git a/plugins/redmineup_tags/lib/redmine_tags/hooks/views_layouts_hook.rb b/plugins/redmineup_tags/lib/redmine_tags/hooks/views_layouts_hook.rb
deleted file mode 100644
index b8d413b..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags/hooks/views_layouts_hook.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineTags
-  module Hooks
-    class ViewsLayoutsHook < Redmine::Hook::ViewListener
-      render_on :view_layouts_base_html_head, partial: 'tags/additional_assets'
-      render_on :view_layouts_base_body_bottom, partial: 'tags/select2_transformation_rules'
-    end
-  end
-end
diff --git a/plugins/redmineup_tags/lib/redmine_tags/patches/action_controller_patch.rb b/plugins/redmineup_tags/lib/redmine_tags/patches/action_controller_patch.rb
deleted file mode 100644
index 3d01e8c..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags/patches/action_controller_patch.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineTags
-  module Patches
-    module ActionControllerPatch
-      def self.included(base)
-        base.extend(ClassMethods) if Rails::VERSION::MAJOR < 4
-
-        base.class_eval do
-        end
-      end
-
-      module ClassMethods
-        def before_action(*filters, &block)
-          before_filter(*filters, &block)
-        end
-
-        def after_action(*filters, &block)
-          after_filter(*filters, &block)
-        end
-
-        def skip_before_action(*filters)
-          skip_before_filter(*filters)
-        end
-      end
-    end
-  end
-end
-
-unless ActionController::Base.included_modules.include?(RedmineTags::Patches::ActionControllerPatch)
-  ActionController::Base.send(:include, RedmineTags::Patches::ActionControllerPatch)
-end
diff --git a/plugins/redmineup_tags/lib/redmine_tags/patches/add_helpers_for_issue_tags_patch.rb b/plugins/redmineup_tags/lib/redmine_tags/patches/add_helpers_for_issue_tags_patch.rb
deleted file mode 100644
index d3a5dc8..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags/patches/add_helpers_for_issue_tags_patch.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-module RedmineTags
-  module Patches
-    module AddHelpersForIssueTagsPatch
-      def self.apply(controller)
-        controller.send(:helper, 'tags')
-        controller.send(:helper, 'issues_tags')
-      end
-    end
-  end
-end
diff --git a/plugins/redmineup_tags/lib/redmine_tags/patches/agile_query_patch.rb b/plugins/redmineup_tags/lib/redmine_tags/patches/agile_query_patch.rb
deleted file mode 100644
index 5bc69bc..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags/patches/agile_query_patch.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-require_dependency 'query'
-
-module RedmineTags
-  module Patches
-    module AgileQueryPatch
-      def self.included(base)
-        base.send(:include, InstanceMethods)
-        base.class_eval do
-          unloadable
-
-          alias_method :available_filters_without_redmine_tags, :available_filters
-          alias_method :available_filters, :available_filters_with_redmine_tags
-
-          add_available_column QueryTagsColumn.new(:tags_relations, caption: :tags)
-        end
-      end
-
-      module InstanceMethods
-        def sql_for_issue_tags_field(_field, operator, value)
-          case operator
-          when '=', '!'
-            issues = Issue.tagged_with(value.clone)
-          when '!*'
-            issues = Issue.joins(:tags).uniq
-          else
-            issues = Issue.tagged_with(RedmineCrm::Tag.all.map(&:to_s), :any => true)
-          end
-
-          compare   = operator.include?('!') ? 'NOT IN' : 'IN'
-          ids_list  = issues.collect(&:id).push(0).join(',')
-          "( #{Issue.table_name}.id #{compare} (#{ids_list}) ) "
-        end
-
-        def available_filters_with_redmine_tags
-          available_filters_without_redmine_tags
-          selected_tags = []
-          if filters['issue_tags'].present?
-            selected_tags = Issue.all_tags(:project => project, :open_only => RedmineTags.settings['issues_open_only'].to_i == 1).
-                                  where(:name => filters['issue_tags'][:values]).map { |c| [c.name, c.name] }
-          end
-          add_available_filter('issue_tags', type: :issue_tags, name: l(:tags), values: selected_tags)
-        end
-      end
-    end
-  end
-end
-
-unless AgileQuery.included_modules.include?(RedmineTags::Patches::AgileQueryPatch)
-  AgileQuery.send(:include, RedmineTags::Patches::AgileQueryPatch)
-end
diff --git a/plugins/redmineup_tags/lib/redmine_tags/patches/auto_completes_controller_patch.rb b/plugins/redmineup_tags/lib/redmine_tags/patches/auto_completes_controller_patch.rb
deleted file mode 100644
index 3a9e993..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags/patches/auto_completes_controller_patch.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-require_dependency 'auto_completes_controller'
-
-module RedmineContacts
-  module Patches
-    module AutoCompletesControllerPatch
-      def self.included(base)
-        base.send(:include, InstanceMethods)
-
-        base.class_eval do
-        end
-      end
-
-      module InstanceMethods
-        SORTING_FIELDS = {
-          'name' => 'name',
-          'last_created' => 'created_at',
-          'most_used' => 'count'
-        }
-
-        def redmine_tags
-          suggestion_order = RedmineTags.settings['tags_suggestion_order'] || 'name'
-          options = {
-            :name_like => (params[:q] || params[:term]).to_s.strip,
-            :sort_by => SORTING_FIELDS[suggestion_order],
-            :order => (suggestion_order == 'name' ? 'ASC' : 'DESC')
-          }
-          @redmine_tags = Issue.all_tags(options).limit(params[:limit] || 10)
-          render :layout => false, :partial => 'redmine_tags'
-        end
-      end
-    end
-  end
-end
-
-unless AutoCompletesController.included_modules.include?(RedmineContacts::Patches::AutoCompletesControllerPatch)
-  AutoCompletesController.send(:include, RedmineContacts::Patches::AutoCompletesControllerPatch)
-end
diff --git a/plugins/redmineup_tags/lib/redmine_tags/patches/issue_patch.rb b/plugins/redmineup_tags/lib/redmine_tags/patches/issue_patch.rb
deleted file mode 100644
index 4f5d869..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags/patches/issue_patch.rb
+++ /dev/null
@@ -1,115 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-require_dependency 'issue'
-
-module RedmineTags
-  module Patches
-    module IssuePatch
-
-      def self.included(base)
-        base.extend(ClassMethods)
-
-        base.class_eval do
-          include InstanceMethods
-
-          unloadable
-          rcrm_acts_as_taggable
-
-          alias_method :safe_attributes_without_safe_tags=, :safe_attributes=
-          alias_method :safe_attributes=, :safe_attributes_with_safe_tags=
-
-          class << self
-            alias_method :available_tags_without_redmine_tags, :available_tags
-            alias_method :available_tags, :available_tags_with_redmine_tags
-          end
-
-          scope :on_project, lambda { |project|
-            project = project.id if project.is_a? Project
-            { :conditions => ["#{Project.table_name}.id=?", project] }
-          }
-        end
-      end
-
-      # Class used to represent the tags relations of an issue
-      class TagsRelations < IssueRelation::Relations
-        def to_s(*args)
-          map(&:name).join(', ')
-        end
-      end
-
-      module ClassMethods
-        def available_tags_with_redmine_tags(options = {})
-          scope = available_tags_without_redmine_tags(options)
-          return scope unless options[:open_only]
-          scope.joins("JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{table_name}.status_id").
-                where("#{IssueStatus.table_name}.is_closed = ?", false)
-        end
-
-        def all_tags(options = {})
-          scope = RedmineCrm::Tag.where({})
-          scope = scope.where("LOWER(#{RedmineCrm::Tag.table_name}.name) LIKE LOWER(?)", "%#{options[:name_like]}%") if options[:name_like]
-          join = []
-          join << "JOIN #{RedmineCrm::Tagging.table_name} ON #{RedmineCrm::Tagging.table_name}.tag_id = #{RedmineCrm::Tag.table_name}.id "
-          join << "JOIN #{Issue.table_name} ON #{Issue.table_name}.id = #{RedmineCrm::Tagging.table_name}.taggable_id
-            AND #{RedmineCrm::Tagging.table_name}.taggable_type = '#{Issue.name}' "
-          scope = scope.joins(join.join(' '))
-
-          columns = [
-            "#{RedmineCrm::Tag.table_name}.*",
-            "COUNT(DISTINCT #{RedmineCrm::Tagging.table_name}.taggable_id) AS count"
-          ]
-          if options[:sort_by] == 'created_at'
-            columns << "MIN(#{RedmineCrm::Tagging.table_name}.created_at) AS created_at"
-          end
-          scope = scope.select(columns.join(', '))
-
-          scope = scope.group("#{RedmineCrm::Tag.table_name}.id, #{RedmineCrm::Tag.table_name}.name ")
-          scope = scope.having('COUNT(*) > 0')
-
-          column = options[:sort_by] || "#{RedmineCrm::Tag.table_name}.name"
-          order = options[:order] || 'ASC'
-          scope.order("#{column} #{order}")
-        end
-
-        def allowed_tags?(tags)
-          allowed_tags = all_tags.map(&:name)
-          tags.all? { |tag| allowed_tags.include?(tag) }
-        end
-      end
-
-      module InstanceMethods
-        def safe_attributes_with_safe_tags=(attrs, user=User.current)
-          self.safe_attributes_without_safe_tags = attrs
-          if attrs && attrs[:tag_list] && user.allowed_to?(:edit_tags, self.project)
-            tags = attrs[:tag_list].reject(&:empty?)
-            if user.allowed_to?(:create_tags, self.project) || Issue.allowed_tags?(tags)
-              self.tag_list = tags
-            end
-          end
-        end
-
-        def tags_relations
-          TagsRelations.new(self, tags.to_a)
-        end
-      end
-
-    end
-  end
-end
diff --git a/plugins/redmineup_tags/lib/redmine_tags/patches/issue_query_patch.rb b/plugins/redmineup_tags/lib/redmine_tags/patches/issue_query_patch.rb
deleted file mode 100644
index 1b9c1f1..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags/patches/issue_query_patch.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-require_dependency 'issue_query'
-
-module RedmineTags
-  module Patches
-    module IssueQueryPatch
-      def self.included(base)
-        base.send(:include, InstanceMethods)
-
-        base.class_eval do
-          unloadable
-
-          alias_method :statement_without_redmine_tags, :statement
-          alias_method :statement, :statement_with_redmine_tags
-
-          alias_method :available_filters_without_redmine_tags, :available_filters
-          alias_method :available_filters, :available_filters_with_redmine_tags
-
-          add_available_column QueryTagsColumn.new(:tags_relations, caption: :tags)
-        end
-      end
-
-      module InstanceMethods
-        def statement_with_redmine_tags
-          filter  = filters.delete 'issue_tags'
-          clauses = statement_without_redmine_tags || ''
-
-          if filter
-            filters['issue_tags'] = filter
-
-            issues = Issue.where({})
-
-            op = operator_for('issue_tags')
-            case op
-            when '=', '!'
-              issues = issues.tagged_with(values_for('issue_tags').clone)
-            when '!*'
-              issues = issues.joins(:tags).uniq
-            else
-              issues = issues.tagged_with(RedmineCrm::Tag.all.map(&:to_s), :any => true)
-            end
-
-            compare   = op.include?('!') ? 'NOT IN' : 'IN'
-            ids_list  = issues.collect(&:id).push(0).join(',')
-
-            clauses << ' AND ' unless clauses.empty?
-            clauses << "( #{Issue.table_name}.id #{compare} (#{ids_list}) ) "
-          end
-
-          clauses
-        end
-
-        def available_filters_with_redmine_tags
-          available_filters_without_redmine_tags
-          selected_tags = []
-          if filters['issue_tags'].present?
-            selected_tags = Issue.all_tags(:project => project, :open_only => RedmineTags.settings['issues_open_only'].to_i == 1).
-                                  where(:name => filters['issue_tags'][:values]).map { |c| [c.name, c.name] }
-          end
-          add_available_filter('issue_tags', type: :issue_tags, name: l(:tags), values: selected_tags)
-        end
-      end
-    end
-  end
-end
-
-unless IssueQuery.included_modules.include?(RedmineTags::Patches::IssueQueryPatch)
-  IssueQuery.send(:include, RedmineTags::Patches::IssueQueryPatch)
-end
diff --git a/plugins/redmineup_tags/lib/redmine_tags/patches/queries_helper_patch.rb b/plugins/redmineup_tags/lib/redmine_tags/patches/queries_helper_patch.rb
deleted file mode 100644
index c76bb3d..0000000
--- a/plugins/redmineup_tags/lib/redmine_tags/patches/queries_helper_patch.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-# This file is a part of Redmine Tags (redmine_tags) plugin,
-# customer relationship management plugin for Redmine
-#
-# Copyright (C) 2011-2018 RedmineUP
-# http://www.redmineup.com/
-#
-# redmine_tags is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# redmine_tags is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with redmine_tags.  If not, see <http://www.gnu.org/licenses/>.
-
-require_dependency 'queries_helper'
-if ActiveSupport::Dependencies::search_for_file('issue_queries_helper')
-  require_dependency 'issue_queries_query'
-end
-
-module RedmineTags
-  module Patches
-    module QueriesHelperPatch
-      def self.included(base)
-        base.send(:include, InstanceMethods)
-
-        base.class_eval do
-          unloadable
-          alias_method :column_value_without_tags, :column_value
-          alias_method :column_value, :column_value_with_tags
-        end
-      end
-
-      module InstanceMethods
-        include TagsHelper
-
-        def column_value_with_tags(column, list_object, value)
-          if column.name == :tags_relations && list_object.is_a?(Issue)
-            [value].flatten.collect{ |t| render_issue_tag_link(t) }.join(RedmineTags.settings['issues_use_colors'].to_i > 0 ? ' ' : ', ').html_safe
-          else
-            column_value_without_tags(column, list_object, value)
-          end
-        end
-      end
-    end
-  end
-end
diff --git a/plugins/redmineup_tags/test/functional/auto_completes_controller_test.rb b/plugins/redmineup_tags/test/functional/auto_completes_controller_test.rb
index 1ae343d..dd9046f 100644
--- a/plugins/redmineup_tags/test/functional/auto_completes_controller_test.rb
+++ b/plugins/redmineup_tags/test/functional/auto_completes_controller_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -42,16 +42,15 @@ class AutoCompletesControllerTest < ActionController::TestCase
            :custom_fields_projects,
            :custom_fields_trackers
 
-
   def setup
-    @tag = RedmineCrm::Tag.create(:name => 'Test_tag')
+    @tag = RedmineCrm::Tag.create(name: 'Test_tag')
     @request.session[:user_id] = 1
   end
 
   def test_redmine_tags_should_not_be_case_sensitive
     issue = Issue.find(1)
     issue.tags << @tag
-    compatible_request :get, :redmine_tags, :project_id => 'ecookbook', :q => 'te'
+    compatible_request :get, :redmine_tags, project_id: 'ecookbook', q: 'te'
     assert_response :success
     redmine_tags = ActiveSupport::JSON.decode(response.body).map { |item| item['id'] }
     assert_not_nil redmine_tags
@@ -61,7 +60,7 @@ class AutoCompletesControllerTest < ActionController::TestCase
   def test_contacts_should_return_json
     issue = Issue.find(1)
     issue.tags << @tag
-    compatible_request :get, :redmine_tags, :project_id => 'ecookbook', :q => 'te'
+    compatible_request :get, :redmine_tags, project_id: 'ecookbook', q: 'te'
     assert_response :success
     json = ActiveSupport::JSON.decode(response.body)
     assert_kind_of Array, json
@@ -72,38 +71,38 @@ class AutoCompletesControllerTest < ActionController::TestCase
   end
 
   def test_suggestion_order_default
-    with_settings :plugin_redmineup_tags => Setting.available_settings['plugin_redmineup_tags']['default'] do
-      compatible_request :get, :redmine_tags, :project_id => 'ecookbook'
+    with_settings plugin_redmineup_tags: Setting.available_settings['plugin_redmineup_tags']['default'] do
+      compatible_request :get, :redmine_tags, project_id: 'ecookbook'
     end
     assert_response :success
     tags = ActiveSupport::JSON.decode(response.body).map { |item| item['id'] }
-    assert_equal %w(first second third), tags
+    assert_equal %w[first second third], tags
   end
 
   def test_suggestion_order_name
-    with_settings :plugin_redmineup_tags => { :tags_suggestion_order => 'name' } do
-      compatible_request :get, :redmine_tags, :project_id => 'ecookbook'
+    with_settings plugin_redmineup_tags: { tags_suggestion_order: 'name' } do
+      compatible_request :get, :redmine_tags, project_id: 'ecookbook'
     end
     assert_response :success
     tags = ActiveSupport::JSON.decode(response.body).map { |item| item['id'] }
-    assert_equal %w(first second third), tags
+    assert_equal %w[first second third], tags
   end
 
   def test_suggestion_order_most_used
-    with_settings :plugin_redmineup_tags => { :tags_suggestion_order => 'most_used' } do
-      compatible_request :get, :redmine_tags, :project_id => 'ecookbook'
+    with_settings plugin_redmineup_tags: { tags_suggestion_order: 'most_used' } do
+      compatible_request :get, :redmine_tags, project_id: 'ecookbook'
     end
     assert_response :success
     tags = ActiveSupport::JSON.decode(response.body).map { |item| item['id'] }
-    assert_equal %w(second third first), tags
+    assert_equal %w[second third first], tags
   end
 
   def test_suggestion_order_last_created
-    with_settings :plugin_redmineup_tags => { :tags_suggestion_order => 'last_created' } do
-      compatible_request :get, :redmine_tags, :project_id => 'ecookbook'
+    with_settings plugin_redmineup_tags: { tags_suggestion_order: 'last_created' } do
+      compatible_request :get, :redmine_tags, project_id: 'ecookbook'
     end
     assert_response :success
     tags = ActiveSupport::JSON.decode(response.body).map { |item| item['id'] }
-    assert_equal %w(third second first), tags
+    assert_equal %w[third second first], tags
   end
 end
diff --git a/plugins/redmineup_tags/test/functional/issue_tags_controller_test.rb b/plugins/redmineup_tags/test/functional/issue_tags_controller_test.rb
index 39df2fa..cd517d8 100644
--- a/plugins/redmineup_tags/test/functional/issue_tags_controller_test.rb
+++ b/plugins/redmineup_tags/test/functional/issue_tags_controller_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -42,7 +42,6 @@ class IssueTagsControllerTest < ActionController::TestCase
            :custom_fields_projects,
            :custom_fields_trackers
 
-
   def setup
     @request.env['HTTP_REFERER'] = '/update_issue_tags'
     @request.session[:user_id] = 2
@@ -52,117 +51,110 @@ class IssueTagsControllerTest < ActionController::TestCase
     @issue_8 = issues(:issues_008)
     @issues = [@issue_1, @issue_2, @issue_8]
     @ids = [1, 2, 8]
-    @most_used_tags = %w(second third first)
+    @most_used_tags = %w[second third first]
     @role = roles(:roles_001) # Manager role
     @role.add_permission! :edit_tags
   end
 
   def test_should_get_edit_when_one_issue_chose
-    compatible_xhr_request :get, :edit, :ids => [1]
+    compatible_xhr_request :get, :edit, ids: [1]
     assert_response :success
-    assert_equal 'text/javascript', response.content_type
+    assert_match 'text/javascript', response.content_type
 
     html_form = response.body[/<form.+form>/].delete('\\')
 
-    assert_select_in html_form, 'select#issue_tag_list', 1 do
-      assert_select 'option[selected="selected"]', 2
-      assert_select 'option[selected="selected"]', :text => 'second', :count => 1
-      assert_select 'option[selected="selected"]', :text => 'third', :count => 1
-    end
-
-    assert_select_in html_form, '.most_used_tags', :text => /.+second.+third.+first.+/, :count => 1 do
+    assert_select_in html_form, '.most_used_tags', text: /.+second.+third.+first.+/, count: 1 do
       assert_select '.most_used_tag', 3
-      @most_used_tags.each { |tag| assert_select '.most_used_tag', :text => tag, :count => 1 }
+      @most_used_tags.each { |tag| assert_select '.most_used_tag', text: tag, count: 1 }
     end
   end
 
   def test_should_get_edit_when_several_issues_chose
-    compatible_xhr_request :get, :edit, :ids => @ids
+    compatible_xhr_request :get, :edit, ids: @ids
     assert_response :success
-    assert_equal 'text/javascript', response.content_type
+    assert_match 'text/javascript', response.content_type
 
     html_form = response.body[/<form.+form>/].delete('\\')
 
-    assert_select_in html_form, 'select#issue_tag_list', 1 do
-      assert_select 'option[selected="selected"]', 0
-    end
-
-    assert_select_in html_form, '.most_used_tags', :text => /.+second.+third.+first.+/, :count => 1 do
+    assert_select_in html_form, '.most_used_tags', text: /.+second.+third.+first.+/, count: 1 do
       assert_select '.most_used_tag', 3
-      @most_used_tags.each { |tag| assert_select '.most_used_tag', :text => tag, :count => 1 }
+      @most_used_tags.each { |tag| assert_select '.most_used_tag', text: tag, count: 1 }
     end
   end
 
   def test_should_get_not_found_when_no_ids
-    compatible_xhr_request :get, :edit, :ids => []
+    compatible_xhr_request :get, :edit, ids: []
     assert_response :missing
-    compatible_request :post, :update, :ids => [], :issue => {:tag_list => []}
+    compatible_request :post, :update, ids: [], issue: { tag_list: [] }
     assert_response :missing
   end
 
   def test_should_change_issue_tags_empty_tags
-    compatible_request :post, :update, :ids => [1], :issue => {:tag_list => ['', '', '']}
+    compatible_request :post, :update, ids: [1], issue: { tag_list: ['', '', ''] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_tags_added), flash[:notice]
     assert_equal [], @issue_1.tag_list
   end
 
   def test_should_change_issue_tags_no_tags
-    compatible_request :post, :update, :ids => [1], :issue => {:tag_list => []}
+    compatible_request :post, :update, ids: [1], issue: { tag_list: [] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_tags_added), flash[:notice]
     assert_equal [], @issue_1.tag_list
   end
 
   def test_should_change_issue_tags_one_tag
-    compatible_request :post, :update, :ids => [1], :issue => {:tag_list => %w(first)}
+    compatible_request :post, :update, ids: [1], issue: { tag_list: %w[first] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_tags_added), flash[:notice]
-    assert_equal %w(first), @issue_1.tag_list
+    assert_equal %w[first], @issue_1.tag_list
   end
 
   def test_should_change_issue_tags_several_tags
-    compatible_request :post, :update, :ids => [1], :issue => {:tag_list => %w(first second third)}
+    compatible_request :post, :update, ids: [1], issue: { tag_list: %w[first second third] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_tags_added), flash[:notice]
-    assert_equal %w(first second third), @issue_1.tag_list.sort
+    assert_equal %w[first second third], @issue_1.tag_list.sort
   end
 
   def test_should_bulk_change_issue_tags_no_tags
-    compatible_request :post, :update, :ids => @ids, :issue => {:tag_list => []}
+    compatible_request :post, :update, ids: @ids, issue: { tag_list: [] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_tags_added), flash[:notice]
     @issues.each { |issue| assert_equal [], issue.tag_list }
   end
 
   def test_should_bulk_change_issue_tags_one_tag
-    compatible_request :post, :update, :ids => @ids, :issue => {:tag_list => %w(first)}
+    compatible_request :post, :update, ids: @ids, issue: { tag_list: %w[first] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_tags_added), flash[:notice]
-    @issues.each { |issue| assert_equal %w(first), issue.tag_list }
+
+    assert_equal %w[first second third], @issue_1.tag_list.sort
+    assert_equal %w[first second third], @issue_2.tag_list.sort
+    assert_equal %w[first second], @issue_8.tag_list.sort
   end
 
   def test_should_bulk_change_issue_tags_several_tags
-    compatible_request :post, :update, :ids => @ids, :issue => {:tag_list => %w(first second third)}
+    compatible_request :post, :update, ids: @ids, issue: { tag_list: %w[first second third] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_tags_added), flash[:notice]
-    @issues.each { |issue| assert_equal %w(first second third), issue.tag_list.sort }
+    @issues.each { |issue| assert_equal %w[first second third], issue.tag_list.sort }
   end
 
   def test_edit_tags_permission
     tag = 'first'
     assert_not_equal Issue.find(1).tag_list, [tag]
     assert Issue.all_tags.map(&:name).include?(tag)
-    compatible_request :post, :update, :ids => [1], :issue => { :tag_list => [tag] }
+    compatible_request :post, :update, ids: [1], issue: { tag_list: [tag] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_tags_added), flash[:notice]
     assert_equal Issue.find(1).tag_list, [tag]
 
@@ -170,9 +162,9 @@ class IssueTagsControllerTest < ActionController::TestCase
     tag2 = 'second'
 
     assert Issue.all_tags.map(&:name).include?(tag2)
-    compatible_request :post, :update, :ids => [1], :issue => { :tag_list => [tag2] }
+    compatible_request :post, :update, ids: [1], issue: { tag_list: [tag2] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_failed_to_add_tags), flash[:error]
     assert_equal Issue.find(1).tag_list, [tag]
   end
@@ -180,23 +172,23 @@ class IssueTagsControllerTest < ActionController::TestCase
   def test_bulk_edit_tags_permission
     tag = 'first'
     assert Issue.all_tags.map(&:name).include?(tag)
-    compatible_request :post, :update, :ids => [1, 2], :issue => { :tag_list => [tag] }
+    compatible_request :post, :update, ids: [1, 8], issue: { tag_list: [tag] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_tags_added), flash[:notice]
-    assert_equal Issue.find(1).tag_list, [tag]
-    assert_equal Issue.find(2).tag_list, [tag]
+    assert_equal Issue.find(1).tag_list.sort, %w[first second third]
+    assert_equal Issue.find(8).tag_list.sort, %w[first second]
 
     @role.remove_permission! :edit_tags
     tag2 = 'second'
 
     assert Issue.all_tags.map(&:name).include?(tag2)
-    compatible_request :post, :update, :ids => [1, 2], :issue => { :tag_list => [tag2] }
+    compatible_request :post, :update, ids: [1, 8], issue: { tag_list: [tag2] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_failed_to_add_tags), flash[:error]
-    assert_equal Issue.find(1).tag_list, [tag]
-    assert_equal Issue.find(2).tag_list, [tag]
+    assert_equal Issue.find(1).tag_list.sort, %w[first second third]
+    assert_equal Issue.find(8).tag_list.sort, %w[first second]
   end
 
   def test_create_tags_permission
@@ -205,9 +197,9 @@ class IssueTagsControllerTest < ActionController::TestCase
 
     assert_not_equal Issue.find(1).tag_list, [new_tag]
     assert !Issue.all_tags.map(&:name).include?(new_tag)
-    compatible_request :post, :update, :ids => [1], :issue => { :tag_list => [new_tag] }
+    compatible_request :post, :update, ids: [1], issue: { tag_list: [new_tag] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_tags_added), flash[:notice]
     assert_equal Issue.find(1).tag_list, [new_tag]
 
@@ -215,9 +207,9 @@ class IssueTagsControllerTest < ActionController::TestCase
     new_tag2 = 'disable_create_tags_permission'
 
     assert !Issue.all_tags.map(&:name).include?(new_tag2)
-    compatible_request :post, :update, :ids => [1], :issue => { :tag_list => [new_tag2] }
+    compatible_request :post, :update, ids: [1], issue: { tag_list: [new_tag2] }
     assert_response :redirect
-    assert_redirected_to :action => 'update'
+    assert_redirected_to action: 'update'
     assert_equal I18n.t(:notice_failed_to_add_tags), flash[:error]
     assert_equal Issue.find(1).tag_list, [new_tag]
   end
diff --git a/plugins/redmineup_tags/test/functional/issues_controller_test.rb b/plugins/redmineup_tags/test/functional/issues_controller_test.rb
index 31bbd88..b2cd970 100644
--- a/plugins/redmineup_tags/test/functional/issues_controller_test.rb
+++ b/plugins/redmineup_tags/test/functional/issues_controller_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -43,8 +43,8 @@ class IssuesControllerTest < ActionController::TestCase
            :custom_fields_trackers
 
   def setup
-    @tag = RedmineCrm::Tag.create(:name => 'test_tag')
-    @last_tag = RedmineCrm::Tag.create(:name => 'last_tag')
+    @tag = RedmineCrm::Tag.create(name: 'test_tag')
+    @last_tag = RedmineCrm::Tag.create(name: 'last_tag')
     @request.session[:user_id] = 1
   end
 
@@ -54,11 +54,11 @@ class IssuesControllerTest < ActionController::TestCase
     compatible_request(
       :get,
       :index,
-      :f => ['status_id', 'issue_tags',''],
-      :op => { :status_id => 'o', :issue_tags => '=' },
-      :v =>  { :issue_tags => ['test_tag'] },
-      :c => ['status', 'priority', 'subject', 'tags_relations'],
-      :project_id => 'ecookbook'
+      f: ['status_id', 'issue_tags', ''],
+      op: { status_id: 'o', issue_tags: '=' },
+      v: { issue_tags: ['test_tag'] },
+      c: ['status', 'priority', 'subject', 'tags_relations'],
+      project_id: 'ecookbook'
     )
     assert_response :success
     assert_select 'table.list.issues tr.issue', 1
@@ -74,18 +74,18 @@ class IssuesControllerTest < ActionController::TestCase
     issue2 = Issue.find(2)
     issue2.tags << @tag
     issue2.tags << @last_tag
-    RedmineTags.stubs(:settings).returns({ 'issues_sidebar' => 'list',
+    RedmineupTags.stubs(:settings).returns('issues_sidebar' => 'list',
                                            'issues_show_count' => '1',
                                            'issues_sort_by' => 'count',
-                                           'issues_sort_order' => 'desc' })
+                                           'issues_sort_order' => 'desc')
 
-    compatible_request :get, :index, :project_id => 'ecookbook'
+    compatible_request :get, :index, project_id: 'ecookbook'
     assert_response :success
     assert_select '.tag-label', 'test_tag(2)'
   ensure
     issue1.tags = []
     issue2.tags = []
-    RedmineTags.unstub(:settings)
+    RedmineupTags.unstub(:settings)
   end
 
   def test_get_index_with_sidebar_tags_in_cloud_by_count
@@ -96,52 +96,58 @@ class IssuesControllerTest < ActionController::TestCase
     issue2.tags << @tag
     issue2.tags << @last_tag
 
-    RedmineTags.stubs(:settings).returns({ 'issues_sidebar' => 'cloud',
+    RedmineupTags.stubs(:settings).returns('issues_sidebar' => 'cloud',
                                            'issues_show_count' => '1',
                                            'issues_sort_by' => 'count',
-                                           'issues_sort_order' => 'desc' })
-    compatible_request :get, :index, :project_id => 'ecookbook'
+                                           'issues_sort_order' => 'desc')
+    compatible_request :get, :index, project_id: 'ecookbook'
     assert_response :success
     assert_select '.tag-label', 'last_tag(2)'
   ensure
     issue1.tags = []
     issue2.tags = []
-    RedmineTags.unstub(:settings)
+    RedmineupTags.unstub(:settings)
   end
 
   def test_should_not_update_without_tag_list
-    tags = %w(second third)
+    tags = %w[second third]
     assert_equal tags, Issue.find(1).tag_list.sort
-    compatible_request :post, :update, :id => 1, :issue => { :project_id => 1 }
+    compatible_request :post, :update, id: 1, issue: { project_id: 1 }
     assert_response :redirect
     assert_equal tags, Issue.find(1).tag_list.sort
   end
 
   def test_should_update_with_empty_string_tags
-    assert_equal %w(second third), Issue.find(1).tag_list.sort
-    compatible_request :post, :update, :id => 1, :issue => { :project_id => 1, :tag_list => ['', ''] }
+    assert_equal %w[second third], Issue.find(1).tag_list.sort
+    compatible_request :post, :update, id: 1, issue: { project_id: 1, tag_list: ['', ''] }
     assert_response :redirect
     assert_equal [], Issue.find(1).tag_list
   end
 
   def test_should_update_with_new_tags
-    assert_equal %w(second third), Issue.find(1).tag_list.sort
-    compatible_request :post, :update, :id => 1, :issue => { :project_id => 1, :tag_list => %w(new_tag1 new_tag2) }
+    assert_equal %w[second third], Issue.find(1).tag_list.sort
+    compatible_request :post, :update, id: 1, issue: { project_id: 1, tag_list: %w[new_tag1 new_tag2] }
     assert_response :redirect
-    assert_equal %w(new_tag1 new_tag2), Issue.find(1).tag_list.sort
+    assert_equal %w[new_tag1 new_tag2], Issue.find(1).tag_list.sort
   end
 
   def test_should_update_issue_and_tags
-    assert_equal %w(second third), Issue.find(1).tag_list.sort
-    compatible_request :post, :update, :id => 1, :issue => {
-      :project_id => 1,
-      :description => 'Test should update issue and tags',
-      :tag_list => %w(new_tag1 new_tag2)
+    assert_equal %w[second third], Issue.find(1).tag_list.sort
+    compatible_request :post, :update, id: 1, issue: {
+      project_id: 1,
+      description: 'Test should update issue and tags',
+      tag_list: %w[new_tag1 new_tag2]
     }
     assert_response :redirect
     issue = Issue.find(1)
     assert_equal 'Test should update issue and tags', issue.description
-    assert_equal %w(new_tag1 new_tag2), issue.tag_list.sort
+    assert_equal %w[new_tag1 new_tag2], issue.tag_list.sort
+  end
+
+  def test_get_bulk_edit_with_tags
+    compatible_request :get, :bulk_edit, ids: [1, 2]
+    assert_select '#issue_tags'
+    assert_response :success
   end
 
   def test_post_bulk_edit_without_tag_list
@@ -151,19 +157,19 @@ class IssuesControllerTest < ActionController::TestCase
     issue2 = Issue.find(2)
     issue2.tags = [@last_tag]
 
-    compatible_request :post, :bulk_update, :ids => [1, 2], :issue => { :project_id => '', :tracker_id => '' }
+    compatible_request :post, :bulk_update, ids: [1, 2], issue: { project_id: '', tracker_id: '' }
     assert_response :redirect
     assert_equal [@tag.name], Issue.find(1).tag_list
     assert_equal [@last_tag.name], Issue.find(2).tag_list
   ensure
     issue1.tags = []
     issue2.tags = []
-    RedmineTags.unstub(:settings)
+    RedmineupTags.unstub(:settings)
   end
 
   def test_post_bulk_edit_with_empty_string_tags
-    (1..2).each { |i| assert_equal %w(second third), Issue.find(i).tag_list.sort }
-    compatible_request :post, :bulk_update, :ids => [1, 2], :issue => { :project_id => '', :tracker_id => '', :tag_list => ['', ''] }
+    (1..2).each { |i| assert_equal %w[second third], Issue.find(i).tag_list.sort }
+    compatible_request :post, :bulk_update, ids: [1, 2], issue: { project_id: '', tracker_id: '', tag_list: ['', ''] }
     assert_response :redirect
     (1..2).each { |i| assert_equal [], Issue.find(i).tag_list }
   end
@@ -175,16 +181,68 @@ class IssuesControllerTest < ActionController::TestCase
     issue2 = Issue.find(2)
     issue2.tags << @last_tag
 
-    compatible_request :post, :bulk_update, :ids => [1, 2], :issue => { :project_id => '', :tracker_id => '', :tag_list => ['bulk_tag'] }
+    compatible_request :post, :bulk_update, ids: [1, 2], issue: { project_id: '', tracker_id: '', tag_list: ['bulk_tag'] }
     assert_response :redirect
     assert_equal ['bulk_tag'], Issue.find(1).tag_list
     assert_equal ['bulk_tag'], Issue.find(2).tag_list
   ensure
     issue1.tags = []
     issue2.tags = []
-    RedmineTags.unstub(:settings)
+    RedmineupTags.unstub(:settings)
+  end
+
+  def test_get_new_with_permission_edit_tags
+    # User(id: 2) has role Manager in Project(id: 1)
+    @request.session[:user_id] = 2
+    manager_role = Role.find(1)
+    manager_role.add_permission! :edit_tags
+    compatible_request :get, :new, issue: { project_id: 1 }
+    assert_select '#issue_tags'
+  end
+
+  def test_get_new_without_permission_edit_tags
+    @request.session[:user_id] = 2
+    manager_role = Role.find(1)
+    manager_role.remove_permission! :edit_tags
+    compatible_request :get, :new, issue: { project_id: 1 }
+    assert_select '#issue_tags', 0
+  end
+
+  def test_get_new_with_permission_edit_tags_in_other_project
+    # User(id: 2) has role Manager in Project(id: 1) and role Developer in Project(id: 2)
+    @request.session[:user_id] = 2
+    manager_role = Role.find(1)
+    manager_role.add_permission! :edit_tags
+    compatible_request :get, :new, issue: { project_id: 2 }
+    assert_select '#issue_tags', 0
+  end
+
+  def test_get_edit_with_permission_edit_tags
+    # User(id: 2) has role Manager in Project(id: 1) and Project(id: 1) contains Issue(id: 1)
+    @request.session[:user_id] = 2
+    manager_role = Role.find(1)
+    manager_role.add_permission! :edit_tags
+    compatible_request :get, :edit, id: 1, issue: { project_id: 1 }
+    assert_select '#issue_tags'
   end
 
+  def test_get_edit_without_permission_edit_tags
+    @request.session[:user_id] = 2
+    manager_role = Role.find(1)
+    manager_role.remove_permission! :edit_tags
+    compatible_request :get, :edit, id: 1, issue: { project_id: 1 }
+    assert_select '#issue_tags', 0
+  end
+
+  def test_get_edit_with_permission_edit_tags_in_other_project
+    # User(id: 2) has role Manager in Project(id: 1) and role Developer in Project(id: 2)
+    # Project(id: 1) contains Issue(id: 1)
+    @request.session[:user_id] = 2
+    manager_role = Role.find(1)
+    manager_role.add_permission! :edit_tags
+    compatible_request :get, :edit, id: 1, issue: { project_id: 2 }
+    assert_select '#issue_tags', 0
+  end
 
   def test_edit_tags_permission
     # User(id: 2) has role Manager in Project(id: 1) and Project(id: 1) contains Issue(id: 1)
@@ -195,7 +253,7 @@ class IssuesControllerTest < ActionController::TestCase
 
     assert_not_equal Issue.find(1).tag_list, [tag]
     assert Issue.all_tags.map(&:name).include?(tag)
-    compatible_request :post, :update, :id => 1, :issue => { :project_id => 1, :tag_list => [tag] }
+    compatible_request :post, :update, id: 1, issue: { project_id: 1, tag_list: [tag] }
     assert_response :redirect
     assert_equal Issue.find(1).tag_list, [tag]
 
@@ -204,7 +262,7 @@ class IssuesControllerTest < ActionController::TestCase
 
     assert Issue.all_tags.map(&:name).include?(tag2)
     assert_equal Issue.find(1).description, 'Unable to print recipes'
-    compatible_request :post, :update, :id => 1, :issue => { :project_id => 1, :description => 'New description', :tag_list => [tag2] }
+    compatible_request :post, :update, id: 1, issue: { project_id: 1, description: 'New description', tag_list: [tag2] }
     assert_response :redirect
     issue = Issue.find(1)
     assert_equal 'New description', issue.description
@@ -219,7 +277,7 @@ class IssuesControllerTest < ActionController::TestCase
 
     assert_not_equal Issue.find(1).tag_list, [new_tag]
     assert !Issue.all_tags.map(&:name).include?(new_tag) # The project should not contain the new tag
-    compatible_request :post, :update, :id => 1, :issue => { :project_id => 1, :tag_list => [new_tag] }
+    compatible_request :post, :update, id: 1, issue: { project_id: 1, tag_list: [new_tag] }
     assert_response :redirect
     assert_equal [new_tag], Issue.find(1).tag_list
 
@@ -227,8 +285,27 @@ class IssuesControllerTest < ActionController::TestCase
     new_tag2 = 'disable_create_tags_permission'
 
     assert !Issue.all_tags.map(&:name).include?(new_tag2)
-    compatible_request :post, :update, :id => 1, :issue => { :project_id => 1, :tag_list => [new_tag2] }
+    compatible_request :post, :update, id: 1, issue: { project_id: 1, tag_list: [new_tag2] }
     assert_response :redirect
     assert_equal Issue.find(1).tag_list, [new_tag]
   end
+
+  def test_filter_by_tags_equal
+    tags = %w(first second)
+    compatible_request :get, :index, project_id: 1, set_filter: 1, f: ['issue_tags', ''], op: { issue_tags: '=' }, v: { issue_tags: tags }
+    assert_response :success
+    issues_in_list.each { |issue| assert_equal (tags & issue.tag_list), tags }
+  end
+
+  def test_filter_by_tags_not_equal
+    tags = %w(first second)
+    compatible_request :get, :index, project_id: 1, set_filter: 1, f: ['issue_tags', ''], op: { issue_tags: '!' }, v: { issue_tags: tags }
+    assert_response :success
+    issues_in_list.each { |issue| assert_not_equal (tags & issue.tag_list), tags }
+  end
+
+  def test_get_index_without_project
+    compatible_request(:get, :index)
+    assert_response :success
+  end
 end
diff --git a/plugins/redmineup_tags/test/functional/tags_controller_test.rb b/plugins/redmineup_tags/test/functional/tags_controller_test.rb
index 7e415b1..7143c80 100644
--- a/plugins/redmineup_tags/test/functional/tags_controller_test.rb
+++ b/plugins/redmineup_tags/test/functional/tags_controller_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -42,7 +42,6 @@ class TagsControllerTest < ActionController::TestCase
            :custom_fields_projects,
            :custom_fields_trackers
 
-
   def setup
     # run as the admin
     @request.session[:user_id] = 1
@@ -50,16 +49,16 @@ class TagsControllerTest < ActionController::TestCase
     @project_a = Project.generate!
     @project_b = Project.generate!
 
-    add_issue @project_a, %w{a1 a2}, false
-    add_issue @project_a, %w{a2 a3}, false
-    add_issue @project_a, %w{a4 a5}, true
-    add_issue @project_b, %w{b6 b7}, true
-    add_issue @project_b, %w{b8 b9}, false
+    add_issue @project_a, %w[a1 a2], false
+    add_issue @project_a, %w[a2 a3], false
+    add_issue @project_a, %w[a4 a5], true
+    add_issue @project_b, %w[b6 b7], true
+    add_issue @project_b, %w[b8 b9], false
   end
 
   def test_should_get_edit
     tag = RedmineCrm::Tag.find_by_name('a1')
-    compatible_request :get, :edit, :id => tag.id
+    compatible_request :get, :edit, id: tag.id
     assert_response :success
     assert_select "input#tag_name[value='#{tag.name}']", 1
   end
@@ -67,37 +66,37 @@ class TagsControllerTest < ActionController::TestCase
   def test_should_put_update
     tag1 = RedmineCrm::Tag.find_by_name('a1')
     new_name = 'updated main'
-    compatible_request :put, :update, :id => tag1.id, :tag => { :name => new_name }
-    assert_redirected_to :controller => 'settings', :action => 'plugin', :id => 'redmineup_tags', :tab => 'manage_tags'
+    compatible_request :put, :update, id: tag1.id, tag: { name: new_name }
+    assert_redirected_to controller: 'settings', action: 'plugin', id: 'redmineup_tags', tab: 'manage_tags'
     tag1.reload
     assert_equal new_name, tag1.name
   end
 
-  test "should delete destroy" do
-    tag1 = RedmineCrm::Tag.find_by_name("a1")
+  test 'should delete destroy' do
+    tag1 = RedmineCrm::Tag.find_by_name('a1')
     assert_difference 'RedmineCrm::Tag.count', -1 do
-      compatible_request :post, :destroy, :ids => tag1.id
+      compatible_request :post, :destroy, ids: tag1.id
       assert_response 302
     end
   end
 
-  test "should post merge" do
-    tag1 = RedmineCrm::Tag.find_by_name("a1")
-    tag2 = RedmineCrm::Tag.find_by_name("b8")
+  test 'should post merge' do
+    tag1 = RedmineCrm::Tag.find_by_name('a1')
+    tag2 = RedmineCrm::Tag.find_by_name('b8')
     assert_difference 'RedmineCrm::Tag.count', -1 do
-      compatible_request :post, :merge, :ids => [tag1.id, tag2.id], :tag => {:name => "a1"}
-      assert_redirected_to :controller => 'settings', :action => 'plugin', :id => "redmineup_tags", :tab => "manage_tags"
+      compatible_request :post, :merge, ids: [tag1.id, tag2.id], tag: { name: 'a1' }
+      assert_redirected_to controller: 'settings', action: 'plugin', id: 'redmineup_tags', tab: 'manage_tags'
     end
-    assert_equal 0, Issue.tagged_with("b8").count
-    assert_equal 2, Issue.tagged_with("a1").count
+    assert_equal 0, Issue.tagged_with('b8').count
+    assert_equal 2, Issue.tagged_with('a1').count
   end
 
   private
 
   def add_issue(project, tags, closed)
-    issue = Issue.generate!(:project_id => project.id)
+    issue = Issue.generate!(project_id: project.id)
     issue.tag_list = tags
-    issue.status = IssueStatus.where(:is_closed => true).first if closed
+    issue.status = IssueStatus.where(is_closed: true).first if closed
     issue.save
   end
 end
diff --git a/plugins/redmineup_tags/test/test_helper.rb b/plugins/redmineup_tags/test/test_helper.rb
index 78d94c8..a89955b 100644
--- a/plugins/redmineup_tags/test/test_helper.rb
+++ b/plugins/redmineup_tags/test/test_helper.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -21,7 +21,6 @@
 
 require File.expand_path(File.dirname(__FILE__) + '/../../../test/test_helper')
 
-
 def plugin_fixtures(*fixtures)
   fixtures_directory = "#{File.dirname(__FILE__)}/fixtures/"
   fixture_names =
@@ -33,20 +32,29 @@ def plugin_fixtures(*fixtures)
       fixtures.flatten.map { |n| n.to_s }
     end
 
+  create_fixtures(fixtures_directory, fixture_names, class_names = {})
+end
+
+def create_fixtures(fixtures_directory, table_names, class_names = {})
   if ActiveRecord::VERSION::MAJOR >= 4
-    ActiveRecord::FixtureSet.create_fixtures fixtures_directory, fixture_names
+    ActiveRecord::FixtureSet.create_fixtures(fixtures_directory, table_names, class_names)
   else
-    ActiveRecord::Fixtures.create_fixtures fixtures_directory, fixture_names
+    ActiveRecord::Fixtures.create_fixtures(fixtures_directory, table_names, class_names)
   end
 end
 
 plugin_fixtures :all
 
-
 def compatible_request(type, action, parameters = {})
-  Rails.version < '5.1' ? send(type, action, parameters) : send(type, action, :params => parameters)
+  Rails.version < '5.1' ? send(type, action, parameters) : send(type, action, params: parameters)
 end
 
 def compatible_xhr_request(type, action, parameters = {})
-  Rails.version < '5.1' ? xhr(type, action, parameters) : send(type, action, :params => parameters, :xhr => true)
+  Rails.version < '5.1' ? xhr(type, action, parameters) : send(type, action, params: parameters, xhr: true)
+end
+
+# Returns the issues that are displayed in the list in the same order
+def issues_in_list
+  ids = css_select('tr.issue td.id').map{ |tag| tag['text'].to_i }
+  Issue.where(id: ids).sort_by { |issue| ids.index(issue.id) }
 end
diff --git a/plugins/redmineup_tags/test/unit/issue_test.rb b/plugins/redmineup_tags/test/unit/issue_test.rb
index 0be3dc2..f1b6e50 100644
--- a/plugins/redmineup_tags/test/unit/issue_test.rb
+++ b/plugins/redmineup_tags/test/unit/issue_test.rb
@@ -3,7 +3,7 @@
 # This file is a part of Redmine Tags (redmine_tags) plugin,
 # customer relationship management plugin for Redmine
 #
-# Copyright (C) 2011-2018 RedmineUP
+# Copyright (C) 2011-2021 RedmineUP
 # http://www.redmineup.com/
 #
 # redmine_tags is free software: you can redistribute it and/or modify
@@ -21,8 +21,8 @@
 
 require File.expand_path('../../test_helper', __FILE__)
 
-class RedmineTags::Patches::IssueTest < ActiveSupport::TestCase
-  fixtures :users, :projects, :issues, :issue_statuses, :enumerations, :trackers
+class RedmineupTags::Patches::IssueTest < ActiveSupport::TestCase
+  fixtures :users, :projects, :issues, :issue_statuses, :enumerations, :trackers, :enabled_modules
 
   def setup
     # run as the admin
@@ -32,43 +32,43 @@ class RedmineTags::Patches::IssueTest < ActiveSupport::TestCase
     @project_b = Project.find 3
   end
 
-  test "patch was applied" do
+  test 'patch was applied' do
     assert_respond_to Issue, :available_tags, 'Issue has available_tags getter'
     assert_respond_to Issue.new, :tags, 'Issue instance has tags getter'
     assert_respond_to Issue.new, :tags=, 'Issue instance has tags setter'
     assert_respond_to Issue.new, :tag_list=, 'Issue instance has tag_list setter'
   end
 
-  test "available tags should return list of distinct tags" do
+  test 'available tags should return list of distinct tags' do
     assert_equal 3, Issue.available_tags.to_a.size
   end
 
-  test "available tags should allow list tags of open issues only" do
-    assert_equal 2, Issue.available_tags(:open_only => true).to_a.size
+  test 'available tags should allow list tags of open issues only' do
+    assert_equal 2, Issue.available_tags(open_only: true).to_a.size
   end
 
-  test "available tags should allow list tags of specific project only" do
-    assert_equal 3, Issue.available_tags(:project => @project_a).to_a.size
-    assert_equal 2, Issue.available_tags(:project => @project_b).to_a.size
+  test 'available tags should allow list tags of specific project only' do
+    assert_equal 3, Issue.available_tags(project: @project_a).to_a.size
+    assert_equal 2, Issue.available_tags(project: @project_b).to_a.size
 
-    assert_equal 2, Issue.available_tags(:open_only => true, :project => @project_a).to_a.size
-    assert_equal 2, Issue.available_tags(:open_only => true, :project => @project_b).to_a.size
+    assert_equal 2, Issue.available_tags(open_only: true, project: @project_a).to_a.size
+    assert_equal 2, Issue.available_tags(open_only: true, project: @project_b).to_a.size
   end
 
-  test "available tags should allow list tags found by name" do
-    assert_equal 2, Issue.available_tags(:name_like => 'i').to_a.size
-    assert_equal 1, Issue.available_tags(:name_like => 'rd').to_a.size
-    assert_equal 2, Issue.available_tags(:name_like => 's').to_a.size
-    assert_equal 1, Issue.available_tags(:name_like => 'e').to_a.size
+  test 'available tags should allow list tags found by name' do
+    assert_equal 2, Issue.available_tags(name_like: 'i').to_a.size
+    assert_equal 1, Issue.available_tags(name_like: 'rd').to_a.size
+    assert_equal 2, Issue.available_tags(name_like: 's').to_a.size
+    assert_equal 1, Issue.available_tags(name_like: 'e').to_a.size
 
-    assert_equal 1, Issue.available_tags(:name_like => 'f', :project => @project_a).to_a.size
-    assert_equal 0, Issue.available_tags(:name_like => 'b', :project => @project_a).to_a.size
-    assert_equal 1, Issue.available_tags(:name_like => 'sec', :open_only => true, :project => @project_a).to_a.size
-    assert_equal 0, Issue.available_tags(:name_like => 'fir', :open_only => true, :project => @project_a).to_a.size
+    assert_equal 1, Issue.available_tags(name_like: 'f', project: @project_a).to_a.size
+    assert_equal 0, Issue.available_tags(name_like: 'b', project: @project_a).to_a.size
+    assert_equal 1, Issue.available_tags(name_like: 'sec', open_only: true, project: @project_a).to_a.size
+    assert_equal 0, Issue.available_tags(name_like: 'fir', open_only: true, project: @project_a).to_a.size
   end
 
   test 'Issue.all_tags should return all tags kind of Issue' do
-    tags = Issue.all_tags.map &:name
-    assert_equal %w(first second third), tags
+    tags = Issue.all_tags.map(&:name)
+    assert_equal %w[first second third], tags
   end
 end
-- 
GitLab