From 9eef4da21c6954318c828c90515408e123021abc Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 13 Oct 2018 08:41:51 +0200 Subject: [PATCH] remove old client --- .travis.yml | 4 +- DEVELOPMENT.rst | 82 +- README.rst | 48 +- bower.json | 64 - client/angular.json | 2 +- client/package-lock.json | 28 +- client/package.json | 3 +- .../src/assets}/docx/agenda.docx | Bin .../src/assets}/docx/motions.docx | Bin gulpfile.js | 362 -- .../static/css/agenda/_list_of_speakers.scss | 21 - .../agenda/static/css/agenda/_projector.scss | 22 - .../agenda/static/css/agenda/_site.scss | 37 - openslides/agenda/static/js/agenda/base.js | 441 -- openslides/agenda/static/js/agenda/csv.js | 41 - openslides/agenda/static/js/agenda/docx.js | 84 - openslides/agenda/static/js/agenda/pdf.js | 93 - .../agenda/static/js/agenda/projector.js | 137 - openslides/agenda/static/js/agenda/site.js | 890 --- .../agenda/current-list-of-speakers.html | 60 - .../static/templates/agenda/item-detail.html | 54 - .../static/templates/agenda/item-list.html | 551 -- .../static/templates/agenda/item-sort.html | 48 - .../list-of-speakers-partial-management.html | 132 - ...lide-current-list-of-speakers-overlay.html | 36 - ...artial-slide-current-list-of-speakers.html | 43 - .../slide-current-list-of-speakers.html | 8 - .../templates/agenda/slide-item-list.html | 27 - .../agenda/slide-list-of-speakers.html | 36 - openslides/agenda/urls.py | 10 - openslides/agenda/views.py | 9 - .../static/css/assignments/_projector.scss | 19 - .../static/css/assignments/_site.scss | 14 - .../assignments/static/js/assignments/base.js | 500 -- .../assignments/static/js/assignments/pdf.js | 666 --- .../static/js/assignments/projector.js | 46 - .../assignments/static/js/assignments/site.js | 941 --- .../assignments/assignment-detail.html | 299 - .../assignments/assignment-form.html | 22 - .../assignments/assignment-list.html | 348 -- .../assignments/assignmentpoll-form.html | 22 - .../assignments/slide_assignment.html | 101 - .../core/management/commands/collectstatic.py | 66 - openslides/core/static/css/_fonts.scss | 28 - openslides/core/static/css/_helper.scss | 255 - openslides/core/static/css/_mediaqueries.scss | 115 - .../core/static/css/_startup-spinner.scss | 34 - openslides/core/static/css/_ui-override.scss | 169 - openslides/core/static/css/_variables.scss | 10 - openslides/core/static/css/core/_chatbox.scss | 19 - openslides/core/static/css/core/_config.scss | 28 - .../core/static/css/core/_countdown.scss | 32 - .../core/static/css/core/_csv-import.scss | 28 - .../core/static/css/core/_goto-top.scss | 20 - .../static/css/core/_manage-projectors.scss | 31 - .../core/static/css/core/_messaging.scss | 20 - .../core/static/css/core/_multi-table.scss | 47 - .../core/static/css/core/_os-table.scss | 107 - .../static/css/core/_projector-container.scss | 43 - .../static/css/core/_projector-sidebar.scss | 31 - .../core/static/css/core/_projector.scss | 269 - openslides/core/static/css/core/_search.scss | 13 - openslides/core/static/css/core/_site.scss | 612 -- openslides/core/static/css/projector.scss | 14 - openslides/core/static/css/site.scss | 17 - openslides/core/static/img/favicon.png | Bin 581 -> 0 bytes openslides/core/static/img/logo-projector.png | Bin 2961 -> 0 bytes openslides/core/static/img/nav_active.png | Bin 1109 -> 0 bytes openslides/core/static/img/nav_dark-bg.png | Bin 925 -> 0 bytes .../static/img/nav_projector_sidebar_min.png | Bin 1100 -> 0 bytes .../core/static/img/openslides-logo-dark.png | Bin 4103 -> 0 bytes .../core/static/img/openslides-logo.png | Bin 6098 -> 0 bytes openslides/core/static/index.html | 3 + openslides/core/static/js/core/base.js | 1723 ------ openslides/core/static/js/core/csv.js | 23 - openslides/core/static/js/core/docx.js | 356 -- openslides/core/static/js/core/pdf-worker.js | 124 - openslides/core/static/js/core/pdf.js | 1414 ----- openslides/core/static/js/core/projector.js | 409 -- .../static/js/core/remove-format-plugin.js | 50 - openslides/core/static/js/core/site.js | 2135 ------- openslides/core/static/js/core/start.js | 117 - .../static/templates/config-form-field.html | 152 - openslides/core/static/templates/config.html | 35 - .../templates/core/checkbox-buttons.html | 14 - .../core/static/templates/core/checkbox.html | 7 - .../templates/core/countdown-detail.html | 16 - .../core/static/templates/core/editor.html | 2 - .../core/static/templates/core/file.html | 2 - .../static/templates/core/login-form.html | 30 - .../templates/core/manage-projectors.html | 219 - .../core/static/templates/core/password.html | 1 - .../templates/core/projector-controls.html | 312 - .../core/projector-message-form.html | 12 - .../static/templates/core/radio-buttons.html | 17 - .../templates/core/select-multiple.html | 10 - .../static/templates/core/select-single.html | 12 - .../static/templates/core/slide_clock.html | 4 - .../templates/core/slide_countdown.html | 9 - .../static/templates/core/slide_message.html | 4 - .../core/static/templates/core/tag-form.html | 17 - .../core/static/templates/core/tag-list.html | 51 - .../core/static/templates/csv-import.html | 35 - openslides/core/static/templates/home.html | 9 - openslides/core/static/templates/index.html | 279 - .../core/static/templates/legalnotice.html | 32 - .../core/static/templates/messaging.html | 10 - .../core/static/templates/privacypolicy.html | 9 - .../static/templates/privacypolicydialog.html | 9 - .../static/templates/projector-button.html | 27 - .../static/templates/projector-container.html | 52 - .../core/static/templates/projector.html | 118 - openslides/core/static/templates/search.html | 43 - openslides/core/views.py | 150 +- .../static/css/mediafiles/_image-browser.scss | 67 - .../static/css/mediafiles/_projector.scss | 43 - .../static/css/mediafiles/_site.scss | 23 - .../mediafiles/static/js/mediafiles/forms.js | 73 - .../static/js/mediafiles/image-plugin.js | 206 - .../mediafiles/static/js/mediafiles/list.js | 293 - .../static/js/mediafiles/projector.js | 70 - .../static/js/mediafiles/resources.js | 161 - .../mediafiles/static/js/mediafiles/site.js | 13 - .../mediafiles/static/js/mediafiles/states.js | 60 - .../mediafiles/static/js/mediafiles/update.js | 51 - .../mediafiles/static/js/mediafiles/upload.js | 154 - .../templates/mediafiles/image-plugin.html | 23 - .../templates/mediafiles/mediafile-form.html | 23 - .../templates/mediafiles/mediafile-list.html | 432 -- .../mediafiles/mediafile-upload-form.html | 97 - .../templates/mediafiles/slide_mediafile.html | 18 - .../mediafiles/slide_mediafile_partial.html | 1 - .../static/css/motions/_amendments.scss | 94 - .../_change-recommendation-overview.scss | 49 - .../motions/static/css/motions/_diff.scss | 71 - .../static/css/motions/_inline-editing.scss | 45 - .../static/css/motions/_line-numbering.scss | 82 - .../static/css/motions/_personal-note.scss | 25 - .../static/css/motions/_pollresults.scss | 28 - .../static/css/motions/_projector.scss | 69 - .../motions/static/css/motions/_sidebox.scss | 19 - .../motions/static/css/motions/_site.scss | 268 - openslides/motions/static/js/motions/base.js | 1646 ------ openslides/motions/static/js/motions/csv.js | 210 - openslides/motions/static/js/motions/diff.js | 1589 ----- openslides/motions/static/js/motions/docx.js | 221 - .../static/js/motions/linenumbering.js | 772 --- .../js/motions/motion-block-projector.js | 59 - .../motions/static/js/motions/motion-block.js | 262 - .../static/js/motions/motion-services.js | 698 --- openslides/motions/static/js/motions/pdf.js | 1447 ----- .../motions/static/js/motions/projector.js | 98 - openslides/motions/static/js/motions/site.js | 3326 ----------- .../motions/static/js/motions/workflow.js | 253 - .../amendment-paragraph-choose-form.html | 27 - .../templates/motions/category-form.html | 17 - .../templates/motions/category-list.html | 55 - .../templates/motions/category-sort.html | 33 - .../motions/change-recommendation-form.html | 17 - .../motions/motion-amendment-list.html | 414 -- .../motions/motion-block-detail.html | 76 - .../templates/motions/motion-block-form.html | 17 - .../templates/motions/motion-block-list.html | 52 - .../motions/motion-comment-form.html | 16 - .../templates/motions/motion-detail.html | 666 --- .../amendment-paragraph-diff.html | 43 - .../motions/motion-detail/change-summary.html | 55 - .../motions/motion-detail/comments.html | 45 - .../motions/motion-detail/personal-note.html | 45 - .../motion-detail/toolbar-line-numbering.html | 53 - .../motions/motion-detail/toolbar.html | 154 - .../motions/motion-detail/view-diff.html | 111 - .../motion-detail/view-modified-agreed.html | 28 - .../motions/motion-detail/view-original.html | 46 - .../templates/motions/motion-export-form.html | 14 - .../static/templates/motions/motion-form.html | 23 - .../templates/motions/motion-import.html | 143 - .../static/templates/motions/motion-list.html | 466 -- .../templates/motions/motion-poll-form.html | 22 - .../templates/motions/motion-submitters.html | 44 - .../motions/motion-table-filters.html | 357 -- .../templates/motions/slide_motion.html | 171 - .../templates/motions/slide_motion_block.html | 19 - .../static/templates/motions/state-edit.html | 28 - .../templates/motions/workflow-detail.html | 199 - .../templates/motions/workflow-edit.html | 29 - .../templates/motions/workflow-list.html | 46 - openslides/motions/urls.py | 10 - openslides/motions/views.py | 10 - openslides/old_urls.py | 37 - openslides/poll/static/js/poll/majority.js | 41 - openslides/topics/static/js/topics/base.js | 48 - openslides/topics/static/js/topics/csv.js | 32 - .../topics/static/js/topics/projector.js | 28 - openslides/topics/static/js/topics/site.js | 369 -- .../static/templates/topics/slide_topic.html | 4 - .../static/templates/topics/topic-detail.html | 40 - .../static/templates/topics/topic-form.html | 17 - .../static/templates/topics/topic-import.html | 134 - openslides/urls.py | 19 +- openslides/urls_apps.py | 2 - openslides/users/static/css/users/_site.scss | 18 - openslides/users/static/js/users/base.js | 178 - openslides/users/static/js/users/csv.js | 74 - openslides/users/static/js/users/pdf.js | 329 -- openslides/users/static/js/users/projector.js | 28 - openslides/users/static/js/users/site.js | 1898 ------ .../static/templates/users/group-edit.html | 29 - .../static/templates/users/group-list.html | 74 - .../users/profile-password-form.html | 16 - .../static/templates/users/slide_user.html | 3 - .../templates/users/user-change-password.html | 55 - .../static/templates/users/user-detail.html | 52 - .../static/templates/users/user-form.html | 17 - .../static/templates/users/user-import.html | 248 - .../static/templates/users/user-list.html | 487 -- .../static/templates/users/user-presence.html | 31 - openslides/utils/views.py | 58 +- package.json | 35 - tests/integration/core/test_views.py | 24 - tests/unit/utils/test_views.py | 14 - yarn.lock | 5135 ----------------- 222 files changed, 97 insertions(+), 42579 deletions(-) delete mode 100644 bower.json rename {openslides/agenda/static/templates => client/src/assets}/docx/agenda.docx (100%) rename {openslides/motions/static/templates => client/src/assets}/docx/motions.docx (100%) delete mode 100644 gulpfile.js delete mode 100644 openslides/agenda/static/css/agenda/_list_of_speakers.scss delete mode 100644 openslides/agenda/static/css/agenda/_projector.scss delete mode 100644 openslides/agenda/static/css/agenda/_site.scss delete mode 100644 openslides/agenda/static/js/agenda/base.js delete mode 100644 openslides/agenda/static/js/agenda/csv.js delete mode 100644 openslides/agenda/static/js/agenda/docx.js delete mode 100644 openslides/agenda/static/js/agenda/pdf.js delete mode 100644 openslides/agenda/static/js/agenda/projector.js delete mode 100644 openslides/agenda/static/js/agenda/site.js delete mode 100644 openslides/agenda/static/templates/agenda/current-list-of-speakers.html delete mode 100644 openslides/agenda/static/templates/agenda/item-detail.html delete mode 100644 openslides/agenda/static/templates/agenda/item-list.html delete mode 100644 openslides/agenda/static/templates/agenda/item-sort.html delete mode 100644 openslides/agenda/static/templates/agenda/list-of-speakers-partial-management.html delete mode 100644 openslides/agenda/static/templates/agenda/partial-slide-current-list-of-speakers-overlay.html delete mode 100644 openslides/agenda/static/templates/agenda/partial-slide-current-list-of-speakers.html delete mode 100644 openslides/agenda/static/templates/agenda/slide-current-list-of-speakers.html delete mode 100644 openslides/agenda/static/templates/agenda/slide-item-list.html delete mode 100644 openslides/agenda/static/templates/agenda/slide-list-of-speakers.html delete mode 100644 openslides/agenda/urls.py delete mode 100644 openslides/assignments/static/css/assignments/_projector.scss delete mode 100644 openslides/assignments/static/css/assignments/_site.scss delete mode 100644 openslides/assignments/static/js/assignments/base.js delete mode 100644 openslides/assignments/static/js/assignments/pdf.js delete mode 100644 openslides/assignments/static/js/assignments/projector.js delete mode 100644 openslides/assignments/static/js/assignments/site.js delete mode 100644 openslides/assignments/static/templates/assignments/assignment-detail.html delete mode 100644 openslides/assignments/static/templates/assignments/assignment-form.html delete mode 100644 openslides/assignments/static/templates/assignments/assignment-list.html delete mode 100644 openslides/assignments/static/templates/assignments/assignmentpoll-form.html delete mode 100644 openslides/assignments/static/templates/assignments/slide_assignment.html delete mode 100644 openslides/core/management/commands/collectstatic.py delete mode 100644 openslides/core/static/css/_fonts.scss delete mode 100644 openslides/core/static/css/_helper.scss delete mode 100644 openslides/core/static/css/_mediaqueries.scss delete mode 100644 openslides/core/static/css/_startup-spinner.scss delete mode 100644 openslides/core/static/css/_ui-override.scss delete mode 100644 openslides/core/static/css/_variables.scss delete mode 100644 openslides/core/static/css/core/_chatbox.scss delete mode 100644 openslides/core/static/css/core/_config.scss delete mode 100644 openslides/core/static/css/core/_countdown.scss delete mode 100644 openslides/core/static/css/core/_csv-import.scss delete mode 100644 openslides/core/static/css/core/_goto-top.scss delete mode 100644 openslides/core/static/css/core/_manage-projectors.scss delete mode 100644 openslides/core/static/css/core/_messaging.scss delete mode 100644 openslides/core/static/css/core/_multi-table.scss delete mode 100644 openslides/core/static/css/core/_os-table.scss delete mode 100644 openslides/core/static/css/core/_projector-container.scss delete mode 100644 openslides/core/static/css/core/_projector-sidebar.scss delete mode 100644 openslides/core/static/css/core/_projector.scss delete mode 100644 openslides/core/static/css/core/_search.scss delete mode 100644 openslides/core/static/css/core/_site.scss delete mode 100644 openslides/core/static/css/projector.scss delete mode 100644 openslides/core/static/css/site.scss delete mode 100644 openslides/core/static/img/favicon.png delete mode 100644 openslides/core/static/img/logo-projector.png delete mode 100644 openslides/core/static/img/nav_active.png delete mode 100644 openslides/core/static/img/nav_dark-bg.png delete mode 100644 openslides/core/static/img/nav_projector_sidebar_min.png delete mode 100644 openslides/core/static/img/openslides-logo-dark.png delete mode 100644 openslides/core/static/img/openslides-logo.png create mode 100644 openslides/core/static/index.html delete mode 100644 openslides/core/static/js/core/base.js delete mode 100644 openslides/core/static/js/core/csv.js delete mode 100644 openslides/core/static/js/core/docx.js delete mode 100644 openslides/core/static/js/core/pdf-worker.js delete mode 100644 openslides/core/static/js/core/pdf.js delete mode 100644 openslides/core/static/js/core/projector.js delete mode 100644 openslides/core/static/js/core/remove-format-plugin.js delete mode 100644 openslides/core/static/js/core/site.js delete mode 100644 openslides/core/static/js/core/start.js delete mode 100644 openslides/core/static/templates/config-form-field.html delete mode 100644 openslides/core/static/templates/config.html delete mode 100644 openslides/core/static/templates/core/checkbox-buttons.html delete mode 100644 openslides/core/static/templates/core/checkbox.html delete mode 100644 openslides/core/static/templates/core/countdown-detail.html delete mode 100644 openslides/core/static/templates/core/editor.html delete mode 100644 openslides/core/static/templates/core/file.html delete mode 100644 openslides/core/static/templates/core/login-form.html delete mode 100644 openslides/core/static/templates/core/manage-projectors.html delete mode 100644 openslides/core/static/templates/core/password.html delete mode 100644 openslides/core/static/templates/core/projector-controls.html delete mode 100644 openslides/core/static/templates/core/projector-message-form.html delete mode 100644 openslides/core/static/templates/core/radio-buttons.html delete mode 100644 openslides/core/static/templates/core/select-multiple.html delete mode 100644 openslides/core/static/templates/core/select-single.html delete mode 100644 openslides/core/static/templates/core/slide_clock.html delete mode 100644 openslides/core/static/templates/core/slide_countdown.html delete mode 100644 openslides/core/static/templates/core/slide_message.html delete mode 100644 openslides/core/static/templates/core/tag-form.html delete mode 100644 openslides/core/static/templates/core/tag-list.html delete mode 100644 openslides/core/static/templates/csv-import.html delete mode 100644 openslides/core/static/templates/home.html delete mode 100644 openslides/core/static/templates/index.html delete mode 100644 openslides/core/static/templates/legalnotice.html delete mode 100644 openslides/core/static/templates/messaging.html delete mode 100644 openslides/core/static/templates/privacypolicy.html delete mode 100644 openslides/core/static/templates/privacypolicydialog.html delete mode 100644 openslides/core/static/templates/projector-button.html delete mode 100644 openslides/core/static/templates/projector-container.html delete mode 100644 openslides/core/static/templates/projector.html delete mode 100644 openslides/core/static/templates/search.html delete mode 100644 openslides/mediafiles/static/css/mediafiles/_image-browser.scss delete mode 100644 openslides/mediafiles/static/css/mediafiles/_projector.scss delete mode 100644 openslides/mediafiles/static/css/mediafiles/_site.scss delete mode 100644 openslides/mediafiles/static/js/mediafiles/forms.js delete mode 100644 openslides/mediafiles/static/js/mediafiles/image-plugin.js delete mode 100644 openslides/mediafiles/static/js/mediafiles/list.js delete mode 100644 openslides/mediafiles/static/js/mediafiles/projector.js delete mode 100644 openslides/mediafiles/static/js/mediafiles/resources.js delete mode 100644 openslides/mediafiles/static/js/mediafiles/site.js delete mode 100644 openslides/mediafiles/static/js/mediafiles/states.js delete mode 100644 openslides/mediafiles/static/js/mediafiles/update.js delete mode 100644 openslides/mediafiles/static/js/mediafiles/upload.js delete mode 100644 openslides/mediafiles/static/templates/mediafiles/image-plugin.html delete mode 100644 openslides/mediafiles/static/templates/mediafiles/mediafile-form.html delete mode 100644 openslides/mediafiles/static/templates/mediafiles/mediafile-list.html delete mode 100644 openslides/mediafiles/static/templates/mediafiles/mediafile-upload-form.html delete mode 100644 openslides/mediafiles/static/templates/mediafiles/slide_mediafile.html delete mode 100644 openslides/mediafiles/static/templates/mediafiles/slide_mediafile_partial.html delete mode 100644 openslides/motions/static/css/motions/_amendments.scss delete mode 100644 openslides/motions/static/css/motions/_change-recommendation-overview.scss delete mode 100644 openslides/motions/static/css/motions/_diff.scss delete mode 100644 openslides/motions/static/css/motions/_inline-editing.scss delete mode 100644 openslides/motions/static/css/motions/_line-numbering.scss delete mode 100644 openslides/motions/static/css/motions/_personal-note.scss delete mode 100644 openslides/motions/static/css/motions/_pollresults.scss delete mode 100644 openslides/motions/static/css/motions/_projector.scss delete mode 100644 openslides/motions/static/css/motions/_sidebox.scss delete mode 100644 openslides/motions/static/css/motions/_site.scss delete mode 100644 openslides/motions/static/js/motions/base.js delete mode 100644 openslides/motions/static/js/motions/csv.js delete mode 100644 openslides/motions/static/js/motions/diff.js delete mode 100644 openslides/motions/static/js/motions/docx.js delete mode 100644 openslides/motions/static/js/motions/linenumbering.js delete mode 100644 openslides/motions/static/js/motions/motion-block-projector.js delete mode 100644 openslides/motions/static/js/motions/motion-block.js delete mode 100644 openslides/motions/static/js/motions/motion-services.js delete mode 100644 openslides/motions/static/js/motions/pdf.js delete mode 100644 openslides/motions/static/js/motions/projector.js delete mode 100644 openslides/motions/static/js/motions/site.js delete mode 100644 openslides/motions/static/js/motions/workflow.js delete mode 100644 openslides/motions/static/templates/motions/amendment-paragraph-choose-form.html delete mode 100644 openslides/motions/static/templates/motions/category-form.html delete mode 100644 openslides/motions/static/templates/motions/category-list.html delete mode 100644 openslides/motions/static/templates/motions/category-sort.html delete mode 100644 openslides/motions/static/templates/motions/change-recommendation-form.html delete mode 100644 openslides/motions/static/templates/motions/motion-amendment-list.html delete mode 100644 openslides/motions/static/templates/motions/motion-block-detail.html delete mode 100644 openslides/motions/static/templates/motions/motion-block-form.html delete mode 100644 openslides/motions/static/templates/motions/motion-block-list.html delete mode 100644 openslides/motions/static/templates/motions/motion-comment-form.html delete mode 100644 openslides/motions/static/templates/motions/motion-detail.html delete mode 100644 openslides/motions/static/templates/motions/motion-detail/amendment-paragraph-diff.html delete mode 100644 openslides/motions/static/templates/motions/motion-detail/change-summary.html delete mode 100644 openslides/motions/static/templates/motions/motion-detail/comments.html delete mode 100644 openslides/motions/static/templates/motions/motion-detail/personal-note.html delete mode 100644 openslides/motions/static/templates/motions/motion-detail/toolbar-line-numbering.html delete mode 100644 openslides/motions/static/templates/motions/motion-detail/toolbar.html delete mode 100644 openslides/motions/static/templates/motions/motion-detail/view-diff.html delete mode 100644 openslides/motions/static/templates/motions/motion-detail/view-modified-agreed.html delete mode 100644 openslides/motions/static/templates/motions/motion-detail/view-original.html delete mode 100644 openslides/motions/static/templates/motions/motion-export-form.html delete mode 100644 openslides/motions/static/templates/motions/motion-form.html delete mode 100644 openslides/motions/static/templates/motions/motion-import.html delete mode 100644 openslides/motions/static/templates/motions/motion-list.html delete mode 100644 openslides/motions/static/templates/motions/motion-poll-form.html delete mode 100644 openslides/motions/static/templates/motions/motion-submitters.html delete mode 100644 openslides/motions/static/templates/motions/motion-table-filters.html delete mode 100644 openslides/motions/static/templates/motions/slide_motion.html delete mode 100644 openslides/motions/static/templates/motions/slide_motion_block.html delete mode 100644 openslides/motions/static/templates/motions/state-edit.html delete mode 100644 openslides/motions/static/templates/motions/workflow-detail.html delete mode 100644 openslides/motions/static/templates/motions/workflow-edit.html delete mode 100644 openslides/motions/static/templates/motions/workflow-list.html delete mode 100644 openslides/motions/urls.py delete mode 100644 openslides/old_urls.py delete mode 100644 openslides/poll/static/js/poll/majority.js delete mode 100644 openslides/topics/static/js/topics/base.js delete mode 100644 openslides/topics/static/js/topics/csv.js delete mode 100644 openslides/topics/static/js/topics/projector.js delete mode 100644 openslides/topics/static/js/topics/site.js delete mode 100644 openslides/topics/static/templates/topics/slide_topic.html delete mode 100644 openslides/topics/static/templates/topics/topic-detail.html delete mode 100644 openslides/topics/static/templates/topics/topic-form.html delete mode 100644 openslides/topics/static/templates/topics/topic-import.html delete mode 100644 openslides/users/static/css/users/_site.scss delete mode 100644 openslides/users/static/js/users/base.js delete mode 100644 openslides/users/static/js/users/csv.js delete mode 100644 openslides/users/static/js/users/pdf.js delete mode 100644 openslides/users/static/js/users/projector.js delete mode 100644 openslides/users/static/js/users/site.js delete mode 100644 openslides/users/static/templates/users/group-edit.html delete mode 100644 openslides/users/static/templates/users/group-list.html delete mode 100644 openslides/users/static/templates/users/profile-password-form.html delete mode 100644 openslides/users/static/templates/users/slide_user.html delete mode 100644 openslides/users/static/templates/users/user-change-password.html delete mode 100644 openslides/users/static/templates/users/user-detail.html delete mode 100644 openslides/users/static/templates/users/user-form.html delete mode 100644 openslides/users/static/templates/users/user-import.html delete mode 100644 openslides/users/static/templates/users/user-list.html delete mode 100644 openslides/users/static/templates/users/user-presence.html delete mode 100644 package.json delete mode 100644 yarn.lock diff --git a/.travis.yml b/.travis.yml index bb0051e51..74cd05e18 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ matrix: - flake8 openslides tests - isort --check-only --diff --recursive openslides tests - python -m mypy openslides/ - - python -W ignore -m pytest --cov --cov-fail-under=75 + - python -W ignore -m pytest --cov --cov-fail-under=70 - language: python cache: @@ -35,7 +35,7 @@ matrix: - flake8 openslides tests - isort --check-only --diff --recursive openslides tests - python -m mypy openslides/ - - python -W ignore -m pytest --cov --cov-fail-under=75 + - python -W ignore -m pytest --cov --cov-fail-under=70 - language: node_js node_js: diff --git a/DEVELOPMENT.rst b/DEVELOPMENT.rst index 1f7f97a54..b41d15d11 100644 --- a/DEVELOPMENT.rst +++ b/DEVELOPMENT.rst @@ -43,31 +43,18 @@ See step 1. b. in the installation section in the `README.rst `_. -d. Install dependencies -''''''''''''''''''''''' +d. Finish the server +'''''''''''''''''''' Install all required Python packages:: $ pip install --requirement requirements.txt -Install all Node.js and Bower packages and run several JavaScript build tasks:: +Create a settings file, run migrations and start the server:: - $ yarn - -Optional: To enhance performance run Gulp in production mode:: - - $ node_modules/.bin/gulp --production - - -e. Start OpenSlides -''''''''''''''''''' - -Use the command-line interface:: - - $ python manage.py start - -See step 1. d. in the installation section in the `README.rst -`_. + $ python manage.py createsettings + $ python manage.py migrate + $ python manage.py runserver To get help on the command line options run:: @@ -84,13 +71,25 @@ When debugging something email related change the email backend to console:: $ python manage.py start --debug-email -To start OpenSlides with Daphne run:: +e. Setup and start the client +''''''''''''''''''''''''''''' - $ python manage.py runserver +Go in the client's directory in a second command-line interface:: -Use gulp watch in a second command-line interface:: + $ cd client/ - $ node_modules/.bin/gulp watch +Install all dependencies and start the development server:: + + $ npm install + $ yarn start + +Now the client is available under ``localhost:4200``. + +If you do not need to work with the client, you can build the client and let it be delivered by the server directly:: + + $ yarn build + +The client's address is now ``localhost:8000``. 2. Installation on Windows @@ -127,16 +126,20 @@ To run some server tests see `.travis.yml `_. -b. Running AngularJS test cases -''''''''''''''''''''''''''''''' +b. Client tests and commands +'''''''''''''''''''''''''''' -Run client tests by starting karma:: +Change to the client's directory to run every client related command. Run client tests:: - $ yarn run karma + $ yarn test -Watch for file changes and run the tests automatically after each change:: +Fix the code format and lint it with:: - $ yarn run karma:watch + $ yarn pretty-quick && yarn lint + +To extract translations run:: + + $ yarn extract OpenSlides in big mode ====================== @@ -207,23 +210,7 @@ This is an example configuration for a single Daphne listen on port 8000:: server_name _; - location ~* ^/projector.*$ { - rewrite ^.*$ /static/templates/projector-container.html; - } - location ~* ^/real-projector.*$ { - rewrite ^.*$ /static/templates/projector.html; - } - location ~* ^/webclient.*$ { - rewrite ^/webclient/(site|projector).*$ /static/js/webclient-$1.js; - } - location /static { - alias /collected-static; - } - location ~* ^/(?!ws|wss|media|rest|views).*$ { - rewrite ^.*$ /static/templates/index.html; - } - - location / { + location ~* ^/(ws|wss|media|rest|apps).*$ { proxy_pass http://localhost:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; @@ -232,4 +219,7 @@ This is an example configuration for a single Daphne listen on port 8000:: proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Scheme $scheme; } + location / { + alias /collected-static; + } } diff --git a/README.rst b/README.rst index e75215773..776409bcb 100644 --- a/README.rst +++ b/README.rst @@ -165,53 +165,7 @@ OpenSlides uses the following projects or parts of them: * Several Python packages (see ``requirements/production.txt`` and ``requirements/big_mode.txt``). -* Several JavaScript packages (see ``bower.json``) - - * `angular `_, License: MIT - * `angular-animate `_, License: MIT - * `angular-bootstrap-colorpicker `_, License: MIT - * `angular-chosen-localytics `_, License: MIT - * `angular-ckeditor `_, License: MIT - * `angular-file-saver `_, License: MIT - * `angular-formly `_, License: MIT - * `angular-formly-templates-bootstrap `_, License: MIT - * `angular-gettext `_, License: MIT - * `angular-messages `_, License: MIT - * `angular-pdf `_, License: MIT - * `angular-sanitize `_, License: MIT - * `angular-scroll-glue `_, License: MIT - * `angular-ui-bootstrap `_, License: MIT - * `angular-ui-router `_, License: MIT - * `angular-ui-router-title `_, License: MIT - * `angular-ui-tree `_, License: MIT - * `angular-xeditable `_, License: MIT - * `angularjs-scroll-glue `_, License: MIT - * `angularjs-slider `_, License: MIT - * `api-check `_, License: MIT - * `blob-polyfill `_, License: MIT - * `bootstrap `_, License: MIT - * `bootstrap `_, License: MIT - * `bootstrap-css-only `_, License: MIT - * `bootstrap-ui-datetime-picker `_, License: MIT - * `chosen `_, License: MIT - * `ckeditor `_, License: (GPL-2.0 OR LGPL-2.1 OR MPL-1.1) - * `docxtemplater `_, License: MIT - * `file-saver.js `_, License: LICENSE.md - * `font-awesome-bower `_, License: MIT - * `jquery `_, License: MIT - * `jquery.cookie `_, License: MIT - * `js-data `_, License: MIT - * `js-data-angular `_, License: MIT - * `jszip `_, License: MIT or GPLv3 - * `lodash `_, License: MIT - * `ng-dialog `_, License: MIT - * `ng-file-upload `_, License: MIT - * `ngbootbox `_, License: MIT - * `ngStorage `_, License: MIT - * `papaparse `_, License: MIT - * `pdfjs-dist `_, License: Apache-2.0 - * `pdfmake `_, License: MIT - * `roboto-fontface `_, License: Apache-2.0 +* Several JavaScript packages (see ``client/package.json``) License and authors diff --git a/bower.json b/bower.json deleted file mode 100644 index ccb38d1a6..000000000 --- a/bower.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "name": "openslides", - "private": true, - "dependencies": { - "bootstrap": "~3.3.7", - "jquery": "~3.1.0", - "angular": "~1.5.8", - "angular-animate": "~1.5.8", - "angular-bootstrap": "~2.1.3", - "angular-bootstrap-colorpicker": "~3.0.25", - "angular-chosen-localytics": "~1.5.0", - "angular-ckeditor": "~1.0.3", - "angular-file-saver": "~1.1.2", - "angular-formly": "~8.4.0", - "angular-formly-templates-bootstrap": "~6.2.0", - "angular-gettext": "~2.3.7", - "angular-messages": "~1.5.8", - "angular-pdf": "1.3.0", - "angular-sanitize": "~1.5.8", - "angular-scroll-glue": "~2.0.7", - "angular-ui-router": "~0.3.1", - "angular-ui-tree": "https://github.com/FinnStutzenstein/angular-ui-tree.git#94dbaaff6b36f9d7a514843b73c28d2a3684c0c0", - "angular-xeditable": "~0.5.0", - "angularjs-slider": "~6.2.2", - "bootstrap-css-only": "~3.3.6", - "bootstrap-ui-datetime-picker": "~2.4.0", - "ckeditor": "~4.7.2", - "docxtemplater": "~2.1.5", - "font-awesome-bower": "~4.7.0", - "jquery.cookie": "~1.4.1", - "js-data": "~2.9.0", - "js-data-angular": "~3.2.1", - "jszip": "~3.1.3", - "lodash": "~4.16.0", - "ng-dialog": "~0.6.4", - "ng-file-upload": "~11.2.3", - "ngstorage": "~0.3.11", - "ngBootbox": "~0.1.3", - "papaparse": "~4.1.2", - "pdfmake": "0.1.37", - "roboto-fontface": "~0.6.0" - }, - "overrides": { - "pdfmake": { - "main": [] - }, - "pdfjs-dist": { - "main": "build/pdf.combined.js" - }, - "roboto-fontface": { - "main": [ - "fonts/Roboto/Roboto-Regular.woff", - "fonts/Roboto/Roboto-Medium.woff", - "fonts/Roboto-Condensed/Roboto-Condensed-Regular.woff", - "fonts/Roboto-Condensed/Roboto-Condensed-Light.woff" - ] - } - }, - "resolutions": { - "angular": ">=1.5 <1.6", - "jquery": ">=3.1 <3.2", - "angular-bootstrap": "~2.1.3" - } -} diff --git a/client/angular.json b/client/angular.json index fbe7a6140..ad17a72eb 100644 --- a/client/angular.json +++ b/client/angular.json @@ -17,7 +17,7 @@ "build": { "builder": "@angular-devkit/build-angular:browser", "options": { - "outputPath": "dist/client", + "outputPath": "../openslides/static", "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", diff --git a/client/package-lock.json b/client/package-lock.json index 511bbe3b2..2a79ae86c 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -5307,13 +5307,15 @@ "version": "1.0.0", "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5330,19 +5332,22 @@ "version": "1.1.0", "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5473,7 +5478,8 @@ "version": "2.0.3", "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5487,6 +5493,7 @@ "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5503,6 +5510,7 @@ "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5511,13 +5519,15 @@ "version": "0.0.8", "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "resolved": false, "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -5538,6 +5548,7 @@ "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5626,7 +5637,8 @@ "version": "1.0.1", "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5640,6 +5652,7 @@ "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5777,6 +5790,7 @@ "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", diff --git a/client/package.json b/client/package.json index 906b8859c..01c170fa7 100644 --- a/client/package.json +++ b/client/package.json @@ -10,8 +10,7 @@ "e2e": "ng e2e", "compodoc": "./node_modules/.bin/compodoc --hideGenerator -p src/tsconfig.app.json -n 'OpenSlides Documentation' -d ../Compodoc -s -w -t -o --port", "extract": "ngx-translate-extract -i ./src -o ./src/assets/i18n/{en,de,fr}.json --clean --sort --format-indentation ' ' --format namespaced-json", - "format:fix": "pretty-quick --staged", - "precommit": "run-s format:fix lint" + "format:fix": "pretty-quick --staged" }, "private": true, "dependencies": { diff --git a/openslides/agenda/static/templates/docx/agenda.docx b/client/src/assets/docx/agenda.docx similarity index 100% rename from openslides/agenda/static/templates/docx/agenda.docx rename to client/src/assets/docx/agenda.docx diff --git a/openslides/motions/static/templates/docx/motions.docx b/client/src/assets/docx/motions.docx similarity index 100% rename from openslides/motions/static/templates/docx/motions.docx rename to client/src/assets/docx/motions.docx diff --git a/gulpfile.js b/gulpfile.js deleted file mode 100644 index 0d1d43a25..000000000 --- a/gulpfile.js +++ /dev/null @@ -1,362 +0,0 @@ -/** - * Gulp tasks for development and production. - * - * Run - * - * $ ./node_modules/.bin/gulp - * - * for development and - * - * $ ./node_modules/.bin/gulp --production - * - * for production mode. - */ - -var argv = require('yargs').argv, - gulp = require('gulp'), - concat = require('gulp-concat'), - cssnano = require('gulp-cssnano'), - gulpif = require('gulp-if'), - gettext = require('gulp-angular-gettext'), - inject = require('gulp-inject-string'), - jshint = require('gulp-jshint'), - mainBowerFiles = require('main-bower-files'), - path = require('path'), - rename = require('gulp-rename'), - sass = require('gulp-sass'), - sourcemaps = require('gulp-sourcemaps'), - templateCache = require('gulp-angular-templatecache'), - through = require('through2'), - uglify = require('gulp-uglify'), - vsprintf = require('sprintf-js').vsprintf; - - -// Directory where the results go to -var output_directory = path.join('openslides', 'static'); - - -// Container for all watchers -var watchers = []; - - -/** - * Default tasks to be run before start. - */ - -// Catches all JavaScript files (excluded worker files) from all core apps and concats them to one -// file js/openslides.js. In production mode the file is uglified. -var js_src = [ - path.join('openslides', '*', 'static', 'js', '**', '*.js'), - '!' + path.join('openslides', 'core', 'static', 'js', 'core', 'pdf-worker.js'), -]; - -gulp.task('js', function () { - return gulp.src(js_src) - .pipe(sourcemaps.init()) - .pipe(concat('openslides.js')) - .pipe(sourcemaps.write()) - //TODO: Needs rework in all js files that uglified code works correctly. - //.pipe(gulpif(argv.production, uglify())) - .pipe(gulp.dest(path.join(output_directory, 'js'))); -}); - -watchers.push(function () { - gulp.watch(js_src, gulp.series('js')); -}); - - -// Catches all JavaScript files from all bower components and concats them to -// one file js/openslides-libs.js. In production mode the file is uglified. -gulp.task('js_libs', function () { - return gulp.src(mainBowerFiles({ - filter: /\.js$/ - })) - .pipe(sourcemaps.init()) - .pipe(concat('openslides-libs.js')) - .pipe(sourcemaps.write()) - .pipe(inject.prepend("/* set basepath of CKEditor */\n" + - "window.CKEDITOR_BASEPATH = '/static/ckeditor/';\n\n")) - .pipe(inject.prepend("/* Workaround for IE and pdfjs-dist#1.3.100 (see PR#3714) */\n" + - "PDFJS = {workerSrc: 'not used but set'};\n\n")) - .pipe(gulpif(argv.production, uglify())) - .pipe(gulp.dest(path.join(output_directory, 'js'))); -}); - - -// Catches all pdfmake files for pdf worker and pdfmake library. -var pdf_worker_src = path.join('openslides', 'core', 'static', 'js', 'core', 'pdf-worker.js'); - -gulp.task('pdf_worker', function () { - return gulp.src(pdf_worker_src) - .pipe(gulpif(argv.production, uglify())) - .pipe(gulp.dest(path.join(output_directory, 'js', 'workers'))); -}); - -gulp.task('pdf_worker_libs', function () { - return gulp.src(path.join('bower_components', 'pdfmake', 'build', 'pdfmake.min.js')) - .pipe(gulpif(argv.production, uglify())) - .pipe(rename('pdf-worker-libs.js')) - .pipe(gulp.dest(path.join(output_directory, 'js', 'workers'))); -}); - -watchers.push(function () { - gulp.watch(pdf_worker_src, gulp.series('pdf_worker')); -}); - - -// Catches all template files from all core apps and concats them to one -// file js/openslides-templates.js. In production mode the file is uglified. -var templates_src = path.join('openslides', '*', 'static', 'templates', '**', '*.html'); - -gulp.task('templates', function () { - return gulp.src(templates_src) - .pipe(templateCache('openslides-templates.js', { - module: 'OpenSlidesApp-templates', - standalone: true, - moduleSystem: 'IIFE', - transformUrl: function (url) { - var pathList = url.split(path.sep); - pathList.shift(); - return pathList.join(path.sep); - }, - })) - .pipe(gulpif(argv.production, uglify())) - .pipe(gulp.dest(path.join(output_directory, 'js'))); -}); - -watchers.push(function () { - gulp.watch(templates_src, gulp.series('templates')); -}); - - -// Build the openslides-site.css file from the main file core/static/css/site.scss. -// Minimizes the outputfile if the production flag is given. -gulp.task('css_site', function () { - return gulp.src(path.join('openslides', 'core', 'static', 'css', 'site.scss')) - .pipe(sass().on('error', sass.logError)) - .pipe(gulpif(argv.production, cssnano({safe: true}))) - .pipe(rename('openslides-site.css')) - .pipe(gulp.dest(path.join(output_directory, 'css'))); -}); - - -// Build the openslides-projector.css file from the main file core/static/css/projector.scss. -// Minimizes the outputfile if the production flag is given. -gulp.task('css_projector', function () { - return gulp.src(path.join('openslides', 'core', 'static', 'css', 'projector.scss')) - .pipe(sass().on('error', sass.logError)) - .pipe(gulpif(argv.production, cssnano({safe: true}))) - .pipe(rename('openslides-projector.css')) - .pipe(gulp.dest(path.join(output_directory, 'css'))); -}); - - -// Watcher for scss files. -// We cannot differentiate between all scss files which belong to each realm. So if -// one scss file changes the site and projector css is rebuild. -watchers.push(function () { - gulp.watch(path.join('openslides', '*', 'static', 'css', '**', '*.scss'), gulp.parallel('css_site', 'css_projector')); -}); - - -// Catches all CSS files from all bower components and concats them to one file -// css/openslides-libs.css. In production mode the file is uglified. -gulp.task('css_libs', function () { - return gulp.src(mainBowerFiles({ - filter: /\.css$/ - })) - .pipe(concat('openslides-libs.css')) - .pipe(gulpif(argv.production, cssnano({safe: true}))) - .pipe(gulp.dest(path.join(output_directory, 'css'))); -}); - - -// Catches all font files from all bower components. -gulp.task('fonts_libs', function () { - return gulp.src(mainBowerFiles({ - filter: /\.(woff)|(woff2)$/ - })) - .pipe(gulp.dest(path.join(output_directory, 'fonts'))); -}); - - -// Catches image files for angular-chosen. -gulp.task('angular_chosen_img', function () { - return gulp.src(path.join('bower_components', 'chosen', '*.png')) - .pipe(gulp.dest(path.join(output_directory, 'css'))); -}); - - -// Tasks for CKEditor -gulp.task('ckeditor_defaults', function () { - return gulp.src([ - path.join('bower_components', 'ckeditor', 'styles.js'), - path.join('bower_components', 'ckeditor', 'contents.css'), - ]) - .pipe(gulp.dest(path.join(output_directory, 'ckeditor'))); -}); - -gulp.task('ckeditor_skins', function () { - return gulp.src( - [ - path.join('bower_components', 'ckeditor', 'skins', 'moono-lisa', '**', '*'), - ], - { - base: path.join('bower_components', 'ckeditor', 'skins') - } - ) - .pipe(gulp.dest(path.join(output_directory, 'ckeditor', 'skins'))); -}); - -gulp.task('ckeditor_plugins', function () { - return gulp.src( - [ - path.join('bower_components', 'ckeditor', 'plugins', 'clipboard', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'colorbutton', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'colordialog', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'dialog', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'find', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'image', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'justify', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'link', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'liststyle', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'magicline', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'pastefromword', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'panelbutton', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'showblocks', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'specialchar', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'sourcedialog', '**', '*'), - path.join('bower_components', 'ckeditor', 'plugins', 'table', '**', '*'), - ], - { - base: path.join('bower_components', 'ckeditor', 'plugins') - } - ) - .pipe(gulp.dest(path.join(output_directory, 'ckeditor', 'plugins'))); -}); - -gulp.task('ckeditor_lang', function () { - return gulp.src([ - path.join('bower_components', 'ckeditor', 'lang', 'en.js'), - path.join('bower_components', 'ckeditor', 'lang', 'de.js'), - path.join('bower_components', 'ckeditor', 'lang', 'pt.js'), - path.join('bower_components', 'ckeditor', 'lang', 'es.js'), - path.join('bower_components', 'ckeditor', 'lang', 'fr.js'), - path.join('bower_components', 'ckeditor', 'lang', 'cs.js'), - path.join('bower_components', 'ckeditor', 'lang', 'ru.js'), - ]) - .pipe(gulp.dest(path.join(output_directory, 'ckeditor', 'lang'))); -}); - -gulp.task('ckeditor', gulp.parallel('ckeditor_defaults', 'ckeditor_skins', 'ckeditor_plugins', 'ckeditor_lang')); - - -// Compiles translation files (*.po) to *.json and saves them in the directory -// openslides/static/i18n/. -gulp.task('translations', function () { - return gulp.src(path.join('openslides', 'locale', 'angular-gettext', '*.po')) - .pipe(gettext.compile({ - format: 'json' - })) - .pipe(gulp.dest(path.join(output_directory, 'i18n'))); -}); - - -// Gulp default task. Runs all other tasks before. -gulp.task('default', gulp.parallel( - 'js', - 'js_libs', - 'pdf_worker', - 'pdf_worker_libs', - 'templates', - 'css_site', - 'css_projector', - 'css_libs', - 'fonts_libs', - 'angular_chosen_img', - 'ckeditor', - 'translations' -)); - - -/** - * Extra tasks that have to be called manually. Useful for development. - */ - -// Watches changes in JavaScript and templates. -gulp.task('watching', function () { - // This tasks never completes because it starts all watchers and let them - // watch forever ... - for (var i = 0; i < watchers.length; i++) { - watchers[i](); - } -}); - -gulp.task('watch', gulp.series(gulp.parallel('js', 'pdf_worker', 'templates', 'css_site', 'css_projector'), 'watching')); - - -// Extracts translatable strings using angular-gettext and saves them in file -// openslides/locale/angular-gettext/template-en.pot. -gulp.task('pot', function () { - return gulp.src([ - templates_src, - path.join('openslides', '*', 'static', 'js', '**', '*.js'), - ]) - .pipe(gettext.extract('template-en.pot', {})) - .pipe(gulp.dest('openslides/locale/angular-gettext/')); -}); - - -// Checks JavaScript using JSHint -gulp.task('jshint', function () { - return gulp.src([ - 'gulpfile.js', - path.join('openslides', '*', 'static', 'js', '**', '*.js'), - ]) - .pipe(jshint()) - .pipe(jshint.reporter('default')) - .pipe(jshint.reporter('fail')); -}); - - -// Extracts names, URLs and licensed of all uses bower components and prints -// it to the console. This is useful to update the README.rst during release -// process. -gulp.task('bower-components-for-readme', function () { - var files = []; - return gulp.src([ - path.join('bower_components', '*', 'bower.json'), - path.join('bower_components', '*', 'package.json'), - path.join('bower_components', '*', 'component.json'), - ]) - .pipe( - through.obj( - function (chunk, encoding, callback) { - files.push(chunk); - callback(); - }, - function (callback) { - // Extract JSON from bower.json or components.json file. - var extracted = []; - for (var index = 0; index < files.length; index++) { - extracted.push(JSON.parse(files[index].contents.toString())); - } - // Sort files. - extracted.sort(function (a, b) { - return a.name < b.name ? -1 : 1; - }); - // Print out line for README.rst. - for (var index2 = 0; index2 < extracted.length; index2++) { - var data = [ - extracted[index2].name, - extracted[index2].homepage, - extracted[index2].license, - ]; - console.log(vsprintf(' * `%s <%s>`_, License: %s', data)); - } - // End stream without further file processing. - callback(); - } - ) - ); -}); diff --git a/openslides/agenda/static/css/agenda/_list_of_speakers.scss b/openslides/agenda/static/css/agenda/_list_of_speakers.scss deleted file mode 100644 index 2661ed101..000000000 --- a/openslides/agenda/static/css/agenda/_list_of_speakers.scss +++ /dev/null @@ -1,21 +0,0 @@ -/* List of speakers */ -.lastSpeakers { - color: #9a9898; - margin-left: 27px; -} - -.currentSpeaker { - font-weight: bold; - - i.fa-microphone { - padding-right: 5px; - } -} - -.nextSpeakers { - margin-left: 13px; - - li { - line-height: 150%; - } -} diff --git a/openslides/agenda/static/css/agenda/_projector.scss b/openslides/agenda/static/css/agenda/_projector.scss deleted file mode 100644 index 815771cf5..000000000 --- a/openslides/agenda/static/css/agenda/_projector.scss +++ /dev/null @@ -1,22 +0,0 @@ -@import 'list_of_speakers'; - -/* Agenda list */ -.agendalist-table { - td { - vertical-align: top; - padding-left: 15px; - } - - td.number { - padding-left: 0; - white-space: nowrap; - } - - p.mainitem { - font-size: 140%; - } - - p.subitem { - font-size: 100%; - } -} diff --git a/openslides/agenda/static/css/agenda/_site.scss b/openslides/agenda/static/css/agenda/_site.scss deleted file mode 100644 index f5fb60b1b..000000000 --- a/openslides/agenda/static/css/agenda/_site.scss +++ /dev/null @@ -1,37 +0,0 @@ -@import 'list_of_speakers'; - -#agenda-table { - .icon-column { - padding: 3px; - width: 5%; - } - - .title-column { - padding-left: 3px; - padding-right: 10px; - width: calc(95% - 15px ); - } - - .caret-spacer { - width: 15px; - padding: 3px; - color: #337ab7; - font-size: 115%; - } -} - -.agenda-sort { - .internal { - padding: 7px; - opacity: 0.6; - } - .angular-ui-tree-node { - min-height: 0; - } -} - -div.speakers-toolbar { - margin: -20px -20px 30px -20px; - padding: 12px 15px 10px 15px; - -} diff --git a/openslides/agenda/static/js/agenda/base.js b/openslides/agenda/static/js/agenda/base.js deleted file mode 100644 index d2ef7c990..000000000 --- a/openslides/agenda/static/js/agenda/base.js +++ /dev/null @@ -1,441 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.agenda', ['OpenSlidesApp.users']) - -.factory('Speaker', [ - 'DS', - function(DS) { - return DS.defineResource({ - name: 'agenda/speaker', - relations: { - belongsTo: { - 'users/user': { - localField: 'user', - localKey: 'user_id', - } - } - } - }); - } -]) - -.factory('Agenda', [ - '$http', - 'DS', - 'Speaker', - 'jsDataModel', - 'Projector', - 'ProjectHelper', - 'gettextCatalog', - 'gettext', - 'CamelCase', - 'EditForm', - function($http, DS, Speaker, jsDataModel, Projector, ProjectHelper, gettextCatalog, - gettext, CamelCase, EditForm) { - var name = 'agenda/item'; - return DS.defineResource({ - name: name, - useClass: jsDataModel, - verboseName: gettext('Agenda'), - computed: { - is_public: function () { - return !this.is_internal && !this.is_hidden; - }, - }, - methods: { - getResourceName: function () { - return name; - }, - getContentObject: function () { - return DS.get(this.content_object.collection, this.content_object.id); - }, - getContentObjectDetailState: function () { - return CamelCase(this.content_object.collection).replace('/', '.') + - '.detail({id: ' + this.content_object.id + '})'; - }, - getContentObjectForm: function () { - return EditForm.fromCollectionString(this.content_object.collection); - }, - getContentResource: function () { - return DS.definitions[this.content_object.collection]; - }, - getTitle: function () { - var title; - try { - title = this.getContentObject().getAgendaTitle(); - } catch (e) { - // when the content object is not in the DS store. - title = this.title; - } - if (this.item_number) { - title = this.item_number + ' · ' + title; - } - return title; - }, - getListOfSpeakersTitle: function () { - var title; - try { - title = this.getContentObject().getListOfSpeakersTitle(); - if (this.item_number) { - title = this.item_number + ' · ' + title; - } - } catch (e) { - title = this.getTitle(); - } - return title; - }, - getAgendaTitle: function () { - return this.title; - }, - getCSVExportText: function () { - var text; - try { - text = this.getContentObject().getCSVExportText(); - } catch (e) { - // when the content object is not in the DS store - // or 'getCSVExportText' is not defined return nothing. - } - return text; - }, - // link name which is shown in search result - getSearchResultName: function () { - return this.getAgendaTitle(); - }, - // return true if a specific relation matches for given searchquery - // (here: speakers) - hasSearchResult: function (results) { - var item = this; - // search for speakers (check if any user.id from already found users matches) - return _.some(results, function(result) { - if (result.getResourceName() === "users/user") { - if (_.some(item.speakers, {'user_id': result.id})) { - return true; - } - } - }); - }, - getProjectorTitle: function () { - try { - return this.getContentObject().getAgendaListViewTitle(); - } catch (e) { - // when the content object is not in the DS store - return this.list_view_title; - } - }, - getListViewTitle: function () { - var title = this.getProjectorTitle(); - if (this.item_number) { - title = this.item_number + ' · ' + title; - } - return title; - }, - getItemNumberWithAncestors: function (agendaId) { - if (!agendaId) { - agendaId = this.id; - } - var agendaItem = DS.get(name, agendaId); - if (!agendaItem) { - return ''; - } else if (agendaItem.item_number) { - return agendaItem.item_number; - } else if (agendaItem.parent_id) { - return this.getItemNumberWithAncestors(agendaItem.parent_id); - } else { - return ''; - } - }, - // override project function of jsDataModel factory - project: function (projectorId, tree) { - if (tree) { - var isProjectedIds = this.isProjected(tree); - var requestData = { - clear_ids: isProjectedIds, - }; - // Activate, if the projector_id is a new projector. - if (_.indexOf(isProjectedIds, projectorId) == -1) { - requestData.prune = { - id: projectorId, - element: { - name: 'agenda/item-list', - tree: true, - id: this.id, - }, - }; - } - return ProjectHelper.project(requestData); - } else { // Project the content object - var contentObject = DS.get(this.content_object.collection, this.content_object.id); - return contentObject.project(projectorId); - } - }, - // override isProjected function of jsDataModel factory - isProjected: function (tree) { - // Returns all ids of all projectors with an agenda-item element. Otherwise an empty list. - if (typeof tree === 'undefined') { - tree = false; - } - var self = this; - var predicate = function (element) { - var value; - if (tree) { - // Item tree slide for sub tree - value = element.name == 'agenda/item-list' && - typeof element.id !== 'undefined' && - element.id == self.id; - } else { - // Releated item detail slide - value = element.name == self.content_object.collection && - typeof element.id !== 'undefined' && - element.id == self.content_object.id; - } - return value; - }; - var isProjectedIds = []; - Projector.getAll().forEach(function (projector) { - if (typeof _.findKey(projector.elements, predicate) === 'string') { - isProjectedIds.push(projector.id); - } - }); - return isProjectedIds; - }, - isRelatedProjected: function () { - // related objects for agenda items: list of speakers slide. - return this.isListOfSpeakersProjected(); - }, - // project list of speakers - projectListOfSpeakers: function(projectorId) { - var isProjectedIds = this.isListOfSpeakersProjected(); - var requestData = { - clear_ids: isProjectedIds, - }; - if (_.indexOf(isProjectedIds, projectorId) == -1) { - requestData.prune = { - id: projectorId, - element: { - name: 'agenda/list-of-speakers', - id: this.id, - }, - }; - } - return ProjectHelper.project(requestData); - }, - // check if list of speakers is projected - isListOfSpeakersProjected: function () { - // Returns all ids of all projectors with an element with the - // name 'agenda/list-of-speakers' and the same id. Else returns an empty list. - var self = this; - var predicate = function (element) { - return element.name == 'agenda/list-of-speakers' && - typeof element.id !== 'undefined' && - element.id == self.id; - }; - var isProjecteds = []; - Projector.getAll().forEach(function (projector) { - if (typeof _.findKey(projector.elements, predicate) === 'string') { - isProjecteds.push(projector.id); - } - }); - return isProjecteds; - }, - hasSubitems: function(items) { - var self = this; - var hasChild = false; - // Returns true if the item has at least one child item. - _.each(items, function (item) { - if (item.parent_id == self.id) { - hasChild = true; - } - }); - return hasChild; - } - }, - relations: { - hasMany: { - 'core/tag': { - localField: 'tags', - localKeys: 'tags_id', - }, - 'agenda/speaker': { - localField: 'speakers', - foreignKey: 'item_id', - } - } - }, - beforeInject: function (resource, instance) { - Speaker.ejectAll({where: {item_id: {'==': instance.id}}}); - } - }); - } -]) - -.factory('AgendaTree', [ - function () { - return { - getTree: function (items) { - // Sort items after there weight - items.sort(function(itemA, itemB) { - return itemA.weight - itemB.weight; - }); - - // Build a dict with all children (dict-value) to a specific - // item id (dict-key). - var itemChildren = {}; - - _.each(items, function (item) { - if (item.parent_id) { - // Add item to his parent. If it is the first child, then - // create a new list. - try { - itemChildren[item.parent_id].push(item); - } catch (error) { - itemChildren[item.parent_id] = [item]; - } - } - - }); - - // Recursive function that generates a nested list with all - // items with there children - function getChildren(items) { - var returnItems = []; - _.each(items, function (item) { - returnItems.push({ - item: item, - children: getChildren(itemChildren[item.id]), - id: item.id, - }); - }); - return returnItems; - } - - // Generates the list of root items (with no parents) - var parentItems = items.filter(function (item) { - return !item.parent_id; - }); - return getChildren(parentItems); - }, - - // Returns a list of all items as a flat tree - getFlatTree: function(items) { - var tree = this.getTree(items); - var flatItems = []; - - function generateFlatTree(tree, parentCount) { - _.each(tree, function (item) { - item.item.childrenCount = item.children.length; - item.item.parentCount = parentCount; - flatItems.push(item.item); - generateFlatTree(item.children, parentCount + 1); - }); - } - generateFlatTree(tree, 0); - return flatItems; - } - }; - } -]) - -.factory('CurrentListOfSpeakersItem', [ - 'Projector', - 'Agenda', - function (Projector, Agenda) { - return { - getItem: function (projectorId) { - var projector = Projector.get(projectorId), item; - if (projector) { - _.forEach(projector.elements, function(element) { - if (element.agenda_item_id) { - item = Agenda.get(element.agenda_item_id); - } - }); - } - return item; - } - }; - } -]) - -.factory('CurrentListOfSpeakersSlide', [ - '$http', - 'Projector', - function($http, Projector) { - var name = 'agenda/current-list-of-speakers'; - return { - project: function (projectorId, overlay) { - var isProjected = this.isProjectedWithOverlayStatus(); - _.forEach(isProjected, function (mapping) { - $http.post('/rest/core/projector/' + mapping.projectorId + '/deactivate_elements/', - [mapping.uuid] - ); - }); - - // The slide was projected, if the id matches. If the overlay is given, also - // the overlay is checked - var wasProjectedBefore = _.some(isProjected, function (mapping) { - var value = (mapping.projectorId === projectorId); - if (overlay !== undefined) { - value = value && (mapping.overlay === overlay); - } - return value; - }); - overlay = overlay || false; // set overlay if it wasn't defined - - if (!wasProjectedBefore) { - var activate = function () { - return $http.post( - '/rest/core/projector/' + projectorId + '/activate_elements/', - [{name: name, - stable: overlay, // if this is an overlay, it should not be - // removed by changing the main content - overlay: overlay}] - ); - }; - if (!overlay) { - // clear all elements on this projector, because we are _not_ using the overlay. - $http.post('/rest/core/projector/' + projectorId + '/clear_elements/').then(activate); - } else { - activate(); - } - } - }, - isProjected: function () { - // Returns the ids of all projectors with an agenda-item element. Else return an empty list. - var predicate = function (element) { - return element.name === name; - }; - var isProjectedIds = []; - Projector.getAll().forEach(function (projector) { - if (typeof _.findKey(projector.elements, predicate) === 'string') { - isProjectedIds.push(projector.id); - } - }); - return isProjectedIds; - }, - // Returns a list of mappings between pojector id, overlay and uuid. - isProjectedWithOverlayStatus: function () { - var mapping = []; - _.forEach(Projector.getAll(), function (projector) { - _.forEach(projector.elements, function (element, uuid) { - if (element.name === name) { - mapping.push({ - projectorId: projector.id, - uuid: uuid, - overlay: element.overlay || false, - }); - } - }); - }); - return mapping; - }, - }; - } -]) - - - -// Make sure that the Agenda resource is loaded. -.run(['Agenda', function(Agenda) {}]); - -}()); diff --git a/openslides/agenda/static/js/agenda/csv.js b/openslides/agenda/static/js/agenda/csv.js deleted file mode 100644 index 9de5fcb93..000000000 --- a/openslides/agenda/static/js/agenda/csv.js +++ /dev/null @@ -1,41 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.agenda.csv', []) - -.factory('AgendaCsvExport', [ - 'HumanTimeConverter', - 'gettextCatalog', - 'CsvDownload', - function (HumanTimeConverter, gettextCatalog, CsvDownload) { - var makeHeaderline = function () { - var headerline = ['Title', 'Text', 'Duration', 'Comment', 'Internal item']; - return _.map(headerline, function (entry) { - return gettextCatalog.getString(entry); - }); - }; - return { - export: function (agenda) { - var csvRows = [ - makeHeaderline() - ]; - _.forEach(agenda, function (item) { - var row = []; - var duration = item.duration ? HumanTimeConverter.secondsToHumanTime(item.duration*60, - { seconds: 'disabled', - hours: 'enabled' }) : ''; - row.push('"' + (item.title || '') + '"'); - row.push('"' + (item.getCSVExportText() || '') + '"'); - row.push('"' + duration + '"'); - row.push('"' + (item.comment || '') + '"'); - row.push('"' + (item.is_hidden ? '1' : '') + '"'); - csvRows.push(row); - }); - CsvDownload(csvRows, gettextCatalog.getString('Agenda') + '.csv'); - }, - }; - } -]); - -}()); diff --git a/openslides/agenda/static/js/agenda/docx.js b/openslides/agenda/static/js/agenda/docx.js deleted file mode 100644 index 1de1c92cb..000000000 --- a/openslides/agenda/static/js/agenda/docx.js +++ /dev/null @@ -1,84 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.agenda.docx', ['OpenSlidesApp.core.docx']) - -.factory('AgendaDocxExport', [ - '$http', - 'gettextCatalog', - 'FileSaver', - 'Agenda', - 'AgendaTree', - 'Config', - function ($http, gettextCatalog, FileSaver, Agenda, AgendaTree, Config) { - - var getData = function (items) { - // Item structure: The top layer has subitems, that are flat. - // The first layer is bold and all sublayers not. The docx - // templater cannot render items recursively, so the second - // layer are all subitems flated out. Spacing is done with tabs. - var tree = AgendaTree.getTree(items); - var subitems = []; // This will be used as a temporary variable. - var flatSubitems = function (children, parentCount) { - _.forEach(children, function (child) { - var taps = _.repeat('\t', parentCount - 1); - subitems.push({ - item_number: taps + child.item.item_number, - item_title: child.item.list_view_title, - }); - flatSubitems(child.children, parentCount + 1); - }); - }; - var twoLayerTree = _.map(tree, function (mainItem) { - subitems = []; - flatSubitems(mainItem.children, 1); - return { - item_number: mainItem.item.item_number, - item_title: mainItem.item.list_view_title, - subitems: subitems, - }; - }); - - // header - var headerline1 = [ - Config.translate(Config.get('general_event_name').value), - Config.translate(Config.get('general_event_description').value) - ].filter(Boolean).join(' – '); - var headerline2 = [ - Config.get('general_event_location').value, - Config.get('general_event_date').value - ].filter(Boolean).join(', '); - - // Data structure for the docx templater. - return { - header: [headerline1, headerline2].join('\n'), - agenda_translation: gettextCatalog.getString('Agenda'), - top_list: twoLayerTree, - }; - }; - - return { - export: function (items) { - // TODO: use filtered items. - var filename = gettextCatalog.getString('Agenda') + '.docx'; - $http.get('/agenda/docxtemplate/').then(function (success) { - var content = window.atob(success.data); - var doc = new Docxgen(content); - - var data = getData(items); - doc.setData(data); - doc.render(); - - var zip = doc.getZip(); - //zip = converter.updateZipFile(zip); - - var out = zip.generate({type: 'blob'}); - FileSaver.saveAs(out, filename); - }); - }, - }; - } -]); - -})(); diff --git a/openslides/agenda/static/js/agenda/pdf.js b/openslides/agenda/static/js/agenda/pdf.js deleted file mode 100644 index 47532d835..000000000 --- a/openslides/agenda/static/js/agenda/pdf.js +++ /dev/null @@ -1,93 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.agenda.pdf', ['OpenSlidesApp.core.pdf']) - -.factory('AgendaContentProvider', [ - 'gettextCatalog', - 'PDFLayout', - function(gettextCatalog, PDFLayout) { - - var createInstance = function(items) { - - // page title - var title = PDFLayout.createTitle(gettextCatalog.getString("Agenda")); - - // generate the item list with all subitems - var createItemList = function() { - var agenda_items = []; - _.forEach(items, function (item) { - if (item.is_public) { - var itemIndent = item.parentCount * 15; - - var itemStyle; - if (item.parentCount === 0) { - itemStyle = 'listParent'; - } else { - itemStyle = 'listChild'; - } - - var agendaJsonString = { - style: itemStyle, - columns: [ - { - width: itemIndent, - text: '' - }, - { - width: 60, - text: item.item_number - }, - { - text: item.title - } - ] - }; - - agenda_items.push(agendaJsonString); - } - }); - return agenda_items; - }; - - var getContent = function() { - return [ - title, - createItemList() - ]; - }; - - return { - getContent: getContent - }; - }; - - return { - createInstance: createInstance - }; - -}]) - -.factory('AgendaPdfExport', [ - 'gettextCatalog', - 'AgendaContentProvider', - 'PdfMakeDocumentProvider', - 'PdfCreate', - 'Messaging', - function (gettextCatalog, AgendaContentProvider, PdfMakeDocumentProvider, PdfCreate, Messaging) { - return { - export: function (items) { - var filename = gettextCatalog.getString('Agenda') + '.pdf'; - var agendaContentProvider = AgendaContentProvider.createInstance(items); - PdfMakeDocumentProvider.createInstance(agendaContentProvider).then(function (documentProvider) { - PdfCreate.download(documentProvider, filename); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - }); - }, - }; - } -]); - -}()); diff --git a/openslides/agenda/static/js/agenda/projector.js b/openslides/agenda/static/js/agenda/projector.js deleted file mode 100644 index 2c4176145..000000000 --- a/openslides/agenda/static/js/agenda/projector.js +++ /dev/null @@ -1,137 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.agenda.projector', ['OpenSlidesApp.agenda']) - -.config([ - 'slidesProvider', - function(slidesProvider) { - slidesProvider.registerSlide('agenda/list-of-speakers', { - template: 'static/templates/agenda/slide-list-of-speakers.html', - }); - slidesProvider.registerSlide('agenda/item-list', { - template: 'static/templates/agenda/slide-item-list.html', - }); - slidesProvider.registerSlide('agenda/current-list-of-speakers', { - template: 'static/templates/agenda/slide-current-list-of-speakers.html', - }); - } -]) - -.controller('SlideCurrentListOfSpeakersCtrl', [ - '$scope', - 'Agenda', - 'CurrentListOfSpeakersItem', - 'Config', - 'Projector', - function ($scope, Agenda, CurrentListOfSpeakersItem, Config, Projector) { - $scope.overlay = $scope.element.overlay; - // Watch for changes in the current list of speakers reference - $scope.$watch(function () { - return Config.lastModified('projector_currentListOfSpeakers_reference'); - }, function () { - $scope.currentListOfSpeakersReference = $scope.config('projector_currentListOfSpeakers_reference'); - $scope.updateCurrentListOfSpeakers(); - }); - // Watch for changes in the referenced projector - $scope.$watch(function () { - return Projector.lastModified($scope.currentListOfSpeakersReference); - }, function () { - $scope.updateCurrentListOfSpeakers(); - }); - // Watch for changes in the current item. - $scope.$watch(function () { - return Agenda.lastModified(); - }, function () { - $scope.updateCurrentListOfSpeakers(); - }); - $scope.updateCurrentListOfSpeakers = function () { - $scope.agendaItem = CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference); - }; - } -]) - -.controller('SlideListOfSpeakersCtrl', [ - '$scope', - 'Agenda', - 'User', - function ($scope, Agenda, User) { - // Attention! Each object that is used here has to be dealt on server side. - // Add it to the coresponding get_requirements method of the ProjectorElement - // class. - var id = $scope.element.id; - Agenda.bindOne(id, $scope, 'item'); - } -]) - -.controller('SlideItemListCtrl', [ - '$scope', - '$http', - '$filter', - 'Agenda', - 'AgendaTree', - 'Config', - function ($scope, $http, $filter, Agenda, AgendaTree, Config) { - // Attention! Each object that is used here has to be dealt on server side. - // Add it to the coresponding get_requirements method of the ProjectorElement - // class. - - // Bind agenda tree to the scope - var items; - $scope.$watch(function () { - return Agenda.lastModified() + - Config.lastModified('agenda_hide_internal_items_on_projector'); - }, function () { - if ($scope.element.id) { - // remove hidden items - items = _.filter(Agenda.getAll(), function (item) { - return !item.is_hidden; - }); - if (Config.get('agenda_hide_internal_items_on_projector').value) { - items = _.filter(items, function (item) { - return item.is_public; - }); - } - var tree = AgendaTree.getTree(items); - - var getRootNode = function (node) { - if (node.id == $scope.element.id) { - return node; - } - for (var i = 0; i < node.children.length; i++) { - var result = getRootNode(node.children[i]); - if (result) { - return result; - } - } - return false; - }; - _.forEach(tree, function (node) { - var result = getRootNode(node); - if (result) { - $scope.rootItem = result.item; - $scope.tree = result.children; - return false; - } - }); - } else if ($scope.element.tree) { - items = _.filter(Agenda.getAll(), function (item) { - return item.is_public; - }); - $scope.tree = AgendaTree.getTree(items); - } else { - items = Agenda.filter({ - where: { parent_id: null }, - orderBy: 'weight' - }); - items = _.filter(items, function (item) { - return item.is_public; - }); - $scope.tree = AgendaTree.getTree(items); - } - }); - } -]); - -}()); diff --git a/openslides/agenda/static/js/agenda/site.js b/openslides/agenda/static/js/agenda/site.js deleted file mode 100644 index 2735aabdf..000000000 --- a/openslides/agenda/static/js/agenda/site.js +++ /dev/null @@ -1,890 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.agenda.site', [ - 'OpenSlidesApp.agenda', - 'OpenSlidesApp.core.pdf', - 'OpenSlidesApp.agenda.pdf', - 'OpenSlidesApp.agenda.csv', - 'OpenSlidesApp.agenda.docx', -]) - -.config([ - 'mainMenuProvider', - 'gettext', - function (mainMenuProvider, gettext) { - mainMenuProvider.register({ - 'ui_sref': 'agenda.item.list', - 'img_class': 'calendar-o', - 'title': gettext('Agenda'), - 'weight': 200, - 'perm': 'agenda.can_see', - }); - } -]) - -.config([ - 'SearchProvider', - 'gettext', - function (SearchProvider, gettext) { - SearchProvider.register({ - 'verboseName': gettext('Agenda'), - 'collectionName': 'agenda/item', - 'urlDetailState': 'agenda.item.detail', - 'weight': 200, - }); - } -]) - -.config([ - '$stateProvider', - 'gettext', - function ($stateProvider, gettext) { - $stateProvider - .state('agenda', { - url: '/agenda', - abstract: true, - template: "", - data: { - title: gettext('Agenda'), - basePerm: 'agenda.can_see', - }, - }) - .state('agenda.item', { - abstract: true, - template: "", - }) - .state('agenda.item.list', {}) - .state('agenda.item.detail', { - resolve: { - itemId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }], - } - }) - .state('agenda.item.sort', { - url: '/sort', - controller: 'AgendaSortCtrl', - }) - .state('agenda.current-list-of-speakers', { - url: '/speakers', - controller: 'CurrentListOfSpeakersViewCtrl', - data: { - title: gettext('Current list of speakers'), - }, - }); - } -]) - -// Set the sensitivity of moving nodes horizontal for the ui-tree. -.config([ - 'treeConfig', - function (treeConfig) { - treeConfig.dragMoveSensitivity = 20; - } -]) - -.factory('ShowAsAgendaItemField', [ - 'operator', - 'gettext', - 'gettextCatalog', - function (operator, gettext, gettextCatalog) { - return function (managePermission) { - return { - key: 'agenda_type', - type: 'select-single', - templateOptions: { - label: gettextCatalog.getString('Agenda visibility'), - options: [ - {type: 1, displayName: gettext('Public item')}, - {type: 2, displayName: gettext('Internal item')}, - {type: 3, displayName: gettext('Hidden item')} - ], - ngOptions: 'type.type as (type.displayName | translate) for type in to.options', - }, - hide: !(operator.hasPerms(managePermission) && operator.hasPerms('agenda.can_manage')) - }; - }; - } -]) - -.controller('ItemListCtrl', [ - '$scope', - '$filter', - '$http', - '$state', - 'DS', - 'operator', - 'ngDialog', - 'Agenda', - 'TopicForm', // TODO: Remove this dependency. Use template hook for "New" and "Import" buttons. - 'AgendaTree', - 'Projector', - 'ProjectionDefault', - 'gettextCatalog', - 'gettext', - 'osTableFilter', - 'osTablePagination', - 'AgendaCsvExport', - 'AgendaPdfExport', - 'AgendaDocxExport', - 'ErrorMessage', - function($scope, $filter, $http, $state, DS, operator, ngDialog, Agenda, TopicForm, - AgendaTree, Projector, ProjectionDefault, gettextCatalog, gettext, osTableFilter, - osTablePagination, AgendaCsvExport, AgendaPdfExport, AgendaDocxExport, ErrorMessage) { - - $scope.AGENDA_ITEM = 1; - $scope.INTERNAL_ITEM = 2; - $scope.HIDDEN_ITEM = 3; - - // Bind agenda tree to the scope - $scope.$watch(function () { - return Agenda.lastModified(); - }, function () { - // Filter out items that doesn't have the list_item_title. This happens, if the - // item is a hidden item but provides the list of speakers, but should not be - // visible in the list view. - var allowedItems = _.filter(Agenda.getAll(), function (item) { - return item.list_view_title; - }); - $scope.items = AgendaTree.getFlatTree(allowedItems); - $scope.agendaHasSubitems = $filter('filter')($scope.items, {'parent_id': ''}).length; - }); - Projector.bindAll({}, $scope, 'projectors'); - $scope.mainListTree = true; - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - var projectiondefault = ProjectionDefault.filter({name: 'agenda_all_items'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId_all_items = projectiondefault.projector_id; - } - $scope.projectionDefaults = ProjectionDefault.getAll(); - }); - $scope.alert = {}; - - - // Filtering - $scope.filter = osTableFilter.createInstance('AgendaTableFilter'); - - if (!$scope.filter.existsStorageEntry()) { - $scope.filter.booleanFilters = { - closed: { - value: undefined, - defaultValue: undefined, - displayName: gettext('Closed items'), - choiceYes: gettext('Closed items'), - choiceNo: gettext('Open items'), - }, - // The next filters are just on-off, so no undefined there - is_public: { - value: true, - defaultValue: true, - choiceYes: gettext('Public items'), - choiceNo: gettext('No public items'), - }, - is_internal: { - value: true, - defaultValue: true, - choiceYes: gettext('Internal items'), - choiceNo: gettext('No internal items'), - permission: 'agenda.can_see_internal_items', - }, - is_hidden: { - value: false, - defaultValue: false, - choiceYes: gettext('Hidden items'), - choiceNo: gettext('No hidden items'), - permission: 'agenda.can_manage', - }, - }; - } - $scope.filter.propertyList = ['item_number', 'title', 'title_list_view', 'comment', 'duration']; - $scope.filter.propertyFunctionList = [ - function (item) {return item.getListViewTitle();}, - ]; - $scope.areFiltersSet = function () { - return ($scope.areVisibilityFiltersSet() || - $scope.filter.booleanFilters.closed.value !== $scope.filter.booleanFilters.closed.defaultValue); - }; - $scope.areVisibilityFiltersSet = function () { - return ($scope.filter.booleanFilters.is_public.value !== $scope.filter.booleanFilters.is_public.defaultValue || - $scope.filter.booleanFilters.is_internal.value !== $scope.filter.booleanFilters.is_internal.defaultValue || - $scope.filter.booleanFilters.is_hidden.value !== $scope.filter.booleanFilters.is_hidden.defaultValue); - - }; - $scope.resetFilters = function (isSelectMode) { - if (!isSelectMode) { - _.forEach($scope.filter.booleanFilters, function (filter) { - filter.value = filter.defaultValue; - }); - $scope.filter.save(); - } - }; - - // Expand all items during searching. - $scope.filter.changed = function () { - $scope.collapseState = true; - $scope.toggleCollapseState(); - }; - - // pagination - $scope.pagination = osTablePagination.createInstance('AgendaTablePagination', 50); - - // parse duration for inline editing - $scope.generateDurationText = function (item) { - //convert data from model format (m) to view format (hh:mm) - if (item.duration) { - var time = "", - totalminutes = item.duration; - if (totalminutes < 0) { - time = "-"; - totalminutes = -totalminutes; - } - var hh = Math.floor(totalminutes / 60); - var mm = Math.floor(totalminutes % 60); - // Add leading "0" for double digit values - mm = ("0"+mm).slice(-2); - time += hh + ":" + mm; - item.durationText = time; - } else { - item.durationText = ""; - } - }; - $scope.setDurationText = function (item) { - //convert data from view format (hh:mm) to model format (m) - var time = item.durationText.replace('h', '').split(':'); - var data; - if (time.length > 1 && !isNaN(time[0]) && !isNaN(time[1])) { - data = (+time[0]) * 60 + (+time[1]); - if (data < 0) { - data = "-"+data; - } - item.duration = parseInt(data); - } else if (time.length == 1 && !isNaN(time[0])) { - data = (+time[0]); - item.duration = parseInt(data); - } else { - item.duration = 0; - } - $scope.save(item); - }; - - /** Duration calculations **/ - $scope.sumDurations = function () { - var totalDuration = 0; - $scope.items.forEach(function (item) { - if (item.duration) { - totalDuration += item.duration; - } - }); - return totalDuration; - }; - $scope.calculateEndTime = function () { - var totalDuration = $scope.sumDurations(); - var startTimestamp = $scope.config('agenda_start_event_date_time'); - if (startTimestamp) { - var endTimestamp = startTimestamp + totalDuration * 60 * 1000; - var endDate = new Date(endTimestamp); - var mm = ("0" + endDate.getMinutes()).slice(-2); - var dateStr = endDate.getHours() + ':' + mm; - return dateStr; - } else { - return ''; - } - }; - - // Agenda collapse function - $scope.toggleCollapseState = function () { - $scope.collapseState = !$scope.collapseState; - _.forEach($scope.items, function (item) { - item.hideChildren = $scope.collapseState; - }); - }; - - // Check, if an item has childs in all filtered items - $scope.hasChildren = function (item) { - return _.some($scope.itemsFiltered, function (_item) { - return _item.parent_id == item.id; - }); - }; - - // returns true, if the agenda has at least two layers - $scope.agendaHasMultipleLayers = function () { - return _.some($scope.items, function (item) { - return item.parent_id; - }); - }; - - /** Agenda item functions **/ - // open dialog for new topics // TODO Remove this. Don't forget import button in template. - $scope.newDialog = function () { - ngDialog.open(TopicForm.getDialog()); - }; - // save changed item - $scope.save = function (item) { - Agenda.save(item).then( - function (success) { - $scope.alert.show = false; - }, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - // delete related item - $scope.deleteRelatedItem = function (item) { - DS.destroy(item.content_object.collection, item.content_object.id); - }; - // auto numbering of agenda items - $scope.autoNumbering = function() { - $http.post('/rest/agenda/item/numbering/', {}); - }; - // check open permission - // TODO: Use generic solution here. - $scope.isAllowedToSeeOpenLink = function (item) { - var collection = item.content_object.collection; - switch (collection) { - case 'topics/topic': - return operator.hasPerms('agenda.can_see'); - case 'motions/motion': - return operator.hasPerms('motions.can_see'); - case 'motions/motion-block': - return operator.hasPerms('motions.can_see'); - case 'assignments/assignment': - return operator.hasPerms('assignments.can_see'); - default: - return false; - } - }; - $scope.edit = function (item) { - ngDialog.open(item.getContentObjectForm().getDialog({id: item.content_object.id})); - }; - - // export - $scope.pdfExport = function () { - AgendaPdfExport.export($scope.itemsFiltered); - }; - $scope.csvExport = function () { - AgendaCsvExport.export($scope.itemsFiltered); - }; - $scope.docxExport = function () { - AgendaDocxExport.export($scope.itemsFiltered); - }; - - /** select mode functions **/ - $scope.isSelectMode = false; - // check all checkboxes - $scope.checkAll = function () { - $scope.selectedAll = !$scope.selectedAll; - angular.forEach($scope.items, function (item) { - item.selected = $scope.selectedAll; - }); - }; - // uncheck all checkboxes if isDeleteMode is closed - $scope.uncheckAll = function () { - if (!$scope.isSelectMode) { - $scope.selectedAll = false; - angular.forEach($scope.items, function (item) { - item.selected = false; - }); - } - }; - // set type for selected items - $scope.setTypeMultiple = function (type) { - _.forEach($scope.items, function (item) { - if (item.selected) { - item.type = type; - $scope.save(item); - } - }); - $scope.isSelectMode = false; - $scope.uncheckAll(); - }; - // set closed for selected items - $scope.setStateMultiple = function (closed) { - _.forEach($scope.items, function (item) { - if (item.selected) { - item.closed = closed; - $scope.save(item); - } - }); - $scope.isSelectMode = false; - $scope.uncheckAll(); - }; - // delete selected items - $scope.deleteMultiple = function () { - _.forEach($scope.items, function (item) { - if (item.selected) { - DS.destroy(item.content_object.collection, item.content_object.id); - } - }); - $scope.isSelectMode = false; - $scope.uncheckAll(); - }; - - /** Project functions **/ - // get ProjectionDefault for item - $scope.getProjectionDefault = function (item) { - if (item.tree) { - return $scope.defaultProjectorId_all_items; - } else { - var app_name = item.content_object.collection.split('/')[0]; - var id = 1; - $scope.projectionDefaults.forEach(function (projectionDefault) { - if (projectionDefault.name == app_name) { - id = projectionDefault.projector_id; - } - }); - return id; - } - }; - // project agenda - $scope.projectAgenda = function (projectorId, tree, id) { - var isAgendaProjectedIds = $scope.isAgendaProjected($scope.mainListTree); - _.forEach(isAgendaProjectedIds, function (id) { - $http.post('/rest/core/projector/' + id + '/clear_elements/'); - }); - if (_.indexOf(isAgendaProjectedIds, projectorId) == -1) { - $http.post('/rest/core/projector/' + projectorId + '/prune_elements/', - [{name: 'agenda/item-list', tree: tree, id: id}]); - } - }; - // change whether all items or only main items should be projected - $scope.changeMainListTree = function () { - var isAgendaProjectedId = $scope.isAgendaProjected($scope.mainListTree); - $scope.mainListTree = !$scope.mainListTree; - if (isAgendaProjectedId > 0) { - $scope.projectAgenda(isAgendaProjectedId, $scope.mainListTree); - } - }; - // change whether one item or all subitems should be projected - $scope.changeItemTree = function (item) { - var tree = item.tree; - item.tree = !item.tree; - var isProjected = item.isProjected(tree); - _.forEach(isProjected, function (projectorId) { - // Deactivate and reactivate - item.project(projectorId, tree).then(function (s) { - item.project(projectorId, !tree); - }); - }); - }; - // check if agenda is projected - $scope.isAgendaProjected = function (tree) { - // Returns the ids of all projectors with an element with - // the name 'agenda/item-list'. Else returns an empty list. - var predicate = function (element) { - var value; - if (tree) { - // tree with all agenda items - value = element.name == 'agenda/item-list' && - typeof element.id === 'undefined' && - element.tree; - } else { - // only main agenda items - value = element.name == 'agenda/item-list' && - typeof element.id === 'undefined' && - !element.tree; - } - return value; - }; - var projectorIds = []; - $scope.projectors.forEach(function (projector) { - if (typeof _.findKey(projector.elements, predicate) === 'string') { - projectorIds.push(projector.id); - } - }); - return projectorIds; - }; - } -]) - -// Filter for the item type that filters the selected items by type. filters -// are the boolean filters from the ui. -.filter('itemTypeFilter', [ - function () { - return function (items, filters) { - return _.filter(items, function (item) { - return (item.is_public && filters.is_public.value) || - (item.is_internal && filters.is_internal.value) || - (item.is_hidden && filters.is_hidden.value); - }); - }; - } -]) - -// filter to hide collapsed items. Items has to be a flat tree. -.filter('collapsedItemFilter', [ - function () { - return function (items) { - return _.filter(items, function (item) { - var index = _.findIndex(items, item); - var parentId = item.parent_id; - // Search for parents, if one has the hideChildren attribute set. All parents - // have a higher index as this item, because items is a flat tree. - // If a parent has this attribute, we should remove this item. Else if we hit - // the top or an item on the first layer, the item is not collapsed. - for (--index; index >= 0 && parentId !== null; index--) { - var p = items[index]; - if (p.id === parentId) { - if (p.hideChildren) { - return false; - } else { - parentId = p.parent_id; - } - } - } - return true; - }); - }; - } -]) - -.controller('ItemDetailCtrl', [ - '$scope', - '$filter', - 'Agenda', - 'itemId', - 'Projector', - 'ProjectionDefault', - 'gettextCatalog', - 'WebpageTitle', - 'ErrorMessage', - function ($scope, $filter, Agenda, itemId, Projector, ProjectionDefault, gettextCatalog, WebpageTitle, - ErrorMessage) { - $scope.alert = {}; - - $scope.$watch(function () { - return Agenda.lastModified(itemId); - }, function () { - $scope.item = Agenda.get(itemId); - WebpageTitle.updateTitle(gettextCatalog.getString('List of speakers') + ' ' + - gettextCatalog.getString('of') + ' ' + $scope.item.getTitle()); - // all speakers - $scope.speakers = $filter('orderBy')($scope.item.speakers, 'weight'); - // next speakers - $scope.nextSpeakers = $filter('filter')($scope.speakers, {'begin_time': null}); - // current speaker - $scope.currentSpeaker = $filter('filter')($scope.speakers, {'begin_time': '!!', 'end_time': null}); - // last speakers - $scope.lastSpeakers = $filter('filter')($scope.speakers, {'end_time': '!!'}); - $scope.lastSpeakers = $filter('orderBy')($scope.lastSpeakers, 'begin_time'); - }); - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - var item_app_name = $scope.item.content_object.collection.split('/')[0]; - var projectiondefaultItem = ProjectionDefault.filter({name: item_app_name})[0]; - if (projectiondefaultItem) { - $scope.defaultProjectorItemId = projectiondefaultItem.projector_id; - } - var projectiondefaultListOfSpeakers = ProjectionDefault.filter({name: 'agenda_list_of_speakers'})[0]; - if (projectiondefaultListOfSpeakers) { - $scope.defaultProjectorListOfSpeakersId = projectiondefaultListOfSpeakers.projector_id; - } - }); - } -]) - -/* This is the controller for the list of speakers partial management template. - * The parent controller needs to provide a $scope.item, $scope.speakers, $scope.nextSpeakers, - * $scope.currentSpeakers, $scope.lastSpeakers. See (as example) ItemDetailCtrl. */ -.controller('ListOfSpeakersManagementCtrl', [ - '$scope', - '$http', - '$filter', - 'Agenda', - 'User', - 'operator', - 'ErrorMessage', - function ($scope, $http, $filter, Agenda, User, operator, ErrorMessage) { - User.bindAll({}, $scope, 'users'); - $scope.speakerSelectBox = {}; - - // close/open list of speakers of current item - $scope.closeList = function (listClosed) { - $scope.item.speaker_list_closed = listClosed; - Agenda.save($scope.item); - }; - - // add user to list of speakers - $scope.addSpeaker = function (userId) { - $http.post('/rest/agenda/item/' + $scope.item.id + '/manage_speaker/', {'user': userId}).then( - function (success) { - $scope.alert.show = false; - $scope.speakerSelectBox = {}; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - $scope.speakerSelectBox = {}; - } - ); - }; - - // delete speaker(!) from list of speakers - $scope.removeSpeaker = function (speakerId) { - $http.delete( - '/rest/agenda/item/' + $scope.item.id + '/manage_speaker/', - {headers: {'Content-Type': 'application/json'}, - data: JSON.stringify({speaker: speakerId})} - ) - .then(function (success) { - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - - //delete all speakers from list of speakers - $scope.removeAllSpeakers = function () { - var speakersOnList = []; - angular.forEach($scope.item.speakers, function (speaker) { - speakersOnList.push(speaker.id); - }); - $http.delete( - '/rest/agenda/item/' + $scope.item.id + '/manage_speaker/', - {headers: {'Content-Type': 'application/json'}, - data: JSON.stringify({speaker: speakersOnList})} - ) - .then(function (success) { - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - - // Return true if the requested user is allowed to do a specific action - // and see the corresponding button (e.g. 'add me' or 'remove me'). - $scope.isAllowed = function (action) { - var nextUsers = []; - angular.forEach($scope.nextSpeakers, function (speaker) { - nextUsers.push(speaker.user_id); - }); - switch (action) { - case 'add': - return (operator.hasPerms('agenda.can_be_speaker') && - !$scope.item.speaker_list_closed && - $.inArray(operator.user.id, nextUsers) == -1); - case 'remove': - if (operator.user) { - return ($.inArray(operator.user.id, nextUsers) != -1); - } - return false; - case 'removeAll': - return (operator.hasPerms('agenda.can_manage_list_of_speakers') && - $scope.speakers.length > 0); - case 'showLastSpeakers': - return $scope.lastSpeakers.length > 0; - } - }; - - // begin speech of selected/next speaker - $scope.beginSpeech = function (speakerId) { - $http.put('/rest/agenda/item/' + $scope.item.id + '/speak/', {'speaker': speakerId}) - .then(function (success) { - $scope.alert.show = false; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - - // end speech of current speaker - $scope.endSpeech = function () { - $http.delete( - '/rest/agenda/item/' + $scope.item.id + '/speak/', - {headers: {'Content-Type': 'application/json'}, data: {}} - ).then( - function (success) {}, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - // gets speech duration of selected speaker in seconds - $scope.getDuration = function (speaker) { - var beginTimestamp = new Date(speaker.begin_time).getTime(); - var endTimestamp = new Date(speaker.end_time).getTime(); - // calculate duration in seconds - return Math.floor((endTimestamp - beginTimestamp) / 1000); - - }; - // save reordered list of speakers - $scope.treeOptions = { - dropped: function (event) { - var sortedSpeakers = _.map($scope.nextSpeakers, function (speaker) { - return speaker.id; - }); - $http.post('/rest/agenda/item/' + $scope.item.id + '/sort_speakers/', - {speakers: sortedSpeakers} - ); - } - }; - - // Marking a speaker - $scope.toggleMarked = function (speaker) { - $http.patch('/rest/agenda/item/' + $scope.item.id + '/manage_speaker/', { - user: speaker.user.id, - marked: !speaker.marked, - }).then(function (success) { - $scope.alert.show = false; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('AgendaSortCtrl', [ - '$scope', - '$http', - 'Agenda', - 'AgendaTree', - 'ErrorMessage', - function($scope, $http, Agenda, AgendaTree, ErrorMessage) { - // Bind agenda tree to the scope - $scope.$watch(function () { - return Agenda.lastModified(); - }, function () { - $scope.items = AgendaTree.getTree(Agenda.getAll()); - }); - $scope.showInternalItems = true; - $scope.alert = {}; - - // save parent and weight of moved agenda item (and all items on same level) - $scope.treeOptions = { - dropped: function(event) { - var parentID = null; - var droppedItemID = event.source.nodeScope.$modelValue.id; - if (event.dest.nodesScope.item) { - parentID = event.dest.nodesScope.item.id; - } - $http.post('/rest/agenda/item/sort/', { - nodes: event.dest.nodesScope.$modelValue, - parent_id: parentID} - ).then( - function(success) {}, - function(error){ - $scope.alert = ErrorMessage.forAlert(error); - } - ); - } - }; - } -]) - -.controller('CurrentListOfSpeakersViewCtrl', [ - '$scope', - '$http', - '$filter', - 'Projector', - 'ProjectionDefault', - 'Agenda', - 'Config', - 'CurrentListOfSpeakersItem', - 'CurrentListOfSpeakersSlide', - 'gettextCatalog', - 'WebpageTitle', - function($scope, $http, $filter, Projector, ProjectionDefault, Agenda, Config, - CurrentListOfSpeakersItem, CurrentListOfSpeakersSlide, gettextCatalog, WebpageTitle) { - $scope.alert = {}; - $scope.currentListOfSpeakers = CurrentListOfSpeakersSlide; - - // Watch for changes in the current list of speakers reference - $scope.$watch(function () { - return Config.lastModified('projector_currentListOfSpeakers_reference'); - }, function () { - $scope.currentListOfSpeakersReference = $scope.config('projector_currentListOfSpeakers_reference'); - $scope.updateCurrentListOfSpeakersItem(); - }); - $scope.$watch(function () { - return Projector.lastModified(); - }, function() { - $scope.projectors = Projector.getAll(); - // If there is just one projector we provide just the overlay. - if ($scope.projectors.length === 1) { - $scope.currentListOfSpeakersAsOverlay = true; - } - $scope.updateCurrentListOfSpeakersItem(); - - $scope.listOfSpeakersDefaultProjectorId = ProjectionDefault.filter({name: 'agenda_current_list_of_speakers'})[0].projector_id; - }); - - $scope.$watch(function () { - return $scope.item ? Agenda.lastModified($scope.item.id) : void 0; - }, function () { - $scope.updateCurrentListOfSpeakersItem(); - }); - - $scope.updateCurrentListOfSpeakersItem = function () { - $scope.item = CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference); - if ($scope.item) { - // all speakers - $scope.speakers = $filter('orderBy')($scope.item.speakers, 'weight'); - // next speakers - $scope.nextSpeakers = $filter('filter')($scope.speakers, {'begin_time': null}); - // current speaker - $scope.currentSpeaker = $filter('filter')($scope.speakers, {'begin_time': '!!', 'end_time': null}); - // last speakers - $scope.lastSpeakers = $filter('filter')($scope.speakers, {'end_time': '!!'}); - $scope.lastSpeakers = $filter('orderBy')($scope.lastSpeakers, 'begin_time'); - } else { - $scope.speakers = void 0; - $scope.nextSpeakers = void 0; - $scope.currentSpeaker = void 0; - $scope.lastSpeakers = void 0; - } - if ($scope.item) { - WebpageTitle.updateTitle(gettextCatalog.getString('Current list of speakers') + ' ' + - gettextCatalog.getString('of') + ' ' + $scope.item.getTitle()); - } else { - WebpageTitle.updateTitle(gettextCatalog.getString('Current list of speakers')); - } - }; - - // Set the current overlay status - if ($scope.currentListOfSpeakers.isProjected().length) { - var isProjected = $scope.currentListOfSpeakers.isProjectedWithOverlayStatus(); - $scope.currentListOfSpeakersAsOverlay = isProjected[0].overlay; - } else { - $scope.currentListOfSpeakersAsOverlay = false; - } - $scope.setOverlay = function (overlay) { - $scope.currentListOfSpeakersAsOverlay = overlay; - var isProjected = $scope.currentListOfSpeakers.isProjectedWithOverlayStatus(); - if (isProjected.length) { - _.forEach(isProjected, function (mapping) { - if (mapping.overlay != overlay) { // change the overlay if it is different - $scope.currentListOfSpeakers.project(mapping.projectorId, overlay); - } - }); - } - }; - } -]) - -//mark all agenda config strings for translation with Javascript -.config([ - 'gettext', - function (gettext) { - gettext('Enable numbering for agenda items'); - gettext('Numbering prefix for agenda items'); - gettext('This prefix will be set if you run the automatic agenda numbering.'); - gettext('Agenda'); - gettext('Invalid input.'); - gettext('Numeral system for agenda items'); - gettext('Arabic'); - gettext('Roman'); - gettext('Begin of event'); - gettext('Input format: DD.MM.YYYY HH:MM'); - gettext('Hide internal items when projecting subitems'); - gettext('Number of last speakers to be shown on the projector'); - gettext('List of speakers'); - gettext('Show orange countdown in the last x seconds of speaking time'); - gettext('Enter duration in seconds. Choose 0 to disable warning color.'); - gettext('Couple countdown with the list of speakers'); - gettext('[Begin speech] starts the countdown, [End speech] stops the ' + - 'countdown.'); - gettext('Agenda visibility'); - gettext('Default visibility for new agenda items (except topics)'); - } - ]); - -}()); diff --git a/openslides/agenda/static/templates/agenda/current-list-of-speakers.html b/openslides/agenda/static/templates/agenda/current-list-of-speakers.html deleted file mode 100644 index 3b665e018..000000000 --- a/openslides/agenda/static/templates/agenda/current-list-of-speakers.html +++ /dev/null @@ -1,60 +0,0 @@ -
-
- - -

Current list of speakers

-

{{ item.getTitle() }} - - Closed - -

-
-
- - diff --git a/openslides/agenda/static/templates/agenda/item-detail.html b/openslides/agenda/static/templates/agenda/item-detail.html deleted file mode 100644 index de3f43d5e..000000000 --- a/openslides/agenda/static/templates/agenda/item-detail.html +++ /dev/null @@ -1,54 +0,0 @@ -
-
- -

{{ item.getTitle() }}

-

- List of speakers - Closed -

-
-
- - diff --git a/openslides/agenda/static/templates/agenda/item-list.html b/openslides/agenda/static/templates/agenda/item-list.html deleted file mode 100644 index 77b5e3506..000000000 --- a/openslides/agenda/static/templates/agenda/item-list.html +++ /dev/null @@ -1,551 +0,0 @@ - - -
-
-
- - - - - - - Sort ... - - - - - - - -
- - -
- - -
-
-
-
- - - - - - - Set visibility - - - - - Done - - - - Open - - - - - Delete selected items - -
-
- -
-
- {{ itemsFiltered.length }} / - {{ (items|filter:{is_hidden:false}).length }} {{ "items" | translate }}, - {{(items|filter:{selected:true}).length}} {{ "selected" | translate }} - - · - Duration: - {{ sumDurations() | osMinutesToTime }}h - - (Estimated end: {{ calculateEndTime() }}) - - - - - · - - Expand all - Collapse all - - -
-
- - - « - - Page {{ pagination.currentPage }} / - {{ pagination.getPageCount(itemsFiltered) }} - - » - - -
-
- -
-
-
- -
-
- - - - - Filter - - - - - - State - - - - - - - - Visibility - - - - - - - - - - - - - - - - - - - {{ booleanFilter.value ? booleanFilter.choiceYes : booleanFilter.choiceNo | translate }} - - - -
-
- - -
- - -
- -
- - -
- - -
-
- -
-
- -
- - {{ item.getListViewTitle() }} - - - {{ item.getListViewTitle() }} - -
- -
- - List of speakers - · - Edit · - Delete - - -
- -
-
- -
-
- - -
-
- - - - - Public - - - - Internal - - - - Hidden - - - - - -
-
- -
- - - Public - - - - Internal - - - - Hidden - -
- -
-
- - - Set duration ... - - {{ (item.duration | osMinutesToTime)}} - h - - -
-
-
- - {{ item.duration | osMinutesToTime }} - h - -
-
-
- -
-
- - - Set comment ... - {{ item.comment }} - -
-
- -
-
- - - Set item number ... - Change item number ... - -
-
- -
-
-
-
-
- - Done -
-
-
-
- - Done -
-
-
-
-
- -
- -
    -
-
diff --git a/openslides/agenda/static/templates/agenda/item-sort.html b/openslides/agenda/static/templates/agenda/item-sort.html deleted file mode 100644 index e33c301a3..000000000 --- a/openslides/agenda/static/templates/agenda/item-sort.html +++ /dev/null @@ -1,48 +0,0 @@ -
-
- -

Sort agenda

-
-
- -
-

Drag and drop items to change the order of the agenda. Your modification will be saved immediately.

- -

- - -

- {{ alert.msg }} -
- -
-
    -
  1. -
-
-
- - - - - diff --git a/openslides/agenda/static/templates/agenda/list-of-speakers-partial-management.html b/openslides/agenda/static/templates/agenda/list-of-speakers-partial-management.html deleted file mode 100644 index 5299fdea2..000000000 --- a/openslides/agenda/static/templates/agenda/list-of-speakers-partial-management.html +++ /dev/null @@ -1,132 +0,0 @@ -
-
-
- - - - - - - -
-
- - -

- The list of speakers is empty. -

- - - - -
- -
-
    -
  1. - {{ speaker.user.get_full_name() }} - - - {{ getDuration(speaker) | osSecondsToTime }} minutes - (Start time: - {{ speaker.begin_time | date:'yyyy-MM-dd HH:mm:ss' }}) - - -
-
-
- - -

- - {{ speaker.user.get_full_name() }} - - - - - - -

- - -
-
-
    -
  1. - - {{ $index + 1 }}. - {{ speaker.user.get_full_name() }} - - - -   - - - -
-
-
- - -
-
- {{ alert.msg }} -
-
- -
-

- - -

-
diff --git a/openslides/agenda/static/templates/agenda/partial-slide-current-list-of-speakers-overlay.html b/openslides/agenda/static/templates/agenda/partial-slide-current-list-of-speakers-overlay.html deleted file mode 100644 index 36aeebf97..000000000 --- a/openslides/agenda/static/templates/agenda/partial-slide-current-list-of-speakers-overlay.html +++ /dev/null @@ -1,36 +0,0 @@ -
-

List of speakers

- - -

- {{ speaker.user.get_full_name() }} - -

- - -
- - - {{ speaker.user.get_full_name() }} - - -
- - -
    -
  1. - {{ speaker.user.get_full_name() }} - -
  2. -
-

- + {{ nextSpeakers.length - 3 }} -

-
diff --git a/openslides/agenda/static/templates/agenda/partial-slide-current-list-of-speakers.html b/openslides/agenda/static/templates/agenda/partial-slide-current-list-of-speakers.html deleted file mode 100644 index 2020d3727..000000000 --- a/openslides/agenda/static/templates/agenda/partial-slide-current-list-of-speakers.html +++ /dev/null @@ -1,43 +0,0 @@ -
- - -
-

List of speakers

-

{{ agendaItem.getListOfSpeakersTitle() }} - - – {{ (agendaItem.speakers | filter: {begin_time: null}).length }} speakers - - Closed -

-
- -
- -

- {{ speaker.user.get_full_name() }} - -

- - -

- - {{ speaker.user.get_full_name() }} - -

- - -
    -
  1. - {{ speaker.user.get_full_name() }} - -
  2. -
-
-
diff --git a/openslides/agenda/static/templates/agenda/slide-current-list-of-speakers.html b/openslides/agenda/static/templates/agenda/slide-current-list-of-speakers.html deleted file mode 100644 index f4a0357f8..000000000 --- a/openslides/agenda/static/templates/agenda/slide-current-list-of-speakers.html +++ /dev/null @@ -1,8 +0,0 @@ -
- - - - -
diff --git a/openslides/agenda/static/templates/agenda/slide-item-list.html b/openslides/agenda/static/templates/agenda/slide-item-list.html deleted file mode 100644 index c9fc98b0b..000000000 --- a/openslides/agenda/static/templates/agenda/slide-item-list.html +++ /dev/null @@ -1,27 +0,0 @@ -
-

Agenda

-

{{ rootItem.getTitle() }}

- -
- - -
-
-
- - - diff --git a/openslides/agenda/static/templates/agenda/slide-list-of-speakers.html b/openslides/agenda/static/templates/agenda/slide-list-of-speakers.html deleted file mode 100644 index c2b9371d0..000000000 --- a/openslides/agenda/static/templates/agenda/slide-list-of-speakers.html +++ /dev/null @@ -1,36 +0,0 @@ -
- - -
-

List of speakers

-

- {{ item.getListOfSpeakersTitle() }} - - – {{ (item.speakers | filter: {begin_time: null}).length }} speakers - - Closed -

-
- -
- -

- {{ speaker.user.get_full_name() }} - - - -

- - {{ speaker.user.get_full_name() }} - - - -

    -
  1. - {{ speaker.user.get_full_name() }} - -
-
-
diff --git a/openslides/agenda/urls.py b/openslides/agenda/urls.py deleted file mode 100644 index 2589aa2c9..000000000 --- a/openslides/agenda/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf.urls import url - -from . import views - - -urlpatterns = [ - url(r'^docxtemplate/$', - views.AgendaDocxTemplateView.as_view(), - name='agenda_docx_template'), -] diff --git a/openslides/agenda/views.py b/openslides/agenda/views.py index f324c57d0..3d2d143f2 100644 --- a/openslides/agenda/views.py +++ b/openslides/agenda/views.py @@ -15,7 +15,6 @@ from openslides.utils.rest_api import ( detail_route, list_route, ) -from openslides.utils.views import BinaryTemplateView from ..utils.auth import has_perm from .access_permissions import ItemAccessPermissions @@ -340,11 +339,3 @@ class ItemViewSet(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericV inform_changed_data(items) return Response({'detail': _('The agenda has been sorted.')}) - - -# Special views -class AgendaDocxTemplateView(BinaryTemplateView): - """ - Returns the template for motions docx export - """ - template_name = 'templates/docx/agenda.docx' diff --git a/openslides/assignments/static/css/assignments/_projector.scss b/openslides/assignments/static/css/assignments/_projector.scss deleted file mode 100644 index bfd37c454..000000000 --- a/openslides/assignments/static/css/assignments/_projector.scss +++ /dev/null @@ -1,19 +0,0 @@ -.electionresults table { - width: calc(100% - 230px); -} - -#speakerbox { - width: 40%; - float: right; - margin: 20px; - right: 0; - bottom: 0; - position: absolute; - background: #d3d3d3; - border-radius: 7px; - border: 1px solid #999; - padding: 0px 7px 10px 19px; - z-index: 99; - box-shadow: 3px 3px 10px 1px rgba(0,0,0,0.5); - overflow: hidden; -} diff --git a/openslides/assignments/static/css/assignments/_site.scss b/openslides/assignments/static/css/assignments/_site.scss deleted file mode 100644 index e7c8fa269..000000000 --- a/openslides/assignments/static/css/assignments/_site.scss +++ /dev/null @@ -1,14 +0,0 @@ -#content .col1 .ballot-tabs { - ul { - margin-left: 0px; - } - li.active a { - background-color: #f5f5f5 !important; - } - .tab-content { - background-color: #f5f5f5; - border: 1px solid #ddd; - border-width: 0px 1px 1px 1px; - padding: 15px; - } -} diff --git a/openslides/assignments/static/js/assignments/base.js b/openslides/assignments/static/js/assignments/base.js deleted file mode 100644 index 08f481809..000000000 --- a/openslides/assignments/static/js/assignments/base.js +++ /dev/null @@ -1,500 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.assignments', []) - -.factory('AssignmentPollOption', [ - 'DS', - 'jsDataModel', - 'gettextCatalog', - 'Config', - 'MajorityMethods', - function (DS, jsDataModel, gettextCatalog, Config, MajorityMethods) { - return DS.defineResource({ - name: 'assignments/polloption', - useClass: jsDataModel, - // Change the stringified numbers to floats. - beforeInject: function (resource, instance) { - _.forEach(instance.votes, function (vote) { - vote.weight = parseFloat(vote.weight); - }); - }, - methods: { - getVotes: function () { - if (!this.poll.has_votes) { - // Return undefined if this poll has no votes. - return; - } - - // Initial values for the option - var votes = [], - config = Config.get('assignments_poll_100_percent_base').value; - - var base = this.poll.getPercentBase(config); - if (typeof base === 'object' && base !== null) { - // this.poll.pollmethod === 'yna' - base = base[this.id]; - } - - _.forEach(this.votes, function (vote) { - // Initial values for the vote - var order = '', - value = '', - percentStr = '', - percentNumber; - - // Check for special value - switch (vote.weight) { - case -1: - value = gettextCatalog.getString('majority'); - break; - case -2: - value = gettextCatalog.getString('undocumented'); - break; - default: - if (vote.weight >= 0) { - value = vote.weight; - } else { - value = 0; // Vote was not defined. Set value to 0. - } - } - switch (vote.value) { - case "Yes": - order = 1; - break; - case "No": - order = 2; - break; - case "Abstain": - order = 3; - break; - default: - order = 0; - } - - // Special case where to skip percents - var skipPercents = config === 'YES_NO' && vote.value === 'Abstain'; - - if (base && !skipPercents) { - percentNumber = Math.round(vote.weight * 100 / base * 100) / 100; - if (percentNumber >= 0) { - percentStr = '(' + percentNumber + ' %)'; - } - } - votes.push({ - 'order': order, - 'label': gettextCatalog.getString(vote.value), - 'value': value, - 'percentStr': percentStr, - 'percentNumber': percentNumber - }); - }); - return _.sortBy(votes, 'order'); - }, - - // Returns 0 or positive integer if quorum is reached or surpassed. - // Returns negativ integer if quorum is not reached. - // Returns undefined if we can not calculate the quorum. - isReached: function (method) { - if (!this.poll.has_votes) { - // Return undefined if this poll has no votes. - return; - } - var isReached; - var config = Config.get('assignments_poll_100_percent_base').value; - var base = this.poll.getPercentBase(config); - if (typeof base === 'object' && base !== null) { - // this.poll.pollmethod === 'yna' - base = base[this.id]; - } - if (base) { - // Provide result only if base is not undefined and not 0. - isReached = MajorityMethods[method](this.getVoteYes(), base); - } - return isReached; - }, - - // Returns the weight for the vote or the vote 'yes' in case of YNA poll method. - getVoteYes: function () { - var voteYes = 0; - if (this.poll.pollmethod === 'yna') { - var voteObj = _.find(this.votes, function (vote) { - return vote.value === 'Yes'; - }); - if (voteObj) { - voteYes = voteObj.weight; - } - } else { - // pollmethod === 'votes' - voteYes = this.votes[0].weight; - } - return voteYes; - } - }, - relations: { - belongsTo: { - 'assignments/poll': { - localField: 'poll', - localKey: 'poll_id', - }, - 'users/user': { - localField: 'candidate', - localKey: 'candidate_id', - } - } - }, - }); - } -]) - -.factory('AssignmentPoll', [ - '$http', - 'DS', - 'jsDataModel', - 'gettextCatalog', - 'AssignmentPollOption', - 'Config', - function ($http, DS, jsDataModel, gettextCatalog, AssignmentPollOption, Config) { - var name = 'assignments/poll'; - return DS.defineResource({ - name: name, - useClass: jsDataModel, - // Change the stringified numbers to floats. - beforeInject: function (resource, instance) { - var attrs = ['votescast', 'votesinvalid', 'votesvalid', 'votesabstain', 'votesno']; - _.forEach(attrs, function (attr) { - if (instance[attr] !== null) { - instance[attr] = parseFloat(instance[attr]); - } - }); - }, - methods: { - getResourceName: function () { - return name; - }, - - // Returns percent base. Returns undefined if calculation is not possible in general. - getPercentBase: function (config, type) { - var base; - switch (config) { - case 'CAST': - if (this.votescast <= 0 || this.votesinvalid < 0) { - // It would be OK to check only this.votescast < 0 because 0 - // is checked again later but this is a little bit faster. - break; - } - base = this.votescast; - /* falls through */ - case 'VALID': - if (this.votesvalid < 0) { - base = void 0; - break; - } - if (typeof base === 'undefined' && type !== 'votescast' && type !== 'votesinvalid') { - base = this.votesvalid; - } - /* falls through */ - case 'YES_NO_ABSTAIN': - case 'YES_NO': - if (this.pollmethod === 'yna') { - if (typeof base === 'undefined' && type !== 'votescast' && type !== 'votesinvalid' && type !== 'votesvalid') { - base = {}; - _.forEach(this.options, function (option) { - var allVotes = option.votes; - if (config === 'YES_NO') { - allVotes = _.filter(allVotes, function (vote) { - // Extract abstain votes in case of YES_NO percent base. - // Do not extract abstain vote if it is set to majority so the predicate later - // fails and therefor we get an undefined base. Reason: It should not be possible - // to set abstain to majority and nevertheless calculate percents of yes and no. - return vote.value !== 'Abstain' || vote.weight === -1; - }); - } - var predicate = function (vote) { - return vote.weight < 0; - }; - if (_.findIndex(allVotes, predicate) === -1) { - base[option.id] = _.reduce(allVotes, function (sum, vote) { - return sum + vote.weight; - }, 0); - } - }); - } - } else { - // this.pollmethod === 'votes' - var predicate = function (option) { - return option.votes[0].weight < 0; - }; - if (_.findIndex(this.options, predicate) !== -1) { - base = void 0; - } else { - if (typeof base === 'undefined' && type !== 'votesabstain' && - type !== 'votesno' && type !== 'votescast' && - type !== 'votesinvalid' && type !== 'votesvalid') { - base = _.reduce(this.options, function (sum, option) { - return sum + option.votes[0].weight; - }, 0); - } - } - } - } - return base; - }, - - // Returns object with value and percent for this poll (for votes valid/invalid/cast only). - getVote: function (type) { - if (!this.has_votes) { - // Return undefined if this poll has no votes. - return; - } - - // Initial values - var value = '', - percentStr = '', - percentNumber, - vote, - config = Config.get('assignments_poll_100_percent_base').value; - - switch (type) { - case 'votesabstain': - vote = this.votesabstain; - break; - case 'votesno': - vote = this.votesno; - break; - case 'votesinvalid': - vote = this.votesinvalid; - break; - case 'votesvalid': - vote = this.votesvalid; - break; - case 'votescast': - vote = this.votescast; - break; - } - - // Check special values - switch (vote) { - case -1: - value = gettextCatalog.getString('majority'); - break; - case -2: - value = gettextCatalog.getString('undocumented'); - break; - default: - if (vote >= 0) { - value = vote; - } else { - value = 0; // value was not defined - } - } - - // Calculate percent value - var base = this.getPercentBase(config, type); - if (base) { - percentNumber = Math.round(vote * 100 / (base) * 10) / 10; - percentStr = '(' + percentNumber + ' %)'; - } - return { - 'value': value, - 'percentStr': percentStr, - 'percentNumber': percentNumber, - 'display': value + ' ' + percentStr - }; - } - }, - relations: { - belongsTo: { - 'assignments/assignment': { - localField: 'assignment', - localKey: 'assignment_id', - } - }, - hasMany: { - 'assignments/polloption': { - localField: 'options', - foreignKey: 'poll_id', - } - } - }, - }); - } -]) - -.provider('AssignmentPollDecimalPlaces', [ - function () { - this.$get = ['$q', function ($q) { - return { - getPlaces: function (poll, find) { - if (find) { - return $q(function (resolve) { - resolve(0); - }); - } else { - return 0; - } - }, - }; - }]; - } -]) - -.factory('AssignmentRelatedUser', [ - 'DS', - function (DS) { - return DS.defineResource({ - name: 'assignments/relateduser', - relations: { - belongsTo: { - 'users/user': { - localField: 'user', - localKey: 'user_id', - } - } - } - }); - } -]) - -.factory('Assignment', [ - '$http', - 'DS', - 'Projector', - 'ProjectHelper', - 'AssignmentRelatedUser', - 'AssignmentPoll', - 'jsDataModel', - 'gettext', - function ($http, DS, Projector, ProjectHelper, AssignmentRelatedUser, AssignmentPoll, - jsDataModel, gettext) { - var name = 'assignments/assignment'; - return DS.defineResource({ - name: name, - useClass: jsDataModel, - verboseName: gettext('Election'), - verboseNamePlural: gettext('Elections'), - methods: { - getResourceName: function () { - return name; - }, - getTitle: function () { - return this.title; - }, - getAgendaTitle: function () { - return this.getTitle(); - }, - // link name which is shown in search result - getSearchResultName: function () { - return this.getAgendaTitle(); - }, - // return true if a specific relation matches for given searchquery - // (here: related_users/candidates) - hasSearchResult: function (results) { - var assignment = this; - // search for related users (check if any user.id from already found users matches) - return _.some(results, function(result) { - if (result.getResourceName() === "users/user") { - if (_.some(assignment.assignment_related_users, {'user_id': result.id})) { - return true; - } - } - }); - }, - // override project function of jsDataModel factory - project: function (projectorId, pollId) { - var isProjectedIds = this.isProjected(pollId); - var requestData = { - clear_ids: isProjectedIds, - }; - if (_.indexOf(isProjectedIds, projectorId) == -1) { - requestData.prune = { - id: projectorId, - element: { - name: 'assignments/assignment', - id: this.id, - poll: pollId - }, - }; - } - return ProjectHelper.project(requestData); - }, - // override isProjected function of jsDataModel factory - isProjected: function (poll_id, anyPoll) { - // Returns the ids of all projectors with an element - // with the name 'assignments/assignment'. Else returns an empty list. - // Provide a poll_id to query a specific poll or set anyPoll to true, to - // query whether any poll (but not the assignment itself) is projected. - var self = this; - var predicate = function (element) { - var value; - if (typeof poll_id === 'undefined') { - // Assignment detail slide without poll - value = element.name == 'assignments/assignment' && - typeof element.id !== 'undefined' && - element.id == self.id && - typeof element.poll === 'undefined'; - } else if (anyPoll) { - // Assignment detail slide with any poll - value = element.name == 'assignments/assignment' && - typeof element.id !== 'undefined' && - element.id == self.id && - typeof element.poll !== 'undefined'; - } else { - // Assignment detail slide with specific poll - value = element.name == 'assignments/assignment' && - typeof element.id !== 'undefined' && - element.id == self.id && - typeof element.poll !== 'undefined' && - element.poll == poll_id; - } - return value; - }; - var isProjectedIds = []; - Projector.getAll().forEach(function (projector) { - if (typeof _.findKey(projector.elements, predicate) === 'string') { - isProjectedIds.push(projector.id); - } - }); - return isProjectedIds; - }, - isRelatedProjected: function () { - var listOfSpeakers = []; - if (this.agenda_item) { - listOfSpeakers = this.agenda_item.isListOfSpeakersProjected(); - } - return listOfSpeakers.concat(this.isProjected(null, true)); - }, - }, - relations: { - belongsTo: { - 'agenda/item': { - localKey: 'agenda_item_id', - localField: 'agenda_item', - } - }, - hasMany: { - 'core/tag': { - localField: 'tags', - localKeys: 'tags_id', - }, - 'assignments/relateduser': { - localField: 'assignment_related_users', - foreignKey: 'assignment_id', - }, - 'assignments/poll': { - localField: 'polls', - foreignKey: 'assignment_id', - } - } - }, - beforeInject: function (resource, instance) { - AssignmentRelatedUser.ejectAll({where: {assignment_id: {'==': instance.id}}}); - } - }); - } -]) - -.run(['Assignment', function(Assignment) {}]); - -}()); diff --git a/openslides/assignments/static/js/assignments/pdf.js b/openslides/assignments/static/js/assignments/pdf.js deleted file mode 100644 index bbd4399dc..000000000 --- a/openslides/assignments/static/js/assignments/pdf.js +++ /dev/null @@ -1,666 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.assignments.pdf', ['OpenSlidesApp.core.pdf']) - -.factory('AssignmentContentProvider', [ - '$filter', - 'HTMLValidizer', - 'gettextCatalog', - 'PDFLayout', - 'AssignmentPollDecimalPlaces', - function($filter, HTMLValidizer, gettextCatalog, PDFLayout, AssignmentPollDecimalPlaces) { - - var createInstance = function(assignment) { - - // page title - var title = PDFLayout.createTitle(assignment.title); - var isElectedSemaphore = false; - - // open posts - var createPreamble = function() { - var preambleText = gettextCatalog.getString("Number of persons to be elected") + ": "; - var memberNumber = ""+assignment.open_posts; - var preamble = { - text: [ - { - text: preambleText, - bold: true, - style: 'textItem' - }, - { - text: memberNumber, - style: 'textItem' - } - ] - }; - return preamble; - }; - - // description - var createDescription = function() { - if (assignment.description) { - var html = HTMLValidizer.validize(assignment.description); - var descriptionText = gettextCatalog.getString("Description") + ":"; - var description = [ - { - text: descriptionText, - bold: true, - style: 'textItem' - }, - { - text: $(html).text(), - style: 'textItem', - margin: [10, 0, 0, 0] - } - ]; - return description; - } else { - return ""; - } - }; - - // show candidate list (if assignment phase is not 'finished') - var createCandidateList = function() { - if (assignment.phase != 2) { - var candidates = $filter('orderBy')(assignment.assignment_related_users, 'weight'); - var candidatesText = gettextCatalog.getString("Candidates") + ": "; - var userList = []; - - _.forEach(candidates, function(assignmentsRelatedUser) { - userList.push({ - text: assignmentsRelatedUser.user.get_full_name(), - margin: [0, 0, 0, 10], - } - ); - }); - - var cadidateList = { - columns: [ - { - text: candidatesText, - bold: true, - width: "25%", - style: 'textItem' - }, - { - ul: userList, - style: 'textItem' - } - ] - }; - return cadidateList; - } else { - return ""; - } - }; - - // handles the case if a candidate is elected or not - var electedCandidateLine = function(candidateName, pollOption, pollTableBody) { - if (pollOption.is_elected) { - isElectedSemaphore = true; - return { - text: candidateName + "*", - bold: true, - style: PDFLayout.flipTableRowStyle(pollTableBody.length) - }; - } else { - return { - text: candidateName, - style: PDFLayout.flipTableRowStyle(pollTableBody.length) - }; - } - }; - - //creates the voting string for the result table and differentiates between special values - var parseVoteValue = function(voteObject, printLabel, precision) { - var voteVal = ''; - if (voteObject) { - if (printLabel) { - voteVal += voteObject.label + ': '; - } - voteVal += $filter('number')(voteObject.value, precision); - - if (voteObject.percentStr) { - voteVal += ' ' + voteObject.percentStr; - } - } - voteVal += '\n'; - return voteVal; - }; - - // creates the election result table - var createPollResultTable = function() { - var resultBody = []; - _.forEach(assignment.polls, function(poll, pollIndex) { - if (poll.published) { - var pollTableBody = []; - var precision = AssignmentPollDecimalPlaces.getPlaces(poll); - - resultBody.push({ - text: gettextCatalog.getString('Ballot') + ' ' + (pollIndex+1), - bold: true, - style: 'textItem', - margin: [0, 15, 0, 0] - }); - - pollTableBody.push([ - { - text: gettextCatalog.getString('Candidates'), - style: 'tableHeader', - }, - { - text: gettextCatalog.getString('Votes'), - style: 'tableHeader', - } - ]); - - _.forEach(poll.options, function(pollOption, optionIndex) { - var candidateName = pollOption.candidate.get_full_name(); - var votes = pollOption.getVotes(); // 0 = yes, 1 = no, 2 = abstain - var tableLine = []; - - tableLine.push(electedCandidateLine(candidateName, pollOption, pollTableBody)); - if (poll.pollmethod == 'votes') { - tableLine.push( - { - text: parseVoteValue(votes[0], false, precision), - style: PDFLayout.flipTableRowStyle(pollTableBody.length) - } - ); - } else { - var resultBlock = []; - _.forEach(votes, function(vote) { - resultBlock.push(parseVoteValue(vote, true, precision)); - }); - tableLine.push({ - text: resultBlock, - style: PDFLayout.flipTableRowStyle(pollTableBody.length) - } - ); - } - pollTableBody.push(tableLine); - }); - - var pushConcludeRow = function (title, fieldName) { - if (poll[fieldName]) { - pollTableBody.push([ - { - text: gettextCatalog.getString(title), - style: 'tableConclude' - }, - { - text: parseVoteValue(poll.getVote(fieldName), false, precision), - style: 'tableConclude' - }, - ]); - } - }; - - pushConcludeRow('Abstain', 'votesabstain'); - pushConcludeRow('No', 'votesno'); - pushConcludeRow('Valid ballots', 'votesvalid'); - pushConcludeRow('Invalid ballots', 'votesinvalid'); - pushConcludeRow('Casted ballots', 'votescast'); - - var resultTableJsonSting = { - table: { - widths: ['64%','33%'], - headerRows: 1, - body: pollTableBody, - }, - layout: 'headerLineOnly', - }; - - resultBody.push(resultTableJsonSting); - } - }); - - // add the legend to the result body - if (assignment.polls.length > 0 && isElectedSemaphore) { - resultBody.push({ - text: '* = ' + gettextCatalog.getString('is elected'), - margin: [0, 5, 0, 0], - }); - } - - return resultBody; - }; - - var getContent = function() { - return [ - title, - createPreamble(), - createDescription(), - createCandidateList(), - createPollResultTable() - ]; - }; - - return { - getContent: getContent, - title: assignment.title - }; - }; - - return { - createInstance: createInstance - }; - } -]) - -.factory('BallotContentProvider', [ - '$q', - '$filter', - 'gettextCatalog', - 'PDFLayout', - 'Config', - 'User', - 'ImageConverter', - function($q, $filter, gettextCatalog, PDFLayout, Config, User, ImageConverter) { - var createInstance = function(assignment, poll, pollNumber) { - - var logoBallotPaperUrl = Config.get('logo_pdf_ballot_paper').value.path; - var imageMap = {}; - - // PDF header - var header = function() { - var columns = []; - - // logo - if (logoBallotPaperUrl) { - columns.push({ - image: logoBallotPaperUrl, - fit: [90,20], - width: '20%' - }); - } - var text = Config.get('general_event_name').value; - columns.push({ - text: text, - fontSize: 8, - alignment: 'right', - }); - - return { - color: '#555', - margin: [30, 10, 10, -10], // [left, top, right, bottom] - columns: columns, - columnGap: 10 - }; - }; - - // page title - var createTitle = function() { - return { - text: assignment.title, - style: 'title', - }; - }; - - // poll description - var createPollHint = function() { - var description = poll.description ? ': ' + poll.description : ''; - return { - text: gettextCatalog.getString("Ballot") + " " + pollNumber + description, - style: 'description', - }; - }; - - // election entries - var createYNBallotEntry = function(decision) { - var YNColumn = [ - { - width: "auto", - stack: [ - PDFLayout.createBallotEntry(gettextCatalog.getString("Yes")) - ] - }, - { - width: "auto", - stack: [ - PDFLayout.createBallotEntry(gettextCatalog.getString("No")) - ] - }, - ]; - - if (poll.pollmethod == 'yna') { - YNColumn.push({ - width: "auto", - stack: [ - PDFLayout.createBallotEntry(gettextCatalog.getString("Abstain")) - ] - }); - } - - return [ - { - text: decision, - margin: [40, 10, 0, 0], - }, - { - columns: YNColumn - } - ]; - }; - - var createSelectionField = function() { - var candidates = $filter('orderBy')(poll.options, 'weight'); - var candidateBallotList = []; - - if (poll.pollmethod == 'votes') { - _.forEach(candidates, function(option) { - var candidate = option.candidate.get_full_name(); - candidateBallotList.push(PDFLayout.createBallotEntry(candidate)); - }); - // Add 'no' option - var no = gettextCatalog.getString('No'); - var ballotEntry = PDFLayout.createBallotEntry(no); - ballotEntry.margin[1] = 25; // top margin - candidateBallotList.push(ballotEntry); - } else { - _.forEach(candidates, function(option) { - var candidate; - if (option.candidate) { - candidate = option.candidate.get_full_name(); - } - candidateBallotList.push(createYNBallotEntry(candidate)); - }); - } - return candidateBallotList; - }; - - var createSection = function(marginTop) { - - // since it is not possible to give a column a fixed height, we draw an "empty" column - // with a one px width and a fixed top-margin - return { - columns: [ - { - width: 1, - margin: [0, marginTop], - text: '', - }, - { - width: '*', - stack: [ - header(), - createTitle(), - createPollHint(), - createSelectionField(), - ], - }, - ] - }; - }; - - var createTableBody = function(numberOfRows, sheetend, maxballots) { - var ballotstoprint = numberOfRows * 2; - if (Number.isInteger(maxballots) && maxballots > 0 && maxballots < ballotstoprint) { - ballotstoprint = maxballots; - } - var tableBody = []; - while (ballotstoprint > 1){ - tableBody.push([createSection(sheetend), createSection(sheetend)]); - ballotstoprint -= 2; - } - if (ballotstoprint == 1) { - tableBody.push([createSection(sheetend), '']); - } - return tableBody; - }; - - var createContentTable = function() { - // first, determine how many ballots we need - var amount; - var amount_method = Config.get('assignments_pdf_ballot_papers_selection').value; - switch (amount_method) { - case 'NUMBER_OF_ALL_PARTICIPANTS': - amount = User.getAll().length; - break; - case 'NUMBER_OF_DELEGATES': - //TODO: assumption that DELEGATES is always group id 2. This may not be true - var group_id = 2; - amount = User.filter({where: {'groups_id': {contains:group_id} }}).length; - break; - case 'CUSTOM_NUMBER': - amount = Config.get('assignments_pdf_ballot_papers_number').value; - break; - default: - // should not happen. - amount = 0; - } - var tabledContent = []; - var rowsperpage; - var sheetend; - if (poll.pollmethod == 'votes') { - if (poll.options.length <= 4) { - sheetend = 105; - rowsperpage = 4; - } else if (poll.options.length <= 8) { - sheetend = 140; - rowsperpage = 3; - } else if (poll.options.length <= 12) { - sheetend = 210; - rowsperpage = 2; - } - else { //works untill ~30 people - sheetend = 418; - rowsperpage = 1; - } - } else { - if (poll.options.length <= 2) { - sheetend = 105; - rowsperpage = 4; - } else if (poll.options.length <= 4) { - sheetend = 140; - rowsperpage = 3; - } else if (poll.options.length <= 6) { - sheetend = 210; - rowsperpage = 2; - } else { - sheetend = 418; - rowsperpage = 1; - } - } - var page_entries = rowsperpage * 2; - var fullpages = Math.floor(amount / page_entries); - for (var i=0; i < fullpages; i++) { - tabledContent.push({ - table: { - headerRows: 1, - widths: ['50%', '50%'], - body: createTableBody(rowsperpage, sheetend), - pageBreak: 'after' - }, - layout: PDFLayout.getBallotLayoutLines(), - rowsperpage: rowsperpage - }); - } - // fill the last page only partially - var lastpage_ballots = amount - (fullpages * page_entries); - if (lastpage_ballots < page_entries && lastpage_ballots > 0){ - var partialpage = createTableBody(rowsperpage, sheetend, lastpage_ballots); - tabledContent.push({ - table: { - headerRows: 1, - widths: ['50%', '50%'], - body: partialpage - }, - layout: PDFLayout.getBallotLayoutLines(), - rowsperpage: rowsperpage - }); - } - return tabledContent; - }; - - var getContent = function() { - return createContentTable(); - }; - - var getImageMap = function () { - return imageMap; - }; - - return $q(function (resolve, reject) { - var imageSources = [ - logoBallotPaperUrl, - ]; - ImageConverter.toBase64(imageSources).then(function (_imageMap) { - imageMap = _imageMap; - resolve({ - getContent: getContent, - getImageMap: getImageMap, - }); - }, reject); - }); - }; - - return { - createInstance: createInstance - }; - } -]) - -.factory('AssignmentCatalogContentProvider', [ - 'gettextCatalog', - 'PDFLayout', - 'Config', - function(gettextCatalog, PDFLayout, Config) { - - var createInstance = function(allAssignments) { - - var title = PDFLayout.createTitle( - Config.translate(Config.get('assignments_pdf_title').value) - ); - - var createPreamble = function() { - var preambleText = Config.get('assignments_pdf_preamble').value; - if (preambleText) { - return { - text: preambleText, - style: "preamble" - }; - } else { - return ""; - } - }; - - var createTOContent = function(assignmentTitles) { - var heading = { - text: gettextCatalog.getString("Table of contents"), - style: "heading2", - }; - - var toc = []; - _.forEach(assignmentTitles, function(title) { - toc.push({ - text: title, - style: "tableofcontent" - }); - }); - - return [ - heading, - toc, - PDFLayout.addPageBreak() - ]; - }; - - var getContent = function() { - var content = []; - var assignmentContent = []; - var assignmentTitles = []; - - _.forEach(allAssignments, function(assignment, key) { - assignmentTitles.push(assignment.title); - assignmentContent.push(assignment.getContent()); - if (key < allAssignments.length - 1) { - assignmentContent.push(PDFLayout.addPageBreak()); - } - }); - - content.push(title); - content.push(createPreamble()); - content.push(createTOContent(assignmentTitles)); - content.push(assignmentContent); - return content; - }; - - return { - getContent: getContent - }; - }; - - return { - createInstance: createInstance - }; - } -]) - -.factory('AssignmentPdfExport', [ - 'gettextCatalog', - 'AssignmentContentProvider', - 'AssignmentCatalogContentProvider', - 'PdfMakeDocumentProvider', - 'BallotContentProvider', - 'PdfMakeBallotPaperProvider', - 'PdfCreate', - 'Messaging', - function (gettextCatalog, AssignmentContentProvider, AssignmentCatalogContentProvider, - PdfMakeDocumentProvider, BallotContentProvider, PdfMakeBallotPaperProvider, PdfCreate, - Messaging) { - return { - export: function (assignments, singleAssignment) { - var filename = singleAssignment ? - gettextCatalog.getString('Election') + '_' + assignments.title : - gettextCatalog.getString('Elections'); - filename += '.pdf'; - if (singleAssignment) { - assignments = [assignments]; - } - - // Convert the assignments to content providers - var assignmentContentProviderArray = _.map(assignments, function (assignment) { - return AssignmentContentProvider.createInstance(assignment); - }); - - var documentProviderPromise; - if (singleAssignment) { - documentProviderPromise = - PdfMakeDocumentProvider.createInstance(assignmentContentProviderArray[0]); - } else { - var assignmentCatalogContentProvider = - AssignmentCatalogContentProvider.createInstance(assignmentContentProviderArray); - documentProviderPromise = - PdfMakeDocumentProvider.createInstance(assignmentCatalogContentProvider); - } - documentProviderPromise.then(function (documentProvider) { - PdfCreate.download(documentProvider, filename); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - }); - }, - createBallotPdf: function (assignment, pollId) { - var thePoll; - var pollNumber; - _.forEach(assignment.polls, function(poll, pollIndex) { - if (poll.id == pollId) { - thePoll = poll; - pollNumber = pollIndex+1; - } - }); - var filename = gettextCatalog.getString('Ballot') + '_' + pollNumber + '_' + assignment.title + '.pdf'; - BallotContentProvider.createInstance(assignment, thePoll, pollNumber).then(function (ballotContentProvider) { - var documentProvider = PdfMakeBallotPaperProvider.createInstance(ballotContentProvider); - PdfCreate.download(documentProvider, filename); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - }); - }, - }; - } -]); - -}()); diff --git a/openslides/assignments/static/js/assignments/projector.js b/openslides/assignments/static/js/assignments/projector.js deleted file mode 100644 index ac27a8c21..000000000 --- a/openslides/assignments/static/js/assignments/projector.js +++ /dev/null @@ -1,46 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.assignments.projector', ['OpenSlidesApp.assignments']) - -.config([ - 'slidesProvider', - function(slidesProvider) { - slidesProvider.registerSlide('assignments/assignment', { - template: 'static/templates/assignments/slide_assignment.html', - }); - } -]) - -.controller('SlideAssignmentCtrl', [ - '$scope', - 'Assignment', - 'AssignmentPoll', - 'AssignmentPhases', - 'AssignmentPollDecimalPlaces', - 'User', - function($scope, Assignment, AssignmentPoll, AssignmentPhases, AssignmentPollDecimalPlaces, User) { - // Attention! Each object that is used here has to be dealt on server side. - // Add it to the coresponding get_requirements method of the ProjectorElement - // class. - var id = $scope.element.id; - $scope.showResult = $scope.element.poll; - - if ($scope.showResult) { - var poll = AssignmentPoll.get($scope.showResult); - $scope.votesPrecision = 0; - if (poll) { - AssignmentPollDecimalPlaces.getPlaces(poll, true).then(function (decimalPlaces) { - $scope.votesPrecision = decimalPlaces; - }); - } - } - - Assignment.bindOne(id, $scope, 'assignment'); - $scope.phases = AssignmentPhases; - User.bindAll({}, $scope, 'users'); - } -]); - -}()); diff --git a/openslides/assignments/static/js/assignments/site.js b/openslides/assignments/static/js/assignments/site.js deleted file mode 100644 index 155a4725d..000000000 --- a/openslides/assignments/static/js/assignments/site.js +++ /dev/null @@ -1,941 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.assignments.site', [ - 'OpenSlidesApp.assignments', - 'OpenSlidesApp.core.pdf', - 'OpenSlidesApp.assignments.pdf', - 'OpenSlidesApp.poll.majority' -]) - -.config([ - 'mainMenuProvider', - 'gettext', - function (mainMenuProvider, gettext) { - mainMenuProvider.register({ - 'ui_sref': 'assignments.assignment.list', - 'img_class': 'pie-chart', - 'title': gettext('Elections'), - 'weight': 400, - 'perm': 'assignments.can_see' - }); - } -]) - -.config([ - 'SearchProvider', - 'gettext', - function (SearchProvider, gettext) { - SearchProvider.register({ - 'verboseName': gettext('Elections'), - 'collectionName': 'assignments/assignment', - 'urlDetailState': 'assignments.assignment.detail', - 'weight': 400, - }); - } -]) - -.config([ - '$stateProvider', - 'gettext', - function($stateProvider, gettext) { - $stateProvider - .state('assignments', { - url: '/assignments', - abstract: true, - template: "", - data: { - title: gettext('Elections'), - basePerm: 'assignments.can_see', - }, - }) - .state('assignments.assignment', { - abstract: true, - template: "", - }) - .state('assignments.assignment.list', {}) - .state('assignments.assignment.detail', { - controller: 'AssignmentDetailCtrl', - resolve: { - assignmentId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }], - } - }) - // redirects to assignment detail and opens assignment edit form dialog, uses edit url, - // used by ui-sref links from agenda only - // (from assignment controller use AssignmentForm factory instead to open dialog in front - // of current view without redirect) - .state('assignments.assignment.detail.update', { - onEnter: ['$stateParams', '$state', 'ngDialog', - function($stateParams, $state, ngDialog) { - ngDialog.open({ - template: 'static/templates/assignments/assignment-form.html', - controller: 'AssignmentUpdateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - assignmentId: function() { - return $stateParams.id; - }, - }, - preCloseCallback: function() { - $state.go('assignments.assignment.detail', {assignment: $stateParams.id}); - return true; - } - }); - } - ] - }); - } -]) - -// Service for generic assignment form (create and update) -.factory('AssignmentForm', [ - 'gettextCatalog', - 'operator', - 'Editor', - 'Mediafile', - 'Tag', - 'Assignment', - 'Agenda', - 'AgendaTree', - 'ShowAsAgendaItemField', - function (gettextCatalog, operator, Editor, Mediafile, Tag, Assignment, Agenda, AgendaTree, ShowAsAgendaItemField) { - return { - // ngDialog for assignment form - getDialog: function (assignment) { - return { - template: 'static/templates/assignments/assignment-form.html', - controller: (assignment) ? 'AssignmentUpdateCtrl' : 'AssignmentCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - assignmentId: function () {return assignment ? assignment.id : void 0;} - }, - }; - }, - // angular-formly fields for assignment form - getFormFields: function (isCreateForm) { - var images = Mediafile.getAllImages(); - var formFields = [ - { - key: 'title', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Title'), - required: true - } - }, - { - key: 'description', - type: 'editor', - templateOptions: { - label: gettextCatalog.getString('Description') - }, - data: { - ckeditorOptions: Editor.getOptions(images) - } - }, - { - key: 'open_posts', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Number of persons to be elected'), - type: 'number', - min: 1, - required: true - } - }, - { - key: 'poll_description_default', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Default comment on the ballot paper') - } - }]; - - // show as agenda item + parent item - if (isCreateForm) { - formFields.push(ShowAsAgendaItemField('assignments.can_manage')); - formFields.push({ - key: 'agenda_parent_id', - type: 'select-single', - templateOptions: { - label: gettextCatalog.getString('Parent item'), - options: AgendaTree.getFlatTree(Agenda.getAll()), - ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id', - placeholder: gettextCatalog.getString('Select a parent item ...') - }, - hide: !operator.hasPerms('agenda.can_manage') - }); - } - // more (with tags field) - if (Tag.getAll().length > 0) { - formFields.push( - { - key: 'more', - type: 'checkbox', - templateOptions: { - label: gettextCatalog.getString('Show extended fields') - }, - hide: !operator.hasPerms('assignments.can_manage') - }, - { - template: '
', - hideExpression: '!model.more' - }, - { - key: 'tags_id', - type: 'select-multiple', - templateOptions: { - label: gettextCatalog.getString('Tags'), - options: Tag.getAll(), - ngOptions: 'option.id as option.name for option in to.options', - placeholder: gettextCatalog.getString('Select or search a tag ...') - }, - hideExpression: '!model.more' - } - ); - } - - return formFields; - } - }; - } -]) - -// Cache for AssignmentPollDetailCtrl so that users choices are keeped during user actions (e. g. save poll form). -.value('AssignmentPollDetailCtrlCache', {}) - -// Child controller of AssignmentDetailCtrl for each single poll. -.controller('AssignmentPollDetailCtrl', [ - '$scope', - 'MajorityMethodChoices', - 'Config', - 'AssignmentPollDetailCtrlCache', - 'AssignmentPoll', - 'AssignmentPollDecimalPlaces', - function ($scope, MajorityMethodChoices, Config, AssignmentPollDetailCtrlCache, - AssignmentPoll, AssignmentPollDecimalPlaces) { - // Define choices. - $scope.methodChoices = MajorityMethodChoices; - // TODO: Get $scope.baseChoices from config_variables.py without copying them. - - $scope.votesPrecision = AssignmentPollDecimalPlaces.getPlaces($scope.poll); - - // Setup empty cache with default values. - if (typeof AssignmentPollDetailCtrlCache[$scope.poll.id] === 'undefined') { - AssignmentPollDetailCtrlCache[$scope.poll.id] = { - method: $scope.config('assignments_poll_default_majority_method'), - }; - } - - // Fetch users choices from cache. - $scope.method = AssignmentPollDetailCtrlCache[$scope.poll.id].method; - - $scope.recalculateMajorities = function (method) { - $scope.method = method; - _.forEach($scope.poll.options, function (option) { - option.majorityReached = option.isReached(method); - }); - }; - $scope.recalculateMajorities($scope.method); - - $scope.saveDescriptionChange = function (poll) { - AssignmentPoll.save(poll); - }; - - // Save current values to cache on destroy of this controller. - $scope.$on('$destroy', function() { - AssignmentPollDetailCtrlCache[$scope.poll.id] = { - method: $scope.method, - }; - }); - } -]) - -.controller('AssignmentListCtrl', [ - '$scope', - 'ngDialog', - 'AssignmentForm', - 'Assignment', - 'Tag', - 'Agenda', - 'Projector', - 'ProjectionDefault', - 'gettextCatalog', - 'User', - 'osTableFilter', - 'osTableSort', - 'osTablePagination', - 'gettext', - 'AssignmentPhases', - 'AssignmentPdfExport', - function($scope, ngDialog, AssignmentForm, Assignment, Tag, Agenda, Projector, - ProjectionDefault, gettextCatalog, User, osTableFilter, osTableSort, osTablePagination, - gettext, AssignmentPhases, AssignmentPdfExport) { - $scope.$watch(function () { - return Assignment.lastModified(); - }, function () { - $scope.assignments = _.orderBy(Assignment.getAll(), ['title']); - }); - Tag.bindAll({}, $scope, 'tags'); - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - var projectiondefault = ProjectionDefault.filter({name: 'assignments'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId = projectiondefault.projector_id; - } - }); - $scope.phases = AssignmentPhases; - $scope.alert = {}; - - // Filtering - $scope.filter = osTableFilter.createInstance('AssignmentTableFilter'); - - if (!$scope.filter.existsStorageEntry()) { - $scope.filter.multiselectFilters = { - tag: [], - phase: [], - }; - } - $scope.filter.propertyList = ['title', 'description']; - $scope.filter.propertyFunctionList = [ - function (assignment) { - return gettextCatalog.getString($scope.phases[assignment.phase].display_name); - }, - ]; - $scope.filter.propertyDict = { - 'assignment_related_users': function (candidate) { - return candidate.user.get_short_name(); - }, - 'tags': function (tag) { - return tag.name; - }, - }; - $scope.getItemId = { - tag: function (assignment) {return assignment.tags_id;}, - phase: function (assignment) {return assignment.phase;}, - }; - - // Sorting - $scope.sort = osTableSort.createInstance('AssignmentTableSort'); - if (!$scope.sort.column) { - $scope.sort.column = 'title'; - } - $scope.sortOptions = [ - {name: 'agenda_item.getItemNumberWithAncestors()', - display_name: gettext('Item')}, - {name: 'title', - display_name: gettext('Title')}, - {name: 'phase', - display_name: gettext('Phase')}, - {name: 'assignment_related_users.length', - display_name: gettext('Number of candidates')}, - ]; - $scope.hasTag = function (assignment, tag) { - return _.indexOf(assignment.tags_id, tag.id) > -1; - }; - $scope.toggleTag = function (assignment, tag) { - if ($scope.hasTag(assignment, tag)) { - assignment.tags_id = _.filter(assignment.tags_id, function (tag_id){ - return tag_id != tag.id; - }); - } else { - assignment.tags_id.push(tag.id); - } - Assignment.save(assignment); - }; - - // Pagination - $scope.pagination = osTablePagination.createInstance('AssignmentTablePagination'); - - // update phase - $scope.updatePhase = function (assignment, phase_id) { - assignment.phase = phase_id; - Assignment.save(assignment); - }; - // open new/edit dialog - $scope.openDialog = function (assignment) { - ngDialog.open(AssignmentForm.getDialog(assignment)); - }; - // *** select mode functions *** - $scope.isSelectMode = false; - // check all checkboxes - $scope.checkAll = function () { - $scope.selectedAll = !$scope.selectedAll; - angular.forEach($scope.assignments, function (assignment) { - assignment.selected = $scope.selectedAll; - }); - }; - // uncheck all checkboxes if isSelectMode is closed - $scope.uncheckAll = function () { - if (!$scope.isSelectMode) { - $scope.selectedAll = false; - angular.forEach($scope.assignments, function (assignment) { - assignment.selected = false; - }); - } - }; - // delete all selected assignments - $scope.deleteMultiple = function () { - angular.forEach($scope.assignments, function (assignment) { - if (assignment.selected) - Assignment.destroy(assignment.id); - }); - $scope.isSelectMode = false; - $scope.uncheckAll(); - }; - // delete single assignment - $scope.delete = function (assignment) { - Assignment.destroy(assignment.id); - }; - // create the PDF List - $scope.pdfExport = function () { - AssignmentPdfExport.export($scope.assignmentsFiltered); - }; - } -]) - -.controller('AssignmentDetailCtrl', [ - '$scope', - '$http', - '$filter', - '$timeout', - 'filterFilter', - 'gettext', - 'ngDialog', - 'AssignmentForm', - 'operator', - 'Assignment', - 'User', - 'assignmentId', - 'Projector', - 'ProjectionDefault', - 'gettextCatalog', - 'AssignmentPhases', - 'AssignmentPdfExport', - 'WebpageTitle', - 'ErrorMessage', - function($scope, $http, $filter, $timeout, filterFilter, gettext, ngDialog, AssignmentForm, operator, - Assignment, User, assignmentId, Projector, ProjectionDefault, gettextCatalog, AssignmentPhases, - AssignmentPdfExport, WebpageTitle, ErrorMessage) { - User.bindAll({}, $scope, 'users'); - var assignment = Assignment.get(assignmentId); - Assignment.loadRelations(assignment, 'agenda_item'); - // This flag is for setting 'activeTab' to recently added (last) ballot tab. - // Set this flag, if ballots are added/removed. When the next autoupdate comes - // in, the tabset will be updated. - var updateBallotTabsFlag = true; - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - var projectiondefault = ProjectionDefault.filter({name: 'assignments'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId = projectiondefault.projector_id; - } - }); - $scope.$watch(function () { - return Assignment.lastModified(assignmentId); - }, function () { - // setup sorting of candidates - $scope.relatedUsersSorted = $filter('orderBy')(assignment.assignment_related_users, 'weight'); - $scope.assignment = Assignment.get(assignment.id); - if (updateBallotTabsFlag) { - $scope.activeTab = $scope.assignment.polls.length - 1; - updateBallotTabsFlag = false; - } - WebpageTitle.updateTitle(gettextCatalog.getString('Election') + ' ' + $scope.assignment.title); - }); - $scope.candidateSelectBox = {}; - $scope.phases = AssignmentPhases; - $scope.alert = {}; - - // open edit dialog - $scope.openDialog = function () { - ngDialog.open(AssignmentForm.getDialog($scope.assignment)); - }; - // add (nominate) candidate - $scope.addCandidate = function (userId) { - $http.post('/rest/assignments/assignment/' + assignmentId + '/candidature_other/', {'user': userId}) - .then(function (success){ - $scope.alert.show = false; - $scope.candidateSelectBox = {}; - }, function (error){ - $scope.alert = ErrorMessage.forAlert(error); - $scope.candidateSelectBox = {}; - }); - }; - // remove candidate - $scope.removeCandidate = function (userId) { - $http.delete('/rest/assignments/assignment/' + assignmentId + '/candidature_other/', - {headers: {'Content-Type': 'application/json'}, - data: JSON.stringify({user: userId})}) - .then(function (success) {}, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - // add me (nominate self as candidate) - $scope.addMe = function () { - $http.post('/rest/assignments/assignment/' + assignmentId + '/candidature_self/', {}).then( - function (success) { - $scope.alert.show = false; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - // remove me (withdraw own candidature) - $scope.removeMe = function () { - $http.delete('/rest/assignments/assignment/' + assignmentId + '/candidature_self/').then( - function (success) { - $scope.alert.show = false; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - // check if current user is already a candidate (elected==false) - $scope.isCandidate = function () { - var check = $scope.assignment.assignment_related_users.map(function(candidate) { - if (!candidate.elected) { - return candidate.user_id; - } - }).indexOf(operator.user.id); - if (check > -1) { - return true; - } else { - return false; - } - }; - // Sort all candidates - $scope.treeOptions = { - dropped: function () { - var sortedCandidates = []; - _.forEach($scope.relatedUsersSorted, function (user) { - sortedCandidates.push(user.id); - }); - $http.post('/rest/assignments/assignment/' + assignmentId + '/sort_related_users/', - {related_users: sortedCandidates} - ); - } - }; - // update phase - $scope.updatePhase = function (phase_id) { - $scope.assignment.phase = phase_id; - Assignment.save($scope.assignment); - }; - // create new ballot - $scope.createBallot = function () { - $http.post('/rest/assignments/assignment/' + assignmentId + '/create_poll/').then( - function (success) { - $scope.alert.show = false; - if (assignment.phase === 0) { - $scope.updatePhase(1); - } - updateBallotTabsFlag = true; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - // delete ballot - $scope.deleteBallot = function (poll) { - poll.DSDestroy().then( - function (success) { - $scope.activeTab = $scope.activeTab - 1; - updateBallotTabsFlag = true; - } - ); - }; - // edit poll dialog - $scope.editPollDialog = function (poll, ballot) { - ngDialog.open({ - template: 'static/templates/assignments/assignmentpoll-form.html', - controller: 'AssignmentPollUpdateCtrl', - className: 'ngdialog-theme-default', - closeByEscape: false, - closeByDocument: false, - resolve: { - assignmentpollId: function () {return poll.id;}, - ballot: function () {return ballot;}, - } - }); - }; - // publish ballot - $scope.togglePublishBallot = function (poll) { - poll.DSUpdate({ - assignment_id: assignmentId, - published: !poll.published, - }) - .then(function (success) { - $scope.alert.show = false; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - - // mark candidate as (not) elected - $scope.markElected = function (user, reverse) { - if (reverse) { - $http.delete( - '/rest/assignments/assignment/' + assignmentId + '/mark_elected/', { - headers: { - 'Content-Type': 'application/json' - }, - data: JSON.stringify({user: user}) - }); - } else { - $http.post('/rest/assignments/assignment/' + assignmentId + '/mark_elected/', {'user': user}); - } - - }; - - // Creates the document as pdf - $scope.pdfExport = function() { - AssignmentPdfExport.export($scope.assignment, true); - }; - // Creates the ballotpaper as pdf - $scope.ballotpaperExport = function(pollId) { - AssignmentPdfExport.createBallotPdf($scope.assignment, pollId); - }; - - // Just mark some vote value strings for translation. - gettext('Yes'); - gettext('No'); - gettext('Abstain'); - } -]) - -.controller('AssignmentCreateCtrl', [ - '$scope', - '$state', - 'Assignment', - 'AssignmentForm', - 'Agenda', - 'Config', - 'ErrorMessage', - function($scope, $state, Assignment, AssignmentForm, Agenda, Config, ErrorMessage) { - $scope.model = { - agenda_type: parseInt(Config.get('agenda_new_items_default_visibility').value), - }; - // set default value for open posts form field - $scope.model.open_posts = 1; - // get all form fields - $scope.formFields = AssignmentForm.getFormFields(true); - // save assignment - $scope.save = function(assignment, gotoDetailView) { - Assignment.create(assignment).then( - function (success) { - if (gotoDetailView) { - $state.go('assignments.assignment.detail', {id: success.id}); - } - $scope.closeThisDialog(); - }, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('AssignmentUpdateCtrl', [ - '$scope', - '$state', - 'Assignment', - 'AssignmentForm', - 'Agenda', - 'assignmentId', - 'ErrorMessage', - function($scope, $state, Assignment, AssignmentForm, Agenda, assignmentId, ErrorMessage) { - var assignment = Assignment.get(assignmentId); - $scope.alert = {}; - // set initial values for form model by create deep copy of assignment object - // so list/detail view is not updated while editing - $scope.model = angular.copy(assignment); - // get all form fields - $scope.formFields = AssignmentForm.getFormFields(); - - // save assignment - $scope.save = function (assignment, gotoDetailView) { - // inject the changed assignment (copy) object back into DS store - Assignment.inject(assignment); - // save changed assignment object on server - Assignment.save(assignment).then( - function(success) { - if (gotoDetailView) { - $state.go('assignments.assignment.detail', {id: success.id}); - } - $scope.closeThisDialog(); - }, - function (error) { - // save error: revert all changes by restore - // (refresh) original assignment object from server - Assignment.refresh(assignment); - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('AssignmentPollUpdateCtrl', [ - '$scope', - '$filter', - 'gettextCatalog', - 'AssignmentPoll', - 'assignmentpollId', - 'AssignmentPollDecimalPlaces', - 'ballot', - 'ErrorMessage', - function($scope, $filter, gettextCatalog, AssignmentPoll, assignmentpollId, - AssignmentPollDecimalPlaces, ballot, ErrorMessage) { - // set initial values for form model by create deep copy of assignmentpoll object - // so detail view is not updated while editing poll - var assignmentpoll = angular.copy(AssignmentPoll.get(assignmentpollId)); - $scope.model = assignmentpoll; - $scope.ballot = ballot; - $scope.formFields = []; - $scope.alert = {}; - - // For number inputs - var step = Math.pow(10, -AssignmentPollDecimalPlaces.getPlaces(assignmentpoll)); - - // add dynamic form fields - var options = $filter('orderBy')(assignmentpoll.options, 'weight'); - _.forEach(options, function(option) { - var defaultValue; - if (assignmentpoll.pollmethod == 'yna' || assignmentpoll.pollmethod == 'yn') { - defaultValue = {}; - _.forEach(option.votes, function (vote) { - defaultValue[vote.value.toLowerCase()] = vote.weight; - }); - - var columnClass = (assignmentpoll.pollmethod === 'yna') ? 'col-xs-4' : 'col-xs-6'; - var columns = [ - { - key: 'yes_' + option.candidate_id, - type: 'input', - className: columnClass + ' no-padding-left', - templateOptions: { - label: gettextCatalog.getString('Yes'), - type: 'number', - min: -2, - step: step, - required: true - }, - defaultValue: defaultValue.yes - }, - { - key: 'no_' + option.candidate_id, - type: 'input', - className: columnClass + ' no-padding-left' + - (assignmentpoll.pollmethod === 'yn' ? ' no-padding-right' : ''), - templateOptions: { - label: gettextCatalog.getString('No'), - type: 'number', - min: -2, - step: step, - required: true - }, - defaultValue: defaultValue.no - } - ]; - if (assignmentpoll.pollmethod == 'yna'){ - columns.push({ - key:'abstain_' + option.candidate_id, - type: 'input', - className: columnClass + ' no-padding-left no-padding-right', - templateOptions: { - label: gettextCatalog.getString('Abstain'), - type: 'number', - min: -2, - step: step, - required: true - }, - defaultValue: defaultValue.abstain - }); - } - $scope.formFields.push({ - noFormControl: true, - template: '' + option.candidate.get_full_name() + '' - }, - { - className: 'row', - fieldGroup: columns, - }); - } else { // votes method - if (option.votes.length) { - defaultValue = option.votes[0].weight; - } - $scope.formFields.push({ - key: 'vote_' + option.candidate_id, - type: 'input', - templateOptions: { - label: option.candidate.get_full_name(), - type: 'number', - min: -2, - step: step, - required: true, - }, - defaultValue: defaultValue - }); - } - }); - if (assignmentpoll.pollmethod == 'votes'){ - $scope.formFields.push( - { - key: 'votesabstain', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Abstain'), - type: 'number', - step: step, - min: -2, - } - }, - { - key: 'votesno', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('No'), - type: 'number', - step: step, - min: -2, - } - } - ); - } - // add general form fields - $scope.formFields.push( - { - template: '
', - }, - { - key: 'votesvalid', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Valid ballots'), - type: 'number', - step: step, - min: -2, - } - }, - { - key: 'votesinvalid', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Invalid ballots'), - type: 'number', - step: step, - min: -2, - } - }, - { - key: 'votescast', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Casted ballots'), - type: 'number', - step: step, - min: -2, - } - } - ); - - // save assignmentpoll - $scope.save = function (poll) { - var votes = []; - if (assignmentpoll.pollmethod == 'yna') { - assignmentpoll.options.forEach(function(option) { - votes.push({ - 'Yes': poll['yes_' + option.candidate_id], - 'No': poll['no_' + option.candidate_id], - 'Abstain': poll['abstain_' + option.candidate_id] - }); - }); - } else if (assignmentpoll.pollmethod == 'yn') { - assignmentpoll.options.forEach(function(option) { - votes.push({ - 'Yes': poll['yes_' + option.candidate_id], - 'No': poll['no_' + option.candidate_id] - }); - }); - } else { - assignmentpoll.options.forEach(function(option) { - votes.push({ - 'Votes': poll['vote_' + option.candidate_id], - }); - }); - } - // save change poll object on server - poll.DSUpdate({ - assignment_id: poll.assignment_id, - votes: votes, - votesabstain: poll.votesabstain, - votesno: poll.votesno, - votesvalid: poll.votesvalid, - votesinvalid: poll.votesinvalid, - votescast: poll.votescast, - }) - .then(function(success) { - $scope.alert.show = false; - $scope.closeThisDialog(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -//mark all assignment config strings for translation with Javascript -.config([ - 'gettext', - function (gettext) { - gettext('Election method'); - gettext('Automatic assign of method'); - gettext('Always one option per candidate'); - gettext('Always Yes-No-Abstain per candidate'); - gettext('Always Yes/No per candidate'); - gettext('Elections'); - gettext('Ballot and ballot papers'); - gettext('The 100-%-base of an election result consists of'); - gettext('For Yes/No/Abstain per candidate and Yes/No per candidate the 100-%-base ' + - 'depends on the election method: If there is only one option per candidate, ' + - 'the sum of all votes of all candidates is 100 %. Otherwise for each ' + - 'candidate the sum of all votes is 100 %.'); - gettext('Yes/No/Abstain per candidate'); - gettext('Yes/No per candidate'); - gettext('All valid ballots'); - gettext('All casted ballots'); - gettext('Disabled (no percents)'); - gettext('Number of ballot papers (selection)'); - gettext('Number of all delegates'); - gettext('Number of all participants'); - gettext('Use the following custom number'); - gettext('Custom number of ballot papers'); - gettext('Required majority'); - gettext('Default method to check whether a candidate has reached the required majority.'); - gettext('Simple majority'); - gettext('Two-thirds majority'); - gettext('Three-quarters majority'); - gettext('Disabled'); - gettext('Put all candidates on the list of speakers'); - gettext('Title for PDF document (all elections)'); - gettext('Preamble text for PDF document (all elections)'); - //other translations - gettext('Searching for candidates'); - gettext('Voting'); - gettext('Finished'); - } -]); - -}()); diff --git a/openslides/assignments/static/templates/assignments/assignment-detail.html b/openslides/assignments/static/templates/assignments/assignment-detail.html deleted file mode 100644 index d585d345e..000000000 --- a/openslides/assignments/static/templates/assignments/assignment-detail.html +++ /dev/null @@ -1,299 +0,0 @@ -
-
- -

{{ assignment.agenda_item.getTitle() || assignment.title }}

-

- Election -

-
-
- -
-
-
- - Meta information -
-
- -
-
-
-
-
- -

Number of persons to be elected

- {{ assignment.open_posts }}
- -

Tags

- - {{ tag.name }}{{$last ? '' : ', '}} - -
-
- -

Phase

- - {{ phases[assignment.phase].display_name | translate }} - -
- -
-
-
-
-
- -
-
-

Description

-
-
- -
-

Candidates

- - -
-
- {{ alert.msg }} -
-
- -
-

- - -

-
- -

Election result

- - - - - - - -
- -
- - Set hint for ballot paper ... - {{ poll.description }} - - - - - - Delete - -
-
- - - - - Print ballot paper - - - - - - - - -
- - - - -
- - - - - -
-
- Required majority: - - - - -
-
- - -
-
- {{ assignmentsFiltered.length }} / - {{ assignments.length }} {{ "elections" | translate }}, - {{(assignments|filter:{selected:true}).length}} {{ "selected" | translate }} -
-
- - - « - - Page {{ pagination.currentPage }} / - {{ pagination.getPageCount(assignmentsFiltered) }} - - » - - -
-
- -
-
-
- -
-
- - - - - Filter - - - - - Tag - - - - - - - - Phase - - - - - - - - Sort - - - - - - - - - - - - - - - - - - {{ tag.name }} - - - - - No tag set - - - - - {{ phase.display_name | translate }} - - - - - -
-
- - -
- - -
- -
- - -
- - -
-
- - -
-
- -
- - {{ assignment.title }} - - - - {{ phases[assignment.phase].display_name | translate }} - - - - - - -
- -
- - Edit · - Delete - -
-
-
- - -
-
- - -
- - - - - - - - - - {{ tag.name }}, - - - - - - -
- -
- - - {{ tag.name }}, - -
- -
-
- -
- - {{ assignment.assignment_related_users.length }} - -
-
-
{{ assignment.agenda_item.getItemNumberWithAncestors() }}
-
-
-
- -
    -
- -
-
diff --git a/openslides/assignments/static/templates/assignments/assignmentpoll-form.html b/openslides/assignments/static/templates/assignments/assignmentpoll-form.html deleted file mode 100644 index dcf905596..000000000 --- a/openslides/assignments/static/templates/assignments/assignmentpoll-form.html +++ /dev/null @@ -1,22 +0,0 @@ -

Ballot {{ ballot }}

- -
- {{ alert.msg }} -
- -

- Special values: - -1 = majority - -2 = undocumented - -

- - - - - -
diff --git a/openslides/assignments/static/templates/assignments/slide_assignment.html b/openslides/assignments/static/templates/assignments/slide_assignment.html deleted file mode 100644 index fb62f65cb..000000000 --- a/openslides/assignments/static/templates/assignments/slide_assignment.html +++ /dev/null @@ -1,101 +0,0 @@ -
- - -
-

{{ assignment.agenda_item.getTitle() || assignment.title }}

-

- Election -

-
- - -
-
- - -
-

Candidates

-
    -
  • - {{ related_user.user.get_full_name() }} - -
-
- - -
-
- Waiting for results ... -
- -
- - - - - - - - - - - - - -
Candidates - Votes
- - {{ option.candidate.get_full_name() }} - - - -
-
- - {{ votes[0].label | translate }}: {{ votes[0].value | number:votesPrecision }} {{ votes[0].percentStr }}
- {{ votes[1].label | translate }}: {{ votes[1].value | number:votesPrecision }} {{ votes[1].percentStr }}
- {{ votes[2].label | translate }}: {{ votes[2].value | number:votesPrecision }} {{ votes[2].percentStr }}
- - {{ votes[0].label | translate }}: {{ votes[0].value | number:votesPrecision }} {{ votes[0].percentStr }}
- {{ votes[1].label | translate }}: {{ votes[1].value | number:votesPrecision }} {{ votes[1].percentStr }}
-
-
-
- {{ vote.value | number:votesPrecision }} {{ vote.percentStr }} -
- -
-
-
-
- - -
- Abstain - - {{ vote.value | number:votesPrecision }} {{ vote.percentStr }} -
- No - - {{ vote.value | number:votesPrecision }} {{ vote.percentStr }} -
- Valid ballots - - {{ vote.value | number:votesPrecision }} {{ vote.percentStr }} - -
- Invalid ballots - - {{ vote.value | number:votesPrecision }} {{ vote.percentStr }} - -
- Casted ballots - - {{ vote.value | number:votesPrecision }} {{ vote.percentStr }} -
-
- -
-
diff --git a/openslides/core/management/commands/collectstatic.py b/openslides/core/management/commands/collectstatic.py deleted file mode 100644 index 0cc2785a2..000000000 --- a/openslides/core/management/commands/collectstatic.py +++ /dev/null @@ -1,66 +0,0 @@ -import os -from typing import Any, Dict - -from django.conf import settings -from django.contrib.staticfiles.management.commands.collectstatic import ( - Command as CollectStatic, -) -from django.contrib.staticfiles.utils import matches_patterns -from django.core.management.base import CommandError -from django.db.utils import OperationalError - -from ...views import WebclientJavaScriptView - - -class Command(CollectStatic): - """ - Custom collectstatic command. - """ - realms = ['site', 'projector'] - js_filename = 'webclient-{}.js' - - def handle(self, **options: Any) -> str: - if options['link']: - raise CommandError("Option 'link' is not supported.") - try: - self.view = WebclientJavaScriptView() - except OperationalError: - raise CommandError('You have to run OpenSlides first to create a ' + - 'database before collecting staticfiles.') - return super().handle(**options) - - def collect(self) -> Dict[str, Any]: - result = super().collect() - - try: - destination_dir = os.path.join(settings.STATIC_ROOT, 'js') - except IndexError: - # If the user does not want do have staticfiles, he should not get - # the webclient files either. - pass - else: - if self.dry_run: - self.log('Pretending to write WebclientJavaScriptView for all realms.', level=1) - else: - if not os.path.exists(destination_dir): - os.makedirs(destination_dir) - - for realm in self.realms: - filename = self.js_filename.format(realm) - # Matches only the basename. - if matches_patterns(filename, self.ignore_patterns): - continue - path = os.path.join(destination_dir, filename) - if matches_patterns(path, self.ignore_patterns): - continue - - content = self.view.get(realm=realm).content - with open(path, 'wb+') as f: - f.write(content) - message = "Written WebclientJavaScriptView for realm {} to '{}'".format( - realm, - path) - self.log(message, level=1) - result['modified'].append(path) - - return result diff --git a/openslides/core/static/css/_fonts.scss b/openslides/core/static/css/_fonts.scss deleted file mode 100644 index 12ab0c028..000000000 --- a/openslides/core/static/css/_fonts.scss +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Font definitions for OpenSlides site - */ - -@font-face { - font-family: $font; - src: $font-src; - font-weight: 400; - font-style: normal; -} -@font-face { - font-family: $font-medium; - src: $font-medium-src; - font-weight: 400; - font-style: normal; -} -@font-face { - font-family: $font-condensed; - src: $font-condensed-src; - font-weight: 100; - font-style: normal; -} -@font-face { - font-family: $font-condensed-light; - src: $font-condensed-light-src; - font-weight: 100; - font-style: normal; -} diff --git a/openslides/core/static/css/_helper.scss b/openslides/core/static/css/_helper.scss deleted file mode 100644 index bfdb7328c..000000000 --- a/openslides/core/static/css/_helper.scss +++ /dev/null @@ -1,255 +0,0 @@ -/** General helper classes **/ -.disabled { - color: #555; - cursor: not-allowed !important; -} - -.bold { - font-weight: bold; -} - -.dropdown-menu { - margin-left: 0px !important; - max-height: 300px; - overflow-y: auto; -} - -.slimDropDown { - padding-left: 4px !important; - padding-right: 4px !important; -} - -.btn-primary { - background-color: #317796; -} - -.btn-slim { - padding-left: 6px; - padding-right: 6px; -} - -.spacer, .spacer-top { - margin-top: 7px; -} - -.spacer-top-lg { - margin-top: 25px; -} - -.spacer-bottom { - margin-bottom: 7px; -} - -.spacer-right { - margin-right: 5px; -} - -.spacer-left { - margin-left: 5px; -} - -.spacer-left-lg { - margin-left: 10px; -} - -.no-padding { - padding: 0px; -} - -.lead-div { - margin-bottom: 20px; - - .lead { - margin-bottom: 0; - } -} - -.italic { - font-style: italic; -} - -.hoverActions { - font-size: 85%; -} - -.hiddenDiv { - visibility: hidden; -} - -.vcenter { - vertical-align: middle; -} - -.white-space-pre-line { - white-space: pre-line; -} - -.slimlabel.label { - padding: 0px 10px; -} - -.indentation, .indentation20 { - margin-left: 20px !important; -} - -.indentation-lg { - margin-left: 35px !important; -} - -.halfWidth { - width: 50%; -} - -.badge-info { - background-color: #f0ad4e; -} - -.badge-success { - background-color: #5bb85b; -} - -.smallhr { - margin-top: 5px; - margin-bottom: 5px; - border-color: #cccccc; -} - -.nobr { - white-space: nowrap; -} - -.rotate-45-deg-right { - filter: "progid: DXImageTransform.Microsoft.BasicImage(rotation=0.5)"; - -webkit-transform: rotate(45deg); - -moz-transform: rotate(45deg); - -ms-transform: rotate(45deg); - -o-transform: rotate(45deg); - transform: rotate(45deg); -} - -.optional { /* show optional column */ - display: auto; -} - -.inline { - display: inline; -} - -.login-logo { - width: 250px; -} - -.loginForm .input-group-addon i { - width: 15px; -} - -.modal-header { - padding: 5px; -} - -.no-padding { - padding: 0 !important; -} - -.no-padding-left { - padding-left: 0 !important; -} - -.no-padding-right { - padding-right: 0 !important; -} - -.scroll-x-container { - overflow-x: auto; -} - -.btn-group > label > input[type=radio] { - display: none; -} - -/* tables */ -.topictext table { - border-spacing: 10px 0; - border-collapse: separate; -} - -.minimum { - width: 1px; -} - -.minimumTable { - width: auto; -} - -.deleteColumn { - text-align: center; - background-color: #ff9999 !important; -} - -th.sortable:hover, tr.pointer:hover, .pointer { - cursor: pointer; -} - -/** background colors for table rows **/ -tr.hiddenrow td { - background-color: #F5DCDC; -} - -tr.activeline td, li.activeline, .projected { - background-color: #bed4de !important; -} - -.related-projected { - background-color: #e1ecfe !important; -} - -tr.selected td { - background-color: #ff9999; -} - -.help-block { - font-size: 85%; - margin-top: 2px; - margin-left: 2px; -} - -.pagination-arrow { - font-size: 150%; - padding: 3px; - text-decoration: none; - - &:hover, &:focus { - text-decoration: none; - } -} - -.grey { - color: gray; -} - -.info { - color: #222; - background-color: #bed4de; - border-color: #46b8da; -} -.error { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.warning { - color: #725523; - background-color: #fcf8e3; - border-color: #faebcc; -} -.close { - opacity: 0.3 !important; -} -.close:hover { - opacity: 0.6 !important; -} diff --git a/openslides/core/static/css/_mediaqueries.scss b/openslides/core/static/css/_mediaqueries.scss deleted file mode 100644 index 62e1f5906..000000000 --- a/openslides/core/static/css/_mediaqueries.scss +++ /dev/null @@ -1,115 +0,0 @@ -/*//////////////////////////////////////// - =MEDIA QUERIES (RESPONSIVE DESIGN) -////////////////////////////////////////*/ - -/* display for 900 to 1200px resolutions */ -@media only screen and (max-width: 1200px) { - - body { font-size: 12px; } - - h1 { font-size: 30px; padding-bottom: 6px; } - - h2 { font-size: 22px; padding-bottom: 10px; } - - h3 { font-size: 18px; padding-bottom: 10px; } - - .col-md-4 { - float: left; - width: 50%; - } - - .motion-text.line-numbers-outside .os-line-number:after { - font-size: 10px; - top: 9px; - } -} - - -/* display for resolutions smaller that 900px */ -@media only screen and (max-width: 900px) { - - #nav .navbar li a { padding: 24px 5px; } - - #multi-table .info-head { - width: 200px; - &.small { - width: 250px; - } - } - - /* hide text in multi-table earlier */ - #multi-table .optional { display: none; } - - /* show replacement elements, if any */ - #multi-table .optional-show { display: block !important; } - - /* hide searchbar input */ - #nav .searchbar input { display: none !important; } - #nav .searchbar .btn { - border-top-left-radius: 4px !important; - border-bottom-left-radius: 4px !important; - } -} - - -/* display for resolutions smaller that 760px */ -@media only screen and (max-width: 760px) { - - h1 { font-size: 22px; padding-bottom: 4px; } - - h2 { font-size: 16px; padding-bottom: 4px; } - - h3 { font-size: 14px; padding-bottom: 4px; } - - .user i { font-size: 16px; padding: 3px; } - - #nav .navbar { box-shadow: none; padding-right: 60px !important; } - #nav .navbar ul li a { padding: 10px 15px; } - #nav .searchbar { margin: 15px -53px 0 0 !important; } - - #chatbox { width: 100%; top: 40px; } - - .badge { font-size: 10px; } - - /* show replacement elements, if any */ - .optional-show { display: block !important; } - - /* hide marked element / column */ - .optional, .hide-sm { display: none !important; } -} - -/* display for resolutions smaller that 560px */ -@media only screen and (max-width: 560px) { - #content .containerOS { padding: 0; } - - #content .col2 { width: 100%; } - - #content .col2.sidebar-max, #content .col2.sidebar-min, - #content .col1.sidebar-min, #content .col1.sidebar-max { - width: 100%; - } - - #sidebar { - display: none !important; - } - - #sidebar-xs { - display: block !important; - } - - #sidebar-xs .projector_full { - margin-left: 0 !important; - width: 100%; - } - - #multi-table .info-head { - width: 150px; - &.small { - width: 100px; - } - } - - .personalNoteFixed { - width: 100%; - } -} diff --git a/openslides/core/static/css/_startup-spinner.scss b/openslides/core/static/css/_startup-spinner.scss deleted file mode 100644 index fe496aecf..000000000 --- a/openslides/core/static/css/_startup-spinner.scss +++ /dev/null @@ -1,34 +0,0 @@ -#wrapper { - float: left; - width: 100%; - margin: 0 auto 0 auto; -} - -#startup-overlay { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - background-color: #fff; - z-index: 900; -} - -#spinner-container { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - max-height: 340px; - z-index: 1000; - - div { - width: 100%; - position: absolute; - bottom: 0; - font-size: 56px; - text-align: center; - color: #317796; - } -} diff --git a/openslides/core/static/css/_ui-override.scss b/openslides/core/static/css/_ui-override.scss deleted file mode 100644 index ee90d3f2d..000000000 --- a/openslides/core/static/css/_ui-override.scss +++ /dev/null @@ -1,169 +0,0 @@ -/* override booststrap's label class to fix linebreak and add spacing */ -.label { - display: inline-block; - padding: .4em .6em; - margin-right: .2em; - white-space: normal; -} - -/* Custom OpenSlides slider classes */ -.os-slider { - margin-top: 40px; - margin-bottom: 20px; - - &.rzslider { - .rz-bar { - background: #317796; - opacity: 0.3; - height: 2px; - } - - .rz-selection { - background: #317796; - } - - .rz-pointer { - width: 14px; - height: 14px; - top: -7px; - bottom: 0; - background-color: #317796; - border-radius: 7px; - - &:after { - display: none; - } - } - } -} - -/* ngDialog: override ngdialog-theme-default */ -.ngdialog.ngdialog-theme-default { - padding-top: 40px; - - .ngdialog-content { - font-family: $font, sans-serif !important; - } - - h2 { - margin-top: 5px; - } - - label { - font-family: $font-medium; - font-family: $font-medium, sans-serif; - font-weight: normal; - } - - .row:after, .row:before { - display: table-cell !important; - } -} - -.ngdialog.ngdialog-theme-default.wide-form .ngdialog-content { - width: 750px; - line-height: 1em; -} - -/* Bootbox: override z-index, that bootboxes are higher than ngDialogs */ -.bootbox { - z-index: 100000; -} - -/* angular-chosen: override default width of select fields in quickmode */ -.chosen-container { - width: 100% !important; - font-size: 14px; - - ul.chosen-results { - margin: 0 !important; - - li.active-result { - margin: 6px 5px 0 0 !important; - } - } - - input { - margin: 4px 0 !important; - } -} - -.chosen-container-active .chosen-choices { - border-color: rgba(82,168,236,.8); - box-shadow: 0 0 8px rgba(82,168,236,.6); -} - -/* angular-ui-tree style */ -.angular-ui-tree-handle { - background: none repeat scroll 0 0 #f8faff; - border: 1px solid #dae2ea; - color: #7c9eb2; - padding: 10px; - - &:hover { - background: none repeat scroll 0 0 #f4f6f7; - border-color: #dce2e8; - color: #438eb9; - } -} - -.angular-ui-tree-placeholder { - background: none repeat scroll 0 0 #f0f9ff; - border: 2px dashed #bed2db; - box-sizing: border-box; -} - -/* ngAnimate classes */ -.animate-item.ng-enter { - -webkit-animation: fade-in 0.5s linear; - animation: fade-in 0.5s linear; -} - -.animate-item.ng-leave { - -webkit-animation: fade-out 0.25s linear; - animation: fade-out 0.25s linear; -} - -/* xeditable */ -.editable-click { - border: none; - cursor: pointer; - color: #555; - - &:hover { - color: #555; - } -} - -.popover-wrapper { - .editable-hide { - display: inline !important; - } - - .small-form { - input{ - width: 100px; - height: 30px; - } - - button{ - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - } - } -} - -@keyframes fade-out { - 0% { opacity: 1; background: none; } - 100% { opacity: 0; background: none; } -} -@keyframes fade-in { - 0% { opacity: 0; background: none; } - 100% { opacity: 1; background: none; } -} - -/* Angular formly */ -.checkbox label { - padding-left: 0; -} diff --git a/openslides/core/static/css/_variables.scss b/openslides/core/static/css/_variables.scss deleted file mode 100644 index 8894c57e4..000000000 --- a/openslides/core/static/css/_variables.scss +++ /dev/null @@ -1,10 +0,0 @@ -/** Fonts **/ -/* Note: The font naming has to be consistent to the projector.html */ -$font: 'OSFont'; -$font-src: url('../fonts/Roboto-Regular.woff') format('woff'); -$font-medium: 'OSFont Medium'; -$font-medium-src: url('../fonts/Roboto-Medium.woff') format('woff'); -$font-condensed: 'OSFont Condensed'; -$font-condensed-src: url('../fonts/Roboto-Condensed-Regular.woff') format('woff'); -$font-condensed-light: 'OSFont Condensed Light'; -$font-condensed-light-src: url('../fonts/Roboto-Condensed-Light.woff') format('woff'); diff --git a/openslides/core/static/css/core/_chatbox.scss b/openslides/core/static/css/core/_chatbox.scss deleted file mode 100644 index f53fb00f1..000000000 --- a/openslides/core/static/css/core/_chatbox.scss +++ /dev/null @@ -1,19 +0,0 @@ -/* Chatbox */ -#chatbox { - position: fixed; - top: 40px; - right: 0; - width: 40%; - border-color: #dddddd; - border-width: 1px; - box-shadow: -5px 5px 5px rgba(0, 0, 0, 0.2); - height: 234px; - padding: 0 10px 10px 10px; - z-index: 5; -} - -#chatbox-text { - overflow-y: scroll; - height: 190px; - color: #222; -} diff --git a/openslides/core/static/css/core/_config.scss b/openslides/core/static/css/core/_config.scss deleted file mode 100644 index 98e4c52a2..000000000 --- a/openslides/core/static/css/core/_config.scss +++ /dev/null @@ -1,28 +0,0 @@ -/** Config **/ -#content .col1 #config { - .comments > div { - margin-bottom: 5px; - } - .config-checkbox { - padding: 6px 12px; - } - .custom-translations { - > div { - margin-bottom: 5px; - padding-right: 15px; - width: 100%; - } - - .inputs { - input { - width: 47%; - } - - .arrow { - width: 6%; - float: left; - text-align: center; - } - } - } -} diff --git a/openslides/core/static/css/core/_countdown.scss b/openslides/core/static/css/core/_countdown.scss deleted file mode 100644 index 4528805ee..000000000 --- a/openslides/core/static/css/core/_countdown.scss +++ /dev/null @@ -1,32 +0,0 @@ -/** Countdown fullscreen mode **/ -#countdownWrapper { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 1000; - background-color: white; -} -#countdownWrapper > div { - text-align: center; - margin-top: 50px; -} -#countdown { - display: inline-block; - padding: 20px 50px; - border-radius: 8px; - font-size: 30vw; - line-height: 1; - font-weight: bold; -} -#countdown .description { - font-weight: normal; - font-size: 5vw; -} -.warning_time { - color: #ed940d; -} -.negative { - color: #CC0000; -} diff --git a/openslides/core/static/css/core/_csv-import.scss b/openslides/core/static/css/core/_csv-import.scss deleted file mode 100644 index ddd57a81d..000000000 --- a/openslides/core/static/css/core/_csv-import.scss +++ /dev/null @@ -1,28 +0,0 @@ -/* for csv import form */ -#content .col1 #csv-import { - margin-left: 15px; - width: 35%; - - .file-select { - input { - display: none; - } - - label { - font-weight: normal; - cursor: pointer; - } - } - - .clear-file { - color: #555; - } - - .help-block { - padding-bottom: 0; - } - - .help-block-big { - font-size: 100%; - } -} diff --git a/openslides/core/static/css/core/_goto-top.scss b/openslides/core/static/css/core/_goto-top.scss deleted file mode 100644 index b50fe0c9d..000000000 --- a/openslides/core/static/css/core/_goto-top.scss +++ /dev/null @@ -1,20 +0,0 @@ -/** Goto top link **/ -#goto-top { - position: fixed; - bottom: 15px; - right: 30px; - padding: 10px 30px; - background: white; - opacity: 0.8; - transition: opacity 250ms ease-out; - z-index: 100; - - &:hover { - opacity: 1; - transition: opacity 250ms ease-in; - } - - a:hover { - text-decoration: none; - } -} diff --git a/openslides/core/static/css/core/_manage-projectors.scss b/openslides/core/static/css/core/_manage-projectors.scss deleted file mode 100644 index 7fd497698..000000000 --- a/openslides/core/static/css/core/_manage-projectors.scss +++ /dev/null @@ -1,31 +0,0 @@ -/** manage-projectors **/ -#manage-projectors { - > div { - display: inline-table; - width: 256px; - margin: 10px 20px 35px 10px; - - > div { - margin-bottom: 10px; - } - } - - .middle { - width: 100%; - - > div { - margin-left: auto; - margin-right: auto; - display: table; - } - } - - .dropdown button { - width: 100%; - } - - .resolution { - display: inline-block; - width: 120px; - } -} diff --git a/openslides/core/static/css/core/_messaging.scss b/openslides/core/static/css/core/_messaging.scss deleted file mode 100644 index 6cede6d82..000000000 --- a/openslides/core/static/css/core/_messaging.scss +++ /dev/null @@ -1,20 +0,0 @@ -/** Messaging status bar **/ -#messaging { - position: fixed; - bottom: 0; - width: 100%; - z-index: 100000; -} - -#messaging-container { - margin: 0 auto 0 auto; - padding: 0px 20px; - max-width: 1400px; - - > div { - margin-bottom: 10px; - padding: 10px 20px; - border-radius: 6px; - box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); - } -} diff --git a/openslides/core/static/css/core/_multi-table.scss b/openslides/core/static/css/core/_multi-table.scss deleted file mode 100644 index ce2141e2f..000000000 --- a/openslides/core/static/css/core/_multi-table.scss +++ /dev/null @@ -1,47 +0,0 @@ -/* multi list */ -#multi-table { - table-layout: fixed; - text-align: center; - - thead tr th { - vertical-align: top; - text-align: center; - min-width: 40px; - overflow: hidden; - } - - .info-head { - width: 300px; - &.small { - width: 200px; - } - } - - tbody tr:hover { - background-color: #f5f5f5 !important; - } - - tbody tr.bg-grey { - background-color: #f9f9f9; - } - - tbody tr td .no-overflow{ - overflow: hidden; - } - - tbody tr td:first-child { - text-align: left; - } - - tbody tr td div { - text-align: center; - } - - .optional-show { /* hide optional-show column */ - display: none; - } - - .editable-click { - color: #000; - } -} diff --git a/openslides/core/static/css/core/_os-table.scss b/openslides/core/static/css/core/_os-table.scss deleted file mode 100644 index 54197360e..000000000 --- a/openslides/core/static/css/core/_os-table.scss +++ /dev/null @@ -1,107 +0,0 @@ -/** OS-Table **/ -#content .col1 .os-table { - font-weight: normal; - - small { - color: #555; - } - - .row { - border: 1px solid #ddd; - border-top: 0px; - - .col-xs-1 { - width: 50px; - } - - .col-xs-4 { - padding-right: 10px; - } - - .col-space { - padding: 5px 7px 5px 7px; - } - - // TODO: Isn't this defined in the _helper.scss? - .centered { - text-align: center; - } - } - - .data-row:hover { - background-color: #f5f5f5; - } - - .data-row > div { - padding: 10px 0px 10px 0px; - } - - .id-col { - width: 50px; - min-height: 1px; - word-wrap: break-word; - } - - .id-col-space { - width: calc(100% - 50px); - } - - .no-projector-spacer { - margin-right: 20px; - float: left; - } - - .header-row { - border-top: 1px solid #ddd; - background-color: #f5f5f5; - } - - .header-row > div { - padding: 10px; - } - - .main-header { - width: calc(100% - 50px); - float: right; - - .form-inline { - margin-left: 15px; - } - } - - .content > div { /* horizontal blocks */ - display: inline-block; - float: left; - } - - .content > div > div { /* vertival blocks */ - margin-bottom: 3px; - } - - .content > div > div:last-child { - margin-bottom: 0px; - } - - .projector { - width: 70px !important; - } - - - .header-row .dropdown > span, .sort-spacer { - padding: 5px 10px 5px 10px; - } - - .title { - font-family: $font-medium; - font-family: $font-medium, sans-serif; - font-weight: normal; - font-size: 120%; - margin-right: 3px; - padding: 0; - background-color: transparent; - } - - .dropdown-hover-space { - padding: 5px 5px 5px 0; - } -} diff --git a/openslides/core/static/css/core/_projector-container.scss b/openslides/core/static/css/core/_projector-container.scss deleted file mode 100644 index f686c7922..000000000 --- a/openslides/core/static/css/core/_projector-container.scss +++ /dev/null @@ -1,43 +0,0 @@ -/* ProjectorContainer */ -.pContainer { - background-color: #222; - width: 100%; - height: 100%; - position: absolute; - top: 0; - left: 0; - display: table; - - & > div { - display: table-cell; - vertical-align: middle; - } - - #iframe { - -moz-transform-origin: 0 0; - -webkit-transform-origin: 0 0; - -o-transform-origin: 0 0; - transform-origin: 0 0 0; - } - - #iframewrapper, .error { - position: relative; - overflow: hidden; - margin-left: auto; - margin-right: auto; - } - - .error > p { - color: #f00; - font-size: 150%; - text-align: center; - } - - #iframeoverlay { - position: absolute; - top: 0px; - left: 0px; - display: block; - z-index: 1; - } -} diff --git a/openslides/core/static/css/core/_projector-sidebar.scss b/openslides/core/static/css/core/_projector-sidebar.scss deleted file mode 100644 index 7ed81d270..000000000 --- a/openslides/core/static/css/core/_projector-sidebar.scss +++ /dev/null @@ -1,31 +0,0 @@ -/* Pojector sidebar */ -.col2 .projectorSelector { - margin-top: 10px; - margin-bottom: 10px; - - & > div { - margin-bottom: 10px; - - & > div { - width: 65%; - padding-right: 5px; - float: left; - margin-bottom: 10px; - - & > button { - width: 100%; - overflow: hidden; - } - } - } - - .manageBtn { - width: 35%; - } - - .btn-group { - margin-left: auto; - margin-right: auto; - display: table; - } -} diff --git a/openslides/core/static/css/core/_projector.scss b/openslides/core/static/css/core/_projector.scss deleted file mode 100644 index d3834ae54..000000000 --- a/openslides/core/static/css/core/_projector.scss +++ /dev/null @@ -1,269 +0,0 @@ -@import "projector-container"; - -/* Template styles for OpenSlides projector */ -html, body { - height: 100%; -} - -body { - font-family: $font, sans-serif; - font-size: 20px !important; - line-height: 24px !important; - overflow: hidden; - color: #222; -} - -h1, h2, h3, h4, h5, h6 { - font-size: inherit; - font-family: $font-condensed, sans-serif; - font-weight: normal; -} - -h1 { - font-size: 2.25em; - line-height: 1.1em; - margin-bottom: 30px; -} - -h2 { - font-size: 28px; - margin-top: 15px; - display: block; - color: #9a9898; -} - -h3 { - color: #222; - margin-bottom: 10px -} - -ul, ol { - margin: 0 0 10px 0; -} - -hr { - margin: 10px 0; -} - -#header { - font-family: $font-condensed-light; - background-repeat: no-repeat; - background-size: 100% 100%; - box-shadow: 0 0 7px rgba(0,0,0,0.6); - height: 70px; - margin-bottom: 20px; -} - -#logo { - position: relative; - left: 50px; - top: 10px; - height: 50px; - margin-right: 25px; - float: left; -} - -#eventdata { - position: relative; - left: 50px; - top: 12px; - height: 50px; - overflow: hidden; - - .title { - font-size: 26px; - font-weight: bold; - - &.titleonly { - position: relative; - top: 12px; - } - } - - .description { - font-size: 18px; - opacity: 0.8; - } -} - -#currentTime { - font-family: $font-condensed-light; - border: 0 solid #000000; - font-size: 24px; - position: absolute; - text-align: right; - top: 23px; - right: 50px; - padding-left: 30px; -} - -#footer { - font-family: $font-condensed-light; - position: fixed; - bottom: 0; - height: 35px; - width: 100%; - font-size: 16px; - padding-left: 50px; - padding-right: 50px; - padding-top: 5px; - overflow: hidden; - text-align: right; - - span { - opacity: 0.8; - } -} - -/* Content */ -.content { - position: absolute; - left: 50px; - right: 50px; - z-index: -1; - line-height: 1.5em; - - img { - max-width: 65%; - height: auto; - } -} - -.scrollcontent { - transition-property: margin; - transition-duration: 1s; -} - -.zoomcontent { - transition-property: font-size; - transition-duration: 1s; -} - -.fullscreen { - position: fixed; - top: 0 !important; - left: 0; - width: 100%; - height: 100%; - z-index: 100; - background-color: black; - - canvas { - margin: auto; - display: block !important; - } -} - -#title { - border-bottom: 5px solid #d3d3d3; - margin-bottom: 40px; - - h1 { - margin-bottom: 0; - padding-bottom: 0; - } - - h2 { - margin-top: 10px; - margin-bottom: 5px; - } -} - -/* Overlays: countdown and message */ -.countdown { - min-width: 260px; - position: relative; - margin: 0 0 10px 10px; - top: 0px; - right: 0px; - padding: 26px 45px 7px 19px; - min-height: 72px; - font-family: $font-condensed, sans-serif; - font-size: 3.7em; - font-weight: bold; - text-align: right; - border-radius: 7px 7px 7px 7px; - z-index: 320; - - .description { - font-weight: normal; - font-size: 18px; - margin-top: 20px; - padding-right: 6px; - } -} - -.message_background { - background-color: #777777; - opacity: 0.8; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 300; -} -.message { - font-family: $font-condensed, sans-serif; - font-weight: normal; - position: fixed; - top: 35%; - left: 10%; - width: 80%; - text-align: center; - border-radius: 0.2em; - background: #FFFFFF; - font-size: 2.75em; - padding: 0.2em 0; - line-height: normal !important; - z-index: 350; -} -.identify { - background-color: #D9F8C4; - z-index: 400; -} - -/* Table style */ -.table { - border-collapse:collapse; - border-color:#CCCCCC -moz-use-text-color #CCCCCC #CCCCCC; - border-style:solid none solid solid; - border-width:1px medium 1px 1px; - margin:0; - border-spacing:0px; - - th { - border-right:1px solid #CCCCCC; - color:#333333; - font-weight:normal; - padding:10px 10px 10px 10px; - text-align:left; - text-transform:uppercase; - } - - tr.odd td { - background:none repeat scroll 0 0 #F1F1F1; - } - - td { - background:none repeat scroll 0 0 #F7F7F7; - border-right:1px solid #CCCCCC; - line-height:120%; - padding: 10px 10px; - vertical-align:middle; - } - - tr.total td { - border-top: 1px solid #333333; - background-color: #e3e3e3; - } - - tr.elected td { - background-color: #BED4DE !important; - } -} - -.transparentTable td { - background-color: transparent; - padding-right: 10px; -} diff --git a/openslides/core/static/css/core/_search.scss b/openslides/core/static/css/core/_search.scss deleted file mode 100644 index 7e869cb55..000000000 --- a/openslides/core/static/css/core/_search.scss +++ /dev/null @@ -1,13 +0,0 @@ -/* search results */ -.searchresults li { - margin-bottom: 12px; -} -.searchresults h3 { - margin-bottom: 3px; - padding-bottom: 0; -} -.searchresults .hits { - margin-bottom: 10px; - color: #999999; - font-size: 85%; -} diff --git a/openslides/core/static/css/core/_site.scss b/openslides/core/static/css/core/_site.scss deleted file mode 100644 index df85cc729..000000000 --- a/openslides/core/static/css/core/_site.scss +++ /dev/null @@ -1,612 +0,0 @@ -/** Template styles for OpenSlides site **/ - -@import "config"; -@import "search"; -@import "os-table"; -@import "multi-table"; -@import "csv-import"; -@import "chatbox"; -@import "countdown"; -@import "goto-top"; -@import "messaging"; - -@import "projector-sidebar"; -@import "manage-projectors"; - -* { /* set margin/padding for all (block) elements to null */ - margin: 0; - padding: 0; -} - -body { - font-size: 14px; - line-height: 1.5; - color: #222; - text-align: center; - background: #e8eaed; - font-family: $font, sans-serif !important; -} - -div { - text-align: left; -} - -img { - border: none; -} - -a { - text-decoration: none; - color: #2b6883; - - &:hover { - text-decoration: underline; - } -} - -blockquote { - font-size: inherit; -} - -h1, h2, h3, h4, h5, h6 { - line-height: 1.1; - font-family: $font-condensed, sans-serif; - font-weight: 400; -} - -h1 { - font-size: 36px; - color: #317796; - padding-bottom: 8px; -} - -h2 { - font-size: 22px; - color: #9a9898; - padding-bottom: 14px; -} - -h3 { - font-size: 22px; - color: #222; - padding-bottom: 14px; -} - -h4 { - font-size: 18px; - color: #222; -} - -strong, b, th { - font-family: $font-medium; - font-family: $font-medium, sans-serif; - font-weight: normal; -} - -.heading { - a { - color: #444; - - &:hover { - color: #2b6883; - text-decoration: none; - } - } - -} - -/** Header **/ -#header { - float: left; - width: 100%; - height: 40px; - background: #002c42; - color: #999; - - a.headerlink { - text-decoration: none; - color: #999; - } - - a.headerlink:hover, a.headerlink:active, a.headerlink.active { - color: #e8eaed; - } - - .title { - float: left; - margin: 2px 0 0 -5px; - color: #fff; - font-family: $font-condensed, sans-serif; - font-weight: 400; - } - - .user { - float: right; - margin: 5px 0 0 0; - height: 100%; - - .inline { - display: inline; - } - } -} - -#nav { - float: left; - width: 100%; - height: auto; - background: #317796; - color: #fff; - overflow-y: hidden; - - .navbar { - width: 100%; - border: none; - margin: 0; - } - - .navbar-toggle { - padding: 5px 0; - z-index: 2; - - i { - font-size: 28px; - color: #fff; - opacity: 0.5; - } - - &:hover i { - opacity: 1; - } - } - - .navbar-collapse { - padding: 0; - } - - .navbar ul { - list-style: none; - margin: 0; - - li { - float: left; - text-align: center; - - a { - display: block; - color: #fff; - padding: 22px 15px; - text-decoration: none; - - &:hover { - background: url('/static/img/nav_dark-bg.png'); - text-decoration: none; - } - } - - i { - font-size: 28px; - display: block; - margin-bottom: 6px; - margin-right: 0; - opacity: 0.5; - } - } - - li.active { - background: url('/static/img/nav_dark-bg.png'); - - a { - background: url('/static/img/nav_active.png') no-repeat bottom; - } - - i { - opacity: 1; - } - } - } - - .searchbar { - float: right; - margin-top: 33px; - margin-bottom: 32px; - display: inline-table; - - input { - width: 150px; - } - - .btn { - background: #e8eaed; - color: #555; - } - } -} - -#header .containerOS, #nav .containerOS, #content .containerOS { - max-width: 1300px; - height: 100%; - margin: 0 auto 0 auto; - padding: 0 30px; -} - -#content .containerOSExpanded { - height: 100%; - margin: 0 auto 0 auto; - padding: 0 20px; -} - -/** Content **/ -#content { - float: left; - width: 100%; - margin-top: 20px; - - .containerOS { - height: 30px; - } -} - - -/** Main column **/ -#content .col1 { - float: left; - - &.sidebar-max { /*with maximized sidebar*/ - width: calc(100% - 325px); - } - - &.sidebar-min { /*with minimized sidebar*/ - width: calc(100% - 70px); - } - - &.sidebar-none { /*without sidebar*/ - width: 100%; - } - - .header { - background: #fff; - border: 1px solid #d3d3d3; - - .submenu { - float: right; - } - } - - .title { - padding: 0 20px; - width: auto; - } - - - .meta { - .heading, h3 { - font-family: $font-condensed-light; - font-size: 22px; - line-height: 24px; - font-weight: 300; - color: #444; - padding-bottom: 0; - margin-top: 20px; - margin-bottom: 5px; - } - - .title { - width: 100%; - cursor: pointer; - height: 30px; - color: #fff; - background: #317796; - padding: 5px 20px 0 20px; - - &:hover { - color: #d3d3d3; - } - - .name { - float: left; - font-size: 14px; - } - - .icon { - float: right; - } - } - - .detail { - padding: 0 20px 10px 20px; - width: 100%; - min-height: 30px; - background: #d3d3d3; - color: #444; - } - - .heading .drop-down-name { - font-family: $font-condensed-light; - } - } - - .details { - padding: 20px; - width: auto; - margin-top: 20px; - background: #fff; - border: 1px solid #d3d3d3; - - img { - max-width: 100%; - height: auto; - } - } - - ol, ul { - margin-left: 15px; - } -} - -/** Projector sidebar column **/ -#sidebar-xs { - display: none !important; -} - -#content .col2 { - float: right; - position: relative; - display: inline-flex; - z-index: 3; - margin-bottom: 20px; - - &.sidebar-max { - width: 325px; - } - - &.sidebar-min { - width: 70px; - } - - &.sidebar-none { - width: 0px; - } - - h4 { - font-size: 20px; - line-height: 24px; - color: #444; - font-family: $font-condensed-light; - font-weight: 300; - } - - a:hover h4 { - text-decoration: none; - } - - .projector_min { - background: url('/static/img/nav_projector_sidebar_min.png') no-repeat left 17px; - width: 50px; - margin-left: 20px; - padding-left: 8px; - float: right; - - .icon { - float: left; - color: #fff; - font-size: 24px; - width: 50px; - text-align: center; - padding: 7px 0; - background: #317796; - - a { - color: #fff; - display: block; - - i { - opacity: 0.5; - } - - &:hover i { - opacity: 1; - } - } - } - } - - .projector_full { - margin-left: 30px; - width: auto; - - .title { - width: 100%; - color: #fff; - height: 50px; - background: #317796; - cursor: pointer; - - &:hover { - color: #d3d3d3; - } - - a, a:hover { - color: #fff; - text-decoration: none; - display: block; - } - - i { - margin-right: 10px; - } - - .name { - float: left; - padding: 8px 0 0 20px; - font-size: 22px; - font-weight: 400; - } - - .icon { - float: right; - width: 50px; - text-align: center; - padding-top: 7px; - font-size: 24px; - } - - } - - .details { - clear: both; - width: 100%; - background: #d3d3d3; - - .section { - padding: 1px 20px; - width: auto; - border-bottom: 1px solid #c2c2c2; - - a:hover { - text-decoration: none; - } - - div.in.collapse { - padding-bottom: 15px; - } - } - } - - .toggle-icon { - font-size: 20px; - float: right; - margin-top: 10px; - } - } - - /* countdown and message controls */ - .countdown { - &.panel { - margin-bottom: 7px; - } - - .panel-heading { - padding: 3px 15px; - } - - .panel-body { - padding: 5px 15px; - } - - .icons { - padding-right: 10px; - } - } - - .message { - &.panel { - margin-bottom: 7px; - } - - .panel-heading { - padding: 3px 15px; - } - - .panel-body { - padding: 10px 15px; - } - - projector-button { - float: left; - width: auto; - margin: 5px 10px 5px 0px; - } - - .innermessage { - float: left; - width: 180px; - max-width: 170px; - overflow: hidden; - } - - .panel-input { - width: 228px; - float: left; - margin-top: 10px; - } - - .editicon { - padding-right: 10px; - } - } - - .countdown_timer { - font-size: 2.2em; - font-weight: bold; - - &.warning { - color: #ed940d; - } - - &.negative { - color: #CC0000; - } - } - - .notNull { - color: red; - font-weight: bold; - } -} - -/* iframe for live view */ -.iframe { - -moz-transform-origin: 0 0; - -webkit-transform-origin: 0 0; - -o-transform-origin: 0 0; - transform-origin: 0 0 0; -} - -.iframewrapper { - width: 258px; - position: relative; - overflow: hidden; - border: 1px solid #D5D5D5; - margin-bottom: 10px; -} - -.iframeoverlay { - width: 256px; - position: absolute; - top: 0px; - left: 0px; - display: block; - z-index: 1; -} - -/** Footer **/ -#footer { - float: left; - height: 50px; - padding-top: 15px; - font-size: 90%; -} - -.details h1 { - font-size: 20px; - color: #000; -} -.details h2 { - font-size: 18px; - color: #000; -} -.details h3 { - font-size: 16px; - color: #000; -} - -/** Config **/ -#config .panel-body { - padding-top: 0; -} -#config.details h3 { - font-size: 22px; - padding-top: 30px; - margin-top: 0; -} diff --git a/openslides/core/static/css/projector.scss b/openslides/core/static/css/projector.scss deleted file mode 100644 index 3090e8bc6..000000000 --- a/openslides/core/static/css/projector.scss +++ /dev/null @@ -1,14 +0,0 @@ -/* Main projector CSS file. Just import all files needed for the site here. */ - -/* General */ -@import "variables"; -@import "helper"; -@import "ui-override"; -@import "core/countdown"; - -/* Apps */ -@import "core/projector"; -@import "../../../agenda/static/css/agenda/projector"; -@import "../../../assignments/static/css/assignments/projector"; -@import "../../../motions/static/css/motions/projector"; -@import "../../../mediafiles/static/css/mediafiles/projector"; diff --git a/openslides/core/static/css/site.scss b/openslides/core/static/css/site.scss deleted file mode 100644 index 824a03fc9..000000000 --- a/openslides/core/static/css/site.scss +++ /dev/null @@ -1,17 +0,0 @@ -/* Main site CSS file. Just import all files needed for the site here. */ - -/* General */ -@import "variables"; // TODO: Add colors, ... -@import "fonts"; -@import "helper"; -@import "mediaqueries"; -@import "ui-override"; -@import "startup-spinner"; - -/* Apps */ -@import "core/site"; -@import "../../../agenda/static/css/agenda/site"; -@import "../../../assignments/static/css/assignments/site"; -@import "../../../motions/static/css/motions/site"; -@import "../../../users/static/css/users/site"; -@import "../../../mediafiles/static/css/mediafiles/site"; diff --git a/openslides/core/static/img/favicon.png b/openslides/core/static/img/favicon.png deleted file mode 100644 index 026653c142bd93e6ff1656ac485e9006c074557b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 581 zcmV-L0=oT)P)HO^i| z*dma>8s^OW-+#`)h^o>`nU=cm0RBZmQc=Rw`(Wb?kOw^oasUvNj|lHUN~Cu!>dS>s z{{=ud77&ITxEPsU;3#N;_4OifubIUkBa*B=B9T2;2e@mA0ia6d*nP?60txLS2}k=9 z0g^p6z$Q;oZc4`P*Xr^L%6wCMl_}L}M=gC87=hG^WX7^yfMjfhbB__afIS$QXdJl; zeDm19(zVh9ksAh;ADwn%%rl8jW_|-mE*FsEF0eNi@1hLk59!*{JK(k_{v-h5i*ev^ ztk8*?J5ng5esxf5zl@UI;?Tw>GxcRK1Jq)*=URDKs&lyRfe-Qa^A!N0Ki|o2_W|he zj6pGGP~R-QmdIX0xdnjZgjUN>B(fHzB!&wm=GMd!#RZX2H1((n2nx)6`s3c8?|tw4 zyze{A3=GG^_n(=$-)Fhc{XNUQ_jyKSJq1t)Tn~HzyaXKXxG6w0iuZutovG`34Kw5= zz{9{qpu5S(QjDcQZ(ty$nB&@e0$5>&AJ%yzTTcL77mPL4*U-1#INxi-S=Ucd;TZ|7KGEduBX zG`T_l6X<4KoOLglyau@1K0p)jCD-0HHalC4?``t;5(eH8xWN6r95|$NEU}gVoRJv# zH;roq-Za@ez(MWk=U_8@ju}8VGpff!9XGnl=(3Lb=~hmbWmyx@UqrSOk;Pe-ecIMe zvMl>MdPp8GBD1qBTLknJk-AW{dt3YHGyq+Iu4TA-iuc<80Da1o-2^xmXbkcJz+Zv+ zsQb3D<9eWb`;ani-st*n0Zyzij;_GEns}`Pa2D!DH3Po`Y7%Ao0_Owcfci4+3;_P? zjB&NeYfxX<<8tG+F{9_03S^<(THsXF4LrC^-?jE3nr$p+wcj!w0Msw_`yRk|T3K%- z(BNb@g?5^O1~X#BY^yUN!8ID!A2=S^p%wa!3T>`d!-7% z_eA7H5xG-DUZW7DZIaNhhlso*B5wz-sZ1RakxhV+BJ#3`+#@0{+2570Tll+HL>{w2TttsRHa-4{|M~|2iXI5z=qck9R(eVcWmjh=5 zJrea(`}>~273lt+`^0aDKHh4@_|CL3JOUg7oD|w<2DWaUwb}sS7E1A2qFjGqG;k#0 zz%l;X(8lNreRc&>+kgO@;iAI8jSqcXlqi#--p6RvRgC35mKHdca)+9ABn=@4l@7_78mY zTmY_Zt-aP5Xy(fsuZ7_D0Cxt3T7Ye;w0j`%2VfGgt;y}$Gt+!4wg9Ljt`M<$O@-|#WiFPx9MOg({{f%}1ei8^;D3|uzB0_+zIxd07s zhf<2$tj>c8ZkL37Wrcnl5^da-DA&)%dXtU4!ECV$nPiPj%&%qbq4&;8q*co{2iKk7wIr z+q|(=355qbz5z`m=2!vjjLxR9z&FtSwi~cznLf@>^zlXGb_1pXPXa%5b!vd~OnxU8 zNCaJSlx!2|_S4PUIo5RD#dP-u{E*^6XtzZBtJ-7Wj}r~{cKn$M{@l>U9LK#4)CIl| z>Kf&k(hhw`P`qb01-_1A_wC<-KQ}R+QI7AO;Fq;kUjcp)6rLXV=$r$b;`nh1ep;fR z8r1(CR7Iarz;x7`DYqi(QSapF1b2TzpK*>qCBYvR+L-RR7BnhdGoVXDK&fwdmE-GxXB_t~ z&Q(Ka^pAmu(46h&z;A$+=nVTymHu}~7~pTl#Z22$pchqUWulMirprpxZFAH0Av8SC zT-ohyj5(gcogwIzmPbXGRDgXF%1(9s5efe1j*nOEPX>kS(3$u?FpJUyuREolz;(uL z3(Pgya^Q56KakMr!qDd|>vKjhP=?+QdW?1arHQuv`;64h_g<9@+y!{S6>b5Jar|M4 z1}}2_tOUP}<6jM2Ndg8KPT{1ViDk`bWbnvNc~IKD5%S6z=0vU$MQ%~q3uttuI|77be)%N8mqH4;t4Y#O+J z6qfN4U_{{K%}0w=0P5KLFnXHFd!tF99M4y1BYxD?fgedBpZz?rUEurJSVvOk&e4@^ zG#l1NVq=Pz_uc+Wp|g5Ca3wC1b9@7u_}&QJ*LFm2b7Q$1!eH-+WkUHkDaAl^CPgIg z(15!_xspHzJuG)L-Ro>DC2kim$ZRkkH(SxI>t5H#<3%5XQXdoGNZ0v7Ctrrk?4wVa z@+ACRU<=1>3QPu`LYGia)Q7x*R=gXd^gRP;@RKt9-4)syW_=E^@k}zECgWBiy3OQg zP&06W;|AE+b1c9n5$S+ylvf~?4>--ZlrNj74f|1g!*Qi+cUs_XbX+}}Rs>zfpLr{; z%#)#h3;K~#dFcaPy{Qzoh7WLiGw}OR?;giRx2qZ3GSCN#5yu&KdV(A02eLE8d53qg znTUS=k-0G@*X$GK$0SKcl|v3gA*FZ%nl7Uth)y z-EzmJ+^)TiJ14=b9q9_W&}7I%Gu?l1RwWr1GC)lur!9qsD+_h?Xj zi5V|S0WOKNcufIZmT31Q$3GglCdUmA++6HR3Vjgc6Ib_A?Bn-0+f4@cSW~4lG*@~U z;Uq;bLVt-a%VPq!H--Is9>uB7xcs35Zf()MGU5rx?`paLyiN!V&=;Un(W|DICg^H5 zIDkZsqLNZW(GYk41lI%D9rskpadp@$$qH>}r2W%68(<5%1x#qQ!c~AiMp;!C`HU32h8&;mjcd`saj$>hT&VMjh|IO$R|l?H);?k%xejwhb@RwCFlR`?CL%i4f7tIapqZ9s zS>EPlQf$kK`kNkdX9N5Bny6Dh0rff0!Nu6Xp|)IR11CFq4e)DQev5FALaYzJM_nju z8R&W7`yAuA5vJo(;8zr<2TrlE%>=&eyk5 diff --git a/openslides/core/static/img/nav_active.png b/openslides/core/static/img/nav_active.png deleted file mode 100644 index 48be908464a56a27343b9ea49f442af675e7e34e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1109 zcmbVLPiWIn7>`!R{7b<-yyD3<8 zih_tYPl^XcK@hwt3VQIMCqWTJJbBWCD0o%$WoxIx@L(W$-~00Ye&7F#W1|N*tlhDe z%d)W8u@l#jXv7L@o={T9BO)JFQkV7UgU-GOeT*j6&+jxNoI4)6h zD^*%G59>DZL@TxtTVBA>95*o73M_jXQ+@(Zxqe>w^5}`cyG~v>s+hnG3|w;$&4qY; zZnR?0P1~9y3?ASIS~?@}FtzxWSN9{ml@}IxbtcDZN#GYu=yYCK46165@dgPouZSRJ zgDm8;84+fHmeKlo89*RGSpsSb0$ovc0Qt@%FmItVsh3f)$Ho#IDM`R|nSwANCbiu54|8^7`vS{=pq05?L z)e*60JnYjF$_wm|=(r9mQ4T<;W`HV#Yz{&V0&OJ61b`5dm137;Ic%<|A{7+VxnY!r za6}n4)U0Nx#xPQhAw@O1*peSn%eQgYugm;)u<}2#x)EZFlCVNZy*mM8H9|>LBLQ!W zD+UiU0Ojw@b2%fcX)u@8ketrQNa$dl|EZe9 zf|26!_+wnUDmH_$xHSCiva~+fXFC(J<#_P=)?2n%XG>_P(t0=X{pZiY#QMi4e_UR% z<}242NIqKG;1V03&KrFbE9zB`{m`*o5g_MU#Q?!t+7wa?smclXC0 zw6d{3`Rzln{rv7rHwvqIu7fMrdY@^9{+ESq_4(EPo5s@5x11}yH*ZdEzS_>*<{Ev8 ePh7pX&|bm0uXgUb_Tln;yy&HoQFL$k*qL8?HCspk diff --git a/openslides/core/static/img/nav_dark-bg.png b/openslides/core/static/img/nav_dark-bg.png deleted file mode 100644 index f53619aff156b0467818673836eabb18009900c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 925 zcmaJ=&5qMB5Dr>tS*^6%&N;9Mr0=yW{NT1+v{r(4OD9X{ZXgJ~{|BV&USi>?#%i~m_ z6{T^Rr#M&;4klz4CARwe>o*ldp{>5H_{dKkGLNn{j0`s2VX#>QW~g4i0*%}f1TobR5VlyP*n%jvZj}jSvFq+1L+9r21K=z zj;u<}LOR%es>sH|snvHoyIkUAt8>m%3&PcErL8VB%4SeEO%oym8b(P(l(Kcgab8NY z^Mb)8S-_%{M>GMF5l`rn+p17{TSA=fX_IVsP2$2}j#H>>NS0It`u_hz<9HvP@jkiJ z`%mI*xK0V|lZ-A|AR0G4m#I?AVFYu^hLm0xx7eFgPO~{pfitLRI`DB2CDOA+_`c;O z8OKRLJl9qQhZaSlg*q){)Y_H0TQ+rFTGy<%UB_wFtIeuYDX=aLmN7|qfer7l)q_|$ z3~?$lyM#p_NZ4UC2HT>o=&+WHgM2$|cvwsMAQlQSP`383Ru?WYKXSVtTyfZsK1sy9 uGcnlam*Y?3Q9SV6<}m*`8IQ-kqZ6bkFO=s`e!aUdAB@-Tx}RI)kADDaz887` diff --git a/openslides/core/static/img/nav_projector_sidebar_min.png b/openslides/core/static/img/nav_projector_sidebar_min.png deleted file mode 100644 index d8d3e371fefa9b6a72c03e0f7a50e0c9db366711..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1100 zcmbVL+e;Kt7$50of+eIxB6J!-h}@YoTRhX!WOcfR?4zwf>@GG#TV&pmovAgLnByJMuR`=VLT#0Rf?-n0M&=3-h6XR&7pMF7riCe5+R<-^W}1xD|d0O zKh6t^qVQ1UMKMAxBEhsn^h(4D+G`952ZnEX#Bv=FGU}u56iKi&()|={FPl9ec7kf5 zXvz4B?(qT#dD{;2+OrNw4*v<`Q0t&D?O{HL19!?dXg|iO0e_~XIdsLkqiYP9))oc#$-WI1gNBBDnNvgD5Yy0hhe33T1HwtEhj^c zlqAQczLX}SXeuV9k(R7sGfqHs$G|ne7WG@jrjEp_NgwOP^$V^$S(|{Nl1tp6<;$qh`DA1ERP)=Nm8Rpn zGT8d~e70rn+~O;(t)sJH_r6@Wp~bYemt7YZw@2nSJLXq!tPiZ5*e!xB^mzXA=0fYk zmf6>z=Xr3g;f!|cQNFIebNyNU_W5GV>Z#@j?Bu1Tr;Dc-zV0j+fBtG(TdV^gm>1vA T-W~R@TH$_YQiJGC-^ko=xi?tG diff --git a/openslides/core/static/img/openslides-logo-dark.png b/openslides/core/static/img/openslides-logo-dark.png deleted file mode 100644 index 52e16582869ce17a32cddbf86d348ba024b8fe03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4103 zcmV+i5cuzjP)id1B~SK4h=ry^UnM#biQM_P;6 znhF~cXF;J<+`z|p|8hoQr+kc zJOV5RW&*bWb9NX7F0#O#fGdH$t*CbbF91(Q#4<_01~vj88a~XE)DgH8_}t2VKF|`F zxV2oAl{X?nk`4ndFr3*1Xa%eXK9e*T_+>;)_lDf=lsN5^q;CKhTH!hZ)gH^IlI8#p zM8xZ^+8OBU=Pz6qpf;y?3xP#t>P?M})q1~w$ASuyI!L-t(l!3wM$!mL*GuZ;v^^#L zOw!Gg_L4MS(i@WYkaW4E-%2_(vm}yklXQ%Kx0Q62q+2BI+FUm;Df~S`(rlHMcO;#& zJwpn+{Zl1<+{joKNIFwe7&>IKDw}h+*ICkOlKxiG07tC#}k3{n)B zwk2e=`r9A;NfCXav^MP8SVaLF7#-_oDFuT{{=h<^aAz( zMoO9;5lbay?p6`ekZlN;TLQcs5$mh~8D7ceqAhR)(8KSY9T6Yq7P=~FU#_y8LQjRzxh`;+Q1Aqa@As&KqMlf1KUbP0sHs=`u;D=XUG_ zTkYL#ZGR}KT2d=Xr%SrpFr?bg^CX?(`WcJyXaWh!?f-F-K1nRb=pt_U`$?J4>@m)g zG+)wcL!rTv-uCY=Y{~y1X;=Y^cA2CPTxN~GKQlAa)n$5kUcNK~FjUetwyJBA&yAUt zG_1;Gp|Nm&Jgt(AMnEwFq;Zly^_*sNwMx?SlKO2GI49{0NmoibTGDlrI(gx5_CRLk%3mR=&e-$eggU!k zQoW>g#$0P9J)P9+;c}96rPKDaQXeeoc1cG!0Y2Q}cV)6~5$bP{CoPi;Bwb>qlcYtS z+(I%((&0&4w|MM@WRs!MP9E#zM6u>unG4AgHYR80+8QsZb0c$6N`b$V8joG(LHEPnRzxvrCPws!ZkW4O|c01$2vu+kvrmCG&x6fvXb^ zvBoa8wW+dk{@iDG_n*KOz#%{zpqH&n3$qa8%t&;Vv<4VqR%1Bu1K>AJfDZ?lZ2+jx zqU@A1v#c^hlF2@ft{eP&cT-hY*frjklsyPI&+VOPI&YLP} zy`=ADn8wla0ZC+gtfU(ZJ*r*ilFWKJ8kQkLwJn+KVmf(&mYDshNdu1(<9Hz#ptUSg zu9u`KN!j})-6Uy6lJ>2nd>cuRCwb4C;J-Ubd(c#2KJd-@vcIHBN!rtr_DagnO7b3c zTD9l3n3a1^(s`10YK8@##iaMSz^{Ri z95l?d)y}NP24G*{=fG*U+{1u(Bcg{7L8drBDoHsdoG11e#b%?yW5}dMu5M=iGTG?y zWwJByO_ThYyq-{kZGpFvyw*Vf+&f>6h=s|5tP&+)dKXz|thGA8?nX6C`z$)HWjO zn#DroqP`)(H-Ptisx}Td92gxDxB38}J8-;Dw)T{CHt-)n*F-UDfC(=5KfoD24azLe za1-udv)g~xgB}k2!b<;h;1av>0l=BS%f+J9h-i?MDoC3WQEj#1OD5}c%0*70Hvu2# z6tb5ONHRIY%23E)wD7!bDq&Ef*xE$IbYP$vf{|uccJqlR-tdbgUDZSdZUFX7U>Amo zrTW$2cXvwCZpdxGuDQ9|4VVTzZdME6IDb9`CP;b!m}8cw;2PI7V!>)`>1u3gvGTPC zUiXEaj^2?q-ceJ0#Q0G}OqF!CX`e2@{Sk4UCw+|D882y1TjW|#{8G2q8t4YR;*+E| z+^6fjXirLN84>j*hEbb5?+R2$#D)T!wY9BiCd<75rC>^ZPPyCWHbEhR|I6HWl<q5eaB!eERDlzffL)B!CcVzH!a{dUB>Ntarg{=tfMvMpkbgP=ZazHzp~gRY5)xsvt< zo&Y|!MR*KY8xie{V|Vubi7G!YFqhfh7VL2=_({O|5%GYr>`lPG0uPn&B5&EXx3<;W z2Y9Q%f^_yVcqZ?=PO()BbC#(a{Vd0kJ`TKD0JelT$83AICQHVnCcoDKW6Y;s?&Ov> zFijy*!-|%AhISFLrbw`t)>)sV{gdaT5NutM1k>})^B->SJix)?E^c=WoA1)hZqh-9 z0rb}$LH4utx+k%WV{Pt7JAHvM?Ww?lz%^F7-*{1{dq8!@M{A7>j|a}S63#R9sIevf zo+H!S3_l*ucwy%c0zN6Ez=?7`XNI7AVwJAQ@~sF}#?u+@TLQdoI1@P~O&9IpB-VE;Dv%WraTt zSSjhZ5iv>93gDB7ST5-kmtSv%|F$jom9|WEUVyp4OKiTU`VnxiEyHp@e_&ir2^>7x z9QE-TttIId;OU51?fbAN0i%*?6M;8;GL*`&gu*+>82pbHVItx=pEhRh^8!h)M#S?; zPA(Ctgq(cj}2u9amg`X9|us=JrO4u zo8D{Uy4sAwtH4=Ss5cz|tOtGqOw90UM7)tG)$7TE-s@m!m3hG{tvLOCY`NUZ@+NS* z*^g5qVuE?+(`EN;=9`ZlEK6 ztRzolmdDU)e=p{pl3p+t-^GxqkAureO;p|*&%r={hI(_aG+a2s-Y=6oZFrXC0@>>% z?H>_i3+Y5eyd`O7vJuY(Hc49MO>&|)MK(Xx4zwP%lD0Q&yVu5JgY{yNkE~C3fPSfi zy+y$9BrSBC-%PCUI^gN%@W2hu8NG;`*~WR)-ccS+__lKwH*_ml^I!ejofInz>7=L=HVt-u=dwW-W^dn1yx50Z;M zeI>nHr2UBrKmNY&hGZvk*OoXlXHb8U*?%%OZAF(%hUd@1)i}h zpKDY9jz2O3@kIvcOGU4_yk*|iOWejBTjklnRO6#J4QCekUANjgPbmXqJuB%WyVzrF z9oySNy$8GqJQES`mT2Z|Q)AghoA(VVp0In(#xucIWthu;#q+h)9_v|u*O-=k&Cf68 z-uH;p0(i}ri5`+P!`60?Eq!)kxZK?HWMFa@c-fm=V1}Zv*KCFZ*STJ2xzEcok11)S z+l%#Kc6+M~YhP^_)6&Y+$)Arsu|0rg#^t*M zwGmNktJlH*yDMv>nDyvx2BC%BTdi-*ZOYBTCp4wW$ z>VTD*VtJ0+B$#!b*@U!y#yxLH|2LQffgRm1+1;*;h}ygda^JdJX&bUJd;Qua-1gdR z9$de($53OYVR1xka^GrPr_P%s9eZcLtCh>F@f_C|Sd$KhS8bAGc!sm z)wU9aj;%LE#Hv#Nb}i$myX?A+m1*vs)28;NoP&xYj7qcm)YNe`XC6xRdrSJ&SedQz zr_K^Y`v6mKe^KCA)_Sg)5ix}AQ-U&Y>{aAXBQ@K&{m+cE>Wh%)8~MNI?Aw#nI>4%R zpt7N&GJhu06b!YU{&{kT>1#zQQjv;Oq#_lmNJW|?{|i7fJ#h|6T(0=y~V?000xjK*#bQpZ$XZE%m=0W;^)nA5nW4 z>FWTl|95J7-n{&$&;=UUhX4TV>i-L}K#d>l|3sQlh>0!@j*^K+jJLBM)dB!;xk7X_ zts@t9Dx*rR2WAH2)hQUuYo67$CkWG8$tO@Vk*lP7=m5cWAP=uUvGFJwGuf8EYr~Bt@N-?RH^5 zG_>|?PV;8~Mf7sG?|7j9|KlP+ivW)w%CMO2_*5=(;3|+oi51meOb2L?SE1lE=A9h- zPuzI-W8Jg|l?{`@2L^+;pr)WDhIYVHMq@PmUDx*W`KY8usW0O>Y$xXl%P5c=uo-gV z4`8PDr|y+r^dEvto1aDBL~S?YuOhwo+BmyvpQGRnlUMX^isoy6{F&f9PSHQR55^Ki zn>psB3@JPIWPdn|sqUC2=1VUPEJlmS^K4NWQ#wSS^BA6A_<4br=-SEVxY}6|i(XE) z1hlNYrswqMc-S&KVvkasg8DB@;1hI#MqcR(X+~3t9W{k|k7C3Lf-YIHe=MsslPkmD zG^%K7i&IF{5q*(96v%_zxBc)5^D{L!zCd0q@*%+}p$f%$v)cxxSCd&W%ZSjRRv89V zoNFk!8x64CqdqAcbJc%TYWp6Awb4%;BD2NPxLM+2&P=xPP>kR4nJd`a`lEEq#A#&RU`oTlDjmMcNpZ@CT5{ zQhJLdcNvOhEkai_zGJfYaaBaj4JCz7e!q7coY+;LKIF}Oy_^AdloX`Qd@OU@z41Bhz`8RH3ua*qv0w`qVa5N$8pTC8zoPtw5UVsf{m2ye&S&lyP=>|%Y`{E7}+6G7gho> zrU?Yg(>=0Js_f`_%Q;O)Xl<8C%<~0a4JF8-i}-{lcGhiRRe9j4-s0BLW3yB=*8MrX z8laoz6Hs@a<>_z!qxJ5L&aIdxQ9YncCoj6k;8}bIrCEk~+^ra{%tiJ=ku}Tv00FP7 zoOh_c$16I(ENI}f-+rjuDEgpv)-RBZe2Sl@v!3ks2OrBHl3k@>O0+!`46C!ill2Ci z)&y68e#+V~6Y9U_*AmzZYUouCxL()&^6r`xsV+2|MV<0Uqt# zsZqx|y#SNk$CQxD%v-cM1)lyW_>=6{rS%KW#%2w@p5~*T;eeYY(EjbyH~=GCq5q7hwilXJ{yc05p7iRR8b~4o%jde-^Pw0aN!%%df9*1$JX2HOFSYthcf>1+4 zPtaLj^W^OWhQuSCTMr6Z3EpmAkK^v&7Da$Q3{0Jh=Mr4R`Gy2H0>XYrzCk;Fi%q&( z$^bLy&N%{R$)Bv19+?G7A{U#zV-1vObn|`t#(%U{dyXl=;uxXDGmzmTfAUb47vB_w zBEDW)3a2{QuKKDdu3ARSM(IgwOkj_LTygX@eK`jb{n9xl+cx?StnlDmYFVIMGyi_a zK4Uz4)H&Pm;@#Vlg?OoNmI7G2%%=N*M{II5FZUI2swh%3{u$&m<*YhKB#k_1i5cNY zK7lL!ZPauAoQ$!!bOI=*elPf9#FS3db!glv73^x*g~{@B{D@rUSqclJntxXzW_RRv z`!d`ey}bP^fCdM~F`ahP^JQQ(I3~PhCQT@h&C6J&6ShN41rRh842eFdOa-!XfOA3t zN*c8dTGBvsRmj{qe0FMFvNKE15>k|6{X$3RCvA5+MqSI#ho_d2S0|YLOIs&>loX}$ z@d|GpdOUB7i)wUeV}mB+%{;Y|c7G;KqWYnzXI_2_oQjca!a5qBCC*^1#x`r=wqFJpR!0Ol>5)h(k$C15yM{_T53ERF*i=P4CIPhkz;1UC)MkcaC8eEFHfK$XL(r`zYO>(wY#wt+~*eW>86)fQk2Oo@UG3JDohg{M3XxFZAc`Skd6_o zBhI`F`Wa=PFetFeKG>HK4{+W1W>G{YmoK|!_0F*jr0_Pb}lQ-MjBHT@8Z!dPT~T>!;fX`=Mh8m)EZ|VBC%F$si{G?_IceT4f`@N5;dVdzK6RRu=?$CksvA)cj6%2 zv)P*)`-kJe$)eryXRL;dG6hr?_CO$6`p9LIe~OWNy$B;fnZh*w&z-c`oI*3#n$MuR z*~D+E;(B}V0^weZD}Ue^2Py!GR&>lQ%nZ6q_wV0&V^A{~vJ#3SyCV+lv#;=>W_%dz zfgmD)Ef`ORoFB#hk|<2-;M__BkDq-w4K76=_R$C&b1Hv2S(wtIK69QB?kx>YRAG^v zR`|ow5|;hkxBS^+FyHK*^qP2jqA^Eq`<0^KhZQ|y^5eN7|L%(^i{0EF znb{L-8U%R64I)^FC%9rNY;RiW$lkLhe13S?m$;JU4;G=^!uJ?d}Rs z_tWp4yDvvD85htIKXS1%6^Gxa91lAtSyf90e*Ba(XTuMi#cPG3+th<;&xDbuYG2C( zCk{W_`Dlb|h->5S9z^DHM+!Gy2|avI%fOE7h9#N&=jn_NLe!ofa2rl=ZFJf)U19`E zTzHc)$t~Vz%y0SBgt7_gW%eXG1-*_RLRtoUBR__$QrwQ3;wiD+X^u3&K?;yx3w|jl zykl>hK5Fjbc64Wp5&A=UN!EMVF4L$!AtC{9H^mGc2juD!q!T0t5UylD6T#BLyZafS zswxHAE75Jy_tU+%`zCYm6MR{t1L?g*WBP!p;xWT>S$4&4MPqHkRF>~N z|CGR>uUi;D?Av|ewz2V`KQskusbBShwI(5SQ7eGBOw6TLivKc5CeNj|+^VwS9#oXw zy{vsib90G|SUg@K>JTf6Y1}3mNVDkjBQ129(%GPqjQ)~y%Vof5ai89H3aC_|N;_W(? z^DRhmL_)2se4}|dJJ`5T2fe2k1-k0@@RaydZgIxazy66TPDstLF)oG;!Rs;BCQKJo zlHvEr^0jmGLdM+5>qzob?u1+2_L609wuN(*jUQ-~WMKq1j90LWb)>f`9o{|d)_dgf z#~xhOlGp!TEKTq#ZFWxFZDe=in4hr@;mdwQwLrKRHQd_bU95HiKuUgv+v*OR4I)Z& z2jP!`cdj#RF6b7mIFYu)onT;Emz1urvJlHhnt%>@5#`=nGpY-#fnjxb3GqqU@l{^{ZNj;Hlz-#G|*l0DKAqn=kt6YA&{PCnen>%j`xMbK~ zE(Y0YMhngiwfbP64Zgol@AZJXm&iTGB}%PE1#RqybM>S{Z@^0@Q>w`XzqeI;m=rl) z6e>~LTHv9?*?|U@L>)7j?AV9{VoxwUig(QhKNFsc@9$1Kut`jEDfHxf9}G&+YR=?8 z<*YMq{4%hgTW!x4L_E;#K1$2*W6F;Fptb+#ty!G(_w`1H{`O_bQz@Qp|0hY9&EeF| zEa|EnT$fa^D~ZPL$(m3{STEIIAuO@q-_UJr#Uh&4kR1ad1|EU{g#Ntl3I;_*8+11?!L zxkkSca;5BVz!c0?n1ZrGy2f+iR4?uC8PT;#BTseg)2ve3|5Ofb@`qDj97Me5 zskd}6d3B?c+m65Gg9U%nu@AbfVC@kW+ZP*HsWhUkp~{C4~=o3{}1J2Et~M0_ zYr)^`IPubIc)@Yl*paWAK76HD*bf((J$qm{!J&7-quk47ch5o;{9FEDTmHQX>a@}j7K((Rx> zBdS247&ErLzno+bTy%J4^Ne%h{(S5`e#9GgWbf!WXNBPon1#wdcQI%{vO2#p^8A)4 zY_$}D7?Kh1NlSFp`C(7n?E2c~rC6C%k^-}mbMZ}PuIvx5*i{T;c-9XImAdAt8Sl#g zx%536jV+)yrpK@QY&^!EIsNvmwA;#KU`T zn~e>CW!2ZWi1x?!3CkhM?5}v9LHT^Rk@HzGwjOP7tnMi^cC#U)sBC*eSP3`A0-e;) z4c>hsFX^h3z!C)JPo(9r3{FfMEL$qEy@^z>d7y)yW86r4?SY5Q-t<@}spH>~uN(sX z!LsT=*2Iuzyepea1<3#O1xoc}G$GJwC-MoB?z?$~O@PITkM7I8r)I zAoCNV^oTs{PIqK2P!(t3%zDLehi6@Z*DbCLS|cZ+BN!#ykOFA=0p z-R@3(v7gYFUNg?TdYMnS2QR^PC*fh{cBGWJ`&4m7?)FQhgLYlXj%I7Ti}?F$moDJ$ zx+18D8xf;B`r^G<=+FC}&oL9BkI|PUn@7!=YT)1y;FhSo0JH%#YXS*WXuda4*mb`p ze4uARLWyL~1r_Dg4laLXASD{tZJ0s~c5_}9E!~Cz+ZzxN;al&2c@^^Od_74(%lygl zRo)8>a$FkWpRN(lSK}c4ZkYX5`lWrX0%CVJJNSidqoZS&_nO>i6Hmp)$fqBOWBVET zl61s=cvRKU$5+%7zPWZa)w=CF*`g1Sg>fn`w^WY4_4^^&sUp+}h*wiRNp&F!=~|m{ z6wqZ__4rgSam4L%@PGA0Ce*QV#?XAbM-d(qZ$W4{wVSfqow8wsv>R*y-ITF8z%N^n zlP=aqG~97RrAKs{Vd!!y&`7hKK4?)Q{qQh*Aot}_eS>n59?9J+3RP@a^ zLJH!E2eF2On|I6PdIk*A7F8*sS50hzi?e%9 zEu-NExnJScwZxvfdD~UYvyoLl$Q??x5KWdE0L(i!6n$fdL&IAg{2Z zD`~6~;M;74f}6S_2{?hHqf>9JEBF6S-rADz_xFvmcK4kXiOt85CjwF}-Y!)$uAh6d z2;J7FP1{p_tJslk+HtR^xlq1zOoZncOZMZbH+WX!GLTP;8ye&@gNw4s^mr<$nV_84 z(w4+FI}u`j#(`mYbb+*c{<{1+fay#yKLx6r*YN+ ziCJz>Wka-Eo8XGseIxR2(5=I)l5_0-gB4SK9@v=fT6Lk%ms_UJH9;^ycat}i4WwM% zE7HNO3QoGVEb0csQ_q_p{aldt-9I}t=_oQ#btwso%DNayE-*(nO=rHTrfEcKW~6Wa zsKSxx+PxaUtE+r1L3Tko?3xp#(<(@6OXF+Lhbj6uJ}DQ}J8y2D$1@&s5T}Ap@wRG- zJ-2iVl}bG62%)3ZzxM0`JSd@`lKZdx6n<=HX9`ghJ>@p;Ji5A?R>i~fy((x%2jBzk zNtGPJ@~J&gnYVv~SJ7nCpTw6saqU44D%ZCC6*(kew?S^Uvv z0IKaFq@1yFwGNgnVcW>rGY(gatZ#SUa#J9^oQR<<>p!U zubqG_OpnDGt^H%`nDW-enMVuLjxH$HBL2BMM-n|vRE8ui!WC%A7CZh@|N8IBh{aj? zp>Iq7xlo9!nU7IP_kwxM@wz5x$!swGdSOEfn$tOadiiQP=AW|_0=Iq$NH;+PBB!tX ziw69xO89p6Q`^-VDvJj8s?U0U-TAL(HX>nrsafuZwjdOQ3e_ZJD1Cgt>bewG8`L^cuZw6UNP+;>x4FUNT> zY(;9u$M5iylVdOb+6t;HJKLFwV^a8XO}}Dt4RQOw`|Lk#f8*za?;g|Bg-ZWx-vJO^ LQ=LXFx1|38o|LSX diff --git a/openslides/core/static/index.html b/openslides/core/static/index.html new file mode 100644 index 000000000..e212cbf1e --- /dev/null +++ b/openslides/core/static/index.html @@ -0,0 +1,3 @@ +

Hello user!

+ +Use This url to get to the client. For further information see the DEVELOPMENT.rst diff --git a/openslides/core/static/js/core/base.js b/openslides/core/static/js/core/base.js deleted file mode 100644 index 4f1992ed5..000000000 --- a/openslides/core/static/js/core/base.js +++ /dev/null @@ -1,1723 +0,0 @@ -(function () { - -'use strict'; - -// The core module used for the OpenSlides site and the projector -angular.module('OpenSlidesApp.core', [ - 'js-data', - 'gettext', - 'ngAnimate', - 'ngBootbox', - 'ngSanitize', // TODO: only use this in functions that need it. - 'ngStorage', - 'ui.bootstrap', - 'ui.bootstrap.datetimepicker', - 'ui.tree', - 'pdf', - 'OpenSlidesApp-templates', -]) - -.config([ - 'DSProvider', - 'DSHttpAdapterProvider', - function(DSProvider, DSHttpAdapterProvider) { - DSProvider.defaults.reapAction = 'none'; - DSProvider.defaults.basePath = '/rest'; - DSProvider.defaults.afterReap = function(model, items) { - if (items.length > 5) { - model.findAll({}, {bypassCache: true}); - } else { - _.forEach(items, function (item) { - model.refresh(item[model.idAttribute]); - }); - } - }; - DSHttpAdapterProvider.defaults.forceTrailingSlash = true; - } -]) - -.factory('ProjectorID', [ - function () { - return function () { - return /projector\/(\d+)\//.exec(location.pathname)[1]; - }; - } -]) - -.config([ - '$sessionStorageProvider', - function ($sessionStorageProvider) { - $sessionStorageProvider.setKeyPrefix('OpenSlides'); - } -]) - -.factory('autoupdate', [ - 'DS', - 'REALM', - 'ProjectorID', - '$q', - '$timeout', - 'ErrorMessage', - function (DS, REALM, ProjectorID, $q, $timeout, ErrorMessage) { - var socket = null; - var retryConnectCallbacks = []; - - var websocketProtocol; - if (location.protocol == 'https:') { - websocketProtocol = 'wss:'; - } else { - websocketProtocol = 'ws:'; - } - - var websocketPath; - if (REALM === 'site') { - websocketPath = '/ws/site/'; - } else if (REALM === 'projector') { - websocketPath = '/ws/projector/' + ProjectorID() + '/'; - } else { - console.error('The constant REALM is not set properly.'); - } - - // Get a random retry timeout between 2000 and 5000 ms. - var getTimeoutTime = function () { - return Math.floor(Math.random() * 3000 + 2000); - }; - - /* The callbacks are invoked if the ws connection closed and this factory tries to - * reconnect after 1 second. The callbacks should return a promise. If the promise - * resolves, the retry-process is stopped, so the callback can indicate whether it - * has managed the reconnecting different.*/ - var runRetryConnectCallbacks = function () { - var callbackPromises = _.map(retryConnectCallbacks, function (callback) { - return callback(); - }); - $q.all(callbackPromises).then(function (success) { - ErrorMessage.clearConnectionError(); - }, function (error) { - $timeout(runRetryConnectCallbacks, getTimeoutTime()); - }); - }; - - var Autoupdate = {}; - Autoupdate.messageReceivers = []; - // We use later a promise to defer the first message of the established ws connection. - Autoupdate.firstMessageDeferred = $q.defer(); - Autoupdate.onMessage = function (receiver) { - Autoupdate.messageReceivers.push(receiver); - }; - Autoupdate.newConnect = function () { - socket = new WebSocket(websocketProtocol + '//' + location.host + websocketPath); - // Make shure the servers state hasn't changed: Send a whoami request. If no users is logged and - // anonymous are deactivated, reboot the client in fact that the server has lost all login information. - socket.onclose = function (event) { - socket = null; - if (event.code !== 1000) { // 1000 is a normal close, like the close on logout - ErrorMessage.setConnectionError(); - } - $timeout(runRetryConnectCallbacks, getTimeoutTime()); - }; - socket.onmessage = function (event) { - var data; - try { - data = JSON.parse(event.data); - _.forEach(Autoupdate.messageReceivers, function (receiver) { - receiver(data); - }); - } catch(err) { - console.error(err); - } - // Check if the promise is not resolved yet. - if (Autoupdate.firstMessageDeferred.promise.$$state.status === 0) { - Autoupdate.firstMessageDeferred.resolve(); - } - ErrorMessage.clearConnectionError(); - }; - }; - Autoupdate.send = function (type, content) { - if (!socket) { - return; - } - - var message = { - type: type, - content: content, - id: '', - }; - - // Generate random id - var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; - for (var i = 0; i < 8; i++) { - message.id += possible.charAt(Math.floor(Math.random() * possible.length)); - } - socket.send(JSON.stringify(message)); - }; - Autoupdate.closeConnection = function () { - if (socket) { - socket.close(); - } - Autoupdate.firstMessageDeferred = $q.defer(); - }; - Autoupdate.registerRetryConnectCallback = function (callback) { - retryConnectCallbacks.push(callback); - }; - return Autoupdate; - } -]) - -.factory('operator', [ - 'User', - 'Group', - function (User, Group) { - var setUserCallbacks = []; - var operator = { - user: null, - perms: [], - isAuthenticated: function () { - return !!this.user; - }, - setUser: function(user_id, user_data) { - if (user_id && user_data) { - operator.user = User.inject(user_data); - } else { - operator.user = null; - } - operator.reloadPerms(); - _.forEach(setUserCallbacks, function (cb) { - cb(operator.user); - }); - }, - // Returns true if the operator has at least one perm of the perms-list. - hasPerms: function(perms) { - if (typeof perms === 'string') { - perms = perms.split(' '); - } - return _.intersection(perms, operator.perms).length > 0; - }, - reloadPerms: function () { - if (operator.user) { - operator.perms = operator.user.getPerms(); - } else { - var defaultGroup = Group.get(1); - operator.perms = defaultGroup ? defaultGroup.permissions : []; - } - }, - // Returns true if the operator is a member of group. - isInGroup: function(group) { - var groups = operator.user.groups_id; - if (groups.length === 0) { - groups = [1]; // Set the default group, if no other groups are set. - } - return _.indexOf(groups, group.id) > -1; - }, - registerSetUserCallback: function (cb) { - setUserCallbacks.push(cb); - }, - }; - return operator; - } -]) - -// gets all in OpenSlides available languages -.factory('Languages', [ - '$sessionStorage', - '$ngBootbox', - 'gettext', - 'gettextCatalog', - 'OpenSlidesPlugins', - function ($sessionStorage, $ngBootbox, gettext, gettextCatalog, OpenSlidesPlugins) { - return { - // get all available languages - getLanguages: function () { - var current = $sessionStorage.language; - // Define here new languages... - var languages = [ - { code: 'en', name: 'English' }, - { code: 'de', name: 'Deutsch' }, - { code: 'fr', name: 'Français' }, - { code: 'es', name: 'Español' }, - { code: 'pt', name: 'Português' }, - { code: 'cs', name: 'Čeština'}, - { code: 'ru', name: 'русский'}, - ]; - angular.forEach(languages, function (language) { - if (language.code == current) - language.selected = true; - }); - return languages; - }, - // get detected browser language code - getBrowserLanguage: function () { - var lang = navigator.language || navigator.userLanguage; - if (!navigator.language && !navigator.userLanguage) { - lang = 'en'; - } else { - if (lang.indexOf('-') !== -1) - lang = lang.split('-')[0]; - if (lang.indexOf('_') !== -1) - lang = lang.split('_')[0]; - } - return lang; - }, - // set current language and return updated languages object array - setCurrentLanguage: function (lang) { - var languages = this.getLanguages(); - var plugins = OpenSlidesPlugins.getAll(); - angular.forEach(languages, function (language) { - language.selected = false; - if (language.code == lang) { - language.selected = true; - $sessionStorage.language = lang; - gettextCatalog.setCurrentLanguage(lang); - // Plugins - if (lang != 'en') { - gettextCatalog.loadRemote("static/i18n/" + lang + ".json").then(function (success) { - // translate ng-bootbox directives when the translations are available. - $ngBootbox.addLocale(lang, { - OK: gettextCatalog.getString('OK'), - CANCEL: gettextCatalog.getString('Cancel'), - CONFIRM: gettextCatalog.getString('OK'), // Yes, 'OK' is the original string. - }); - $ngBootbox.setLocale(lang); - }); - // load language files from plugins - angular.forEach(plugins, function (plugin) { - if (plugin.languages.indexOf(lang) != -1) { - gettextCatalog.loadRemote("static/i18n/" + plugin.name + '/' + lang + ".json"); - } - }); - } - } - }); - return languages; - } - }; - } -]) - -// Hook into gettextCatalog to include custom translations by wrapping -// the getString method. The translations are stored in the config. -.decorator('gettextCatalog', [ - '$delegate', - '$rootScope', - function ($delegate, $rootScope) { - var oldGetString = $delegate.getString; - var customTranslations = {}; - - $delegate.getString = function () { - var translated = oldGetString.apply($delegate, arguments); - if (customTranslations[translated]) { - translated = customTranslations[translated]; - } - return translated; - }; - $delegate.setCustomTranslations = function (translations) { - customTranslations = translations; - $rootScope.$broadcast('gettextLanguageChanged'); - }; - - return $delegate; - } -]) - -.run([ - '$rootScope', - 'Config', - 'gettextCatalog', - function ($rootScope, Config, gettextCatalog) { - $rootScope.$watch(function () { - return Config.lastModified('translations'); - }, function () { - var translations = Config.get('translations'); - if (translations) { - var customTranslations = {}; - _.forEach(translations.value, function (entry) { - customTranslations[entry.original] = entry.translation; - }); - // Update all translate directives - gettextCatalog.setCustomTranslations(customTranslations); - } - }); - } -]) - -// set browser language as default language for OpenSlides -.run([ - '$sessionStorage', - 'gettextCatalog', - 'Languages', - function($sessionStorage, gettextCatalog, Languages) { - // set detected browser language as default language (fallback: 'en') - if ($sessionStorage.language) { - Languages.setCurrentLanguage($sessionStorage.language); - } else { - Languages.setCurrentLanguage(Languages.getBrowserLanguage()); - } - // Set this to true for debug. Helps to find untranslated strings by - // adding "[MISSING]:". - gettextCatalog.debug = false; - } -]) - -.factory('dsEject', [ - 'DS', - function (DS) { - return function (collection, instance) { - var Resource = DS.definitions[collection]; - if (Resource.relationList) { - Resource.relationList.forEach(function (relationDef) { - if (relationDef.foreignKey && !relationDef.osProtectedRelation) { - var query = {}; - query[relationDef.foreignKey] = instance[Resource.idAttribute]; - Resource.getResource(relationDef.relation).ejectAll(query); - } - }); - } - }; - } -]) - -.run([ - 'DS', - 'autoupdate', - 'dsEject', - function (DS, autoupdate, dsEject) { - // Handler for normal autoupdate messages. - autoupdate.onMessage(function(data) { - if (data.type !== 'autoupdate') { - return; - } - - var dataList = data.content; - var dataListByCollection = _.groupBy(dataList, 'collection'); - _.forEach(dataListByCollection, function (list, key) { - var changedElements = []; - var deletedElements = []; - var collectionString = key; - _.forEach(list, function (data) { - // Uncomment this line for debugging to log all autoupdates: - // console.log("Received object: " + data.collection + ", " + data.id); - - // remove (=eject) object from local DS store - var instance = DS.get(data.collection, data.id); - if (instance) { - dsEject(data.collection, instance); - } - // check if object changed or deleted - if (data.action === 'changed') { - changedElements.push(data.data); - } else if (data.action === 'deleted') { - deletedElements.push(data.id); - } else { - console.error('Error: Undefined action for received object' + - '(' + data.collection + ', ' + data.id + ')'); - } - }); - // add (=inject) all given objects into local DS store - if (changedElements.length > 0) { - DS.inject(collectionString, changedElements); - } - // delete (=eject) all given objects from local DS store - // (note: js-data does not provide 'bulk eject' as for DS.inject) - _.forEach(deletedElements, function(id) { - DS.eject(collectionString, id); - }); - }); - }); - } -]) - -.factory('Notify', [ - 'autoupdate', - 'operator', - function (autoupdate, operator) { - var anonymousTrackId; - - // Handler for notify messages. - autoupdate.onMessage(function(data) { - if (data.type !== 'notify') { - return; - } - - var dataList = data.content; - var dataListByCollection = _.groupBy(dataList, 'collection'); - _.forEach(dataListByCollection.notify, function (notifyItem) { - // Check, if this current user (or anonymous instance) has send this notify. - if (notifyItem.senderUserId) { - if (operator.user) { // User send to user - notifyItem.sendBySelf = (notifyItem.senderUserId === operator.user.id); - } else { // User send to anonymous - notifyItem.sendBySelf = false; - } - } else { - if (operator.user) { // Anonymous send to user - notifyItem.sendBySelf = false; - } else { // Anonymous send to anonymous - notifyItem.sendBySelf = (notifyItem.anonymousTrackId === anonymousTrackId); - } - } - // notify registered receivers. - _.forEach(callbackReceivers[notifyItem.name], function (item) { - item.fn(notifyItem); - }); - }); - }); - - var callbackReceivers = {}; - /* Structure of callbackReceivers: - * event_name_one: [ {id:0, fn:fn}, {id:3, fn:fn} ], - * event_name_two: [ {id:2, fn:fn} ], - * */ - var idCounter = 0; - var eventNameRegex = new RegExp('^[a-zA-Z0-9_-]+$'); - var externIdRegex = new RegExp('^[a-zA-Z0-9_-]+\/[0-9]+$'); - return { - registerCallback: function (eventName, fn) { - if (!eventNameRegex.test(eventName)) { - throw 'eventName should only consist of [a-zA-Z0-9_-]'; - } else if (typeof fn === 'function') { - var id = idCounter++; - - if (!callbackReceivers[eventName]) { - callbackReceivers[eventName] = []; - } - callbackReceivers[eventName].push({ - id: id, - fn: fn, - }); - return eventName + '/' + id; - } else { - throw 'fn should be a function.'; - } - }, - deregisterCallback: function (externId) { - if (externIdRegex.test(externId)){ - var split = externId.split('/'); - var eventName = split[0]; - var id = parseInt(split[1]); - callbackReceivers[eventName] = _.filter(callbackReceivers[eventName], function (item) { - return item.id !== id; - }); - } else { - throw externId + ' is not a valid id'; - } - }, - // variable length of parameters, just pass ids. - deregisterCallbacks: function () { - _.forEach(arguments, this.deregisterCallback); - }, - notify: function(eventName, params, users, channels, projectors) { - if (eventNameRegex.test(eventName)) { - if (!params || typeof params !== 'object') { - params = {}; - } - - var notifyItem = { - collection: 'notify', - name: eventName, - params: params, - users: users, - replyChannels: channels, - projectors: projectors, - }; - if (!operator.user) { - if (!anonymousTrackId) { - anonymousTrackId = Math.floor(Math.random()*1000000); - } - notifyItem.anonymousTrackId = anonymousTrackId; - } - autoupdate.send('notify', [notifyItem]); - } else { - throw 'eventName should only consist of [a-zA-Z0-9_-]'; - } - }, - }; - } -]) - -.run([ - 'autoupdate', - function (autoupdate) { - // Handler for normal autoupdate messages. - autoupdate.onMessage(function (data) { - if (data.type === 'error') { - console.error("Websocket error", data.content); - } - }); - } -]) - -// Save the server time to the rootscope. -.run([ - '$http', - '$rootScope', - function ($http, $rootScope) { - // Loads server time and calculates server offset - $rootScope.serverOffset = 0; - $http.get('/core/servertime/') - .then(function(data) { - $rootScope.serverOffset = Math.floor(Date.now() / 1000 - data.data); - }); - } -]) - -.run([ - 'Config', - '$rootScope', - function (Config, $rootScope) { - $rootScope.config = function (key) { - try { - return Config.get(key).value; - } - catch(err) { - return ''; - } - }; - } -]) - -// Make the indexOf available in every scope; needed for the projectorbuttons -.run([ - '$rootScope', - function ($rootScope) { - $rootScope.inArray = function (array, value) { - return _.indexOf(array, value) > -1; - }; - } -]) - -// Put the Math object into every scope. -.run([ - '$rootScope', - function ($rootScope) { - $rootScope.Math = window.Math; - } -]) - -// Template hooks -// Possible uses: -// 1. { id: 'myHookId', template: '' } -// 2. { id: 'myHookId', templateUrl: '/static/templates/plugin_name/my-hook.html' } -// 3. { id: 'myHookId' } -// -// Deprecated: Give the id with 'Id'. Please use 'id'. -// -// Option 3 is for just changing the scope (see below), but not the original content. This -// is usefull to alter a JS behavior, e.g. on a ng-click. In this case, override is false -// for this template hook. -// -// It is possible to provide a scope, that is merged into the surrounding scope. -// You can override functions or values of the surrounding scope by providing them: -// { id: 'hookId', template: '', -// scope: { -// customOrOverwritten: function () { /*Do something */ }, -// }, -// } -// Or you provide a function that returns an object of functions/values to overwrite to -// get access to the scope merged in: -// { id: 'hookId', template: '', -// scope: function (scope) { -// return { -// customOrOverwritten: function () { -// scope.value = /* change it */; -// }, -// }; -// }, -// } -// -// As a default, template hooks in flavour of option 1 and 2 override the content that was -// originally there. Provide 'override: false', to prevent overriding the original content. -.factory('templateHooks', [ - function () { - var hooks = {}; - return { - hooks: hooks, - registerHook: function (hook) { - // Deprecated: Set the new style 'id', if 'Id' is given. - if (hook.id === void 0) { - hook.id = hook.Id; - } - - if (hooks[hook.id] === void 0) { - hooks[hook.id] = []; - } - // set override default - if (hook.override === void 0) { - hook.override = !!(hook.template || hook.templateUrl); - } - hooks[hook.id].push(hook); - } - }; - } -]) - -.directive('templateHook', [ - '$compile', - '$http', - '$q', - '$templateCache', - '$timeout', - 'templateHooks', - function ($compile, $http, $q, $templateCache, $timeout, templateHooks) { - return { - restrict: 'E', - template: '', - link: function (scope, iElement, iAttr) { - var hooks = templateHooks.hooks[iAttr.hookName]; - if (hooks) { - // Populate scopes - _.forEach(hooks, function (hook) { - var _scope = hook.scope; - // If it is a function, get the scope from the function and provide - // the original scope. - if (typeof hook.scope === 'function') { - _scope = hook.scope(scope); - } - - _.forEach(_scope, function (value, key) { - scope[key] = value; - }); - }); - - // Check, if at least one hook overrides the original content. - var override = _.some(hooks, function (hook) { - return hook.override; - }); - - // filter hooks, that does actually have a template - hooks = _.filter(hooks, function (hook) { - return hook.template || hook.templateUrl; - }); - - // Get all templates - var templates = _.map(hooks, function (hook) { - // Either a template (html given as string) or a templateUrl has - // to be given. If a scope is provided, the schope of this templateHook - // is populated with the given functions/values. - if (hook.template) { - return hook.template; - } else { - return $templateCache.get(hook.templateUrl); - } - }); - - // Wait for the dom to build up, so we can retrieve the inner html of iElement. - $timeout(function () { - var html = override ? '' : iElement.html(); - if (templates.length) { - html += templates.join(''); - } - - iElement.empty(); - iElement.append($compile(html)(scope)); - }); - } - } - }; - } -]) - -/* - * This places a projector button in the document. - * - * Example: - * This button references to model (in this example 'motion'). Also a defaultProjectionId - * has to be given. In the example it's a scope variable. The next two parameters are additional: - * - arg: Then the model.project and model.isProjected will be called with - * this argument (e. g.: model.project(2)) - * - content: A text placed behind the projector symbol. - */ -.directive('projectorButton', [ - 'Projector', - function (Projector) { - return { - restrict: 'E', - templateUrl: 'static/templates/projector-button.html', - link: function (scope, element, attributes) { - if (!attributes.model) { - throw 'A model has to be given!'; - } else if (!attributes.defaultProjectorId) { - throw 'A default-projector-id has to be given!'; - } - - Projector.bindAll({}, scope, 'projectors'); - - scope.$watch(attributes.model, function (model) { - scope.model = model; - }); - - scope.$watch(attributes.defaultProjectorId, function (defaultProjectorId) { - scope.defaultProjectorId = defaultProjectorId; - }); - - if (attributes.arg) { - scope.$watch(attributes.arg, function (arg) { - scope.arg = arg; - }); - } - - scope.content = ''; - if (attributes.content) { - attributes.$observe('content', function (content) { - scope.content = content; - }); - } - } - }; - } -]) - -.factory('jsDataModel', [ - '$http', - 'Projector', - 'ProjectHelper', - function($http, Projector, ProjectHelper) { - var BaseModel = function() {}; - BaseModel.prototype.project = function(projectorId) { - // if this object is already projected on projectorId, delete this element from this projector - var isProjectedIds = this.isProjected(); - var requestData = { - clear_ids: isProjectedIds, - }; - // Show the element, if it was not projected before on the given projector - if (_.indexOf(isProjectedIds, projectorId) == -1) { - requestData.prune = { - id: projectorId, - element: {name: this.getResourceName(), id: this.id}, - }; - } - return ProjectHelper.project(requestData); - }; - BaseModel.prototype.isProjected = function() { - // Returns the ids of all projectors if there is a projector element - // with the same name and the same id. Else returns an empty list. - var self = this; - var predicate = function (element) { - return element.name == self.getResourceName() && - typeof element.id !== 'undefined' && - element.id == self.id; - }; - var isProjectedIds = []; - Projector.getAll().forEach(function (projector) { - if (typeof _.findKey(projector.elements, predicate) === 'string') { - isProjectedIds.push(projector.id); - } - }); - return isProjectedIds; - }; - // Override this method to get object specific behavior - BaseModel.prototype.isRelatedProjected = function() { - throw "needs to be implemented!"; - }; - return BaseModel; - } -]) - -.factory('ErrorMessage', [ - '$timeout', - 'gettextCatalog', - 'Messaging', - function ($timeout, gettextCatalog, Messaging) { - return { - forAlert: function (error) { - var message = gettextCatalog.getString('Error') + ': '; - - if (!error.data) { - message += gettextCatalog.getString("The server didn't respond."); - } else if (error.data.detail) { - message += error.data.detail; - } else if (error.status > 500) { // Some kind of server error. - message += gettextCatalog.getString("A server error occurred (%%code%%). Please check the system logs."); - message = message.replace('%%code%%', error.status); - } else { - for (var e in error.data) { - message += e + ': ' + error.data[e] + ' '; - } - } - return { type: 'danger', msg: message, show: true }; - }, - setConnectionError: function () { - $timeout(function () { - Messaging.createOrEditMessage( - 'connectionLostMessage', - gettextCatalog.getString('Offline mode: You can use OpenSlides but changes are not saved.'), - 'warning', - {noClose: true}); - }, 1); - }, - clearConnectionError: function () { - $timeout(function () { - Messaging.deleteMessage('connectionLostMessage'); - }, 1); - }, - }; - } -]) - -/* Messaging factory. The text is html-binded into the document, so you can - * provide also html markup for the messages. There are 4 types: 'info', - * 'success', 'warning', 'error'. The timeout is for autodeleting the message. - * Args that could be provided: - * - timeout: Milliseconds until autoclose the message (default: not set, no auto close) - * - noClose: Whether to show the close button (default: false) - */ -.factory('Messaging', [ - '$timeout', - function($timeout) { - var callbackList = [], - messages = {}, - idCounter = 0; - - var onChange = function () { - _.forEach(callbackList, function (callback) { - callback(); - }); - }; - - return { - addMessage: function (text, type, args) { - var id = idCounter++; - return this.createOrEditMessage(id, text, type, args); - }, - createOrEditMessage: function (id, text, type, args) { - if (!args) { - args = {}; - } - if (messages[id] && messages[id].timeout) { - $timeout.cancel(messages[id].timeout); - } - messages[id] = { - text: text, - type: type, - id: id, - args: args, - }; - if (typeof args.timeout === 'number' && args.timeout > 0) { - var self = this; - messages[id].timeout = $timeout(function () { - self.deleteMessage(id); - }, args.timeout); - } - onChange(); - return id; - }, - deleteMessage: function (id) { - delete messages[id]; - onChange(); - }, - getMessages: function () { - return messages; - }, - registerMessageChangeCallback: function (fn) { - if (typeof fn === 'function') { - callbackList.push(fn); - } else { - throw 'fn has to be a function'; - } - }, - }; - } -]) - -.factory('Logos', [ - 'Config', - 'gettext', - function (Config, gettext) { - return { - getKeys: function () { - return Config.get('logos_available').value; - }, - getAll: function () { - var self = this; - return _.map(this.getKeys(), function (key) { - return self.get(key); - }); - }, - get: function (key) { - var config = Config.get(key); - if (config) { - config.value.key = key; - return config.value; - } - }, - set: function (key, path) { - var config = Config.get(key); - if (config) { - config.value.path = path || ''; - Config.save(key); - } - }, - }; - } -]) - -.factory('Fonts', [ - 'Config', - 'gettext', - function (Config, gettext) { - var extensionFormatMap = { - 'ttf': 'truetype', - 'woff': 'woff', - }; - - return { - getKeys: function () { - return Config.get('fonts_available').value; - }, - getAll: function () { - var self = this; - return _.map(this.getKeys(), function (key) { - return self.get(key); - }); - }, - get: function (key) { - var config = Config.get(key); - if (config) { - config.value.key = key; - return config.value; - } - }, - getUrl: function (key) { - var font = this.get(key); - if (font) { - var path = font.path; - if (!path) { - return font.default; - } - return path; - } - }, - getForCss: function (key) { - var url = this.getUrl(key); - if (url) { - var ext = _.last(url.split('.')); - return "url('" + url + "') format('" + - extensionFormatMap[ext] + "')"; - } - }, - set: function (key, path) { - var config = Config.get(key); - if (config) { - config.value.path = path || ''; - Config.save(key); - } - }, - }; - } -]) - -.factory('Tag', [ - 'DS', - function(DS) { - return DS.defineResource({ - name: 'core/tag', - }); - } -]) - -.factory('Config', [ - '$http', - 'gettextCatalog', - 'DS', - function($http, gettextCatalog, DS) { - var configOptions; - return DS.defineResource({ - name: 'core/config', - idAttribute: 'key', - translate: function (value) { - return gettextCatalog.getString(value); - } - }); - } -]) - -.factory('ChatMessage', [ - 'DS', - function(DS) { - return DS.defineResource({ - name: 'core/chat-message', - relations: { - belongsTo: { - 'users/user': { - localField: 'user', - localKey: 'user_id', - } - } - } - }); - } -]) - -/* - * Provides a function for plugins to register as new plugin. - * - * Get all registerd plugins via 'OpenSlidesPlugins.getAll()'. - * - * Example code for plugins: - * - * .config([ - * 'OpenSlidesPluginsProvider', - * function(OpenSlidesPluginsProvider) { - * OpenSlidesPluginsProvider.registerPlugin({ - * name: 'openslides_votecollector', - * display_name: 'VoteCollector', - * languages: ['de'] - * }); - * } - * ]) - */ -.provider('OpenSlidesPlugins', [ - function () { - var provider = this; - provider.plugins = []; - provider.registerPlugin = function (plugin) { - provider.plugins.push(plugin); - }; - provider.$get = [ - function () { - return { - getAll: function () { - return provider.plugins; - } - }; - } - ]; - } -]) - - -// Configs for CKEditor which has to set while startup of OpenSlides -.config( - function() { - CKEDITOR.disableAutoInline = true; - } -) - -// Options for CKEditor used in various create and edit views. -// Required in core/base.js because MotionComment factory which used this -// factory has to placed in motions/base.js. -.factory('Editor', [ - 'gettextCatalog', - function (gettextCatalog) { - var extraPlugins = []; - return { - registerDialog: function (name, dialog) { - CKEDITOR.dialog.add(name, dialog); - }, - registerPlugin: function (name, plugin) { - CKEDITOR.plugins.add(name, plugin); - extraPlugins.push(name); - }, - /* Provide special keyword in the arguments for a special behaviour: - * Example: getOptions('inline', 'YOffset') - * Available keywords: - * - inline: smaller toolbar - * - YOffset: move the editor toolbar 40px up - */ - getOptions: function () { - var extraPluginsString = 'colorbutton,colordialog,find,sourcedialog,justify,showblocks'; - var registeredPluginsString = extraPlugins.join(','); - if (registeredPluginsString) { - extraPluginsString += ',' + registeredPluginsString; - } - var options = { - on: { - instanceReady: function() { - // This adds a listener to ckeditor to remove unwanted blank lines on import. - // Clipboard content varies heavily in structure and html code, depending on the "sender". - // Here it is first parsed into a pseudo-DOM (two lines taken from a ckeditor - // paste example on the ckeditor site). - this.on('paste', function(evt) { - if (evt.data.type == 'html') { - var fragment = CKEDITOR.htmlParser.fragment.fromHtml(evt.data.dataValue); - var writer = new CKEDITOR.htmlParser.basicWriter(); - // html content will now be in a dom-like structure inside 'fragment'. - this.filter.applyTo(fragment); - if (fragment.children) { - // If this fragment is DOM-like, it may contain nested properties - // (being html nodes). Traverse the children and check if it is a - // child only containing empty
or

. - // new_content_children will finally contain all nodes that are - // not empty. - var new_content_children = []; - _.forEach(fragment.children, function (child) { - var empty = true; - if (child.children){ - _.forEach(child.children, function(grandchild) { - if (grandchild.name != 'p' && grandchild.name != 'br') { - empty = false; - } else if (grandchild.isEmpty !== true) { - empty = false; - } - }); - if (empty === false) { - new_content_children.push(child); - } - } else { - if (child.name != 'p' && child.name != 'br' && - child.isEmpty !== true){ - new_content_children.push(child); - } - } - }); - fragment.children = new_content_children; - } - fragment.writeHtml(writer); - // Return the re-created fragment without the empty

and
into the - // editor import processing (same as at the begin of the function: by ckeditor) - evt.data.dataValue = writer.getHtml(); - } - }); - } - }, - customConfig: '', - floatSpaceDockedOffsetY: _.indexOf(arguments, 'YOffset') > -1 ? 35 : 0, - disableNativeSpellChecker: false, - language_list: [ - 'fr:français', - 'es:español', - 'pt:português', - 'en:english', - 'de:deutsch', - 'cs:čeština'], - language: gettextCatalog.getCurrentLanguage(), - allowedContent: - 'h1 h2 h3 b i u strike sup sub strong em;' + - 'blockquote p pre table' + - '(text-align-left,text-align-center,text-align-right,text-align-justify,os-split-before,os-split-after){text-align, float, padding};' + - 'a[!href];' + - 'img[!src,alt]{width,height,float, padding};' + - 'tr th td caption;' + - 'li(os-split-before,os-split-after); ol(os-split-before,os-split-after)[start]{list-style-type};' + - 'ul(os-split-before,os-split-after){list-style};' + - 'span[!*]{color,background-color}(os-split-before,os-split-after,os-line-number,line-number-*);' + - 'br(os-line-break);', - - // there seems to be an error in CKeditor that parses spaces in extraPlugins as part of the plugin name. - extraPlugins: extraPluginsString, - removePlugins: 'wsc,scayt,a11yhelp,filebrowser,sourcearea,liststyle,tabletools,tableselection,contextmenu,image', - removeButtons: 'Scayt,Anchor,Styles,HorizontalRule', - }; - if (_.indexOf(arguments, 'inline') > -1) { - options.toolbarGroups = [ - { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] }, - { name: 'colors', groups: [ 'colors' ] }, - { name: 'paragraph', groups: [ 'list'] }, - { name: 'links', groups: [ 'links' ] }, - { name: 'clipboard', groups: [ 'undo' ] }, - { name: 'document', groups: [ 'mode' ] }, - ]; - options.removeButtons = 'Underline,Subscript,Superscript,PasteFromWord,PasteText,Scayt,Link,Unlink,Anchor,HorizontalRule,Table,Image,Maximize,Source,Format,About,Paste,Cut,Copy'; - } else { - options.toolbarGroups = [ - { name: 'clipboard', groups: [ 'clipboard', 'undo' ] }, - { name: 'editing', groups: [ 'find', 'selection', 'spellchecker', 'editing' ] }, - { name: 'links', groups: [ 'links' ] }, - { name: 'insert', groups: [ 'insert' ] }, - { name: 'tools', groups: [ 'tools' ] }, - { name: 'document', groups: [ 'mode' ] }, - '/', - { name: 'styles', groups: [ 'styles' ] }, - { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] }, - { name: 'colors', groups: [ 'colors' ] }, - { name: 'paragraph', groups: [ 'list', 'indent' ] }, - { name: 'align'}, - { name: 'paragraph', groups: [ 'blocks' ] } - ]; - } - return options; - } - }; - } -]) - -/* Model for a projector. - * - * At the moment we use only one projector, so there will be only one object - * in this model. It has the id 1. For later releases there will be multiple - * projector objects. - * - * This model uses onConfilict: 'replace' instead of 'merge'. This is necessary - * because the keys of the projector objects can change and old keys have to - * be removed. See http://www.js-data.io/docs/dsdefaults#onconflict for - * more information. - */ -.factory('Projector', [ - 'DS', - '$http', - 'EditForm', - 'Config', - function(DS, $http, EditForm, Config) { - return DS.defineResource({ - name: 'core/projector', - onConflict: 'replace', - relations: { - hasMany: { - 'core/projection-default': { - localField: 'projectiondefaults', - foreignKey: 'projector_id', - } - }, - }, - methods: { - controlProjector: function(action, direction) { - $http.post('/rest/core/projector/' + this.id + '/control_view/', - {"action": action, "direction": direction} - ); - }, - getFormOrStateForCurrentSlide: function () { - var return_dict; - angular.forEach(this.elements, function(value, key) { - if (value.name == 'agenda/list-of-speakers') { - return_dict = { - state: 'agenda.item.detail', - id: value.id, - }; - } else if ( - // TODO: - // Find generic solution for whitelist in getFormOrStateForCurrentSlide - // see https://github.com/OpenSlides/OpenSlides/issues/3130 - value.name === 'topics/topic' || - value.name === 'motions/motion' || - value.name === 'motions/motion-block' || - value.name === 'assignments/assignment' || - value.name === 'mediafiles/mediafile' || - value.name === 'users/user') { - return_dict = { - form: EditForm.fromCollectionString(value.name), - id: value.id, - }; - } - }); - return return_dict; - }, - toggleBlank: function () { - $http.post('/rest/core/projector/' + this.id + '/control_blank/', - !this.blank - ); - }, - toggleBroadcast: function () { - $http.post('/rest/core/projector/' + this.id + '/broadcast/'); - } - }, - }); - } -]) - -// This factory sends a request to /rest/core/projectors/project -// with the given data. Also it does the changes done by the server -// locally and may reverts them, if something went wrong. -.factory('ProjectHelper', [ - '$q', - '$http', - 'Projector', - function ($q, $http, Projector) { - var uuid4 = function () { - function s8() { - return Math.floor((1 + Math.random()) * 0x100000000) - .toString(16) - .substring(1); - } - return s8() + s8() + s8() + s8(); - }; - - return { - project: function (data) { - var projector; - // get all projectors that will be changed. - var projectorsChanged = _.filter(_.map(data.clear_ids, function (id) { - return Projector.get(id); - })); - if (data.prune && !_.includes(data.clear_ids, data.prune.id)) { - projector = Projector.get(data.prune.id); - if (projector) { - projectorsChanged.push(projector); - } - } - - // copy original projectors in case we have to reconstruct those - // _.cloneDeep and angular.clone does not work here; I'm not - // exactly sure why.. - var originalProjectors = _.map(projectorsChanged, function (projector) { - var elements = {}; - _.forEach(projector.elements, function (element, key) { - elements[key] = _.cloneDeep(element); - }); - return { - id: projector.id, - elements: elements, - scroll: projector.scroll, - scale: projector.scale, - name: projector.name, - blank: projector.blank, - width: projector.width, - height: projector.height, - projectiondefaults: _.cloneDeep(projector.projectiondefaults), - }; - }); - - // Clear every projector - _.forEach(projectorsChanged, function (projector) { - var elements = {}; - _.forEach(projector.elements, function (element, key) { - if (element.stable) { - elements[key] = element; - } - }); - projector.elements = elements; - }); - - // Add the prune element if given - if (data.prune) { - projector = _.find(projectorsChanged, function (projector) { - return projector.id === data.prune.id; - }); - if (projector) { - projector.scroll = 0; - projector.elements[uuid4()] = data.prune.element; - } - } - - Projector.inject(projectorsChanged); - - return $http.post('/rest/core/projector/project/', data).catch( - function (error) { - // revert the changes made earlier - Projector.inject(originalProjectors); - return $q.reject(error); - } - ); - }, - }; - } -]) - -/* Model for all projection defaults */ -.factory('ProjectionDefault', [ - 'DS', - function(DS) { - return DS.defineResource({ - name: 'core/projection-default', - relations: { - belongsTo: { - 'core/projector': { - localField: 'projector', - localKey: 'projector_id', - } - } - } - }); - } -]) - -/* Model for ProjectorMessages */ -.factory('ProjectorMessage', [ - 'DS', - 'jsDataModel', - 'gettext', - '$http', - 'Projector', - function(DS, jsDataModel, gettext, $http, Projector) { - var name = 'core/projector-message'; - return DS.defineResource({ - name: name, - useClass: jsDataModel, - verboseName: gettext('Message'), - verbosenamePlural: gettext('Messages'), - methods: { - getResourceName: function () { - return name; - }, - // Override the BaseModel.project function - project: function(projectorId) { - // if this object is already projected on projectorId, delete this element from this projector - var isProjectedIds = this.isProjected(); - var self = this; - var predicate = function (element) { - return element.name === name && element.id === self.id; - }; - _.forEach(isProjectedIds, function (id) { - var uuid = _.findKey(Projector.get(id).elements, predicate); - $http.post('/rest/core/projector/' + id + '/deactivate_elements/', [uuid]); - }); - // if it was the same projector before, just delete it but not show again - if (_.indexOf(isProjectedIds, projectorId) == -1) { - // Now check whether other messages are already projected and delete them - var elements = Projector.get(projectorId).elements; - _.forEach(elements, function (element, uuid) { - if (element.name === name) { - $http.post('/rest/core/projector/' + projectorId + '/deactivate_elements/', [uuid]); - } - }); - return $http.post( - '/rest/core/projector/' + projectorId + '/activate_elements/', - [{name: name, id: self.id, stable: true}] - ); - } - }, - } - }); - } -]) - -/* Model for Countdowns */ -.factory('Countdown', [ - 'DS', - 'jsDataModel', - 'gettext', - '$rootScope', - '$http', - 'Projector', - function(DS, jsDataModel, gettext, $rootScope, $http, Projector) { - var name = 'core/countdown'; - return DS.defineResource({ - name: name, - useClass: jsDataModel, - verboseName: gettext('Countdown'), - verbosenamePlural: gettext('Countdowns'), - methods: { - getResourceName: function () { - return name; - }, - start: function () { - // calculate end point of countdown (in seconds!) - var endTimestamp = Date.now() / 1000 - $rootScope.serverOffset + this.countdown_time; - this.running = true; - this.countdown_time = endTimestamp; - DS.save(name, this.id); - }, - stop: function () { - // calculate rest duration of countdown (in seconds!) - var newDuration = Math.floor( this.countdown_time - Date.now() / 1000 + $rootScope.serverOffset ); - this.running = false; - this.countdown_time = newDuration; - DS.save(name, this.id); - }, - reset: function () { - this.running = false; - this.countdown_time = this.default_time; - DS.save(name, this.id); - }, - // Override the BaseModel.project function - project: function(projectorId) { - // if this object is already projected on projectorId, delete this element from this projector - var isProjectedIds = this.isProjected(); - var self = this; - var predicate = function (element) { - return element.name == name && element.id == self.id; - }; - _.forEach(isProjectedIds, function (id) { - var uuid = _.findKey(Projector.get(id).elements, predicate); - $http.post('/rest/core/projector/' + id + '/deactivate_elements/', [uuid]); - }); - // if it was the same projector before, just delete it but not show again - if (_.indexOf(isProjectedIds, projectorId) == -1) { - return $http.post( - '/rest/core/projector/' + projectorId + '/activate_elements/', - [{name: name, id: self.id, stable: true}] - ); - } - }, - }, - }); - } -]) - -/* Two functions to convert between time duration in seconds <-> human readable time span. - * E.g. 90 sec <-> 1:30 (min), 3661 sec <-> 1:01:01 (h) - * - * secondsToHumanTime: Expects seconds and give [h*:]mm[:ss]. The minutes part is always given, the hours - * and minutes could be controlled. The default are forced seconds and hours just if it is not 0. - * - seconds ('enabled', 'auto', 'disabled'): Whether to show seconds (Default 'enabled') - * - hours ('enabled', 'auto', 'disabled'): Whether to show hours (Default 'auto') - * - * humanTimeToSeconds: Expects [h*:]m*[:s*] with each part could have a variable length. The parsed time is - * in seconds. Minutes have to be given and hours and seconds are optional. One have to set 'seconds' or - * 'hours' to true toparse these. - * - * params could be an object with the given settings, e.g. {ignoreHours: true} - */ -.factory('HumanTimeConverter', [ - function () { - return { - secondsToHumanTime: function (seconds, params) { - if (!params) { - params = {seconds: 'enabled', hours: 'auto'}; - } - if (!params.seconds) { - params.seconds = 'enabled'; - - } - if (!params.hours) { - params.hours = 'auto'; - } - var time; - // floor returns the largest integer of the absolut value of seconds - var total = Math.floor(Math.abs(seconds)); - var h = Math.floor(total / 3600); - var m = Math.floor(total % 3600 / 60); - var s = Math.floor(total % 60); - // Add leading "0" for double digit values - time = ('0'+m).slice(-2); //minutes - if ((params.seconds == 'auto' && s > 0) || params.seconds == 'enabled') { - s = ('0'+s).slice(-2); - time = time + ':' + s; - } - if ((params.hours == 'auto' && h > 0) || params.hours == 'enabled') { - time = h + ':' + time; - } - if (seconds < 0) { - time = '-'+time; - } - return time; - }, - humanTimeToSeconds: function (data, params) { - if (!params) { - params = {seconds: false, hours: false}; - } - var minLength = 1; - if (params.seconds) { - minLength++; - } - if (params.hours){ - minLength++; - } - - var negative = data.charAt(0) == '-'; - var time = data.split(':'); - data = 0; - if (time.length >= minLength) { - for (var i = 0; i < minLength; i++) { - data = data*60; - if (!isNaN(+time[i])) { - data += (+time[i]); - } - } - if (!params.seconds) { // the last field was minutes (e.g. h:mm) - data *= 60; - } - if (negative) { - data = -data; - } - } - return data; - }, - }; - } -]) - -/* Converts a snake-case string to camelCase. Example: - * 'motion-block-config' -> 'motionBlockConfig' */ -.factory('CamelCase', [ - function () { - return function (str) { - return str.replace(/-([a-z])/g, function (match) { - return match[1].toUpperCase(); - }); - }; - } -]) - -/* Return the specific EditForm for a given model. */ -.factory('EditForm', [ - '$injector', - 'CamelCase', - function ($injector, CamelCase) { - return { - fromCollectionString: function (collection) { - var modelName = CamelCase(collection).split('/')[1]; - // Convert modelModel to ModelModelForm - var formName = modelName.charAt(0).toUpperCase() + modelName.slice(1) + 'Form'; - return $injector.get(formName); - }, - }; - } -]) - -/* Converts number of seconds into string "h:mm:ss" or "mm:ss" */ -.filter('osSecondsToTime', [ - 'HumanTimeConverter', - function (HumanTimeConverter) { - return function (seconds) { - return HumanTimeConverter.secondsToHumanTime(seconds); - }; - } -]) - -/* Converts number of minutes into string "h:mm" or "hh:mm" */ -.filter('osMinutesToTime', [ - 'HumanTimeConverter', - function (HumanTimeConverter) { - return function (minutes) { - return HumanTimeConverter.secondsToHumanTime(minutes*60, - { seconds: 'disabled', - hours: 'enabled' } - ); - }; - } -]) - -// mark HTML as "trusted" -.filter('trusted', [ - '$sce', - function ($sce) { - return function(text) { - return $sce.trustAsHtml(text); - }; - } -]) - -// filters the requesting object (id=selfid) from a list of input objects -.filter('notself', function () { - return function (input, selfid) { - var result; - if (selfid) { - result = []; - for (var key in input){ - var obj = input[key]; - if (selfid != obj.id) { - result.push(obj); - } - } - } else { - result = input; - } - return result; - }; -}) - -// Wraps the orderBy filter. But puts ("", null, undefined) last. -.filter('orderByEmptyLast', [ - '$filter', - '$parse', - function ($filter, $parse) { - return function (array, sortPredicate, reverseOrder, compareFn) { - var parsed = $parse(sortPredicate); - var falsyItems = []; - var truthyItems = _.filter(array, function (item) { - var falsy = parsed(item) === void 0 || parsed(item) === null || parsed(item) === ''; - if (falsy) { - falsyItems.push(item); - } - return !falsy; - }); - truthyItems = $filter('orderBy')(truthyItems, sortPredicate, reverseOrder, compareFn); - return _.concat(truthyItems, falsyItems); - }; - } -]) - -// Make sure that the DS factories are loaded by making them a dependency -.run([ - 'ChatMessage', - 'Config', - 'Countdown', - 'ProjectorMessage', - 'Projector', - 'ProjectionDefault', - 'Tag', - 'Notify', // For setting up the autoupdate callback - function (ChatMessage, Config, Countdown, ProjectorMessage, Projector, ProjectionDefault, Tag, Notify) {} -]); - -}()); diff --git a/openslides/core/static/js/core/csv.js b/openslides/core/static/js/core/csv.js deleted file mode 100644 index 8361bbf7e..000000000 --- a/openslides/core/static/js/core/csv.js +++ /dev/null @@ -1,23 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.core.csv', []) - -.factory('CsvDownload', [ - 'Config', - 'FileSaver', - function (Config, FileSaver) { - var utf8_BOM = decodeURIComponent('%EF%BB%BF'); - return function (contentRows, filename) { - var separator = Config.get('general_csv_separator').value; - var rows = _.map(contentRows, function (row) { - return row.join(separator); - }); - var blob = new Blob([utf8_BOM + rows.join('\n')]); - FileSaver.saveAs(blob, filename); - }; - } -]); - -}()); diff --git a/openslides/core/static/js/core/docx.js b/openslides/core/static/js/core/docx.js deleted file mode 100644 index 21c0d098c..000000000 --- a/openslides/core/static/js/core/docx.js +++ /dev/null @@ -1,356 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.core.docx', []) - -.factory('Html2DocxConverter', [ - '$q', - 'ImageConverter', - function ($q, ImageConverter) { - var PAGEBREAK = ''; - - var createInstance = function () { - var converter = { - imageMap: {}, - documentImages: [], - relationships: [], - contentTypes: [], - }; - - var html2docx = function (html) { - var docx = ''; - var tagStack = []; - - // With this variable, we keep track, if we are currently inside or outside of a paragraph. - var inParagraph = true; - // the text may not begin with a paragraph. If so, append one because word needs it. - var skipFirstParagraphClosing = true; - - var handleTag = function (tag) { - if (tag.charAt(0) == "/") { // A closing tag - // remove from stack - tagStack.pop(); - - // Special: end paragraphs - if (tag.indexOf('/p') === 0) { - docx += ''; - inParagraph = false; - } - } else { // now all other tags - var tagname = tag.split(' ')[0]; - handleNamedTag(tagname, tag); - } - return docx; - }; - var handleNamedTag = function (tagname, fullTag) { - var tag = { - tag: tagname, - attrs: {}, - }; - switch (tagname) { - case 'p': - if (inParagraph && !skipFirstParagraphClosing) { - // End the paragrapth, if there is one - docx += ''; - } - skipFirstParagraphClosing = false; - docx += ''; - inParagraph = true; - break; - case 'span': - var styleRegex = /(?:\"|\;\s?)([a-zA-z\-]+)\:\s?([a-zA-Z0-9\-\#]+)/g, matchSpan; - while ((matchSpan = styleRegex.exec(fullTag)) !== null) { - switch (matchSpan[1]) { - case 'color': - tag.attrs.color = matchSpan[2].slice(1); // cut off the # - break; - case 'background-color': - tag.attrs.backgroundColor = matchSpan[2].slice(1); // cut off the # - break; - case 'text-decoration': - if (matchSpan[2] === 'underline') { - tag.attrs.underline = true; - } else if (matchSpan[2] === 'line-through') { - tag.attrs.strike = true; - } - break; - } - } - break; - case 'a': - var hrefRegex = /href="([^"]+)"/g; - var href = hrefRegex.exec(fullTag)[1]; - tag.href = href; - break; - case 'img': - imageTag(tag, fullTag); - break; - } - if (tagname !== 'img' && tagname !== 'p') { - tagStack.push(tag); - } - }; - var imageTag = function (tag, fullTag) { - // images has to be placed instantly, so there is no use of 'tag'. - var image = {}; - var attributeRegex = /(\w+)=\"([^\"]*)\"/g, attributeMatch; - while ((attributeMatch = attributeRegex.exec(fullTag)) !== null) { - image[attributeMatch[1]] = attributeMatch[2]; - } - if (image.src && converter.imageMap[image.src]) { - image.width = converter.imageMap[image.src].width; - image.height = converter.imageMap[image.src].height; - - var rrId = converter.relationships.length + 1; - var imageId = converter.documentImages.length + 1; - - // set name ('pic.jpg'), title, ext ('jpg'), mime ('image/jpeg') - image.name = _.last(image.src.split('/')); - - var tmp = image.name.split('.'); - image.ext = tmp.splice(-1); - - // set name without extension as title if there isn't a title - if (!image.title) { - image.title = tmp.join('.'); - } - - image.mime = 'image/' + image.ext; - if (image.ext == 'jpe' || image.ext == 'jpg') { - image.mime = 'image/jpeg'; - } - - // x and y for the container and picture size in EMU (assuming 96dpi)! - var x = image.width * 914400 / 96; - var y = image.height * 914400 / 96; - - // the image does not belong into a paragraph in ooxml - if (inParagraph) { - docx += ''; - } - docx += '' + - '' + - '' + - '' + - '' + - '' + - ''; - - // inParagraph stays untouched, the documents paragraph state is restored here - if (inParagraph) { - docx += ''; - } - - // entries in documentImages, relationships and contentTypes - converter.documentImages.push({ - src: image.src, - zipPath: 'word/media/' + image.name - }); - converter.relationships.push({ - Id: 'rrId' + rrId, - Type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', - Target: 'media/' + image.name - }); - converter.contentTypes.push({ - PartName: '/word/media/' + image.name, - ContentType: image.mime - }); - } - }; - var handleText = function (text) { - // Start a new paragraph, if only loose text is there - if (!inParagraph) { - docx += ''; - inParagraph = true; - } - var docxPart = ''; - var hyperlink = false; - tagStack.forEach(function (tag) { - switch (tag.tag) { - case 'b': - case 'strong': - docxPart += ''; - break; - case 'em': - case 'i': - docxPart += ''; - break; - case 'span': - for (var key in tag.attrs) { - switch (key) { - case 'color': - docxPart += ''; - break; - case 'backgroundColor': - docxPart += ''; - break; - case 'underline': - docxPart += ''; - break; - case 'strike': - docxPart += ''; - break; - } - } - break; - case 'u': - docxPart += ''; - break; - case 'strike': - docxPart += ''; - break; - case 'a': - var id = converter.relationships.length + 1; - docxPart = '' + docxPart; - docxPart += ''; - converter.relationships.push({ - Id: 'rrId' + id, - Type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', - Target: tag.href, - TargetMode: 'External' - }); - hyperlink = true; - break; - } - }); - docxPart += '' + text + ''; - if (hyperlink) { - docxPart += ''; - } - - // append to docx - docx += docxPart; - return docx; - }; - - var replaceEntities = function () { - // replacing of special symbols: - docx = docx.replace(new RegExp('\ä\;', 'g'), 'ä'); - docx = docx.replace(new RegExp('\ü\;', 'g'), 'ü'); - docx = docx.replace(new RegExp('\ö\;', 'g'), 'ö'); - docx = docx.replace(new RegExp('\Ä\;', 'g'), 'Ä'); - docx = docx.replace(new RegExp('\Ü\;', 'g'), 'Ü'); - docx = docx.replace(new RegExp('\Ö\;', 'g'), 'Ö'); - docx = docx.replace(new RegExp('\ß\;', 'g'), 'ß'); - docx = docx.replace(new RegExp('\ \;', 'g'), ' '); - docx = docx.replace(new RegExp('\§\;', 'g'), '§'); - - // remove all entities except gt, lt and amp - var entityRegex = /\&(?!gt|lt|amp)\w+\;/g, matchEntry, indexes = []; - while ((matchEntry = entityRegex.exec(docx)) !== null) { - indexes.push({ - startId: matchEntry.index, - stopId: matchEntry.index + matchEntry[0].length - }); - } - for (var i = indexes.length - 1; i>=0; i--) { - docx = docx.substring(0, indexes[i].startId) + docx.substring(indexes[i].stopId, docx.length); - } - }; - - var parse = function () { - if (html.substring(0,3) != '

') { - docx += ''; - skipFirstParagraphClosing = false; - } - html = html.split(/(<|>)/g); - // remove whitespaces and > brackets. Leave < brackets in there to check, whether - // the following string is a tag or text. - html = _.filter(html, function (part) { - var skippedCharsRegex = new RegExp('^([\s\n\r]|>)*$', 'g'); - return !skippedCharsRegex.test(part); - }); - - for (var i = 0; i < html.length; i++) { - if (html[i] === '<') { - i++; - handleTag(html[i]); - } else { - handleText(html[i]); - } - } - // for finishing close the last paragraph (if open) - if (inParagraph) { - docx += ''; - } - - replaceEntities(); - - return docx; - }; - - return parse(); - }; - - // return a wrapper function for html2docx, that fetches all the images. - converter.html2docx = function (html) { - var imageSources = _.map($(html).find('img'), function (element) { - return element.getAttribute('src'); - }); - // Don't get images multiple times; just if the converter has not seen them befor. - imageSources = _.filter(imageSources, function (src) { - return !converter.imageMap[src]; - }); - return $q(function (resolve) { - ImageConverter.toBase64(imageSources).then(function (_imageMap) { - _.forEach(_imageMap, function (value, key) { - converter.imageMap[key] = value; - }); - var docx = html2docx(html); - resolve(docx); - }); - }); - }; - - converter.updateZipFile = function (zip) { - var updateRelationships = function (oldContent) { - var content = oldContent.split('\n'); - _.forEach(converter.relationships, function (relationship) { - content[1] += '' + - gettextCatalog.getString('Error while generating PDF file') + - ': ' + gettextCatalog.getString('Cannot load image') + ' ' + url + '', - }); - }; - img.onload = function () { - var canvas = document.createElement('canvas'); - canvas.width = img.width; - canvas.height = img.height; - var ctx = canvas.getContext('2d'); - ctx.drawImage(img, 0, 0); - var dataURL = canvas.toDataURL('image/png'); - var imageData = { - data: dataURL, - width: img.width, - height: img.height - }; - resolve(imageData); - }; - img.src = url; - }); - return promise; - }; - - return PDFLayout; - } -]) - - -.factory('HTMLValidizer', function() { - var HTMLValidizer = {}; - - // In some cases copying from word to OpenSlides results in umlauts - // that are the base letter and then the entity #776; to make the dots - // above the base letter. This breaks the PDF. - HTMLValidizer.replaceMalformedUmlauts = function (text) { - return text.replace(/([aeiouAEIOUy])[\u0308]/g, function (match, baseChar) { - return '&' + baseChar + 'uml;'; - }); - }; - - - //checks if str is valid HTML. Returns valid HTML if not, - //return emptystring if empty - HTMLValidizer.validize = function(str) { - if (str) { - str = HTMLValidizer.replaceMalformedUmlauts(str); - // Sometimes, some \n are in the text instead of whitespaces. Replace them. - str = str.replace(/\n/g, ' '); - - var a = document.createElement('div'); - a.innerHTML = str; - angular.forEach(a.childNodes, function (child) { - if (child.nodeType == 1) { - return str; - } - }); - return '

' + str + '

'; - } else { - return ''; //needed for blank 'reasons' field - } - }; - return HTMLValidizer; -}) - - -.factory('PdfMakeDocumentProvider', [ - '$q', - 'Config', - 'PDFLayout', - 'ImageConverter', - function($q, Config, PDFLayout, ImageConverter) { - /** - * Provides the global document - * @constructor - * @param {object} contentProvider - Object with on method `getContent`, which - * returns an array for content - */ - //images shall contain the the logos as URL: base64Str, just like the converter - var createInstance = function(contentProvider, noFooter) { - // Logo urls - var logoHeaderLeftUrl = Config.get('logo_pdf_header_L').value.path, - logoHeaderRightUrl = Config.get('logo_pdf_header_R').value.path, - logoFooterLeftUrl = Config.get('logo_pdf_footer_L').value.path, - logoFooterRightUrl = Config.get('logo_pdf_footer_R').value.path; - var imageMap = contentProvider.getImageMap ? contentProvider.getImageMap() : {}; - - // PDF header - var getHeader = function() { - var columns = []; - - if (logoHeaderLeftUrl) { - if (logoHeaderLeftUrl.indexOf('/') === 0) { - logoHeaderLeftUrl = logoHeaderLeftUrl.substr(1); // remove trailing / - } - columns.push({ - image: logoHeaderLeftUrl, - fit: [180, 40], - width: '20%' - }); - } - - var line1 = [ - Config.translate(Config.get('general_event_name').value), - Config.translate(Config.get('general_event_description').value) - ].filter(Boolean).join(' – '); - var line2 = [ - Config.get('general_event_location').value, - Config.get('general_event_date').value - ].filter(Boolean).join(', '); - var text = [line1, line2].join('\n'); - columns.push({ - text: text, - fontSize: 10, - alignment: logoHeaderRightUrl ? 'left' : 'right', - margin: [0, 10, 0, 0], - }); - - if (logoHeaderRightUrl) { - if (logoHeaderRightUrl.indexOf('/') === 0) { - logoHeaderRightUrl = logoHeaderRightUrl.substr(1); // remove trailing / - } - columns.push({ - image: logoHeaderRightUrl, - fit: [180, 40], - width: '20%' - }); - } - - return { - color: '#555', - fontSize: 9, - margin: [75, 30, 75, 10], // [left, top, right, bottom] - columns: columns, - columnGap: 10, - }; - }; - - - // PDF footer - // Used placeholder for currentPage and pageCount which - // are replaced by dynamic footer function in pdf-worker.js. - var getFooter = function() { - var columns = []; - - // if there is a single logo, give it a lot of space - var logoContainerWidth; - var logoConteinerSize; - if (logoFooterLeftUrl && logoFooterRightUrl) { - logoContainerWidth = '20%'; - logoConteinerSize = [180, 40]; - } else { - logoContainerWidth = '80%'; - logoConteinerSize = [400, 50]; - } - - // the position of the page number depends on the logos - var pageNumberPosition; - if (logoFooterLeftUrl && logoFooterRightUrl) { - pageNumberPosition = 'center'; - } else if (logoFooterLeftUrl && (!logoFooterRightUrl)) { - pageNumberPosition = 'right'; - } else if (logoFooterRightUrl && (!logoFooterLeftUrl)) { - pageNumberPosition = 'left'; - } else { - pageNumberPosition = Config.get('general_export_pdf_pagenumber_alignment').value; - } - - if (logoFooterLeftUrl) { - if (logoFooterLeftUrl.indexOf('/') === 0) { - logoFooterLeftUrl = logoFooterLeftUrl.substr(1); // remove trailing / - } - columns.push({ - image: logoFooterLeftUrl, - fit: logoConteinerSize, - width: logoContainerWidth, - alignment: 'left', - }); - } - columns.push({ - text: '{{currentPage}} / {{pageCount}}', - color: '#555', - fontSize: 9, - alignment: pageNumberPosition, - margin: [0, 15, 0, 0], - }); - - if (logoFooterRightUrl) { - if (logoFooterRightUrl.indexOf('/') === 0) { - logoFooterRightUrl = logoFooterRightUrl.substr(1); // remove trailing / - } - columns.push({ - image: logoFooterRightUrl, - fit: logoConteinerSize, - width: logoContainerWidth, - alignment: 'right', - }); - } - return { - margin: [75, 0, 75, 10], - columns: columns, - columnGap: 10, - }; - }; - // Generates the document(definition) for pdfMake - var getDocument = function() { - var content = contentProvider.getContent(); - var standardFontsize = Config.get('general_export_pdf_fontsize').value; - return { - pageSize: 'A4', - pageMargins: [75, 90, 75, 75], - defaultStyle: { - font: 'PdfFont', - fontSize: standardFontsize - }, - header: getHeader(), - footerTpl: noFooter ? '' : getFooter(), - content: content, - styles: { - title: { - fontSize: 18, - margin: [0,0,0,20], - bold: true - }, - subtitle: { - fontSize: 9, - margin: [0,-20,0,20], - color: 'grey' - }, - preamble: { - fontSize: standardFontsize, - margin: [0,0,0,10], - }, - userDataTitle: { - fontSize: 26, - margin: [0,0,0,0], - bold: true - }, - textItem: { - fontSize: 11, - margin: [0,7] - }, - heading2: { - fontSize: 14, - margin: [0,0,0,10], - bold: true - }, - heading3: { - fontSize: 12, - margin: [0,10,0,0], - bold: true - }, - userDataHeading: { - fontSize: 14, - margin: [0,10], - bold: true - }, - userDataTopic: { - fontSize: 12, - margin: [0,5] - }, - userDataValue: { - fontSize: 12, - margin: [15,5] - }, - tocEntry: { - fontSize: 12, - margin: [0,0,0,0], - bold: false - }, - tocCategoryEntry: { - fontSize: 12, - margin: [10,0,0,0], - bold: false - }, - tocCategoryTitle: { - fontSize: 12, - margin: [0,0,0,4], - bold: true, - }, - tocCategorySection: { - margin: [0,0,0,10], - }, - listParent: { - fontSize: 12, - margin: [0,5] - }, - listChild: { - fontSize: 10, - margin: [0,5] - }, - tableHeader: { - bold: true, - fillColor: 'white' - }, - tableEven: { - fillColor: 'white' - }, - tableOdd: { - fillColor: '#eee' - }, - tableConclude: { - fillColor: '#ddd', - bold: true - }, - grey: { - fillColor: '#ddd', - }, - lightgrey: { - fillColor: '#aaa', - }, - bold: { - bold: true, - }, - small: { - fontSize: 8, - } - } - }; - }; - - var getImageMap = function () { - return imageMap; - }; - - return $q(function (resolve, reject) { - var imageSources = [ - logoHeaderLeftUrl, - logoHeaderRightUrl, - logoFooterLeftUrl, - logoFooterRightUrl, - ]; - ImageConverter.toBase64(imageSources).then(function (_imageMap) { - _.forEach(_imageMap, function (data, path) { - if (!imageMap[path]) { - imageMap[path] = data; - } - }); - resolve({ - getDocument: getDocument, - getImageMap: getImageMap, - }); - }, reject); - }); - }; - return { - createInstance: createInstance, - }; - } -]) - -.factory('PdfMakeBallotPaperProvider', [ - 'PDFLayout', - function(PDFLayout) { - /** - * Provides the global Document - * @constructor - * @param {object} contentProvider - Object with on method `getContent`, which returns an array for content - */ - var createInstance = function(contentProvider) { - /** - * Generates the document(definition) for pdfMake - * @function - */ - var getDocument = function() { - var content = contentProvider.getContent(); - return { - pageSize: 'A4', - pageMargins: [0, 0, 0, 0], - defaultStyle: { - font: 'PdfFont', - fontSize: 10 - }, - content: content, - styles: { - title: { - fontSize: 14, - bold: true, - margin: [30, 30, 0, 0] - }, - description: { - fontSize: 11, - margin: [30, 0, 0, 0] - } - } - }; - }; - - var getImageMap = function() { - return contentProvider.getImageMap(); - }; - - return { - getDocument: getDocument, - getImageMap: getImageMap, - }; - }; - return { - createInstance: createInstance - }; - } -]) - -.factory('PdfMakeConverter', [ - 'HTMLValidizer', - 'Config', - function(HTMLValidizer, Config) { - /** - * Converter component for HTML->JSON for pdfMake - * @constructor - * @param {object} images - Key-Value structure representing image.src/BASE64 of images - */ - var createInstance = function(images) { - var slice = Function.prototype.call.bind([].slice), - map = Function.prototype.call.bind([].map), - - DIFF_MODE_NORMAL = 0, - DIFF_MODE_INSERT = 1, - DIFF_MODE_DELETE = 2, - - // Space between list elements - LI_MARGIN_BOTTOM = 8, - - /** - * Convertes HTML for use with pdfMake - * @function - * @param {object} html - html - * @param {string} lineNumberMode - [inline, outside, none] - */ - convertHTML = function(html, lineNumberMode) { - var elementStyles = { - 'b': ['font-weight:bold'], - 'strong': ['font-weight:bold'], - 'u': ['text-decoration:underline'], - 'em': ['font-style:italic'], - 'i': ['font-style:italic'], - 'h1': ['font-size:14', 'font-weight:bold'], - 'h2': ['font-size:12', 'font-weight:bold'], - 'h3': ['font-size:10', 'font-weight:bold'], - 'h4': ['font-size:10', 'font-style:italic'], - 'h5': ['font-size:10'], - 'h6': ['font-size:10'], - 'a': ['color:blue', 'text-decoration:underline'], - 'strike': ['text-decoration:line-through'], - 'del': ['color:red', 'text-decoration:line-through'], - 'ins': ['color:green', 'text-decoration:underline'] - }, - classStyles = { - 'delete': ['color:red', 'text-decoration:line-through'], - 'insert': ['color:green', 'text-decoration:underline'] - }, - getLineNumber = function (element) { - if (element && element.nodeName == 'SPAN' && element.getAttribute('class') && - element.getAttribute('class').indexOf('os-line-number') > -1) { - return element.getAttribute('data-line-number'); - } - }, - /** - * - * Removes all line number nodes (not line-breaks) - * and returns an array containing the reoved numbers in this format: - * { lineNumber: '', marginBottom: } - * where marginBottom is optional. - * - * @function - * @param {object} element - */ - extractLineNumbers = function(element) { - var foundLineNumbers = []; - var lineNumber = getLineNumber(element); - if (lineNumber) { - foundLineNumbers.push({lineNumber: lineNumber}); - element.parentNode.removeChild(element); - } else if (element.nodeName === 'BR') { - // Check if there is a new line, but it does not get a line number. - // If so, insert a dummy line, so the line nubers stays aligned with - // the text. - if (!getLineNumber(element.nextSibling)) { - foundLineNumbers.push({lineNumber: ''}); - } - } else { - var children = element.childNodes, - childrenLength = children.length, - childrenLineNumbers = []; - for (var i = 0; i < children.length; i++) { - childrenLineNumbers = _.concat(childrenLineNumbers, extractLineNumbers(children[i])); - if (children.length < childrenLength) { - i -= (childrenLength - children.length); - childrenLength = children.length; - } - } - // If this is an list item, add some space to the lineNumbers: - if (childrenLineNumbers.length && element.nodeName === 'LI') { - _.last(childrenLineNumbers).marginBottom = LI_MARGIN_BOTTOM; - } - foundLineNumbers = _.concat(foundLineNumbers, childrenLineNumbers); - } - return foundLineNumbers; - }, - /** - * Parses Children of the current paragraph - * @function - * @param {object} converted - - * @param {object} element - - * @param {object} currentParagraph - - * @param {object} styles - - * @param {number} diff_mode - */ - parseChildren = function(converted, element, currentParagraph, styles, diff_mode) { - var elements = []; - var children = element.childNodes; - if (children.length !== 0) { - _.forEach(children, function(child) { - currentParagraph = ParseElement(elements, child, currentParagraph, styles, diff_mode); - }); - } - if (elements.length !== 0) { - _.forEach(elements, function(el) { - converted.push(el); - }); - } - return currentParagraph; - }, - /** - * Returns the color in a hex format (e.g. #12ff00). - * Tries to convert the rgb form into this. - * @function - * @param {string} color - */ - parseColor = function (color) { - var hexRegex = new RegExp('^#([0-9a-f]{3}|[0-9a-f]{6})$'); - // e.g. #fff or #ff0048 - var rgbRegex = new RegExp('^rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)$'); - // e.g. rgb(0,255,34) or rgb(22, 0, 0) - var nameRegex = new RegExp('^[a-z]+$'); - // matches just text like 'red', 'black', 'green' - - if (hexRegex.test(color)) { - return color; - } else if (rgbRegex.test(color)) { - var decimalColors = rgbRegex.exec(color).slice(1); - for (var i = 0; i < 3; i++) { - var decimalValue = parseInt(decimalColors[i]); - if (decimalValue > 255) { - decimalValue = 255; - } - var hexString = '0' + decimalValue.toString(16); - hexString = hexString.slice(-2); - decimalColors[i] = hexString; - } - return '#' + decimalColors.join(''); - } else if (nameRegex.test(color)) { - return color; - } else { - console.error('Could not parse color "' + color + '"'); - return color; - } - }, - /** - * Extracts the style from an object - * @function - * @param {object} o - the current object - * @param {object} styles - an array with styles - */ - ComputeStyle = function(o, styles) { - styles.forEach(function(singleStyle) { - var styleDefinition = singleStyle.trim().toLowerCase().split(':'); - var style = styleDefinition[0]; - var value = styleDefinition[1]; - if (styleDefinition.length === 2) { - switch (style) { - case 'padding-left': - o.margin = [parseInt(value), 0, 0, 0]; - break; - case 'font-size': - o.fontSize = parseInt(value); - break; - case 'text-align': - switch (value) { - case 'right': - case 'center': - case 'justify': - o.alignment = value; - break; - } - break; - case 'font-weight': - switch (value) { - case 'bold': - o.bold = true; - break; - } - break; - case 'text-decoration': - switch (value) { - case 'underline': - o.decoration = 'underline'; - break; - case 'line-through': - o.decoration = 'lineThrough'; - break; - } - break; - case 'font-style': - switch (value) { - case 'italic': - o.italics = true; - break; - } - break; - case 'color': - o.color = parseColor(value); - break; - case 'background-color': - o.background = parseColor(value); - break; - } - } - }); - }, - // A little helper function to check, if an element has the given class. - hasClass = function (element, className) { - var classes = element.getAttribute('class'); - if (classes) { - classes = classes.toLowerCase().split(' '); - return _.indexOf(classes, className) > -1; - } else { - return false; - } - }, - // Helper function for determinating whether a parent of element is a list item. - isInsideAList = function (element) { - var parent = element.parentNode; - while(parent !== null) { - if (parent.nodeName.toLowerCase() === 'li') { - return true; - } - parent = parent.parentNode; - } - return false; - }, - /** - * Parses a single HTML element - * @function - * @param {object} alreadyConverted - - * @param {object} element - - * @param {object} currentParagraph - - * @param {object} styles - - * @param {number} diff_mode - */ - ParseElement = function(alreadyConverted, element, currentParagraph, styles, diff_mode) { - styles = styles ? _.clone(styles) : []; - var classes = []; - if (element.getAttribute) { - var nodeStyle = element.getAttribute('style'); - if (nodeStyle) { - nodeStyle.split(';').forEach(function(nodeStyle) { - var tmp = nodeStyle.replace(/\s/g, ''); - styles.push(tmp); - }); - } - var nodeClass = element.getAttribute('class'); - if (nodeClass) { - classes = nodeClass.toLowerCase().split(' '); - classes.forEach(function(nodeClass) { - if (typeof(classStyles[nodeClass]) != 'undefined') { - classStyles[nodeClass].forEach(function(style) { - styles.push(style); - }); - } - if (nodeClass == 'insert') { - diff_mode = DIFF_MODE_INSERT; - } - if (nodeClass == 'delete') { - diff_mode = DIFF_MODE_DELETE; - } - }); - } - } - var nodeName = element.nodeName.toLowerCase(); - switch (nodeName) { - case 'h1': - case 'h2': - case 'h3': - case 'h4': - case 'h5': - case 'h6': - if (lineNumberMode === 'outside' && - element.childNodes.length > 0 && - element.childNodes[0].getAttribute) { - // A heading may have multiple lines, so handle line by line separated by line number elements - var outerStack = create('stack'); - var currentCol, currentText; - _.forEach(element.childNodes, function (node) { - if (node.getAttribute && node.getAttribute('data-line-number')) { - if (currentCol) { - ComputeStyle(currentCol, elementStyles[nodeName]); - outerStack.stack.push(currentCol); - } - currentText = create('text'); - currentCol = { - columns: [ - getLineNumberObject({ - lineNumber: node.getAttribute('data-line-number') - }), - currentText, - ], - margin: [0, 2, 0, 0], - }; - } else { - var parsedText = ParseElement([], node, create('text'), styles, diff_mode); - // append the parsed text to the currentText - _.forEach(parsedText.text, function (text) { - currentText.text.push(text); - }); - } - }); - ComputeStyle(currentCol, elementStyles[nodeName]); - outerStack.stack.push(currentCol); - outerStack.margin = [0, 0, 0, 0]; - if (!/h[1-6]/.test(element.previousSibling.nodeName.toLowerCase())) { - outerStack.margin[1] = 10; - } - alreadyConverted.push(outerStack); - } else { - currentParagraph = create('text'); - currentParagraph.marginBottom = 4; - currentParagraph.marginTop = 10; - currentParagraph = parseChildren(alreadyConverted, element, currentParagraph, styles.concat(elementStyles[nodeName]), diff_mode); - alreadyConverted.push(currentParagraph); - } - break; - case 'a': - case 'b': - case 'strong': - case 'u': - case 'em': - case 'i': - case 'ins': - case 'del': - case 'strike': - currentParagraph = parseChildren(alreadyConverted, element, currentParagraph, styles.concat(elementStyles[nodeName]), diff_mode); - break; - case 'table': - var t = create('table', { - widths: [], - body: [] - }); - var border = element.getAttribute('border'); - var isBorder = false; - if (border) { - isBorder = (parseInt(border) === 1); - } else { - t.layout = 'noBorders'; - } - currentParagraph = parseChildren(t.table.body, element, currentParagraph, styles, diff_mode); - var widths = element.getAttribute('widths'); - if (!widths) { - if (t.table.body.length !== 0) { - if (t.table.body[0].length !== 0) - for (var k = 0; k < t.table.body[0].length; k++) - t.table.widths.push('*'); - } - } else { - var w = widths.split(','); - for (var ko = 0; ko < w.length; ko++) t.table.widths.push(w[ko]); - } - alreadyConverted.push(t); - break; - case 'tbody': - currentParagraph = parseChildren(alreadyConverted, element, currentParagraph, styles, diff_mode); - break; - case 'tr': - var row = []; - currentParagraph = parseChildren(row, element, currentParagraph, styles, diff_mode); - alreadyConverted.push(row); - break; - case 'td': - currentParagraph = create('text'); - var st = create('stack'); - st.stack.push(currentParagraph); - var rspan = element.getAttribute('rowspan'); - if (rspan) - st.rowSpan = parseInt(rspan); - var cspan = element.getAttribute('colspan'); - if (cspan) - st.colSpan = parseInt(cspan); - currentParagraph = parseChildren(st.stack, element, currentParagraph, styles, diff_mode); - alreadyConverted.push(st); - break; - case 'span': - if (element.getAttribute('data-line-number')) { - if (lineNumberMode === 'inline') { - if (diff_mode !== DIFF_MODE_INSERT) { - var lineNumberInline = element.getAttribute('data-line-number'), - lineNumberObjInline = { - text: lineNumberInline, - color: 'gray', - fontSize: 5 - }; - currentParagraph.text.push(lineNumberObjInline); - } - } else if (lineNumberMode === 'outside') { - var lineNumberOutline; - if (diff_mode === DIFF_MODE_INSERT) { - lineNumberOutline = ''; - } else { - lineNumberOutline = element.getAttribute('data-line-number'); - } - var col = { - columns: [ - getLineNumberObject({ - lineNumber: lineNumberOutline, - }), - ] - }; - currentParagraph = create('text'); - currentParagraph.lineHeight = 1.25; - col.columns.push(currentParagraph); - alreadyConverted.push(col); - } - } - else { - currentParagraph = parseChildren(alreadyConverted, element, currentParagraph, styles, diff_mode); - } - break; - case 'br': - var brParent = element.parentNode; - var brParentNodeName = brParent.nodeName; - //in case of no or inline-line-numbers and the ignore os-line-breaks. - if ((lineNumberMode === 'inline' || lineNumberMode === 'none') && - hasClass(element, 'os-line-break')) { - break; - } else { - currentParagraph = create('text'); - if (lineNumberMode === 'outside' && - brParentNodeName !== 'LI' && - element.parentNode.parentNode.nodeName !== 'LI') { - if (brParentNodeName === 'INS' || brParentNodeName === 'DEL') { - - var hasPrevSiblingALineNumber = function (element) { - // Iterare all nodes up to the top from element. - while (element) { - if (getLineNumber(element)) { - return true; - } - if (element.previousSibling) { - element = element.previousSibling; - } else { - element = element.parentNode; - } - } - return false; - }; - if (hasPrevSiblingALineNumber(brParent)) { - currentParagraph.margin = [20, 0, 0, 0]; - } - } else { - currentParagraph.margin = [20, 0, 0, 0]; - } - } - // Add a dummy line, if the next tag is a BR tag again. The line could - // not be empty otherwise it will be removed and the empty line is not displayed - if (element.nextSibling && element.nextSibling.nodeName === 'BR') { - currentParagraph.text.push(create('text', ' ')); - } else if (isInsideAList(element) && lineNumberMode === 'none') { - // Put a spacer there, if there is one BR in a list - var spacer = create('text', ' '); - spacer.lineHeight = 0.25; - alreadyConverted.push(spacer); - } - currentParagraph.lineHeight = 1.25; - alreadyConverted.push(currentParagraph); - } - break; - case 'li': - case 'div': - currentParagraph = create('text'); - currentParagraph.lineHeight = 1.25; - var stackDiv = create('stack'); - if (_.indexOf(classes, 'os-split-before') > -1) { - stackDiv.listType = 'none'; - } - if (nodeName === 'li') { - stackDiv.marginBottom = LI_MARGIN_BOTTOM; - } - stackDiv.stack.push(currentParagraph); - ComputeStyle(stackDiv, styles); - currentParagraph = parseChildren(stackDiv.stack, element, currentParagraph, [], diff_mode); - alreadyConverted.push(stackDiv); - break; - case 'p': - var pObjectToPush; //determine what to push later - currentParagraph = create('text'); - // If this element is inside a list (happens if copied from word), do not set spaces - // and margins. Just leave the paragraph there.. - if (!isInsideAList(element)) { - currentParagraph.margin = [0, 0, 0, 0]; - if (classes.indexOf('os-split-before') === -1) { - currentParagraph.margin[1] = 8; - } - if (classes.indexOf('insert') > -1) { - currentParagraph.margin[0] = 20; - } - } - currentParagraph.lineHeight = 1.25; - var stackP = create('stack'); - stackP.stack.push(currentParagraph); - ComputeStyle(stackP, styles); - currentParagraph = parseChildren(stackP.stack, element, currentParagraph, [], diff_mode); - pObjectToPush = stackP; //usually we want to push stackP - if (lineNumberMode === 'outside') { - if (element.childNodes.length > 0) { //if we hit = 0, the code would fail - // add empty line number column for inline diff or pragraph diff mode - if (element.childNodes[0].tagName === 'INS' || - element.childNodes[0].tagName === 'DEL') { - var pLineNumberPlaceholder = { - width: 20, - text: '', - fontSize: 8, - margin: [0, 2, 0, 0] - }; - var pLineNumberPlaceholderCol = { - columns: [ - pLineNumberPlaceholder, - stackP - ] - }; - pObjectToPush = pLineNumberPlaceholderCol; //overwrite the object to push - } - } - } - alreadyConverted.push(pObjectToPush); - break; - case 'img': - var path = element.getAttribute('src'); - var height = images[path].height; - var width = images[path].width; - var maxWidth = 450; - var scale = 100; - - var style = element.getAttribute('style'); - if (style) { - var match = style.match(/width:\s*(\d+)\%/); - if (match) { - scale = parseInt(match[1]); - } - } - - // scale image - width = (width * scale) / 100; - height = (height * scale) / 100; - - if (width > maxWidth) { - height = (height * maxWidth) / width; - width = maxWidth; - } - - // remove trailing / for the virtual file system (there is no root) - if (path.indexOf('/') === 0) { - path = path.substr(1); - } - alreadyConverted.push({ - image: path, - width: width, - height: height, - }); - break; - case 'ul': - case 'ol': - var list = create(nodeName); - if (nodeName == 'ol') { - var start = element.getAttribute('start'); - if (start) { - list.start = start; - } - } - ComputeStyle(list, styles); - if (lineNumberMode === 'outside') { - var lines = extractLineNumbers(element); - currentParagraph = parseChildren(list[nodeName], element, currentParagraph, styles, diff_mode); - if (lines.length > 0) { - var listCol = { - columns: [{ - width: 20, - stack: [] - }] - }; - _.forEach(lines, function(line) { - listCol.columns[0].stack.push(getLineNumberObject(line)); - }); - listCol.columns.push(list); - if (!hasClass(element, 'os-split-before')) { - listCol.margin = [0, 5, 0, 0]; - } - alreadyConverted.push(listCol); - } else { - list.margin = [20, 0, 0, 0]; - alreadyConverted.push(list); - } - } else { - list.margin = [0, LI_MARGIN_BOTTOM, 0, 0]; - currentParagraph = parseChildren(list[nodeName], element, currentParagraph, styles, diff_mode); - alreadyConverted.push(list); - } - break; - default: - var defaultText = create('text', element.textContent.replace(/\n/g, '')); - ComputeStyle(defaultText, styles); - if (!currentParagraph) { - currentParagraph = {}; - currentParagraph.text = []; - } - currentParagraph.text.push(defaultText); - break; - } - return currentParagraph; - }, - /** - * Parses HTML - * @function - * @param {string} converted - - * @param {object} htmlText - - */ - ParseHtml = function(converted, htmlText) { - var html = HTMLValidizer.validize(htmlText); - html = $(html.replace(/\t/g, '').replace(/\n/g, '')); - var emptyParagraph = create('text'); - slice(html).forEach(function(element) { - ParseElement(converted, element, null, [], DIFF_MODE_NORMAL); - }); - }, - /* Returns the object to push first into every column, that represents the given line. */ - getLineNumberObject = function (line) { - var standardFontsize = Config.get('general_export_pdf_fontsize').value; - return { - width: 20, - text: [ - { - text: ' ', // Add a blank with the normal font size here, so in rare cases the text - // is rendered on the next page and the linenumber on the previous page. - fontSize: standardFontsize, - decoration: '', - }, - { - text: line.lineNumber, - color: 'gray', - fontSize: standardFontsize - 2, - decoration: '', - }, - ], - marginBottom: line.marginBottom, - lineHeight: 1.25, - }; - }, - content = []; - ParseHtml(content, html); - return content; - }, - /** - * Creates containerelements for pdfMake - * e.g create('text':'MyText') result in { text: 'MyText' } - * or complex objects create('stack', [{text:'MyText'}, {text:'MyText2'}]) - *for units / paragraphs of text - * - * @function - * @param {string} name - name of the attribute holding content - * @param {object} content - the actual content (maybe empty) - */ - create = function(name, content) { - var o = {}; - content = content || []; - o[name] = content; - return o; - }; - return { - convertHTML: convertHTML, - createElement: create - }; - }; - return { - createInstance: createInstance - }; - } -]) - -.factory('ImageConverter', [ - '$q', - 'PDFLayout', - function ($q, PDFLayout) { - return { - toBase64: function (imageSources) { - var imageMap = {}; - var imagePromises = _.map(imageSources, function (imageSource) { - if (imageSource) { - return PDFLayout.imageURLtoBase64(imageSource).then(function (imgInfo) { - imageMap[imageSource] = imgInfo; - }); - } - }); - - return $q(function (resolve, reject) { - //resolve promises to get base64 - $q.all(imagePromises).then(function() { - resolve(imageMap); - }, reject); - }); - } - }; - } -]) - -// Creates the virtual filesystem for PdfMake. -.factory('PdfVfs', [ - '$q', - '$http', - 'Fonts', - 'Config', - function ($q, $http, Fonts, Config) { - var urlCache = {}; // Caches the get request. Maps urls to base64 data ready to use. - - var loadFont = function (url) { - return $q(function (resolve, reject) { - // Get font - return $http.get(url, {responseType: 'blob'}).then(function (success) { - // Convert to base64 - var reader = new FileReader(); - reader.readAsDataURL(success.data); - reader.onloadend = function() { - resolve(reader.result.split(',')[1]); - }; - }, function (error) { - reject(error); - }); - }); - }; - - /* - * Returns a map from urls to arrays of font types used by PdfMake. - * E.g. if the font 'regular' and 'bold' have the urls 'fonts/myFont.ttf', - * the map fould be 'fonts/myFont.ttf': ['OSFont-regular.ttf', 'OSFont-bold.ttf'] - */ - var getUrlMapping = function () { - var urlMap = {}; - var fonts = ['regular', 'italic', 'bold', 'bold_italic']; - _.forEach(fonts, function (font) { - var url = Fonts.getUrl('font_' + font); - if (!urlMap[url]) { - urlMap[url] = []; - } - urlMap[url].push('OSFont-' + font + '.ttf'); - }); - return urlMap; - }; - - /* - * Create the virtual filesystem needed by PdfMake for the fonts. Gets the url - * mapping and loads all fonts via get requests or the urlCache. - * Adds all image sources to the vfs given by the imageMap. - */ - var getVfs = function (imageMap) { - var vfs = {}; - _.forEach(imageMap || {}, function (data, path) { - if (path.indexOf('/') === 0) { - path = path.substr(1); // remove trailing / - } - vfs[path] = data.data.split(',')[1]; - }); - return $q(function (resolve, reject) { - var urls = getUrlMapping(); - var promises = _.chain(urls) - .map(function (filenames, url) { - if (urlCache[url]) { - // Just save the cache data into vfs. - _.forEach(filenames, function (filename) { - vfs[filename] = urlCache[url]; - }); - return false; // No promise here, it was all cached. - } else { - // Not in the cache, get the font and save the data into vfs. - return loadFont(url).then(function (data) { - urlCache[url] = data; - _.forEach(filenames, function (filename) { - vfs[filename] = data; - }); - }); - } - }) - .filter(function (promise) { - return promise; - }) - .value(); - $q.all(promises).then(function () { - resolve(vfs); - }); - }); - }; - - return { - get: getVfs, - }; - } -]) - -.factory('PdfCreate', [ - '$timeout', - '$q', - 'gettextCatalog', - 'FileSaver', - 'PdfVfs', - 'Messaging', - function ($timeout, $q, gettextCatalog, FileSaver, PdfVfs, Messaging) { - var filenameMessageMap = {}; - var b64toBlob = function(b64Data) { - var byteCharacters = atob(b64Data); - var byteNumbers = new Array(byteCharacters.length); - for (var i = 0; i < byteCharacters.length; i++) { - byteNumbers[i] = byteCharacters.charCodeAt(i); - } - var byteArray = new Uint8Array(byteNumbers); - var blob = new Blob([byteArray]); - return blob; - }; - var stateChange = function (state, filename, error) { - var text, timeout; - switch (state) { - case 'info': - text = '' + - gettextCatalog.getString('Generating PDF file') + ' (' + filename + ') ...'; - break; - case 'success': - text = '' + - gettextCatalog.getString('PDF successfully generated.'); - timeout = 3000; - break; - case 'error': - text = '' + - gettextCatalog.getString('Error while generating PDF file') + - ' (' + filename + '): ' + error + ''; - break; - } - $timeout(function () { - filenameMessageMap[filename] = Messaging.createOrEditMessage( - filenameMessageMap[filename], text, state, {timeout: timeout}); - }, 1); - }; - return { - getBase64FromDocument: function (documentProvider) { - return $q(function (resolve, reject) { - PdfVfs.get(documentProvider.getImageMap()).then(function (vfs) { - var pdfWorker = new Worker('/static/js/workers/pdf-worker.js'); - pdfWorker.addEventListener('message', function (event) { - resolve(event.data); - }); - pdfWorker.addEventListener('error', function (event) { - reject(event); - }); - pdfWorker.postMessage(JSON.stringify({ - pdfDocument: documentProvider.getDocument(), - vfs: vfs, - })); - }); - }); - }, - // Struckture of pdfDocuments: { filname1: doc, filename2: doc, ...} - getBase64FromMultipleDocuments: function (pdfDocuments) { - // concat all image sources together - var imageMap = {}; - _.forEach(pdfDocuments, function (doc) { - _.forEach(doc.getImageMap(), function (data, path) { - if (!imageMap[path]) { - imageMap[path] = data; - } - }); - }); - return $q(function (resolve, reject) { - PdfVfs.get(imageMap).then(function (vfs) { - var pdfWorker = new Worker('/static/js/workers/pdf-worker.js'); - var resultCount = 0; - var base64Map = {}; // Maps filename to base64 - pdfWorker.addEventListener('message', function (event) { - resultCount++; - var data = JSON.parse(event.data); - base64Map[data.filename] = data.base64; - if (resultCount === _.keys(pdfDocuments).length) { - resolve(base64Map); - } - }); - pdfWorker.addEventListener('error', function (event) { - reject(event); - }); - _.forEach(pdfDocuments, function (doc, filename) { - pdfWorker.postMessage(JSON.stringify({ - filename: filename, - pdfDocument: doc.getDocument(), - vfs: vfs, - })); - }); - }); - }); - }, - download: function (documentProvider, filename) { - stateChange('info', filename); - - this.getBase64FromDocument(documentProvider).then(function (data) { - var blob = b64toBlob(data); - stateChange('success', filename); - FileSaver.saveAs(blob, filename); - }, function (error) { - stateChange('error', filename, error.message); - }); - }, - }; - } -]); - -}()); diff --git a/openslides/core/static/js/core/projector.js b/openslides/core/static/js/core/projector.js deleted file mode 100644 index 4dd98042b..000000000 --- a/openslides/core/static/js/core/projector.js +++ /dev/null @@ -1,409 +0,0 @@ -(function () { - -'use strict'; - -// The core module for the OpenSlides projector -angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core']) - -// Can be used to find out if the projector or the side is used -.constant('REALM', 'projector') - -.run([ - '$http', - 'autoupdate', - 'DS', - function ($http, autoupdate, DS) { - autoupdate.newConnect(); - - // If the connection aborts, we try to ping the server with whoami requests. If - // the server is flushed, we clear the datastore, so the message 'this projector - // cannot be shown' will be displayed. Otherwise establish the websocket connection. - autoupdate.registerRetryConnectCallback(function () { - return $http.get('/users/whoami').then(function (success) { - if (success.data.user_id === null && !success.data.guest_enabled) { - DS.clear(); - } else { - autoupdate.newConnect(); - } - }); - }); - } -]) - -// Provider to register slides in a .config() statement. -.provider('slides', [ - function() { - var slidesMap = {}; - - this.registerSlide = function(name, config) { - slidesMap[name] = config; - return this; - }; - - this.$get = function($templateRequest, $q) { - var self = this; - return { - getElements: function(projector) { - var elements = []; - var factory = this; - _.forEach(projector.elements, function(element) { - if (element.name in slidesMap) { - element.template = slidesMap[element.name].template; - elements.push(element); - } else { - console.error("Unknown slide: " + element.name); - } - }); - return elements; - } - }; - }; - } -]) - -.config([ - 'slidesProvider', - function(slidesProvider) { - slidesProvider.registerSlide('core/clock', { - template: 'static/templates/core/slide_clock.html', - }); - - slidesProvider.registerSlide('core/countdown', { - template: 'static/templates/core/slide_countdown.html', - }); - - slidesProvider.registerSlide('core/projector-message', { - template: 'static/templates/core/slide_message.html', - }); - } -]) - -.controller('LanguageAndFontCtrl', [ - '$scope', - 'Languages', - 'Config', - 'Projector', - 'ProjectorID', - 'Fonts', - function ($scope, Languages, Config, Projector, ProjectorID, Fonts) { - // for the dynamic title - $scope.projectorId = ProjectorID(); - $scope.$watch(function () { - return Projector.lastModified($scope.projectorId); - }, function () { - var projector = Projector.get($scope.projectorId); - if (projector) { - $scope.projectorName = projector.name; - } - }); - - $scope.$watch(function () { - return Config.lastModified('projector_language'); - }, function () { - var lang = Config.get('projector_language'); - if (!lang || lang.value == 'browser') { - $scope.selectedLanguage = Languages.getBrowserLanguage(); - } else { - $scope.selectedLanguage = lang.value; - } - Languages.setCurrentLanguage($scope.selectedLanguage); - }); - - $scope.$watch(function () { - return Config.lastModified('font_regular') + - Config.lastModified('font_italic') + - Config.lastModified('font_bold') + - Config.lastModified('font_bold_italic'); - }, function () { - $scope.font = Fonts.getForCss('font_regular'); - $scope.font_medium = Fonts.getForCss('font_italic'); - $scope.font_condensed = Fonts.getForCss('font_bold'); - $scope.font_condensed_light = Fonts.getForCss('font_bold_italic'); - }); - } -]) - -// Projector Container Controller -.controller('ProjectorContainerCtrl', [ - '$scope', - '$timeout', - '$location', - 'gettext', - 'Projector', - function($scope, $timeout, $location, gettext, Projector) { - $scope.showError = true; - - // watch for changes in Projector - $scope.$watch(function () { - return Projector.lastModified($scope.projectorId); - }, function () { - var projector = Projector.get($scope.projectorId); - if (projector) { - $scope.showError = false; - $scope.projectorWidth = projector.width; - $scope.projectorHeight = projector.height; - $scope.recalculateIframe(); - } else { - $scope.showError = true; - // delay displaying the error message, because with a slow internet - // connection, the autoupdate with the projector may be delayed. We - // de not want to irritate the user by showing this error to early. - $scope.error = ''; - $timeout(function () { - if ($scope.showError) { - $scope.error = gettext('Can not open the projector.'); - } - }, 3000); - } - }); - - // recalculate the actual Iframesize and scale - $scope.recalculateIframe = function () { - var scale_width = window.innerWidth / $scope.projectorWidth; - var scale_height = window.innerHeight / $scope.projectorHeight; - - // Iframe has to be scaled down or saceUp is activated - if (scale_width <= scale_height) { - // width is the reference - $scope.iframeWidth = window.innerWidth; - $scope.scale = scale_width; - $scope.iframeHeight = $scope.projectorHeight * scale_width; - } else { - // height is the reference - $scope.iframeHeight = window.innerHeight; - $scope.scale = scale_height; - $scope.iframeWidth = $scope.projectorWidth * scale_height; - } - }; - - // watch for changes in the windowsize - $(window).on("resize.doResize", function () { - $scope.$apply(function() { - $scope.recalculateIframe(); - }); - }); - - $scope.$on("$destroy",function (){ - $(window).off("resize.doResize"); - }); - } -]) - -.controller('ProjectorCtrl', [ - '$scope', - '$location', - '$timeout', - 'Projector', - 'slides', - 'Config', - 'ProjectorID', - 'Logos', - function($scope, $location, $timeout, Projector, slides, Config, ProjectorID, Logos) { - var projectorId = ProjectorID(); - - $scope.broadcast = 0; - - var setElements = function (projector) { - // Get all elements, that should be projected. - var newElements = []; - var enable_clock = Config.get('projector_enable_clock'); - enable_clock = enable_clock ? enable_clock.value : true; - _.forEach(slides.getElements(projector), function (element) { - if (!element.error) { - // Exclude the clock if it should be disabled. - if (enable_clock || element.name !== 'core/clock') { - newElements.push(element); - } - } else { - console.error("Error for slide " + element.name + ": " + element.error); - } - }); - - // Now we have to align $scope.elements to newElements: - // We cannot just assign them, because the ng-repeat would reload every - // element. This should be prevented (see #3259). To change $scope.elements: - // 1) remove all elements from scope, that are not in newElements (compared by the uuid) - // 2) Every new element in newElements, that is not in $scope.elements, get inserted there. - // 3) If there is the same element in newElements and $scope.elements every changed property - // is copied from the new element to the scope element. - - $scope.elements = _.filter($scope.elements, function (element) { - return _.some(newElements, function (newElement) { - return element.uuid === newElement.uuid; - }); - }); - - _.forEach(newElements, function (newElement) { - var matchingElement = _.find($scope.elements, function (element) { - return element.uuid === newElement.uuid; - }); - if (matchingElement) { - // copy all changed properties. - _.forEach(newElement, function (value, key) { - // key has own property and does not start with a '$'. - if (newElement.hasOwnProperty(key) && key.indexOf('$') != 0) { - if (typeof matchingElement[key] === 'undefined' || matchingElement[key] !== value) { - matchingElement[key] = value; - } - } - }); - } else { - $scope.elements.push(newElement); - } - }); - }; - - $scope.scroll = 0; - var setScroll = function (scroll) { - $scope.scroll = -250 * scroll; - }; - - $scope.$watch(function () { - return Projector.lastModified(projectorId); - }, function () { - $scope.projector = Projector.get(projectorId); - if ($scope.projector) { - if ($scope.broadcast === 0) { - setElements($scope.projector); - $scope.blank = $scope.projector.blank; - } - setScroll($scope.projector.scroll); - } else { - // Blank projector on error - $scope.elements = []; - $scope.projector = { - scale: 0, - blank: true - }; - setScroll(0); - } - }); - - $scope.$watch(function () { - return Config.lastModified('projector_broadcast'); - }, function () { - var bc = Config.get('projector_broadcast'); - if (bc) { - if ($scope.broadcast != bc.value) { - $scope.broadcast = bc.value; - if ($scope.broadcastDeregister) { - // revert to original $scope.projector - $scope.broadcastDeregister(); - $scope.broadcastDeregister = null; - setElements($scope.projector); - $scope.blank = $scope.projector.blank; - } - } - if ($scope.broadcast > 0) { - // get elements and blank from broadcast projector - $scope.broadcastDeregister = $scope.$watch(function () { - return Projector.lastModified($scope.broadcast); - }, function () { - if ($scope.broadcast > 0) { - var broadcast_projector = Projector.get($scope.broadcast); - if (broadcast_projector) { - setElements(broadcast_projector); - $scope.blank = broadcast_projector.blank; - } - } - }); - } - } - }); - - $scope.$watch(function () { - return Config.lastModified('projector_enable_clock'); - }, function () { - setElements($scope.projector); - }); - - $scope.$on('$destroy', function() { - if ($scope.broadcastDeregister) { - $scope.broadcastDeregister(); - $scope.broadcastDeregister = null; - } - }); - } -]) - -.controller('SlideClockCtrl', [ - '$scope', - '$interval', - function($scope, $interval) { - // Attention! Each object that is used here has to be dealt on server side. - // Add it to the coresponding get_requirements method of the ProjectorElement - // class. - $scope.servertime = ( Date.now() / 1000 - $scope.serverOffset ) * 1000; - var interval = $interval(function () { - $scope.servertime = ( Date.now() / 1000 - $scope.serverOffset ) * 1000; - }, 30000); // Update the clock every 30 seconds - - $scope.$on('$destroy', function() { - if (interval) { - $interval.cancel(interval); - } - }); - } -]) - -.controller('SlideCountdownCtrl', [ - '$scope', - '$interval', - 'Countdown', - function($scope, $interval, Countdown) { - // Attention! Each object that is used here has to be dealt on server side. - // Add it to the coresponding get_requirements method of the ProjectorElement - // class. - var id = $scope.element.id; - var interval; - var calculateCountdownTime = function (countdown) { - countdown.seconds = Math.floor( $scope.countdown.countdown_time - Date.now() / 1000 + $scope.serverOffset ); - }; - $scope.$watch(function () { - return Countdown.lastModified(id); - }, function () { - $scope.countdown = Countdown.get(id); - if (interval) { - $interval.cancel(interval); - } - if ($scope.countdown) { - if ($scope.countdown.running) { - calculateCountdownTime($scope.countdown); - interval = $interval(function () { calculateCountdownTime($scope.countdown); }, 1000); - } else { - $scope.countdown.seconds = $scope.countdown.countdown_time; - } - } - }); - $scope.$on('$destroy', function() { - // Cancel the interval if the controller is destroyed - if (interval) { - $interval.cancel(interval); - } - }); - } -]) - -.controller('SlideMessageCtrl', [ - '$scope', - 'ProjectorMessage', - 'Projector', - 'ProjectorID', - 'gettextCatalog', - function($scope, ProjectorMessage, Projector, ProjectorID, gettextCatalog) { - // Attention! Each object that is used here has to be dealt on server side. - // Add it to the coresponding get_requirements method of the ProjectorElement - // class. - var id = $scope.element.id; - - if ($scope.element.identify) { - var projector = Projector.get(ProjectorID()); - $scope.identifyMessage = gettextCatalog.getString('Projector') + ' ' + projector.id + ': ' + gettextCatalog.getString(projector.name); - } else { - $scope.message = ProjectorMessage.get(id); - ProjectorMessage.bindOne(id, $scope, 'message'); - } - } -]); - -}()); diff --git a/openslides/core/static/js/core/remove-format-plugin.js b/openslides/core/static/js/core/remove-format-plugin.js deleted file mode 100644 index 046dce06c..000000000 --- a/openslides/core/static/js/core/remove-format-plugin.js +++ /dev/null @@ -1,50 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.core.remove-format-plugin', [ - 'OpenSlidesApp.core', -]) - -/* - * Plugin for the CKEditor that hooks into the removeformat plugin - * which is a default plugin enabled by 'cleanup' in the config - * toolbar. - * We change the behavior of the removeformat command here: - * It should not remove any tags and styles, but only the - * 'DISALLOWED_STYLES'. Removeformat traverses through the DOM - * and calles for every element the custom filter down below. - * We change the element and return false, so the removeformat - * plugin does not clean it up. - */ -.factory('OSRemoveFormatPlugin', [ - 'Editor', - 'gettextCatalog', - function (Editor, gettextCatalog) { - var DISALLOWED_STYLES = ['color', 'background-color']; - return { - getPlugin: function () { - return { - init: function (editor) { - editor.addRemoveFormatFilter(function (element) { - _.forEach(DISALLOWED_STYLES, function (style) { - element.removeStyle(style); - }); - return false; - }); - }, - }; - }, - }; - } -]) - -.run([ - 'Editor', - 'OSRemoveFormatPlugin', - function (Editor, OSRemoveFormatPlugin, gettext) { - Editor.registerPlugin('OSRemoveFormat', OSRemoveFormatPlugin.getPlugin()); - } -]); - -}()); diff --git a/openslides/core/static/js/core/site.js b/openslides/core/static/js/core/site.js deleted file mode 100644 index 4059a376a..000000000 --- a/openslides/core/static/js/core/site.js +++ /dev/null @@ -1,2135 +0,0 @@ -(function () { - -'use strict'; - -// The core module for the OpenSlides site -angular.module('OpenSlidesApp.core.site', [ - 'OpenSlidesApp.core', - 'OpenSlidesApp.core.start', - 'OpenSlidesApp.core.csv', - 'OpenSlidesApp.core.remove-format-plugin', - 'OpenSlidesApp.poll.majority', - 'ui.router', - 'colorpicker.module', - 'formly', - 'formlyBootstrap', - 'localytics.directives', - 'ngDialog', - 'ngFileSaver', - 'ngMessages', - 'ckeditor', - 'luegg.directives', - 'xeditable', - 'rzModule', -]) - -// Can be used to find out if the projector or the side is used -.constant('REALM', 'site') - -.factory('DateTimePickerTranslation', [ - 'gettextCatalog', - function (gettextCatalog) { - return { - getButtons: function () { - return { - show: true, - now: { - show: true, - text: gettextCatalog.getString('now') - }, - today: { - show: true, - text: gettextCatalog.getString('today') - }, - clear: { - show: true, - text: gettextCatalog.getString('clear') - }, - date: { - show: true, - text: gettextCatalog.getString('date') - }, - time: { - show: true, - text: gettextCatalog.getString('time') - }, - close: { - show: true, - text: gettextCatalog.getString('close') - } - }; - } - }; - } - -]) - -// Provider to register entries for the main menu. -.provider('mainMenu', [ - function() { - var mainMenuList = []; - var scope; - - this.register = function(config) { - mainMenuList.push(config); - }; - - this.$get = ['operator', function(operator) { - return { - registerScope: function (scope) { - this.scope = scope; - }, - updateMainMenu: function () { - if (this.scope) { - this.scope.elements = this.getElements(); - } - }, - getElements: function() { - var elements = mainMenuList.filter(function (element) { - return typeof element.perm === "undefined" || operator.hasPerms(element.perm); - }); - - elements.sort(function (a, b) { - return a.weight - b.weight; - }); - return elements; - } - }; - }]; - } -]) - -// Provider to register a searchable module/app. -.provider('Search', [ - function() { - var searchModules = []; - - this.register = function(module) { - searchModules.push(module); - }; - - this.$get = [ - function () { - return { - getAll: function () { - return searchModules; - } - }; - } - ]; - } -]) - -.run([ - 'editableOptions', - 'gettext', - function (editableOptions, gettext) { - editableOptions.theme = 'bs3'; - editableOptions.cancelButtonAriaLabel = gettext('Cancel'); - editableOptions.cancelButtonTitle = gettext('Cancel'); - editableOptions.clearButtonAriaLabel = gettext('Clear'); - editableOptions.clearButtonTitle = gettext('Clear'); - editableOptions.submitButtonAriaLabel = gettext('Submit'); - editableOptions.submitButtonTitle = gettext('Submit'); - } -]) - -.factory('WebpageTitle', [ - '$rootScope', - function ($rootScope) { - $rootScope.activeAppTitle = ''; - return { - updateTitle: function (text) { - $rootScope.activeAppTitle = text || ''; - }, - }; - } -]) - -// Watch for the basePerm on a stateChange and initialize the WebpageTitle factory -.run([ - '$rootScope', - 'operator', - 'WebpageTitle', - function ($rootScope, operator, WebpageTitle) { - $rootScope.$on('$stateChangeSuccess', function(event, toState) { - WebpageTitle.updateTitle(toState.data ? toState.data.title : ''); - if (toState.data) { - $rootScope.baseViewPermissionsGranted = toState.data.basePerm ? - operator.hasPerms(toState.data.basePerm) : true; - } else { - $rootScope.baseViewPermissionsGranted = true; - } - // Scroll to top on every state change - $rootScope.gotoTop(); - }); - } -]) - -// Make the main content expandable -.run([ - '$rootScope', - function ($rootScope) { - $rootScope.$on('$stateChangeSuccess', function() { - $rootScope.expandContent = false; - }); - $rootScope.toggleExpandContent = function () { - $rootScope.expandContent = !$rootScope.expandContent; - }; - } -]) - -.config([ - 'mainMenuProvider', - 'gettext', - function (mainMenuProvider, gettext) { - mainMenuProvider.register({ - 'ui_sref': 'home', - 'img_class': 'home', - 'title': gettext('Home'), - 'weight': 100, - 'perm': 'core.can_see_frontpage', - }); - - mainMenuProvider.register({ - 'ui_sref': 'config', - 'img_class': 'cog', - 'title': gettext('Settings'), - 'weight': 1000, - 'perm': 'core.can_manage_config', - }); - } -]) - -.config([ - '$urlRouterProvider', - '$locationProvider', - function($urlRouterProvider, $locationProvider) { - // define fallback url and html5Mode - $urlRouterProvider.otherwise('/'); - $locationProvider.html5Mode(true); - } -]) - -.config([ - '$httpProvider', - function($httpProvider) { - // Combine the django csrf system with the angular csrf system - $httpProvider.defaults.xsrfCookieName = 'OpenSlidesCsrfToken'; - $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'; - } -]) - - -.config([ - '$stateProvider', - '$urlMatcherFactoryProvider', - function($stateProvider, $urlMatcherFactoryProvider) { - // Make the trailing slash optional - $urlMatcherFactoryProvider.strictMode(false); - - // Use stateProvider.decorator to give default values to our states - $stateProvider.decorator('views', function(state, parent) { - var result = {}, - views = parent(state); - - if (state.abstract || state.data && state.data.extern) { - return views; - } - - angular.forEach(views, function(config, name) { - // Sets additional default values for templateUrl - var templateUrl, - controller, - defaultControllers = { - create: 'CreateCtrl', - update: 'UpdateCtrl', - list: 'ListCtrl', - detail: 'DetailCtrl', - }; - - // Split up state name - // example: "motions.motion.detail.update" -> ['motions', 'motion', 'detail', 'update'] - var patterns = state.name.split('.'); - - // set app and module name from state - // - appName: patterns[0] (e.g. "motions") - // - moduleNames: patterns without first element (e.g. ["motion", "detail", "update"]) - var appName = ''; - var moduleName = ''; - var moduleNames = []; - if (patterns.length > 0) { - appName = patterns[0]; - moduleNames = patterns.slice(1); - } - if (moduleNames.length > 0) { - // convert from camcelcase to dash notation - // example: ["motionBlock", "detail"] -> ["motion-block", "detail"] - for (var i = 0; i < moduleNames.length; i++) { - moduleNames[i] = moduleNames[i].replace(/([a-z\d])([A-Z])/g, '$1-$2').toLowerCase(); - } - - // use special templateUrl for create and update view - // example: ["motion", "detail", "update"] -> "motion-form" - if (_.last(moduleNames).match(/(create|update)/)) { - moduleName = '/' + moduleNames[0] + '-form'; - } else { - // convert modelNames array to url string - // example: ["motion-block", "detail"] -> "motion-block-detail" - moduleName = '/' + moduleNames.join('-'); - } - } - templateUrl = 'static/templates/' + appName + moduleName + '.html'; - config.templateUrl = state.templateUrl || templateUrl; - - // controller - if (patterns.length >= 3) { - controller = _.upperFirst(patterns[1]) + defaultControllers[_.last(patterns)]; - config.controller = state.controller || controller; - } - result[name] = config; - }); - return result; - }) - - .decorator('url', function(state, parent) { - var defaultUrl; - - if (state.abstract) { - defaultUrl = ''; - } else { - var patterns = state.name.split('.'), - defaultUrls = { - create: '/new', - update: '/edit', - list: '', - // The id is expected to be an integer, if not, the url has to - // be defined manually - detail: '/{id:int}', - }; - - defaultUrl = defaultUrls[_.last(patterns)]; - } - - state.url = state.url || defaultUrl; - return parent(state); - }); - } -]) - -.config([ - '$stateProvider', - '$locationProvider', - 'gettext', - function($stateProvider, $locationProvider, gettext) { - // Core urls - $stateProvider - .state('home', { - url: '/', - templateUrl: 'static/templates/home.html', - data: { - title: gettext('Home'), - basePerm: 'core.can_see_frontpage', - }, - }) - .state('projector', { - url: '/projector/{id:int}/', - templateUrl: 'static/templates/projector-container.html', - data: {extern: true}, - onEnter: function($window) { - $window.location.href = this.url; - } - }) - .state('real-projector', { - url: '/real-projector/{id:int}/', - templateUrl: 'static/templates/projector.html', - data: {extern: true}, - onEnter: function($window) { - $window.location.href = this.url; - } - }) - .state('manage-projectors', { - url: '/manage-projectors', - templateUrl: 'static/templates/core/manage-projectors.html', - controller: 'ManageProjectorsCtrl', - data: { - title: gettext('Manage projectors'), - basePerm: 'core.can_manage_projector', - }, - }) - .state('core', { - url: '/core', - abstract: true, - template: "", - }) - - // legal notice and version - .state('legalnotice', { - url: '/legalnotice', - controller: 'LegalNoticeCtrl', - data: { - title: gettext('Legal notice'), - }, - }) - - // privacy policy - .state('privacypolicy', { - url: '/privacypolicy', - controller: 'PrivacyPolicyCtrl', - data: { - title: gettext('Privacy policy'), - }, - }) - - //config - .state('config', { - url: '/config', - controller: 'ConfigCtrl', - data: { - title: gettext('Settings'), - basePerm: 'core.can_manage_config', - }, - }) - - // search - .state('search', { - url: '/search?q', - controller: 'SearchCtrl', - templateUrl: 'static/templates/search.html', - data: { - title: gettext('Search'), - }, - }) - - // tag - .state('core.tag', { - url: '/tag', - abstract: true, - template: "", - data: { - title: gettext('Tags'), - basePerm: 'core.can_manage_tags', - }, - }) - .state('core.tag.list', {}) - - // Countdown - .state('core.countdown', { - url: '/countdown', - abstract: true, - template: "", - data: { - title: gettext('Countdown'), - basePerm: 'core.can_manage_projector', - }, - }) - .state('core.countdown.detail', { - resolve: { - countdownId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }], - } - }); - - $locationProvider.html5Mode(true); - } -]) - -.factory('ProjectorMessageForm', [ - 'Editor', - 'gettextCatalog', - function (Editor, gettextCatalog) { - return { - getDialog: function (message) { - return { - template: 'static/templates/core/projector-message-form.html', - controller: 'ProjectorMessageEditCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - projectorMessageId: function () { - return message.id; - } - }, - }; - }, - getFormFields: function () { - return [ - { - key: 'message', - type: 'editor', - templateOptions: { - label: gettextCatalog.getString('Message'), - }, - data: { - ckeditorOptions: Editor.getOptions() - } - }, - ]; - }, - }; - } -]) - -.factory('TagForm', [ - 'gettextCatalog', - function (gettextCatalog) { - return { - getDialog: function (tag) { - return { - template: 'static/templates/core/tag-form.html', - controller: (tag) ? 'TagUpdateCtrl' : 'TagCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - tagId: function () {return tag ? tag.id : void 0;}, - }, - }; - }, - getFormFields: function() { - return [ - { - key: 'name', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Name'), - required: true - } - }, - ]; - }, - }; - } -]) - -/* This factory handles the filtering of the OS-data-tables. It contains - * all logic needed for the table header filtering. Things to configure: - * - multiselectFilters: A dict associating the filter name to a list (empty per default). E.g. - * { tag: [], - * category: [], } - * - booleanFilters: A dict containing a dict for every filter. The value property is a must. - * For displaying properties like displayName, choiceYes and choiceNo could be usefull. E.g. - * { isPresent: { - * value: undefined, - * displayName: gettext('Is present'), } } - * - propertyList, propertyFunctionList, propertyDict: See function getObjectQueryString - */ -.factory('osTableFilter', [ - '$sessionStorage', - function ($sessionStorage) { - var createInstance = function (tableName) { - var self = { - multiselectFilters: {}, - booleanFilters: {}, - filterString: '', - }; - var existsStorageEntry = function () { - return $sessionStorage[tableName]; - }; - var storage = existsStorageEntry(); - if (storage) { - self = storage; - } - - self.existsStorageEntry = existsStorageEntry; - self.save = function () { - $sessionStorage[tableName] = self; - self.changed(); - }; - self.areFiltersSet = function () { - var areFiltersSet = _.find(self.multiselectFilters, function (filterList) { - return filterList.length > 0; - }); - areFiltersSet = areFiltersSet || _.find(self.booleanFilters, function (filterDict) { - return filterDict.value !== undefined; - }); - areFiltersSet = areFiltersSet || (self.filterString !== ''); - return areFiltersSet !== false; - }; - self.reset = function (danger) { - if (danger) { - return; - } - _.forEach(self.multiselectFilters, function (filterList, filter) { - self.multiselectFilters[filter] = []; - }); - _.forEach(self.booleanFilters, function (filterDict, filter) { - self.booleanFilters[filter].value = undefined; - }); - self.filterString = ''; - self.save(); - }; - self.operateMultiselectFilter = function (filter, id, danger) { - if (!danger) { - if (_.indexOf(self.multiselectFilters[filter], id) > -1) { - // remove id - self.multiselectFilters[filter].splice(_.indexOf(self.multiselectFilters[filter], id), 1); - } else { - // add id - self.multiselectFilters[filter].push(id); - } - self.save(); - } - }; - /* Three things are could be given to create the query string: - * - propertyList: Just a list of object's properties like ['title', 'name'] - * - propertyFunktionList: A list of functions returning a property (e.g. [function(motion) {return motion.getTitle();}] for retrieving the motions title) - * - propertyDict: A dict association properties that are lists to functions on how to handle them. - * E.g.: {'tags': function (tag) {return tag.name;}, } - * The list of tags will be mapped with this function to a list of strings (tag names). - */ - self.getObjectQueryString = function (obj) { - var stringList = []; - _.forEach(self.propertyList, function (property) { - stringList.push(obj[property]); - }); - _.forEach(self.propertyFunctionList, function (fn) { - stringList.push(fn(obj)); - }); - _.forEach(self.propertyDict, function (idFunction, property) { - stringList.push(_.map(obj[property], idFunction).join(' ')); - }); - return stringList.join(' '); - }; - // Stub for callback - self.changed = function () {}; - return self; - }; - - return { - createInstance: createInstance - }; - } -]) - -/* This factory takes care of the sorting of OS-data-tables. Things to configure: - * - column: the default column which is the list sorted by (e.g. - * instance.column='title') - */ -.factory('osTableSort', [ - '$sessionStorage', - function ($sessionStorage) { - var createInstance = function (tableName) { - var self = { - column: '', - reverse: false, - }; - var storage = $sessionStorage[tableName]; - if (storage) { - self = storage; - } - - self.save = function () { - $sessionStorage[tableName] = self; - }; - self.toggle = function (column) { - if (self.column === column) { - self.reverse = !self.reverse; - } - self.column = column; - self.save(); - }; - return self; - }; - - return { - createInstance: createInstance - }; - } -]) - -/* Factory for pagination of the tables. Saves all settings (currentPage, ...) - * to the session storage and recovers them when the table is reloaded. - * You have to provide a 'tableName' where the settings are saved in the session - * storage. Has to be unique for obvious reasons. - * The 'itemsPerPage' is optional. If not given, it defaults to 25. - */ -.factory('osTablePagination', [ - '$rootScope', - '$sessionStorage', - function ($rootScope, $sessionStorage) { - var createInstance = function (tableName, itemsPerPage) { - // Defaults - var self = { - currentPage: 1, - itemsPerPage: itemsPerPage || 25, - limitBegin: 0, - }; - - // Check storage; maybe recover old state. - var storage = $sessionStorage[tableName]; - if (storage) { - self = storage; - } - - self.save = function () { - $sessionStorage[tableName] = self; - }; - self.pageChanged = function () { - self.limitBegin = (self.currentPage - 1) * self.itemsPerPage; - self.save(); - $rootScope.gotoTop(); - }; - self.getPageCount = function (objs) { - if (objs) { - return Math.ceil(objs.length/self.itemsPerPage); - } - }; - self.showNextPageArrow = function (objs) { - if (objs) { - return self.currentPage != self.getPageCount(objs); - } - }; - self.showPrevPageArrow = function () { - return self.currentPage != 1; - }; - self.nextPage = function (objs) { - if (objs && self.currentPage < self.getPageCount(objs)) { - self.currentPage++; - self.pageChanged(); - } - }; - self.prevPage = function () { - if (self.currentPage > 1) { - self.currentPage--; - self.pageChanged(); - } - }; - return self; - }; - - return { - createInstance: createInstance - }; - } -]) - -/* This Factory could be used in any dialog, if the user should be warned, if another user - * also has this dialog open. Use it like in this example in any dialog controller: - var editingStoppedCallback = EditingWarning.editingStarted('editing_name' + item.id); - $scope.$on('$destroy', editingStoppedCallback); - */ -.factory('EditingWarning', [ - 'operator', - 'gettextCatalog', - 'Notify', - 'Messaging', - function (operator, gettextCatalog, Notify, Messaging) { - return { - // This returns the callback function that the controller should call, if - // the dialog got closed by the user. Provide a unique dialog name. - editingStarted: function (dialogName) { - // List of active editors - var editorNames = []; - var messagingId = dialogName + 'EditingWarning'; - // Add an editor (may come from open_request or open_response) - var addActiveEditor = function (editorName) { - editorNames.push(editorName); - updateActiveEditors(); - }; - // Remove an editor, if he closes his dialog (dialog_closed) - var removeActiveEditor = function (editorName) { - var firstIndex = _.indexOf(editorNames, editorName); - editorNames.splice(firstIndex, 1); - updateActiveEditors(); - }; - // Show a warning. - var updateActiveEditors = function () { - if (editorNames.length === 0) { - Messaging.deleteMessage(messagingId); - } else { - // This block is only for getting the message string together... - var editorsWithoutAnonymous = _.filter(editorNames, function (name) { - return name; - }); - var text = gettextCatalog.getString('Warning') + ': '; - if (editorsWithoutAnonymous.length === 0) { // Only anonymous - // Singular vs. plural - if (editorNames.length === 1) { - text += gettextCatalog.getString('One anonymous users is also editing this.'); - } else { - text += editorNames.length + ' ' + gettextCatalog.getString('anonymous users are also editing this.'); - } - } else { - // At least one named user. The max users to display is 5. Anonymous users doesn't get displayed - // by name, but the amount of users is shown. - text += _.slice(editorsWithoutAnonymous, 0, 5).join(', '); - if (editorsWithoutAnonymous.length > 5) { - // More than 5 users with names. - text += ', ... [+' + (editorNames.length - 5) + ']'; - } else if (editorsWithoutAnonymous.length !== editorNames.length) { - // Less than 5 users, so the difference is calculated different. - text += ', ... [+' + (editorNames.length - editorsWithoutAnonymous.length) + ']'; - } - // Singular vs. plural - if (editorNames.length === 1) { - text += ' ' + gettextCatalog.getString('is also editing this.'); - } else { - text += ' ' + gettextCatalog.getString('are also editing this.'); - } - } - Messaging.createOrEditMessage(messagingId, text, 'warning'); - } - }; - - // The stucture of determinating which users are editing this dialog: - // - send an open_query to every user with the name of this operator in the parameter. With - // this information all clients that listen to this request knows that this operator has - // opened the dialog. - // - The clients, which have recieved the query send an answer (open_resonse) to this operator. - // - The operator collects all resonses and fills the editornames list. - // - If the dialog get closed, a dialog_closed is send. All recieven clients remove this - // operato from their editorNames list. - var responseCallbackId = Notify.registerCallback(dialogName + '_open_response', function (notify) { - if (!notify.sendBySelf) { - addActiveEditor(notify.params.name); - } - }); - var queryCallbackId = Notify.registerCallback(dialogName + '_open_query', function (notify) { - if (!notify.sendBySelf) { - addActiveEditor(notify.params.name); - if (notify.senderUserId) { - Notify.notify(dialogName + '_open_response', { - name: operator.user ? operator.user.short_name : '', - }, [notify.senderUserId]); - } else { - Notify.notify(dialogName + '_open_response', { - name: operator.user ? operator.user.short_name : '', - }, null, [notify.senderReplyChannelName]); - } - } - }); - var closeCallbackId = Notify.registerCallback(dialogName + '_dialog_closed', function (notify) { - removeActiveEditor(notify.params.name); - }); - // Send here the open_query to get the notify-chain started. - Notify.notify(dialogName + '_open_query', { - name: operator.user ? operator.user.short_name : '', - }); - // The function returned is to deregister the callbacks and send the dialog_closed notify, if - // the dialog get closed. - return function () { - Notify.deregisterCallbacks(responseCallbackId, queryCallbackId, closeCallbackId); - Messaging.deleteMessage(messagingId); - Notify.notify(dialogName + '_dialog_closed', { - name: operator.user ? operator.user.short_name : '', - }); - }; - }, - }; - } -]) - -/* - * This filter filters all items in an array. If the filterArray is empty, the - * array is passed. The filterArray contains numbers of the multiselect, e. g. [1, 3, 4]. - * Then, all items in the array are passed, if the item_id (get with id_function) matches - * one of the ids in filterArray. id_function could also return a list of ids. Example: - * Item 1 has two tags with ids [1, 4]. filterArray == [3, 4] --> match - * - * If -1 is in the array items without an id will not be filtered. This is for implementing - * a filter option like: "All items without a category" - */ -.filter('MultiselectFilter', [ - function () { - return function (array, filterArray, idFunction) { - if (filterArray.length === 0) { - return array; - } - var itemsWithoutProperty = _.indexOf(filterArray, -1) > -1; - return Array.prototype.filter.call(array, function (item) { - var id = idFunction(item); - if (typeof id === 'number') { - id = [id]; - } else if (id === null || !id.length) { - return itemsWithoutProperty; - } - return _.intersection(id, filterArray).length > 0; - }); - }; - } -]) - -.filter('osFilter', [ - function () { - return function (array, string, getFilterString) { - if (!string) { - return array; - } - return Array.prototype.filter.call(array, function (item) { - return getFilterString(item).toLowerCase().indexOf(string.toLowerCase()) > -1; - }); - }; - } -]) - -// angular formly config options -.run([ - 'formlyConfig', - function (formlyConfig) { - // NOTE: This next line is highly recommended. Otherwise Chrome's autocomplete - // will appear over your options! - formlyConfig.extras.removeChromeAutoComplete = true; - - // Configure custom types - formlyConfig.setType({ - name: 'editor', - extends: 'textarea', - templateUrl: 'static/templates/core/editor.html', - }); - formlyConfig.setType({ - name: 'password', - extends: 'input', - templateUrl: 'static/templates/core/password.html', - }); - formlyConfig.setType({ - name: 'checkbox', - templateUrl: 'static/templates/core/checkbox.html', - overwriteOk: true, - }); - formlyConfig.setType({ - name: 'checkbox-buttons', - templateUrl: 'static/templates/core/checkbox-buttons.html', - overwriteOk: true, - }); - formlyConfig.setType({ - name: 'select-single', - extends: 'select', - templateUrl: 'static/templates/core/select-single.html' - }); - formlyConfig.setType({ - name: 'select-multiple', - extends: 'select', - templateUrl: 'static/templates/core/select-multiple.html' - }); - formlyConfig.setType({ - name: 'radio-buttons', - templateUrl: 'static/templates/core/radio-buttons.html', - wrapper: ['bootstrapHasError'], - defaultOptions: { - noFormControl: false - } - }); - formlyConfig.setType({ - name: 'file', - extends: 'input', - templateUrl: 'static/templates/core/file.html', - }); - } -]) - -// html-tag os-form-field to generate generic from fields -// TODO: make it possible to use other fields then config fields -.directive('osFormField', [ - '$parse', - 'Config', - 'gettextCatalog', - function($parse, Config, gettextCatalog) { - var getHtmlType = function (type) { - return { - string: 'text', - text: 'textarea', - markupText: 'editor', - integer: 'number', - boolean: 'checkbox', - choice: 'choice', - comments: 'comments', - colorpicker: 'colorpicker', - datetimepicker: 'datetimepicker', - majorityMethod: 'choice', - translations: 'translations', - }[type]; - }; - - return { - restrict: 'E', - scope: true, - templateUrl: 'static/templates/config-form-field.html', - link: function ($scope, iElement, iAttrs, controller, transcludeFn) { - var field = $parse(iAttrs.field)($scope); - var config = Config.get(field.key); - $scope.type = getHtmlType(field.input_type); - if ($scope.type == 'choice') { - $scope.choices = field.choices; - $scope.value = config.value; - } else { - $scope.value = gettextCatalog.getString(config.value); - } - $scope.label = field.label; - $scope.key = 'field-' + field.key; - $scope.help_text = field.help_text; - $scope.default_value = field.default_value; - $scope.reset = function () { - if ($scope.type == 'choice') { - $scope.value = $scope.default_value; - } else { - $scope.value = gettextCatalog.getString($scope.default_value); - } - $scope.save(field, $scope.value); - }; - } - }; - } -]) - -/* This directive provides a csv import template. - * Papa Parse is used to parse the csv file. Accepted attributes: - * * change: - * Callback if file changes. The one parameter is csv passing the parsed file - * * config (optional): - * - accept: String with extensions: default '.csv .txt' - * - encodingOptions: List with encodings. Default ['UTF-8', 'ISO-8859-1'] - * - parseConfig: a dict passed to PapaParse - */ -.directive('csvImport', [ - function () { - return { - restrict: 'E', - templateUrl: 'static/templates/csv-import.html', - scope: { - change: '&', - config: '=?', - }, - controller: function ($scope, $element, $attrs, $location) { - // set config if it is not given - if (!$scope.config) { - $scope.config = {}; - } - if (!$scope.config.parseConfig) { - $scope.config.parseConfig = {}; - } - - $scope.inputElement = angular.element($element[0].querySelector('#csvFileSelector')); - - // set accept and encoding - $scope.accept = $scope.config.accept || '.csv'; - $scope.encodingOptions = $scope.config.encodingOptions || ['UTF-8']; - $scope.encoding = $scope.encodingOptions[0]; - - $scope.parse = function () { - var inputElement = $scope.inputElement[0]; - if (!inputElement.files.length) { - $scope.change({csv: {data: {}}}); - } else { - var parseConfig = _.defaults(_.clone($scope.config.parseConfig), { - delimiter: $scope.delimiter, - encoding: $scope.encoding, - header: false, // we do not want to have dicts in result - complete: function (csv) { - if (csv.data.length) { - csv.meta.fields = csv.data[0]; - } - else { - csv.meta.fields = []; - } - csv.data = csv.data.splice(1); // do not interpret the header as data - $scope.$apply(function () { - if (csv.meta.delimiter) { - $scope.autodelimiter = csv.meta.delimiter; - } - $scope.change({csv: csv}); - }); - }, - error: function () { - $scope.$apply(function () { - $scope.change({csv: {data: {}}}); - }); - }, - }); - - Papa.parse(inputElement.files[0], parseConfig); - } - }; - - $scope.clearFile = function () { - $scope.inputElement[0].value = ''; - $scope.selectedFile = undefined; - $scope.parse(); - }; - - $scope.inputElement.on('change', function () { - $scope.selectedFile = _.last($scope.inputElement[0].value.split('\\')); - $scope.parse(); - }); - }, - }; - } -]) - -.directive('messaging', [ - '$timeout', - 'Messaging', - function ($timeout, Messaging) { - return { - restrict: 'E', - templateUrl: 'static/templates/messaging.html', - scope: {}, - controller: function ($scope, $element, $attrs, $location) { - $scope.messages = {}; - - var update = function () { - $scope.messages = Messaging.getMessages(); - }; - Messaging.registerMessageChangeCallback(update); - - $scope.close = function (id) { - Messaging.deleteMessage(id); - }; - }, - }; - } -]) - -.controller('MainMenuCtrl', [ - '$scope', - 'mainMenu', - function ($scope, mainMenu) { - mainMenu.registerScope($scope); - $scope.isMenuOpen = false; - $scope.closeMenu = function () { - $scope.isMenuOpen = false; - }; - } -]) - -.controller('LanguageCtrl', [ - '$scope', - 'gettextCatalog', - 'Languages', - 'filterFilter', - function ($scope, gettextCatalog, Languages, filterFilter) { - $scope.languages = Languages.getLanguages(); - $scope.selectedLanguage = filterFilter($scope.languages, {selected: true}); - // controller to switch app language - $scope.switchLanguage = function (lang) { - $scope.languages = Languages.setCurrentLanguage(lang); - $scope.selectedLanguage = filterFilter($scope.languages, {selected: true}); - }; - } -]) - -.controller('GotoTopCtrl', [ - '$scope', - '$window', - '$timeout', - function ($scope, $window, $timeout) { - $scope.show = false; - angular.element($window).bind('scroll', function () { - $timeout(function () { - $scope.show = ($window.pageYOffset >= 150); - }); - }); - } -]) - -.run([ - '$rootScope', - '$window', - function ($rootScope, $window) { - $rootScope.gotoTop = function () { - $window.scrollTo(0, 0); - }; - } -]) - -// Prevent scrolling in number inputs. Instead of changing the number, the input -// is blurred and the window is scrolled. This is very important for our dialog -// forms, so a user didn't change a value, when he wants to scroll the form. -.run(function () { - $('body').on('mousewheel', function (e) { - if (e.target.nodeName === 'INPUT' && e.target.type === 'number') { - $(e.target).blur(); - } - }); -}) - -// Projector Sidebar Controller -.controller('ProjectorSidebarCtrl', [ - '$scope', - '$document', - '$window', - function ($scope, $document, $window) { - $scope.isProjectorSidebar = false; - $scope.showProjectorSidebar = function (show) { - $scope.isProjectorSidebar = show; - }; - - // Sidebar scroll - var marginTop = 20, // margin-top from #content - marginBottom = 30, // 30px + 20px sidebar margin-bottom = 50px from footer - sidebar; - - var sidebarScroll = function () { - var sidebarHeight = sidebar.height(), - sidebarOffset = sidebar.offset().top, - sidebarMinOffset = $('#header').height() + $('#nav').height() + marginTop, - documentHeight = $document.height(), - windowHeight = $window.innerHeight, - scrollTop = $window.pageYOffset; - - // First, check if there is a need to scroll: scroll if the sidebar is smaller then the content - if (sidebarHeight < $('.col1').height()) { - if ((scrollTop + marginTop + sidebarHeight) > (documentHeight - marginBottom)) { - // Stick to the bottom - var bottom = marginBottom + scrollTop + windowHeight - documentHeight; - sidebar.css({'position': 'fixed', 'top': '', 'bottom': bottom}); - } else if ((scrollTop + marginTop) > sidebarMinOffset) { - // scroll with the user - sidebar.css({'position': 'fixed', 'top': marginTop, 'bottom': ''}); - } else { - // Stick to the top - sidebar.css({'position': 'relative', 'top': 0, 'bottom': ''}); - } - } else { - // Stick to the top, if the sidebar is larger then the content - sidebar.css({'position': 'relative', 'top': 0, 'bottom': ''}); - } - }; - - $scope.initSidebar = function () { - sidebar = $('#sidebar'); - $scope.$watch(function () { - return sidebar.height(); - }, sidebarScroll); - angular.element($window).bind('scroll', sidebarScroll); - }; - - } -]) - -// Legal Notice Controller -.controller('LegalNoticeCtrl', [ - '$scope', - '$http', - function ($scope, $http) { - $http.get('/core/version/').then(function (success) { - $scope.core_version = success.data.openslides_version; - $scope.core_license = success.data.openslides_license; - $scope.core_url = success.data.openslides_url; - $scope.plugins = success.data.plugins; - }); - } -]) - -// Privacy Policy Controller -.controller('PrivacyPolicyCtrl', function () {}) - -// Config Controller -.controller('ConfigCtrl', [ - '$scope', - '$timeout', - 'MajorityMethodChoices', - 'Config', - 'OpenSlidesConfigVariables', - 'gettextCatalog', - 'DateTimePickerTranslation', - 'Editor', - function($scope, $timeout, MajorityMethodChoices, Config, OpenSlidesConfigVariables, - gettextCatalog, DateTimePickerTranslation, Editor) { - Config.bindAll({}, $scope, 'configs'); - $scope.configGroups = OpenSlidesConfigVariables; - $scope.dateTimePickerTranslatedButtons = DateTimePickerTranslation.getButtons(); - - $scope.ckeditorOptions = Editor.getOptions(); - $scope.ckeditorOptions.on.change = function (event) { - // we could just retrieve the key, but we need the configOption object. - var configOption_key = event.editor.element.$.id; - - // find configOption object - var subgroups = _.flatMap($scope.configGroups, function (group) { - return group.subgroups; - }); - var items = _.flatMap(subgroups, function (subgroup) { - return subgroup.items; - }); - var configOption = _.find(items, function (_item) { - return _item.key === configOption_key; - }); - - var editor = this; - // The $timeout executes the given function in an angular context. Because - // this is a standard JS event, all changes may not happen in the digist-cylce. - // By using $timeout angular calls $apply for us that we do not have to care - // about starting the digist-cycle. - $timeout(function () { - $scope.save(configOption, editor.getData()); - }, 1); - }; - - // save changed config value - $scope.save = function(configOption, value) { - Config.get(configOption.key).value = value; - Config.save(configOption.key).then(function (success) { - configOption.success = true; - // fade out the success symbol after 2 seconds. - $timeout(function () { - var element = $('#success-field-' + configOption.key); - element.fadeOut(800, function () { - configOption.success = void 0; - }); - }, 2000); - }, function (error) { - configOption.success = false; - configOption.errorMessage = error.data.detail; - }); - }; - - // For comments input - $scope.addComment = function (configOption, parent) { - var maxId = _.max(_.keys(parent.value)); - if (maxId === undefined) { - maxId = 1; - } else { - maxId = parseInt(maxId) + 1; - } - parent.value[maxId] = { - name: gettextCatalog.getString('New'), - public: false, - }; - $scope.save(configOption, parent.value); - }; - $scope.removeComment = function (configOption, parent, id) { - parent.value[id] = null; - $scope.save(configOption, parent.value); - }; - - // For custom translations input - $scope.addTranslation = function (configOption, parent) { - parent.value.push({ - original: gettextCatalog.getString('New'), - translation: gettextCatalog.getString('New'), - }); - $scope.save(configOption, parent.value); - }; - $scope.removeTranslation = function (configOption, parent, index) { - parent.value.splice(index, 1); - $scope.save(configOption, parent.value); - }; - - // For majority method - angular.forEach( - _.filter($scope.configGroups, function (configGroup) { - return configGroup.name === 'Motions' || configGroup.name === 'Elections'; - }), - function (configGroup) { - var configItem; - _.forEach(configGroup.subgroups, function (subgroup) { - configItem = _.find(subgroup.items, ['input_type', 'majorityMethod']); - if (configItem !== undefined) { - // Break the forEach loop if we found something. - return false; - } - }); - if (configItem !== undefined) { - configItem.choices = MajorityMethodChoices; - } - } - ); - } -]) - -// Search Bar Controller -.controller('SearchBarCtrl', [ - '$scope', - '$state', - '$sanitize', - function ($scope, $state, $sanitize) { - $scope.search = function() { - var query = _.escape($scope.querybar); - $scope.querybar = ''; - $state.go('search', {q: query}); - }; - } -]) - -// Search Controller -.controller('SearchCtrl', [ - '$scope', - '$filter', - '$stateParams', - 'Search', - 'DS', - 'Motion', - function ($scope, $filter, $stateParams, Search, DS, Motion) { - $scope.searchresults = []; - var searchModules = Search.getAll(); - - // search function - $scope.search = function() { - $scope.results = []; - var foundObjects = []; - // search in rest properties of all defined searchModule - // (does not found any related objects, e.g. speakers of items) - _.forEach(searchModules, function(searchModule) { - var result = {}; - result.verboseName = searchModule.verboseName; - result.collectionName = searchModule.collectionName; - result.urlDetailState = searchModule.urlDetailState; - result.weight = searchModule.weight; - result.checked = true; - result.elements = $filter('filter')(DS.getAll(searchModule.collectionName), $scope.searchquery); - $scope.results.push(result); - _.forEach(result.elements, function(element) { - foundObjects.push(element); - }); - }); - // search additionally in specific releations of all defined searchModules - _.forEach(searchModules, function(searchModule) { - _.forEach(DS.getAll(searchModule.collectionName), function(object) { - if (_.isFunction(object.hasSearchResult)) { - if (object.hasSearchResult(foundObjects, $scope.searchquery)) { - // releation found, check if object is not yet in search results - _.forEach($scope.results, function(result) { - if ((object.getResourceName() === result.collectionName) && - _.findIndex(result.elements, {'id': object.id}) === -1) { - result.elements.push(object); - } - }); - } - } else { - return false; - } - }); - }); - }; - - //get search string from parameters submitted from outside the scope - if ($stateParams.q) { - $scope.searchquery = $stateParams.q; - $scope.search(); - } - } -]) - -// Projector Control Controller -.controller('ProjectorControlCtrl', [ - '$scope', - '$http', - '$interval', - '$state', - '$q', - '$filter', - 'Config', - 'Projector', - 'CurrentListOfSpeakersItem', - 'CurrentListOfSpeakersSlide', - 'ProjectionDefault', - 'ProjectorMessage', - 'Countdown', - 'gettextCatalog', - 'ngDialog', - 'ProjectorMessageForm', - function($scope, $http, $interval, $state, $q, $filter, Config, Projector, CurrentListOfSpeakersItem, - CurrentListOfSpeakersSlide, ProjectionDefault, ProjectorMessage, Countdown, gettextCatalog, - ngDialog, ProjectorMessageForm) { - ProjectorMessage.bindAll({}, $scope, 'messages'); - - var intervals = []; - var calculateCountdownTime = function (countdown) { - countdown.seconds = Math.floor( countdown.countdown_time - Date.now() / 1000 + $scope.serverOffset ); - }; - var cancelIntervalTimers = function () { - intervals.forEach(function (interval) { - $interval.cancel(interval); - }); - }; - $scope.$watch(function () { - return Countdown.lastModified(); - }, function () { - $scope.countdowns = Countdown.getAll(); - - // stop ALL interval timer - cancelIntervalTimers(); - $scope.countdowns.forEach(function (countdown) { - if (countdown.running) { - calculateCountdownTime(countdown); - intervals.push($interval(function () { calculateCountdownTime(countdown); }, 1000)); - } else { - countdown.seconds = countdown.countdown_time; - } - }); - }); - $scope.$on('$destroy', function() { - // Cancel all intervals if the controller is destroyed - cancelIntervalTimers(); - }); - - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - $scope.projectors = Projector.getAll(); - if (!$scope.active_projector) { - $scope.active_projector = $filter('orderBy')($scope.projectors, 'id')[0]; - } - $scope.setIframeSize($scope.active_projector); - if ($scope.projectors.length === 1) { - $scope.currentListOfSpeakersAsOverlay = true; - } - - $scope.messageDefaultProjectorId = ProjectionDefault.filter({name: 'messages'})[0].projector_id; - $scope.countdownDefaultProjectorId = ProjectionDefault.filter({name: 'countdowns'})[0].projector_id; - $scope.listOfSpeakersDefaultProjectorId = ProjectionDefault.filter({name: 'agenda_current_list_of_speakers'})[0].projector_id; - }); - // watch for changes in projector_broadcast and currentListOfSpeakersReference - var last_broadcast; - $scope.$watch(function () { - return Config.lastModified(); - }, function () { - var broadcast = Config.get('projector_broadcast').value; - if (!last_broadcast || last_broadcast != broadcast) { - last_broadcast = broadcast; - $scope.broadcast = broadcast; - } - $scope.currentListOfSpeakersReference = $scope.config('projector_currentListOfSpeakers_reference'); - }); - - $scope.changeProjector = function (projector) { - $scope.active_projector = projector; - $scope.setIframeSize(projector); - }; - $scope.setIframeSize = function (projector) { - $scope.scale = 256.0 / projector.width; - $scope.iframeHeight = $scope.scale * projector.height; - }; - - $scope.editCurrentSlide = function (projector) { - var data = projector.getFormOrStateForCurrentSlide(); - if (data) { - if (data.form) { - ngDialog.open(data.form.getDialog({id: data.id})); - } else { - $state.go(data.state, {id: data.id}); - } - } - }; - - // *** countdown functions *** - $scope.calculateCountdownTime = function (countdown) { - countdown.seconds = Math.floor( countdown.countdown_time - Date.now() / 1000 + $scope.serverOffset ); - }; - $scope.editCountdown = function (countdown) { - countdown.editFlag = false; - countdown.description = countdown.new_description; - Countdown.save(countdown); - if (!countdown.running) { - countdown.reset(); - } - }; - $scope.addCountdown = function () { - var default_time = parseInt($scope.config('projector_default_countdown')); - var countdown = { - description: '', - default_time: default_time, - countdown_time: default_time, - running: false, - }; - Countdown.create(countdown); - }; - $scope.removeCountdown = function (countdown) { - Countdown.destroy(countdown.id); - }; - - // *** message functions *** - $scope.editMessage = function (message) { - ngDialog.open(ProjectorMessageForm.getDialog(message)); - }; - $scope.addMessage = function () { - var message = {message: ''}; - ProjectorMessage.create(message); - }; - $scope.removeMessage = function (message) { - ProjectorMessage.destroy(message.id); - }; - - /* Current list of speakers */ - $scope.currentListOfSpeakers = CurrentListOfSpeakersSlide; - // Set the current overlay status - if ($scope.currentListOfSpeakers.isProjected().length) { - var isProjected = $scope.currentListOfSpeakers.isProjectedWithOverlayStatus(); - $scope.currentListOfSpeakersAsOverlay = isProjected[0].overlay; - } else { - $scope.currentListOfSpeakersAsOverlay = true; - } - // go to the list of speakers(management) of the currently displayed list of speakers reference slide - $scope.goToListOfSpeakers = function() { - var item = $scope.currentListOfSpeakersItem(); - if (item) { - $state.go('agenda.item.detail', {id: item.id}); - } - }; - $scope.currentListOfSpeakersItem = function () { - return CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference); - }; - $scope.setOverlay = function (overlay) { - $scope.currentListOfSpeakersAsOverlay = overlay; - var isProjected = $scope.currentListOfSpeakers.isProjectedWithOverlayStatus(); - if (isProjected.length) { - _.forEach(isProjected, function (mapping) { - if (mapping.overlay != overlay) { // change the overlay if it is different - $scope.currentListOfSpeakers.project(mapping.projectorId, overlay); - } - }); - } - }; - } -]) - -.controller('ProjectorMessageEditCtrl', [ - '$scope', - 'projectorMessageId', - 'ProjectorMessage', - 'ProjectorMessageForm', - function ($scope, projectorMessageId, ProjectorMessage, ProjectorMessageForm) { - $scope.formFields = ProjectorMessageForm.getFormFields(); - $scope.model = angular.copy(ProjectorMessage.get(projectorMessageId)); - - $scope.save = function (message) { - ProjectorMessage.inject(message); - ProjectorMessage.save(message); - $scope.closeThisDialog(); - }; - } -]) - -.controller('ManageProjectorsCtrl', [ - '$scope', - '$http', - '$timeout', - 'Projector', - 'ProjectionDefault', - 'Config', - 'ProjectorMessage', - 'ngDialog', - function ($scope, $http, $timeout, Projector, ProjectionDefault, Config, - ProjectorMessage, ngDialog) { - ProjectionDefault.bindAll({}, $scope, 'projectiondefaults'); - - /* Info on resolution calculating: - * Internally the resolution is saved as (width, height) but the user has - * an aspect ratio to choose and a width from 800 to 3840 (4K).*/ - $scope.aspectRatios = { - '4:3': 4/3, - '16:9': 16/9, - '16:10': 16/10, - }; - // when converting (x,y) -> (ratio, percentage) round issues may occur - // (e.g. 800/600 != 4/3 with internal calculation issues). With this environment - // is tested, if the calculated value is in the following interval: - // [expected-environment; expected+environment] - var RATIO_ENVIRONMENT = 0.05; - - // watch for changes in projector_broadcast - // and projector_currentListOfSpeakers_reference - var last_broadcast, last_clos; - $scope.$watch(function () { - return Config.lastModified(); - }, function () { - var broadcast = $scope.config('projector_broadcast'), - currentListOfSpeakers = $scope.config('projector_currentListOfSpeakers_reference'); - if (!last_broadcast || last_broadcast != broadcast) { - last_broadcast = broadcast; - $scope.broadcast = broadcast; - } - if (!last_clos || last_clos != currentListOfSpeakers) { - last_clos = currentListOfSpeakers; - $scope.currentListOfSpeakers = currentListOfSpeakers; - } - }); - - // watch for changes in Projector, and recalc scale and iframeHeight - var first_watch = true; - $scope.resolutions = {}; - $scope.edit = []; - $scope.sliders = {}; - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - $scope.projectors = Projector.getAll(); - $scope.projectors.forEach(function (projector) { - projector.iframeScale = 256.0 / projector.width; - projector.iframeHeight = projector.iframeScale * projector.height; - if (first_watch) { - $scope.resolutions[projector.id] = { - width: projector.width, - height: projector.height - }; - $scope.edit[projector.id] = false; - $scope.sliders[projector.id] = { - value: projector.width, - options: { - id: projector.id, - floor: 800, - ceil: 3840, - step: 10, - translate: function (value) { - return value + 'px'; - }, - onChange: function (v) { - $scope.calcResolution(projector); - }, - onEnd: function (v) { - $scope.saveResolution(projector); - }, - hideLimitLabels: true, - }, - }; - $scope.setAspectRatio(projector, $scope.getAspectRatio(projector)); - } - }); - if ($scope.projectors.length) { - first_watch = false; - } - }); - - $scope.getAspectRatio = function (projector) { - var ratio = projector.width/projector.height; - var foundRatio = _.findKey($scope.aspectRatios, function (value) { - return value >= (ratio-RATIO_ENVIRONMENT) && value <= (ratio+RATIO_ENVIRONMENT); - }); - if (foundRatio === undefined) { - return _.keys($scope.aspectRatios)[0]; - } else { - return foundRatio; - } - }; - $scope.setAspectRatio = function (projector, aspectRatio) { - $scope.resolutions[projector.id].aspectRatio = aspectRatio; - $scope.resolutions[projector.id].aspectRatioNumber = $scope.aspectRatios[aspectRatio]; - $scope.calcResolution(projector); - }; - $scope.calcResolution = function (projector) { - var ratio = $scope.resolutions[projector.id].aspectRatioNumber; - var width = $scope.sliders[projector.id].value; - $scope.resolutions[projector.id].width = width; - $scope.resolutions[projector.id].height = Math.round(width/ratio); - }; - - $scope.toggleEditMenu = function (projectorId) { - $scope.edit[projectorId] = !$scope.edit[projectorId]; - $timeout(function () { - $scope.$broadcast('rzSliderForceRender'); - }); - }; - - // Set list of speakers reference - $scope.setListOfSpeakers = function (projector) { - Config.get('projector_currentListOfSpeakers_reference').value = projector.id; - Config.save('projector_currentListOfSpeakers_reference'); - }; - - // Projector functions - $scope.setProjectionDefault = function (projector, projectiondefault) { - if (projectiondefault.projector_id !== projector.id) { - $http.post('/rest/core/projector/' + projector.id + '/set_projectiondefault/', projectiondefault.id); - } - }; - $scope.createProjector = function (name) { - var projector = { - name: name, - config: {}, - scale: 0, - scroll: 0, - blank: false, - projectiondefaults: [], - }; - Projector.create(projector).then(function (projector) { - $http.post('/rest/core/projector/' + projector.id + '/activate_elements/', [{ - name: 'core/clock', - stable: true - }]); - $scope.resolutions[projector.id] = { - width: projector.width, - height: projector.height - }; - }); - }; - $scope.deleteProjector = function (projector) { - if (projector.id != 1) { - Projector.destroy(projector.id); - } - }; - $scope.editCurrentSlide = function (projector) { - var data = projector.getFormOrStateForCurrentSlide(); - if (data) { - if (data.form) { - ngDialog.open(data.form.getDialog({id: data.id})); - } else { - $state.go(data.state, {id: data.id}); - } - } - }; - $scope.editName = function (projector) { - projector.config = projector.elements; - Projector.save(projector); - }; - $scope.saveResolution = function (projector) { - $http.post( - '/rest/core/projector/' + projector.id + '/set_resolution/', - $scope.resolutions[projector.id] - ).then(function (success) { - $scope.resolutions[projector.id].error = null; - }, function (error) { - if (error.data) { - $scope.resolutions[projector.id].error = error.data.detail; - } else { - $scope.resolutions[projector.id].error = null; - } - }); - }; - - // Identify projectors - $scope.identifyProjectors = function () { - if ($scope.identifyPromise) { - $timeout.cancel($scope.identifyPromise); - $scope.removeIdentifierMessages(); - } else { - // Create new Message - var message = { - message: '', - }; - ProjectorMessage.create(message).then(function(message){ - $scope.projectors.forEach(function (projector) { - $http.post('/rest/core/projector/' + projector.id + '/activate_elements/', [{ - name: 'core/projector-message', - stable: true, - id: message.id, - identify: true, - }]); - }); - $scope.identifierMessage = message; - }); - $scope.identifyPromise = $timeout($scope.removeIdentifierMessages, 3000); - } - }; - $scope.removeIdentifierMessages = function () { - Projector.getAll().forEach(function (projector) { - _.forEach(projector.elements, function (element, uuid) { - if (element.name === 'core/projector-message' && element.id === $scope.identifierMessage.id) { - $http.post('/rest/core/projector/' + projector.id + '/deactivate_elements/', [uuid]); - } - }); - }); - ProjectorMessage.destroy($scope.identifierMessage.id); - $scope.identifyPromise = null; - }; - } -]) - -// Tag Controller -.controller('TagListCtrl', [ - '$scope', - 'Tag', - 'ngDialog', - 'TagForm', - 'gettext', - 'ErrorMessage', - function($scope, Tag, ngDialog, TagForm, gettext, ErrorMessage) { - Tag.bindAll({}, $scope, 'tags'); - $scope.alert = {}; - - // setup table sorting - $scope.sortColumn = 'name'; - $scope.reverse = false; - // function to sort by clicked column - $scope.toggleSort = function ( column ) { - if ( $scope.sortColumn === column ) { - $scope.reverse = !$scope.reverse; - } - $scope.sortColumn = column; - }; - $scope.delete = function (tag) { - Tag.destroy(tag.id).then( - function(success) { - $scope.alert = { - type: 'success', - msg: gettext('The delete was successful.'), - show: true, - }; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - $scope.editOrCreate = function (tag) { - ngDialog.open(TagForm.getDialog(tag)); - }; - } -]) - -.controller('TagCreateCtrl', [ - '$scope', - 'Tag', - 'TagForm', - 'ErrorMessage', - function($scope, Tag, TagForm, ErrorMessage) { - $scope.model = {}; - $scope.alert = {}; - $scope.formFields = TagForm.getFormFields(); - $scope.save = function (tag) { - Tag.create(tag).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('TagUpdateCtrl', [ - '$scope', - 'Tag', - 'tagId', - 'TagForm', - 'ErrorMessage', - function($scope, Tag, tagId, TagForm, ErrorMessage) { - $scope.model = angular.copy(Tag.get(tagId)); - $scope.alert = {}; - $scope.formFields = TagForm.getFormFields(); - $scope.save = function (tag) { - Tag.inject(tag); - Tag.save(tag).then(function(success) { - $scope.closeThisDialog(); - }, function (error) { - // save error: revert all changes by restore - // the original object - Tag.refresh(tag); - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('CountdownDetailCtrl', [ - '$scope', - '$interval', - 'Countdown', - 'countdownId', - function ($scope, $interval, Countdown, countdownId) { - var interval; - var calculateCountdownTime = function (countdown) { - countdown.seconds = Math.floor( $scope.countdown.countdown_time - Date.now() / 1000 + $scope.serverOffset ); - }; - $scope.$watch(function () { - return Countdown.lastModified(countdownId); - }, function () { - $scope.countdown = Countdown.get(countdownId); - if (interval) { - $interval.cancel(interval); - } - if ($scope.countdown) { - if ($scope.countdown.running) { - calculateCountdownTime($scope.countdown); - interval = $interval(function () { calculateCountdownTime($scope.countdown); }, 1000); - } else { - $scope.countdown.seconds = $scope.countdown.countdown_time; - } - } - }); - $scope.$on('$destroy', function() { - // Cancel the interval if the controller is destroyed - if (interval) { - $interval.cancel(interval); - } - }); - } -]) - -// counter of new (unread) chat messages -.value('NewChatMessages', []) - -// ChatMessage Controller -.controller('ChatMessageCtrl', [ - '$scope', - '$http', - '$timeout', - 'ChatMessage', - 'NewChatMessages', - function ($scope, $http, $timeout, ChatMessage, NewChatMessages) { - ChatMessage.bindAll({}, $scope, 'chatmessages'); - $scope.unreadMessages = NewChatMessages.length; - $scope.chatboxIsCollapsed = true; - $scope.openChatbox = function () { - $scope.chatboxIsCollapsed = !$scope.chatboxIsCollapsed; - NewChatMessages = []; - $scope.unreadMessages = NewChatMessages.length; - $timeout(function () { - angular.element('#messageInput').focus(); - }, 0); - }; - $scope.sendMessage = function () { - angular.element('#messageSendButton').addClass('disabled'); - angular.element('#messageInput').attr('disabled', ''); - $http.post( - '/rest/core/chat-message/', - {message: $scope.newMessage} - ).then(function (success) { - $scope.newMessage = ''; - angular.element('#messageSendButton').removeClass('disabled'); - angular.element('#messageInput').removeAttr('disabled'); - $timeout(function () { - angular.element('#messageInput').focus(); - }, 0); - }, function (error) { - angular.element('#messageSendButton').removeClass('disabled'); - angular.element('#messageInput').removeAttr('disabled'); - }); - }; - // increment unread messages counter for each new message - $scope.$watch('chatmessages', function (newVal, oldVal) { - // add new message id if there is really a new message which is not yet tracked - if (oldVal.length > 0 && newVal.length > 0) { - if ((oldVal[oldVal.length-1].id != newVal[newVal.length-1].id) && - ($.inArray(newVal[newVal.length-1].id, NewChatMessages) == -1)) { - NewChatMessages.push(newVal[newVal.length-1].id); - $scope.unreadMessages = NewChatMessages.length; - } - } else if (newVal.length === 0) { - NewChatMessages = []; - $scope.unreadMessages = 0; - } - }); - - $scope.clearChatHistory = function () { - $http.post('/rest/core/chat-message/clear/'); - }; - } -]) - -// format time string for model ("s") and view format ("h:mm:ss" or "mm:ss") -.directive('minSecFormat', [ - 'HumanTimeConverter', - function (HumanTimeConverter) { - return { - require: 'ngModel', - link: function(scope, element, attrs, ngModelController) { - ngModelController.$parsers.push(function(data) { - //convert data from view format (mm:ss) to model format (s) - return HumanTimeConverter.humanTimeToSeconds(data, {seconds: true}); - }); - - ngModelController.$formatters.push(function(data) { - //convert data from model format (s) to view format (mm:ss) - return HumanTimeConverter.secondsToHumanTime(data); - }); - } - }; - } -]) - -// format time string for model ("m") and view format ("h:mm" or "hh:mm") -.directive('hourMinFormat', [ - 'HumanTimeConverter', - function (HumanTimeConverter) { - return { - require: 'ngModel', - link: function(scope, element, attrs, ngModelController) { - ngModelController.$parsers.push(function(data) { - //convert data from view format (hh:mm) to model format (m) - return HumanTimeConverter.humanTimeToSeconds(data, {hours: true})/60; - }); - - ngModelController.$formatters.push(function(data) { - //convert data from model format (m) to view format (hh:mm) - return HumanTimeConverter.secondsToHumanTime(data*60, - { seconds: 'disabled', - hours: 'enabled' } - ); - }); - } - }; - } -]) - -.directive('osFocusMe', [ - '$timeout', - function ($timeout) { - return { - link: function (scope, element, attrs, model) { - $timeout(function () { - element[0].focus(); - }); - } - }; - } -]) - -.filter('toArray', function(){ - /* - * Transforms an object to an array. Items of the array are the values of - * the object elements. - */ - return function(obj) { - var result = []; - angular.forEach(obj, function(val, key) { - result.push(val); - }); - return result; - }; -}) - -//Mark all core config strings for translation in Javascript -.config([ - 'gettext', - function (gettext) { - gettext('Presentation and assembly system'); - gettext('Event name'); - gettext('OpenSlides is a free ' + - 'web based presentation and assembly system for visualizing ' + - 'and controlling agenda, motions and elections of an ' + - 'assembly.'); - gettext('General'); - gettext('Event'); - gettext('Short description of event'); - gettext('Event date'); - gettext('Event location'); - gettext('Event organizer'); - gettext('Legal notice'); - gettext('Privacy policy'); - gettext('Front page title'); - gettext('Welcome to OpenSlides'); - gettext('Front page text'); - gettext('[Space for your welcome text.]'); - gettext('Allow access for anonymous guest users'); - gettext('Show this text on the login page'); - gettext('Separator used for all csv exports and examples'); - gettext('Page number alignment in PDF'); - gettext('Left'); - gettext('Center'); - gettext('Right'); - gettext('Standard font size in PDF'); - gettext('Show logo on projector'); - gettext('You can replace the logo by uploading an image and set it as ' + - 'the "Projector logo" in "files".'); - gettext('Projector'); - gettext('Projector language'); - gettext('Current browser language'); - gettext('Show title and description of event on projector'); - gettext('Display header and footer'); - gettext('Show the clock on projector'); - gettext('Background color of projector header and footer'); - gettext('Font color of projector header and footer'); - gettext('Font color of projector headline'); - gettext('Predefined seconds of new countdowns'); - gettext('Color for blanked projector'); - gettext('List of speakers overlay'); - gettext('Projector logo'); - gettext('Projector header image'); - gettext('PDF header logo (left)'); - gettext('PDF header logo (right)'); - gettext('PDF footer logo (left)'); - gettext('PDF footer logo (right)'); - gettext('Web interface header logo'); - gettext('PDF ballot paper logo'); - gettext('Custom translations'); - - // Mark the string 'Default projector' here, because it does not appear in the templates. - gettext('Default projector'); - } -]); - -}()); diff --git a/openslides/core/static/js/core/start.js b/openslides/core/static/js/core/start.js deleted file mode 100644 index 6eeba1f58..000000000 --- a/openslides/core/static/js/core/start.js +++ /dev/null @@ -1,117 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.core.start', []) - -.factory('OpenSlides', [ - '$http', - '$rootScope', - '$state', - '$q', - 'DS', - 'autoupdate', - 'operator', - 'Group', - 'mainMenu', - 'ngDialog', - 'LoginDialog', - function($http, $rootScope, $state, $q, DS, autoupdate, operator, Group, mainMenu, ngDialog, LoginDialog) { - var OpenSlides = { - bootup: function () { - $rootScope.openslidesBootstrapDone = false; - $http.get('/users/whoami/').then(function (success) { - $rootScope.guest_enabled = success.data.guest_enabled; - if (success.data.user_id === null && !success.data.guest_enabled) { - // Redirect to login dialog if user is not logged in. - $state.go('login', {guest_enabled: success.data.guest_enabled}); - } else { - autoupdate.newConnect(); - autoupdate.firstMessageDeferred.promise.then(function () { - operator.setUser(success.data.user_id, success.data.user); - $rootScope.operator = operator; - mainMenu.updateMainMenu(); - $rootScope.openslidesBootstrapDone = true; - }); - } - }); - }, - shutdown: function () { - // Close connection, clear the store and show the OS overlay. - autoupdate.closeConnection(); - DS.clear(); - operator.setUser(null); - $rootScope.openslidesBootstrapDone = false; - $rootScope.operator = operator; - // close all open dialogs (except the login dialog) - _.forEach(ngDialog.getOpenDialogs(), function (id) { - if (id !== LoginDialog.id) { - ngDialog.close(id); - } - }); - }, - reboot: function () { - this.shutdown(); - this.bootup(); - }, - }; - - // We need to 'ping' the server with a get request to whoami, because then we can decide, - // if the server is down or respond with a 403 (this cannot be differentiated with websockets) - autoupdate.registerRetryConnectCallback(function () { - return $http.get('/users/whoami').then(function (success) { - if (success.data.user_id === null && !success.data.guest_enabled) { - OpenSlides.shutdown(); - // Redirect to login dialog if user is not logged in. - $state.go('login', {guest_enabled: success.data.guest_enabled}); - } else { - autoupdate.newConnect(); - } - }); - }); - - return OpenSlides; - } -]) - -.run([ - 'OpenSlides', - function (OpenSlides) { - OpenSlides.bootup(); - } -]) - -.run([ - '$rootScope', - '$state', - 'operator', - 'User', - 'Group', - 'mainMenu', - function ($rootScope, $state, operator, User, Group, mainMenu) { - var permissionChangeCallback = function () { - operator.reloadPerms(); - mainMenu.updateMainMenu(); - var stateData = $state.current.data; - var basePerm = stateData ? stateData.basePerm : ''; - $rootScope.baseViewPermissionsGranted = basePerm ? - operator.hasPerms(basePerm) : true; - }; - - $rootScope.$watch(function () { - return Group.lastModified(); - }, function () { - if (Group.getAll().length) { - permissionChangeCallback(); - } - }); - - $rootScope.$watch(function () { - return operator.user ? User.lastModified(operator.user.id) : true; - }, function () { - permissionChangeCallback(); - }); - } -]); - -}()); diff --git a/openslides/core/static/templates/config-form-field.html b/openslides/core/static/templates/config-form-field.html deleted file mode 100644 index 756bd441f..000000000 --- a/openslides/core/static/templates/config-form-field.html +++ /dev/null @@ -1,152 +0,0 @@ -
- - -
- - - - -
- -
- - -
-
- - - - - -
-
- -
-
- - - - - - - - - - - - - - - - - -
-
-
- - - -
- - - -
-
- -
-
- - - - - - - - - -
-

{{ help_text | translate }}

-
diff --git a/openslides/core/static/templates/config.html b/openslides/core/static/templates/config.html deleted file mode 100644 index d670f212a..000000000 --- a/openslides/core/static/templates/config.html +++ /dev/null @@ -1,35 +0,0 @@ -
-
-

Settings

-
-
- -
-
- -
-
- -
-
-
-

{{ subgroup.name | translate }}

-
- -
-
-
-
-
-
-
-
- diff --git a/openslides/core/static/templates/core/checkbox-buttons.html b/openslides/core/static/templates/core/checkbox-buttons.html deleted file mode 100644 index a8feb84ed..000000000 --- a/openslides/core/static/templates/core/checkbox-buttons.html +++ /dev/null @@ -1,14 +0,0 @@ -
-
- -
-
- -
-
diff --git a/openslides/core/static/templates/core/checkbox.html b/openslides/core/static/templates/core/checkbox.html deleted file mode 100644 index da3bf5403..000000000 --- a/openslides/core/static/templates/core/checkbox.html +++ /dev/null @@ -1,7 +0,0 @@ -
- -
diff --git a/openslides/core/static/templates/core/countdown-detail.html b/openslides/core/static/templates/core/countdown-detail.html deleted file mode 100644 index c73252506..000000000 --- a/openslides/core/static/templates/core/countdown-detail.html +++ /dev/null @@ -1,16 +0,0 @@ -
-
-
-
-
{{ countdown.seconds | osSecondsToTime}}
-
-
-
- {{ countdown.description }} -
-
-
-
-
diff --git a/openslides/core/static/templates/core/editor.html b/openslides/core/static/templates/core/editor.html deleted file mode 100644 index 13fd511d8..000000000 --- a/openslides/core/static/templates/core/editor.html +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/openslides/core/static/templates/core/file.html b/openslides/core/static/templates/core/file.html deleted file mode 100644 index a303b22d1..000000000 --- a/openslides/core/static/templates/core/file.html +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/openslides/core/static/templates/core/login-form.html b/openslides/core/static/templates/core/login-form.html deleted file mode 100644 index c33858d59..000000000 --- a/openslides/core/static/templates/core/login-form.html +++ /dev/null @@ -1,30 +0,0 @@ -
- - -
diff --git a/openslides/core/static/templates/core/manage-projectors.html b/openslides/core/static/templates/core/manage-projectors.html deleted file mode 100644 index f9b3829ee..000000000 --- a/openslides/core/static/templates/core/manage-projectors.html +++ /dev/null @@ -1,219 +0,0 @@ -
-
- -

Manage projectors

-
-
- -
-
- -
- - -
-
- - -
- -
- - -
- -
- -
- -
-
- -
- -
- -
-

- {{ resolutions[projector.id].error }} -

-
-
- - - - -
- -
-
-
- -
- - - - - - - - {{ projector.scale }} - - - - {{ projector.scroll }} -
- - -
-
- - -
-
- -
-
-
diff --git a/openslides/core/static/templates/core/password.html b/openslides/core/static/templates/core/password.html deleted file mode 100644 index da77f0bfb..000000000 --- a/openslides/core/static/templates/core/password.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/openslides/core/static/templates/core/projector-controls.html b/openslides/core/static/templates/core/projector-controls.html deleted file mode 100644 index 828321c3f..000000000 --- a/openslides/core/static/templates/core/projector-controls.html +++ /dev/null @@ -1,312 +0,0 @@ -
- -
- -
- - -

Live view

-
-
- -
-
-
- - -
-
- -
- - - Manage - -
-
- - -
- -
-
-
- -
- - - - - - - - {{ active_projector.scale }} - - - - {{ active_projector.scroll }} -
-
-
- - -
- - -

Countdowns

-
-
-
-
- {{ countdown.description }} - Countdown {{ $index +1 }} - - - - - - - -
-
- -    - - - - - - - - - - - - - {{ countdown.seconds | osSecondsToTime }} - - -
-
- - -
-
- -
- -
-
- - -
-
-
- - - Add new countdown - -
-
- - -
- - -

Messages

-
-
-
- -
- {{ 'Message' | translate }} {{ $index + 1 }} - - - -
- -
- -    -
-
-
- - - Add new message - -
-
- -
- - -

List of speakers

-
-
- - - - - -
-
-
- -
diff --git a/openslides/core/static/templates/core/projector-message-form.html b/openslides/core/static/templates/core/projector-message-form.html deleted file mode 100644 index 39acae226..000000000 --- a/openslides/core/static/templates/core/projector-message-form.html +++ /dev/null @@ -1,12 +0,0 @@ -

Edit message

- -
- - - - -
diff --git a/openslides/core/static/templates/core/radio-buttons.html b/openslides/core/static/templates/core/radio-buttons.html deleted file mode 100644 index 528179ed8..000000000 --- a/openslides/core/static/templates/core/radio-buttons.html +++ /dev/null @@ -1,17 +0,0 @@ -
-
- -
-
- -
-
diff --git a/openslides/core/static/templates/core/select-multiple.html b/openslides/core/static/templates/core/select-multiple.html deleted file mode 100644 index 93e9fa1ad..000000000 --- a/openslides/core/static/templates/core/select-multiple.html +++ /dev/null @@ -1,10 +0,0 @@ - - diff --git a/openslides/core/static/templates/core/select-single.html b/openslides/core/static/templates/core/select-single.html deleted file mode 100644 index 67e7978b1..000000000 --- a/openslides/core/static/templates/core/select-single.html +++ /dev/null @@ -1,12 +0,0 @@ - - diff --git a/openslides/core/static/templates/core/slide_clock.html b/openslides/core/static/templates/core/slide_clock.html deleted file mode 100644 index ff12cb007..000000000 --- a/openslides/core/static/templates/core/slide_clock.html +++ /dev/null @@ -1,4 +0,0 @@ -
- - {{ servertime | date:'HH:mm' }} -
diff --git a/openslides/core/static/templates/core/slide_countdown.html b/openslides/core/static/templates/core/slide_countdown.html deleted file mode 100644 index 47d519abd..000000000 --- a/openslides/core/static/templates/core/slide_countdown.html +++ /dev/null @@ -1,9 +0,0 @@ -
-
- {{ countdown.seconds | osSecondsToTime}} -
{{ countdown.description }}
-
-
diff --git a/openslides/core/static/templates/core/slide_message.html b/openslides/core/static/templates/core/slide_message.html deleted file mode 100644 index 0ea8c2a0f..000000000 --- a/openslides/core/static/templates/core/slide_message.html +++ /dev/null @@ -1,4 +0,0 @@ -
-
-
-
diff --git a/openslides/core/static/templates/core/tag-form.html b/openslides/core/static/templates/core/tag-form.html deleted file mode 100644 index d76e8c114..000000000 --- a/openslides/core/static/templates/core/tag-form.html +++ /dev/null @@ -1,17 +0,0 @@ -

Edit tag

-

New tag

- -
- {{ alert.msg }} -
- -
- - - - -
diff --git a/openslides/core/static/templates/core/tag-list.html b/openslides/core/static/templates/core/tag-list.html deleted file mode 100644 index f119f14e7..000000000 --- a/openslides/core/static/templates/core/tag-list.html +++ /dev/null @@ -1,51 +0,0 @@ - - -
-
-
-
- {{ alert.msg }} -
-
-
- -
-
- - - - - - -
- Name - - -
- {{ tag.name }} -
- Edit · - Delete -
-
-
diff --git a/openslides/core/static/templates/csv-import.html b/openslides/core/static/templates/csv-import.html deleted file mode 100644 index 54eb63efe..000000000 --- a/openslides/core/static/templates/csv-import.html +++ /dev/null @@ -1,35 +0,0 @@ -
- -
-
- - - - -
- -

- Accepted file types: - {{ accept }} -

-
- -
- - -
-
- - -

- Autodetection: - {{ autodelimiter }} -

-

Leave empty for autodetection of the separator.

-
-
diff --git a/openslides/core/static/templates/home.html b/openslides/core/static/templates/home.html deleted file mode 100644 index 46e7040e3..000000000 --- a/openslides/core/static/templates/home.html +++ /dev/null @@ -1,9 +0,0 @@ -
-
-

{{ config('general_event_welcome_title') | translate }}

-
-
- -
-
diff --git a/openslides/core/static/templates/index.html b/openslides/core/static/templates/index.html deleted file mode 100644 index c049346e8..000000000 --- a/openslides/core/static/templates/index.html +++ /dev/null @@ -1,279 +0,0 @@ - - - - - -OpenSlides -{{ activeAppTitle | translate }} – OpenSlides - - - - - - - - - - -
- - -
-
-
-
- - - - - - - - - -
-
- - -
- -
- - -
- -
- - -
- - -
- -
- - -
-
- - - -
-
- - - -
-
- -
- - diff --git a/openslides/core/static/templates/legalnotice.html b/openslides/core/static/templates/legalnotice.html deleted file mode 100644 index 68e2e77b9..000000000 --- a/openslides/core/static/templates/legalnotice.html +++ /dev/null @@ -1,32 +0,0 @@ -
-
-

Legal notice

-
-
- -
-
-
-

- - OpenSlides {{ core_version }} - - (License: {{ core_license }}) -

-
-

Installed plugins:

- -
-
diff --git a/openslides/core/static/templates/messaging.html b/openslides/core/static/templates/messaging.html deleted file mode 100644 index f12308dcc..000000000 --- a/openslides/core/static/templates/messaging.html +++ /dev/null @@ -1,10 +0,0 @@ -
-
-
- - - - -
-
-
diff --git a/openslides/core/static/templates/privacypolicy.html b/openslides/core/static/templates/privacypolicy.html deleted file mode 100644 index 3a5293d19..000000000 --- a/openslides/core/static/templates/privacypolicy.html +++ /dev/null @@ -1,9 +0,0 @@ -
-
-

Privacy policy

-
-
- -
-
-
diff --git a/openslides/core/static/templates/privacypolicydialog.html b/openslides/core/static/templates/privacypolicydialog.html deleted file mode 100644 index a314af267..000000000 --- a/openslides/core/static/templates/privacypolicydialog.html +++ /dev/null @@ -1,9 +0,0 @@ -

Privacy policy

-
-
- The event manager hasn't set up a privacy policy yet. -
- - diff --git a/openslides/core/static/templates/projector-button.html b/openslides/core/static/templates/projector-button.html deleted file mode 100644 index ab236c6c2..000000000 --- a/openslides/core/static/templates/projector-button.html +++ /dev/null @@ -1,27 +0,0 @@ -
- - - -
diff --git a/openslides/core/static/templates/projector-container.html b/openslides/core/static/templates/projector-container.html deleted file mode 100644 index 4c58e9777..000000000 --- a/openslides/core/static/templates/projector-container.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - -Projector – OpenSlides -{{ 'Projector' | translate }} {{ projectorName }} – OpenSlides - - - - - - - - -
-
- - -
- -
-
- -
-

{{ error | translate }}

-
-
-
- - diff --git a/openslides/core/static/templates/projector.html b/openslides/core/static/templates/projector.html deleted file mode 100644 index c28cf4f52..000000000 --- a/openslides/core/static/templates/projector.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - - -OpenSlides – Projector - - - - - - - - - - -
- - - - - - - - - - - -
-
-
- - -
- - diff --git a/openslides/core/static/templates/search.html b/openslides/core/static/templates/search.html deleted file mode 100644 index b229c09a0..000000000 --- a/openslides/core/static/templates/search.html +++ /dev/null @@ -1,43 +0,0 @@ -
-
-

Search results

-
-
- -
-
- - - - -
- -
- -
- -
-
-
-

{{ result.verboseName | translate }}

-
- {{ result.elements.length}} results -
-
    -
  1. - - {{ element.getSearchResultName() }} - - - {{ element.getSearchResultName() }} - -
-
-
-

No results.

- -
diff --git a/openslides/core/views.py b/openslides/core/views.py index 081d2dbc7..383962a55 100644 --- a/openslides/core/views.py +++ b/openslides/core/views.py @@ -1,21 +1,23 @@ -import json +import os import uuid -from textwrap import dedent -from typing import Any, Dict, List, cast +from typing import Any, Dict, List -from django.apps import apps from django.conf import settings +from django.contrib.staticfiles import finders +from django.contrib.staticfiles.views import serve from django.db.models import F from django.http import Http404, HttpResponse from django.utils.timezone import now from django.utils.translation import ugettext as _ +from django.views import static +from django.views.generic.base import View from mypy_extensions import TypedDict from .. import __license__ as license, __url__ as url, __version__ as version from ..utils import views as utils_views +from ..utils.arguments import arguments from ..utils.auth import anonymous_is_enabled, has_perm from ..utils.autoupdate import inform_changed_data, inform_deleted_data -from ..utils.constants import get_constants from ..utils.plugins import ( get_plugin_description, get_plugin_license, @@ -53,132 +55,38 @@ from .models import ( # Special Django views -class IndexView(utils_views.CSRFMixin, utils_views.TemplateView): +class IndexView(View): """ - The primary view for OpenSlides using AngularJS. - - The default base template is 'openslides/core/static/templates/index.html'. - You can override it by simply adding a custom 'templates/index.html' file - to the custom staticfiles directory. See STATICFILES_DIRS in settings.py. + The primary view for the OpenSlides client. Serves static files. If a file + does not exist or a directory is requested, the index.html is delivered instead. """ - template_name = 'templates/index.html' - -class ProjectorView(utils_views.TemplateView): - """ - The primary view for OpenSlides projector using AngularJS. - - The projector container template is 'openslides/core/static/templates/projector-container.html'. - This container is for controlling the projector resolution. - """ - template_name = 'templates/projector-container.html' - - -class RealProjectorView(utils_views.TemplateView): - """ - The original view without resolutioncontrol for OpenSlides projector using AngularJS. - - The default base template is 'openslides/core/static/templates/projector.html'. - You can override it by simply adding a custom 'templates/projector.html' - file to the custom staticfiles directory. See STATICFILES_DIRS in - settings.py. - """ - template_name = 'templates/projector.html' - - -class WebclientJavaScriptView(utils_views.View): - """ - This view returns JavaScript code for the main entry point in the - AngularJS app for the requested realm (site or projector). Also code - for plugins is appended. The result is not uglified. - """ cache: Dict[str, str] = {} + """ + Saves the path to the index.html. + + May be extended later to cache every template. + """ def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) - if 'site' not in self.cache: - self.init_cache('site') - if 'projector' not in self.cache: - self.init_cache('projector') + no_caching = arguments.get('no_template_caching', False) + if 'index' not in self.cache or no_caching: + self.cache['index'] = finders.find('index.html') - def init_cache(self, realm: str) -> None: - angular_modules: List[str] = [] - js_files: List[str] = [] - for app_config in apps.get_app_configs(): - # Add the angular app if the module has one. - if getattr(app_config, 'angular_{}_module'.format(realm), False): - angular_modules.append('OpenSlidesApp.{app_name}.{realm}'.format( - app_name=app_config.label, - realm=realm)) + self.index_document_root, self.index_path = os.path.split(self.cache['index']) - # Add all JavaScript files that the module needs. Our core apps - # are delivered by an extra file js/openslides.js which can be - # created via gulp. - core_apps = ( - 'openslides.core', - 'openslides.agenda', - 'openslides.motions', - 'openslides.assignments', - 'openslides.users', - 'openslides.mediafiles', - ) - if app_config.name not in core_apps: - try: - app_js_files = app_config.js_files - except AttributeError: - # The app needs no JavaScript files. - pass - else: - js_files.extend(app_js_files) - - # angular constants - angular_constants = '' - for key, value in get_constants().items(): - value = json.dumps(value) - angular_constants += ".constant('{}', {})".format(key, value) - - # Use JavaScript loadScript function from - # http://balpha.de/2011/10/jquery-script-insertion-and-its-consequences-for-debugging/ - # jQuery is required. - content = dedent( - """ - (function () { - var loadScript = function (path) { - var result = $.Deferred(), - script = document.createElement("script"); - script.async = "async"; - script.type = "text/javascript"; - script.src = path; - script.onload = script.onreadystatechange = function(_, isAbort) { - if (!script.readyState || /loaded|complete/.test(script.readyState)) { - if (isAbort) - result.reject(); - else - result.resolve(); - } - }; - script.onerror = function () { result.reject(); }; - $("head")[0].appendChild(script); - return result.promise(); - }; - """ + - """ - angular.module('OpenSlidesApp.{realm}', {angular_modules}){angular_constants}; - var deferres = []; - {js_files}.forEach( function(js_file) {{ deferres.push(loadScript(js_file)); }} ); - $.when.apply(this,deferres).done( function() {{ - angular.bootstrap(document,['OpenSlidesApp.{realm}']); - }} ); - """.format(realm=realm, angular_modules=angular_modules, angular_constants=angular_constants, js_files=js_files) + - """ - }()); - """).replace('\n', '') - self.cache[realm] = content - - def get(self, *args: Any, **kwargs: Any) -> HttpResponse: - realm = cast(str, kwargs.get('realm')) # Result is 'site' or 'projector' - return HttpResponse(self.cache[realm], content_type='application/javascript') + def get(self, request, path, **kwargs) -> HttpResponse: + """ + Tries to serve the requested file. If it is not found or a directory is + requested, the index.html is delivered. + """ + try: + response = serve(request, path, **kwargs) + except Http404: + response = static.serve(request, self.index_path, document_root=self.index_document_root, **kwargs) + return response # Viewsets for the REST API diff --git a/openslides/mediafiles/static/css/mediafiles/_image-browser.scss b/openslides/mediafiles/static/css/mediafiles/_image-browser.scss deleted file mode 100644 index 89964deb1..000000000 --- a/openslides/mediafiles/static/css/mediafiles/_image-browser.scss +++ /dev/null @@ -1,67 +0,0 @@ -/** image plugin for CKEditor **/ -#imageBrowserContainer { - .imageTable { - table-layout: fixed; - width: 100%; - } - - #imagePreviewSection { - position: absolute; - margin: 0px 20px 20px 20px; - - input { - width: 65px; - } - - .hidden { - display: none; - } - - i { - font-size: 130%; - cursor: pointer; - } - - > div { - margin-bottom: 10px; - } - } - - #imagePreview { - max-width: 400px; - max-height: 300px; - overflow: auto; - padding: 2px; - border: 3px solid #317796; - - img[src=""] { - display: none; - } - } - - #imageBrowser { - max-height: 500px; - overflow: auto; - - .image { - position: relative; - float: left; - width: 75px; - height: 75px; - margin: 5px; - background-size: 125%; - background-repeat: no-repeat; - background-position: center center; - border: 2px solid #bed4de; - cursor: pointer; - - &:hover { - border-color: #317796; - } - - &.selected { - border-color: #317796; - } - } - } -} diff --git a/openslides/mediafiles/static/css/mediafiles/_projector.scss b/openslides/mediafiles/static/css/mediafiles/_projector.scss deleted file mode 100644 index b2f1a2ded..000000000 --- a/openslides/mediafiles/static/css/mediafiles/_projector.scss +++ /dev/null @@ -1,43 +0,0 @@ -/* PDF presentation */ -.rotate0 { - transform: rotate(0deg); -} -.rotate90 { - transform: rotate(90deg); -} -.rotate180 { - transform: rotate(180deg); -} -.rotate270 { - transform: rotate(270deg); -} - -/* Video and Image projection */ -img.projector-image { - width: 100%; -} - -div.projector-image { - width: 100%; - height: 100%; - background-size: contain; - background-repeat: no-repeat; - background-position: 50% 50%; - background-color: #fff; -} - -.video-container { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - - & > * { - flex: 1 1 auto; - max-width: 100%; - max-height: 100%; - width: 100%; - height: 100%; - } -} diff --git a/openslides/mediafiles/static/css/mediafiles/_site.scss b/openslides/mediafiles/static/css/mediafiles/_site.scss deleted file mode 100644 index 81a9f3aaa..000000000 --- a/openslides/mediafiles/static/css/mediafiles/_site.scss +++ /dev/null @@ -1,23 +0,0 @@ -@import "image-browser"; - -#mediafile-table { - .icon-column { - width: 10%; - } - .title-column { - padding: 0px 10px; - width: 90%; - } -} - -#dropzone { - padding: 20px 10px; - border-radius: 4px; - border: 1px solid #e6e8eb; - text-align: center; - background-color: #fff; - - &.dragover { - border-color: #317796; - } -} diff --git a/openslides/mediafiles/static/js/mediafiles/forms.js b/openslides/mediafiles/static/js/mediafiles/forms.js deleted file mode 100644 index 8e9124609..000000000 --- a/openslides/mediafiles/static/js/mediafiles/forms.js +++ /dev/null @@ -1,73 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.mediafiles.forms', [ - 'gettext', - 'ngFileUpload', - 'ui.router', - //TODO: Add deps for operator, User -]) - -// Service for mediafile form -.factory('MediafileForm', [ - 'gettextCatalog', - 'operator', - 'User', - function (gettextCatalog, operator, User) { - return { - // ngDialog for mediafile form - getDialog: function (mediafile) { - var dialog = { - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - }; - if (mediafile) { - dialog.template = 'static/templates/mediafiles/mediafile-form.html'; - dialog.controller = 'MediafileUpdateCtrl'; - dialog.resolve = { - mediafileId: function () {return mediafile ? mediafile.id : void 0;} - }; - } else { - dialog.template = 'static/templates/mediafiles/mediafile-upload-form.html'; - dialog.controller = 'MediafileUploadCtrl'; - } - return dialog; - }, - getFormFields: function () { - return [ - { - key: 'title', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Title'), - }, - }, - { - key: 'hidden', - type: 'checkbox', - templateOptions: { - label: gettextCatalog.getString('Hidden'), - }, - hide: !operator.hasPerms('mediafiles.can_see_hidden'), - }, - { - key: 'uploader_id', - type: 'select-single', - templateOptions: { - label: gettextCatalog.getString('Uploaded by'), - options: User.getAll(), - ngOptions: 'option.id as option.full_name for option in to.options', - placeholder: gettextCatalog.getString('Select or search a participant ...') - }, - hide: !operator.hasPerms('mediafiles.can_manage') - }, - ]; - - } - }; - } -]); - -}()); diff --git a/openslides/mediafiles/static/js/mediafiles/image-plugin.js b/openslides/mediafiles/static/js/mediafiles/image-plugin.js deleted file mode 100644 index 169397204..000000000 --- a/openslides/mediafiles/static/js/mediafiles/image-plugin.js +++ /dev/null @@ -1,206 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.mediafiles.image-plugin', [ - 'OpenSlidesApp.mediafiles.resources', - 'gettext', - 'OpenSlidesApp.core', -]) - -.factory('ImageBrowserPlugin', [ - '$templateCache', - 'Mediafile', - 'gettextCatalog', - 'Editor', - function ($templateCache, Mediafile, gettextCatalog, Editor) { - return { - getPlugin: function () { - return { - init: function (editor) { - CKEDITOR.tools.imagebrowser = {}; - - // Initialize this dialog, if it is opened. - editor.on('dialogShow', function (event) { - var dialog = event.data; - if (dialog.getName() === 'imagebrowser-dialog') { - CKEDITOR.dialog.getCurrent().disableButton('ok'); - - // Load the main plugin template and paste it into the container - var template = $templateCache.get('mediafiles/static/templates/mediafiles/image-plugin.html'); - if (!template) { - throw 'Template for image plugin not found!'; - } - $('#imageBrowserContainer').html(template); - - // Load all images. - var images = ''; - _.forEach(Mediafile.getAllImages(), function (image) { - images += '
'; - }); - $('#imageBrowser').html(images); - - // Translate some strings. Angular tags are not available in CKEditor. - $('#scaleLabel').html(gettextCatalog.getString('Scale')); - - // If the dialog was opened via double click, check the selected element. It - // may be an image, so preselect it. - var selectedElement = editor.getSelection().getStartElement(); - if (selectedElement.is('img')) { - // Check for given scale of this image. - var styleAttr = $(selectedElement).attr('style'); - var scale; - var scaleRegex = /width\s*:\s*(\d+)\s*%/g; - var scaleMatch = scaleRegex.exec(styleAttr); - if (scaleMatch) { - scale = parseInt(scaleMatch[1]); - } - CKEDITOR.tools.imagebrowser.selectImage( - selectedElement.getAttribute('src'), scale); - } - // Setup event listeners. - $('#image-scale').bind('keyup mouseup', function (event) { - var scale = parseInt($('#image-scale').val()); - if (scale !== CKEDITOR.tools.imagebrowser.scale) { - CKEDITOR.tools.imagebrowser.updateImageSize(scale); - } - }); - - // Hack: call the resize event, so the dom does an update. - CKEDITOR.tools.callFunction(224, {}); - } - }); - // React on double clicks in the textarea. If an image was selected, open this dialog. - editor.on('doubleclick', function (event) { - var element = event.data.element; - if (!element.isReadOnly()) { - if (element.is('img')) { - event.data.dialog = 'imagebrowser-dialog'; - editor.getSelection().selectElement(element); - } - } - }); - // Handler for selecting an image. It may be called by clicking on a thumbnail or by - // just giving the url. The scale is optional. - CKEDITOR.tools.imagebrowser.selectImage = function (url, scale) { - var browser = $('#imageBrowser'); - _.forEach(browser.children(), function (child) { // check every available image - if (child.getAttribute('data-image') == url) { //match - child.classList.add('selected'); - var image = $('#imagePreview img'); - // Setup an load event handler, so we can get the size of the image when loaded. - image.on('load', function (event) { - var w = event.target.naturalWidth; - var h = event.target.naturalHeight; - $('#originalSizeText').html(gettextCatalog.getString('Original size') + - ': ' + w + ' × ' + h ); - $('#fullSizeContainer').width(w).height(h); - if (scale !== undefined) { - // Use custom scale. - CKEDITOR.tools.imagebrowser.updateImageSize(scale); - } else { - CKEDITOR.tools.imagebrowser.updateImageSize(100); - } - }); - // Set the url of the main preview image. - image.attr('src', url); - $('#imagePreviewSection').removeClass('hidden'); - CKEDITOR.tools.imagebrowser.selected = url; - } else { - // Wrong image, deselect it in the preview window. - child.classList.remove('selected'); - } - }); - }; - // Handler for updateing the image size. - CKEDITOR.tools.imagebrowser.updateImageSize = function (scale) { - if (isNaN(scale) || scale <= 0) { - CKEDITOR.dialog.getCurrent().disableButton('ok'); - } else { - CKEDITOR.dialog.getCurrent().enableButton('ok'); - CKEDITOR.tools.imagebrowser.scale = scale; - $('#imagePreview img').width(scale + '%'); - $('#image-scale').val(scale); - } - }; - // Insert the selected image into the textarea. - CKEDITOR.tools.imagebrowser.insertImage = function (url, scale) { - var editor = CKEDITOR.currentInstance; - var dialog = CKEDITOR.dialog.getCurrent(); - var html = '' + url + ''; - editor.config.allowedContent = true; - editor.insertHtml(html.trim()); - dialog.hide(); - }; - editor.addCommand('imagebrowser-open', new CKEDITOR.dialogCommand('imagebrowser-dialog')); - // By naming the button 'image', it gets the same image as the original image button. - editor.ui.addButton('image', { - label: gettextCatalog.getString('Open image browser'), - command: 'imagebrowser-open', - toolbar: 'insert', - }); - }, - }; - }, - getDialog: function () { - return function (editor) { - return { - title: gettextCatalog.getString('Image browser'), - minWidth: 1000, - minHeight: 500, - contents: [ - { - id: 'imagebrowser-tab1', - label: gettextCatalog.getString('Browse for images'), - elements: [ - { - type: 'html', - align: 'left', - id: 'titleid', - style: 'font-size: 20px; font-weight: bold;', - html: gettextCatalog.getString('Browse for images'), - }, { - type: 'html', - align: 'left', - id: 'msg', - style: '', - html: '
' - } - ], - }, - ], - // insert image on OK. - onOk: function (event) { - var url = CKEDITOR.tools.imagebrowser.selected; - if (url) { - var scale = CKEDITOR.tools.imagebrowser.scale; - CKEDITOR.tools.imagebrowser.insertImage(url, scale); - } - }, - }; - }; - }, - }; - } -]) - -.run([ - 'Editor', - 'ImageBrowserPlugin', - 'gettext', - function (Editor, ImageBrowserPlugin, gettext) { - Editor.registerDialog('imagebrowser-dialog', ImageBrowserPlugin.getDialog()); - Editor.registerPlugin('imagebrowser', ImageBrowserPlugin.getPlugin()); - - // mark all plugin strings - gettext('Original size'); - gettext('Scale'); - gettext('Image browser'); - gettext('Browse for images'); - } -]); - -}()); diff --git a/openslides/mediafiles/static/js/mediafiles/list.js b/openslides/mediafiles/static/js/mediafiles/list.js deleted file mode 100644 index 74231b35e..000000000 --- a/openslides/mediafiles/static/js/mediafiles/list.js +++ /dev/null @@ -1,293 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.mediafiles.list', [ - 'gettext', - 'ngDialog', - 'OpenSlidesApp.mediafiles.forms', - 'OpenSlidesApp.mediafiles.resources', - //TODO: Add deps for operator, User, Projector, ProjectionDefault, osTableFilter, osTableSort, -]) - -.controller('MediafileListCtrl', [ - '$http', - '$scope', - 'gettext', - 'ngDialog', - 'osTableFilter', - 'osTableSort', - 'osTablePagination', - 'ProjectionDefault', - 'Projector', - 'User', - 'Mediafile', - 'MediafileForm', - 'Logos', - 'Fonts', - function ($http, $scope, gettext, ngDialog, osTableFilter, osTableSort, osTablePagination, - ProjectionDefault, Projector, User, Mediafile, MediafileForm, Logos, Fonts) { - $scope.$watch(function () { - return Mediafile.lastModified(); - }, function () { - $scope.mediafiles = _.orderBy(Mediafile.getAll(), ['title']); - }); - User.bindAll({}, $scope, 'users'); - $scope.$watch(function() { - return Projector.lastModified(); - }, function() { - $scope.projectors = Projector.getAll(); - updatePresentedMediafiles(); - var projectiondefault = ProjectionDefault.filter({name: 'mediafiles'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId = projectiondefault.projector_id; - } - }); - - function updatePresentedMediafiles () { - $scope.presentedMediafiles = []; - Projector.getAll().forEach(function (projector) { - var projectorElements = _.map(projector.elements, function(element) { return element; }); - var mediaElements = _.filter(projectorElements, function (element) { - return element.name === 'mediafiles/mediafile'; - }); - mediaElements.forEach(function (element) { - $scope.presentedMediafiles.push(element); - }); - }); - if ($scope.presentedMediafiles.length) { - $scope.isMeta = false; - } else { - $scope.isMeta = true; - } - } - - updatePresentedMediafiles(); - - // Filtering - $scope.filter = osTableFilter.createInstance('MediafilesTableFilter'); - - if (!$scope.filter.existsStorageEntry()) { - $scope.filter.booleanFilters = { - isHidden: { - value: undefined, - displayName: gettext('Hidden'), - choiceYes: gettext('Is hidden'), - choiceNo: gettext('Is not hidden'), - needExtraPermission: true, - }, - isPdf: { - value: undefined, - displayName: gettext('Is PDF'), - choiceYes: gettext('Is PDF file'), - choiceNo: gettext('Is no PDF file'), - }, - }; - } - $scope.filter.propertyList = ['title_or_filename']; - $scope.filter.propertyFunctionList = [ - function (mediafile) {return mediafile.uploader.get_short_name();}, - function (mediafile) {return mediafile.mediafile.type;}, - function (mediafile) {return mediafile.mediafile.name;}, - ]; - // Sorting - $scope.sort = osTableSort.createInstance('MediafileTableSort'); - if (!$scope.sort.column) { - $scope.sort.column = 'title_or_filename'; - } - $scope.sortOptions = [ - {name: 'title_or_filename', - display_name: gettext('Title')}, - {name: 'mediafile.type', - display_name: gettext('Type')}, - {name: 'filesize', - display_name: gettext('File size')}, - {name: 'timestamp', - display_name: gettext('Upload time')}, - {name: 'uploader.get_short_name()', - display_name: gettext('Uploaded by')}, - ]; - - // pagination - $scope.pagination = osTablePagination.createInstance('MediafileTablePagination'); - - // open new/edit dialog - $scope.openDialog = function (mediafile) { - ngDialog.open(MediafileForm.getDialog(mediafile)); - }; - - // *** select mode functions *** - $scope.isSelectMode = false; - // check all checkboxes - $scope.checkAll = function () { - $scope.selectedAll = !$scope.selectedAll; - _.forEach($scope.mediafiles, function (mediafile) { - mediafile.selected = $scope.selectedAll; - }); - }; - // uncheck all checkboxes if SelectMode is closed - $scope.uncheckAll = function () { - if (!$scope.isSelectMode) { - $scope.selectedAll = false; - _.forEach($scope.mediafiles, function (mediafile) { - mediafile.selected = false; - }); - } - }; - // delete all selected mediafiles - $scope.deleteMultiple = function () { - angular.forEach($scope.mediafiles, function (mediafile) { - if (mediafile.selected) - Mediafile.destroy(mediafile.id); - }); - $scope.isSelectMode = false; - $scope.uncheckAll(); - }; - // delete single mediafile - $scope.delete = function (mediafile) { - Mediafile.destroy(mediafile.id); - }; - - // ** PDF presentation functions **/ - // show document on projector - $scope.showMediafile = function (projectorId, mediafile) { - var isProjectedIds = mediafile.isProjected(); - _.forEach(isProjectedIds, function (id) { - $http.post('/rest/core/projector/' + id + '/clear_elements/'); - }); - if (_.indexOf(isProjectedIds, projectorId) == -1) { - var postUrl = '/rest/core/projector/' + projectorId + '/prune_elements/'; - var data = [{ - name: 'mediafiles/mediafile', - id: mediafile.id, - numPages: mediafile.mediafile.pages, - page: 1, - scale: 'page-fit', - rotate: 0, - visible: true, - playing: false, - fullscreen: mediafile.is_pdf || mediafile.is_image - }]; - $http.post(postUrl, data); - } - }; - - var sendMediafileCommand = function (mediafile, data) { - var updateData = _.extend({}, mediafile); - _.extend(updateData, data); - var postData = {}; - postData[mediafile.uuid] = updateData; - - // Find Projector where the mediafile is projected - $scope.projectors.forEach(function (projector) { - if (_.find(projector.elements, function (e) {return e.uuid == mediafile.uuid;})) { - $http.post('/rest/core/projector/' + projector.id + '/update_elements/', postData); - } - }); - }; - - $scope.getTitle = function (mediafile) { - return Mediafile.get(mediafile.id).title; - }; - - $scope.getType = function (presentedMediafile) { - var mediafile = Mediafile.get(presentedMediafile.id); - return mediafile.is_pdf ? 'pdf' : mediafile.is_image ? 'image' : 'video'; - }; - - $scope.mediafileGoToPage = function (mediafile, page) { - page = parseInt(page); - if (page > 0 && page <= mediafile.numPages) { - sendMediafileCommand( - mediafile, - {page: page} - ); - } - }; - $scope.mediafileZoomIn = function (mediafile) { - var scale = 1; - if (parseFloat(mediafile.scale)) { - scale = mediafile.scale; - } - sendMediafileCommand( - mediafile, - {scale: scale + 0.2} - ); - }; - $scope.mediafileFit = function (mediafile) { - sendMediafileCommand( - mediafile, - {scale: 'page-fit'} - ); - }; - $scope.mediafileZoomOut = function (mediafile) { - var scale = 1; - if (parseFloat(mediafile.scale)) { - scale = mediafile.scale; - } - sendMediafileCommand( - mediafile, - {scale: scale - 0.2} - ); - }; - $scope.mediafileChangePage = function (mediafile, pageNum) { - sendMediafileCommand( - mediafile, - {pageToDisplay: pageNum} - ); - }; - $scope.mediafileRotate = function (mediafile) { - var rotation = mediafile.rotate; - if (rotation === 270) { - rotation = 0; - } else { - rotation = rotation + 90; - } - sendMediafileCommand( - mediafile, - {rotate: rotation} - ); - }; - $scope.mediafileToggleFullscreen = function (mediafile) { - sendMediafileCommand( - mediafile, - {fullscreen: !mediafile.fullscreen} - ); - }; - $scope.mediafileTogglePlaying = function (mediafile) { - sendMediafileCommand( - mediafile, - {playing: !mediafile.playing} - ); - }; - - /** Logos and fonts **/ - $scope.logos = Logos.getAll(); - $scope.fonts = Fonts.getAll(); - $scope.hasProjectorHeaderLogo = function (mediafile) { - return _.some(mediafile.getLogos(), function (logo) { - return logo.key === 'logo_projector_header'; - }); - }; - } -]) - -/* - * Special filter only for mediafile list view. - */ -.filter('hiddenFilter', [ - '$filter', - 'operator', - function ($filter, operator) { - return function (array) { - if (operator.hasPerms('mediafiles.can_see_hidden')) { - return array; - } - return Array.prototype.filter.call(array, function (item) { - return !item.hidden; - }); - }; - } -]); - -}()); diff --git a/openslides/mediafiles/static/js/mediafiles/projector.js b/openslides/mediafiles/static/js/mediafiles/projector.js deleted file mode 100644 index 175377ae8..000000000 --- a/openslides/mediafiles/static/js/mediafiles/projector.js +++ /dev/null @@ -1,70 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.mediafiles.projector', [ - 'OpenSlidesApp.mediafiles.resources', - //TODO: Add deps for slidesProvider -]) - -.config([ - 'slidesProvider', - function (slidesProvider) { - slidesProvider.registerSlide('mediafiles/mediafile', { - template: 'static/templates/mediafiles/slide_mediafile.html' - }); - } -]) - -.controller('SlideMediafileCtrl', [ - '$scope', - '$timeout', - 'Mediafile', - function ($scope, $timeout, Mediafile) { - // load mediafile object - Mediafile.bindOne($scope.element.id, $scope, 'mediafile'); - - $scope.showPdf = true; - - // Watch for page changes in the projector element. Adjust the page - // in the canvas scope, so the viewer can change the size automatically. - $scope.$watch(function () { - return $scope.element.page; - }, function () { - var canvasScope = angular.element('#pdf-canvas').scope(); - if (canvasScope) { - canvasScope.pageNum = $scope.element.page; - } - }); - - // Watch for scale changes. If the scale is changed, reload the pdf - // viewer by just disable and re-enable it. - $scope.$watch(function () { - return $scope.element.scale; - }, function () { - $scope.showPdf = false; - $timeout(function () { - $scope.showPdf = true; - }, 1); - }); - - // Allow the elements to render properly - setTimeout(function() { - if ($scope.mediafile) { - if ($scope.mediafile.is_pdf) { - $scope.pdfName = $scope.mediafile.title; - $scope.pdfUrl = $scope.mediafile.mediafileUrl; - } else if ($scope.mediafile.is_video) { - var player = angular.element.find('#video-player')[0]; - if ($scope.element.playing) { - player.play(); - } else { - player.pause(); - } - } - } - }, 0); - } -]); - -}()); diff --git a/openslides/mediafiles/static/js/mediafiles/resources.js b/openslides/mediafiles/static/js/mediafiles/resources.js deleted file mode 100644 index 4c7a8b3e9..000000000 --- a/openslides/mediafiles/static/js/mediafiles/resources.js +++ /dev/null @@ -1,161 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.mediafiles.resources', [ - 'gettext', - 'js-data', - //TODO: Add deps for jsDataModel -]) - -.factory('Mediafile', [ - 'DS', - 'gettext', - 'jsDataModel', - 'Logos', - 'Fonts', - function (DS, gettext, jsDataModel, Logos, Fonts) { - var name = 'mediafiles/mediafile'; - return DS.defineResource({ - name: name, - useClass: jsDataModel, - verboseName: gettext('Files'), - verboseNamePlural: gettext('Files'), - getAllImages: function () { - var images = []; - angular.forEach(this.getAll(), function(file) { - if (file.is_image) { - images.push({title: file.title, value: file.mediafileUrl}); - } - }); - return images; - }, - methods: { - getResourceName: function () { - return name; - }, - // link name which is shown in search result - getSearchResultName: function () { - return this.title; - }, - // return true if a specific relation matches for given searchquery - // (here: speakers) - hasSearchResult: function (results) { - var mediafile = this; - // search for speakers (check if any user.id from already found users matches) - return _.some(results, function(result) { - if ((result.getResourceName() === "users/user") && - (mediafile.uploader_id === result.id)) { - return true; - } - }); - }, - isUsedAsLogo: function () { - var mediafile = this; - return _.find(Logos.getAll(), function (logoPlaceholder) { - return logoPlaceholder.path === mediafile.mediafileUrl; - }); - }, - canBeUsedAsLogo: function () { - return this.is_image; - }, - getLogos: function () { - var mediafile = this; - return _.filter(Logos.getAll(), function (logoPlaceholder) { - return logoPlaceholder.path === mediafile.mediafileUrl; - }); - }, - hasLogo: function (logo) { - var allUrls = _.map(this.getLogos(), function (logo) { - return logo.path; - }); - return _.includes(allUrls, logo.path); - }, - toggleLogo: function (logo) { - if (this.hasLogo(logo)) { - Logos.set(logo.key); - } else { - Logos.set(logo.key, this.mediafileUrl); - } - }, - isUsedAsFont: function () { - var mediafile = this; - return _.find(Fonts.getAll(), function (font) { - return font.path === mediafile.mediafileUrl; - }); - }, - canBeUsedAsFont: function () { - return this.is_font; - }, - getFonts: function () { - var mediafile = this; - return _.filter(Fonts.getAll(), function (font) { - return font.path === mediafile.mediafileUrl; - }); - }, - hasFont: function (font) { - var allUrls = _.map(this.getFonts(), function (font) { - return font.path; - }); - return _.includes(allUrls, font.path); - }, - toggleFont: function (font) { - if (this.hasFont(font)) { - Fonts.set(font.key); - } else { - Fonts.set(font.key, this.mediafileUrl); - } - }, - }, - computed: { - is_pdf: ['filetype', function (filetype) { - var PDF_FILE_TYPES = ['application/pdf']; - return _.includes(PDF_FILE_TYPES, filetype); - }], - is_image: ['filetype', function (filetype) { - var IMAGE_FILE_TYPES = ['image/png', 'image/jpeg', 'image/gif']; - return _.includes(IMAGE_FILE_TYPES, filetype); - }], - is_video: ['filetype', function (filetype) { - var VIDEO_FILE_TYPES = [ 'video/quicktime', 'video/mp4', 'video/webm', - 'video/ogg', 'video/x-flv', 'application/x-mpegURL', 'video/MP2T', - 'video/3gpp', 'video/x-msvideo', 'video/x-ms-wmv', 'video/x-matroska' ]; - return _.includes(VIDEO_FILE_TYPES, filetype); - }], - is_presentable: ['is_pdf', 'is_image', 'is_video', function (is_pdf, is_image, is_video) { - return (is_pdf && !this.mediafile.encrypted) || is_image || is_video; - }], - is_font: [function () { - var FONT_FILE_EXTENSIONS = ['ttf', 'woff']; - var ext = _.last(this.mediafile.name.split('.')); - return _.includes(FONT_FILE_EXTENSIONS, ext); - }], - mediafileUrl: [function () { - return this.media_url_prefix + this.mediafile.name; - }], - filename: [function () { - var filename = this.mediafile.name; - return /\/(.+?)$/.exec(filename)[1]; - }], - filetype: [function () { - return this.mediafile.type || gettext('undefined'); - }], - title_or_filename: ['title', 'mediafile', function (title) { - return title || this.filename; - }] - }, - relations: { - belongsTo: { - 'users/user': { - localField: 'uploader', - localKey: 'uploader_id', - } - } - } - }); - } -]) - -.run(['Mediafile', function (Mediafile) {}]); - -}()); diff --git a/openslides/mediafiles/static/js/mediafiles/site.js b/openslides/mediafiles/static/js/mediafiles/site.js deleted file mode 100644 index 5eda99266..000000000 --- a/openslides/mediafiles/static/js/mediafiles/site.js +++ /dev/null @@ -1,13 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.mediafiles.site', [ - 'OpenSlidesApp.mediafiles.list', - 'OpenSlidesApp.mediafiles.states', - 'OpenSlidesApp.mediafiles.update', - 'OpenSlidesApp.mediafiles.upload', - 'OpenSlidesApp.mediafiles.image-plugin', -]); - -}()); diff --git a/openslides/mediafiles/static/js/mediafiles/states.js b/openslides/mediafiles/static/js/mediafiles/states.js deleted file mode 100644 index c8c806781..000000000 --- a/openslides/mediafiles/static/js/mediafiles/states.js +++ /dev/null @@ -1,60 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.mediafiles.states', [ - 'gettext', - 'ui.router', - //TODO: Add deps for mainMenuProvider -]) - -.config([ - 'gettext', - 'mainMenuProvider', - function (gettext, mainMenuProvider) { - mainMenuProvider.register({ - 'ui_sref': 'mediafiles.mediafile.list', - 'img_class': 'paperclip', - 'title': gettext('Files'), - 'weight': 600, - 'perm': 'mediafiles.can_see', - }); - } -]) - -.config([ - 'SearchProvider', - 'gettext', - function (SearchProvider, gettext) { - SearchProvider.register({ - 'verboseName': gettext('Files'), - 'collectionName': 'mediafiles/mediafile', - 'urlDetailState': 'mediafiles.mediafile.detail', - 'weight': 600, - }); - } -]) - -.config([ - 'gettext', - '$stateProvider', - function (gettext, $stateProvider) { - $stateProvider - .state('mediafiles', { - url: '/mediafiles', - abstract: true, - template: "", - data: { - title: gettext('Files'), - basePerm: 'mediafiles.can_see', - }, - }) - .state('mediafiles.mediafile', { - abstract: true, - template: "", - }) - .state('mediafiles.mediafile.list', {}); - } -]); - -}()); diff --git a/openslides/mediafiles/static/js/mediafiles/update.js b/openslides/mediafiles/static/js/mediafiles/update.js deleted file mode 100644 index 8243753f6..000000000 --- a/openslides/mediafiles/static/js/mediafiles/update.js +++ /dev/null @@ -1,51 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.mediafiles.update', [ - 'OpenSlidesApp.mediafiles.resources', - //TODO: Add deps for operator, User -]) - -.controller('MediafileUpdateCtrl', [ - '$scope', - 'operator', - 'User', - 'Mediafile', - 'mediafileId', - 'MediafileForm', - 'ErrorMessage', - function ($scope, operator, User, Mediafile, mediafileId, MediafileForm, ErrorMessage) { - $scope.alert = {}; - $scope.formFields = MediafileForm.getFormFields(); - - // set initial values for form model by create deep copy of motion object - // so list/detail view is not updated while editing - $scope.model = angular.copy(Mediafile.get(mediafileId)); - - // save mediafile - $scope.save = function (mediafile) { - // reset title and uploader_id if empty - if (!mediafile.title) { - mediafile.title = mediafile.filename; - } - if (!mediafile.uploader_id) { - mediafile.uploader_id = operator.user.id; - } - // inject the changed mediafile (copy) object back into DS store - Mediafile.inject(mediafile); - // save change mediafile object on server - Mediafile.save(mediafile).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - Mediafile.refresh(mediafile); - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]); - -}()); diff --git a/openslides/mediafiles/static/js/mediafiles/upload.js b/openslides/mediafiles/static/js/mediafiles/upload.js deleted file mode 100644 index 53c2dc007..000000000 --- a/openslides/mediafiles/static/js/mediafiles/upload.js +++ /dev/null @@ -1,154 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.mediafiles.upload', [ - 'OpenSlidesApp.mediafiles.forms', - 'ngFileUpload', -]) - -.controller('MediafileUploadCtrl', [ - '$scope', - '$q', - 'User', - 'Upload', - 'operator', - 'gettextCatalog', - 'ErrorMessage', - function ($scope, $q, User, Upload, operator, gettextCatalog, ErrorMessage) { - User.bindAll({}, $scope, 'users'); - $scope.alert = {}; - $scope.files = []; - $scope.uploading = false; - var idCounter = 0; // Used for uniqly identifing each file in $scope.files. - - // Convert bytes to human readable si units. - var humanFileSize = function (bytes) { - if(Math.abs(bytes) < 1000) { - return bytes + ' B'; - } - var units = ['kB','MB','GB','TB','PB','EB','ZB','YB']; - var i = -1; - do { - bytes /= 1000; - i++; - } while(bytes >= 1000 && i < units.length - 1); - - return bytes.toFixed(1) + ' ' + units[i]; - }; - - $scope.addFiles = function (files) { - files = _.map(files, function (file) { - idCounter += 1; - // This is a client side representation used for the template - return { - id: idCounter, - file: file, - title: file.name, - hidden: false, - uploader_id: operator.user.id, - name: file.name, - size: file.size, - humanSize: humanFileSize(file.size), - type: file.type, - progress: 0, - }; - }); - // Add each file, that is not a duplicate to $scope.files - _.forEach(files, function (file) { - var duplicate = _.some($scope.files, function (_file) { - return file.name === _file.name && - file.size === _file.size && - file.type === _file.type; - }); - if (!duplicate) { - $scope.files.push(file); - } - }); - }; - - $scope.removeFile = function (id) { - $scope.files = _.filter($scope.files, function (file) { - return file.id !== id; - }); - }; - - // Add files via drag and drop - $scope.$watch('dropFiles', function () { - if ($scope.dropFiles) { - $scope.addFiles($scope.dropFiles); - } - }); - - // upload all files - $scope.upload = function () { - $scope.uploading = true; - var promises = _.map($scope.files, function (file) { - // clear error - file.error = void 0; - - // Check, if all necessary fields are set. - if (!file.title) { - file.title = file.file.name; - } - if (!file.uploader_id) { - file.uploader_id = operator.user.id; - } - - return Upload.upload({ - url: '/rest/mediafiles/mediafile/', - method: 'POST', - data: { - mediafile: file.file, - title: file.title, - uploader_id: file.uploader_id, - hidden: file.hidden - }, - }).then( - function (success) { - $scope.removeFile(file.id); - }, - function (error) { - file.error = ErrorMessage.forAlert(error).msg; - return error; - }, - function (progress) { - file.progress = parseInt(100.0 * progress.loaded / progress.total); - } - ); - }); - - $q.all(promises).then(function (success) { - var errors = _.filter(success, function (entry) { - return entry; - }); - - if (errors.length) { - $scope.uploading = false; - var message = gettextCatalog.getString('Some files could not be uploaded'); - $scope.alert = { type: 'danger', msg: message, show: true }; - } else { - $scope.close(); - } - }); - }; - - $scope.clear = function () { - $scope.uploading = false; - $scope.files = []; - }; - - $scope.close = function () { - $scope.closeThisDialog(); - }; - } -]) - -.run([ - 'gettext', - function (gettext) { - gettext('Some files could not be uploaded'); - } -]); - -}()); diff --git a/openslides/mediafiles/static/templates/mediafiles/image-plugin.html b/openslides/mediafiles/static/templates/mediafiles/image-plugin.html deleted file mode 100644 index f0f71af81..000000000 --- a/openslides/mediafiles/static/templates/mediafiles/image-plugin.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - -
-
-
- -
diff --git a/openslides/mediafiles/static/templates/mediafiles/mediafile-form.html b/openslides/mediafiles/static/templates/mediafiles/mediafile-form.html deleted file mode 100644 index a9f3e4c13..000000000 --- a/openslides/mediafiles/static/templates/mediafiles/mediafile-form.html +++ /dev/null @@ -1,23 +0,0 @@ -

Edit File

- -
- {{ alert.msg }} -
- -
- - File too large - {{ picFile.size / 1000000|number:1}}MB: max {{ mediafile.mediafile.$errorParam}} - - - {{ progress }}% - - - - - -
diff --git a/openslides/mediafiles/static/templates/mediafiles/mediafile-list.html b/openslides/mediafiles/static/templates/mediafiles/mediafile-list.html deleted file mode 100644 index b13f36553..000000000 --- a/openslides/mediafiles/static/templates/mediafiles/mediafile-list.html +++ /dev/null @@ -1,432 +0,0 @@ -
-
- -

Files

-
-
- -
-
-
- - Presentation control elements -
-
- -
-
-
- -
-
-
-

{{ getTitle(presentedMediafile) }}

- - - - - - - - -
-
- No media file projected. -
-
-
-
-
- -
-
-
-
- - -
-
-
-
- - -
-
- {{ mediafilesFiltered.length }} / - {{ mediafiles.length }} {{ "files" | translate }}, - {{(mediafiles|filter:{selected:true}).length}} {{ "selected" | translate }} -
-
- - - « - - Page {{ pagination.currentPage }} / - {{ pagination.getPageCount(mediafilesFiltered) }} - - » - - -
-
- -
-
-
- -
-
- - - - - Filter - - - - - {{ booleanFilter.displayName | translate }} - - - - - - - - Sort - - - - - - - - - - - - - - - - - - - {{ booleanFilter.value ? booleanFilter.choiceYes : booleanFilter.choiceNo | translate }} - - - -
-
- - -
- - -
- -
- - -
- -
-
- - - - - -
-
- -
{{ mediafile.uploader.get_full_name() }}
-
- - Edit · - Delete - -
-
-
- - -
-
- -
- {{ mediafile.filetype }} - (Encrypted) -
-
{{ mediafile.filesize }}
-
{{ mediafile.timestamp | date:'yyyy-MM-dd HH:mm:ss' }}
-
-
-
- -
- - - - - - - - - - - - {{ logo.display_name | translate }},
-
-
-
-
- - - - -
- -
-
- -
- - - - - - - - - - - - {{ font.display_name | translate }},
-
-
-
-
- -
- -
-
- -
-
- -
-
- -
    -
-
diff --git a/openslides/mediafiles/static/templates/mediafiles/mediafile-upload-form.html b/openslides/mediafiles/static/templates/mediafiles/mediafile-upload-form.html deleted file mode 100644 index e6428634e..000000000 --- a/openslides/mediafiles/static/templates/mediafiles/mediafile-upload-form.html +++ /dev/null @@ -1,97 +0,0 @@ -

Upload files

- -
- {{ alert.msg }} -
- -
-
-
- Drop files into this area OR - - select files - -
-
- -
- - - - - - - - - - - - - - - - - - - - -
- File information - - Title - - Hidden - - Uploader - - Upload status -
-
- {{ $index+1 }}. {{ file.name }} - - - -
-
{{ file.type }}
-
{{ file.humanSize }}
-
- {{ file.error }} -
-
- - - {{ file.title }} - - - - - - - {{ file.progress }}% - -
-
- - -
- - - -
-
diff --git a/openslides/mediafiles/static/templates/mediafiles/slide_mediafile.html b/openslides/mediafiles/static/templates/mediafiles/slide_mediafile.html deleted file mode 100644 index c28f06f48..000000000 --- a/openslides/mediafiles/static/templates/mediafiles/slide_mediafile.html +++ /dev/null @@ -1,18 +0,0 @@ -
- - - - - - -
- - -
- -
-
diff --git a/openslides/mediafiles/static/templates/mediafiles/slide_mediafile_partial.html b/openslides/mediafiles/static/templates/mediafiles/slide_mediafile_partial.html deleted file mode 100644 index cdcc7179f..000000000 --- a/openslides/mediafiles/static/templates/mediafiles/slide_mediafile_partial.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/openslides/motions/static/css/motions/_amendments.scss b/openslides/motions/static/css/motions/_amendments.scss deleted file mode 100644 index 0368198c2..000000000 --- a/openslides/motions/static/css/motions/_amendments.scss +++ /dev/null @@ -1,94 +0,0 @@ -.paragraph-select-list { - display: table; - border: 1px solid #d3d3d3; - width: 100%; - margin-bottom: 10px; - - .paragraph-select-holder { - display: table-row; - cursor: pointer; - border-bottom: 1px solid #d3d3d3; - - .paragraph-select { - display: table-cell; - width: 30px; - padding-top: 5px; - text-align: center; - } - .text-holder { - display: table-cell; - background-color: white; - padding: 5px 10px; - - :last-child { - margin-bottom: 0; - } - - // Show line numbers at the side - @media screen and (min-width: 800px) { - padding-left: 30px; - position: relative; - - .os-line-number { - display: inline-block; - font-size: 0; - line-height: 0; - width: 22px; - height: 22px; - position: absolute; - left: -15px; - padding-right: 45px; - - &:after { - content: attr(data-line-number); - position: absolute; - top: 8px; - right: 5px; - vertical-align: top; - color: #a9a9a9; - font-size: 12px; - font-weight: normal; - } - } - } - - // Show line numbers at the side - @media screen and (max-width: 799px) { - .os-line-break { - display: none; - } - - .os-line-number { - display: inline-block; - - &:after { - display: inline-block; - content: attr(data-line-number); - vertical-align: top; - font-size: 10px; - font-weight: normal; - color: gray; - margin-top: -3px; - margin-left: 0; - margin-right: 0; - } - } - } - } - - &:hover { - .text-holder { - background-color: #f0f0f0; - } - } - &.selected { - .paragraph-select { - background-color: #ddd; - } - .text-holder { - background-color: #ddd; - } - - } - } -} diff --git a/openslides/motions/static/css/motions/_change-recommendation-overview.scss b/openslides/motions/static/css/motions/_change-recommendation-overview.scss deleted file mode 100644 index 7df8e3e50..000000000 --- a/openslides/motions/static/css/motions/_change-recommendation-overview.scss +++ /dev/null @@ -1,49 +0,0 @@ -.change-recommendation-overview { - background-color: #eee; - border: solid 1px #ddd; - border-radius: 3px; - margin-bottom: 5px; - margin-top: -15px; - padding: 5px 5px 0 5px; - - h3 { - margin-top: 10px; - } - - ul { - list-style: none; - display: table; - } - - li { - display: table-row; - cursor: pointer; - - &:hover { - text-decoration: underline; - } - - & > * { - display: table-cell; - padding: 1px; - } - } - - .status { - color: gray; - font-style: italic; - - & > *:before { - content: '('; - } - - & > *:after { - content: ')'; - } - } - - .no-changes { - font-style: italic; - color: grey; - } -} diff --git a/openslides/motions/static/css/motions/_diff.scss b/openslides/motions/static/css/motions/_diff.scss deleted file mode 100644 index db0cb60f5..000000000 --- a/openslides/motions/static/css/motions/_diff.scss +++ /dev/null @@ -1,71 +0,0 @@ -/* Diff view */ -p { - &.os-split-after { - margin-bottom: 0; - } - - &.os-split-before { - margin-top: 0; - } -} - -ul.os-split-after, ol.os-split-after { - margin-bottom: 0; -} - -.motion-text-holder li.os-split-before { - list-style-type: none; -} - -.motion-text-with-diffs { - li.os-split-before { - list-style-type: none; - } - - .original-text { - ul:last-child, ol:last-child { - padding-bottom: 16px; - } - - ul.os-split-after:last-child, ol.os-split-after:last-child { - padding-bottom: 0; - } - } - .collission-hint { - color: red; - float: left; - margin-left: -19px; - margin-top: 10px; - } -} - -.motion-text-diff { - .delete { - color: red; - text-decoration: line-through; - } - - .insert { - color: green; - text-decoration: underline; - } - - &.line-numbers-outside .insert .os-line-number { - display: none; - } - - &.line-numbers-inline .insert .os-line-number { - display: none; - } - .paragraph-context { - opacity: 0.5; - } - &.amendment-context { - .paragraph-context { - opacity: 1; - } - } - .amendment-line-header { - margin: 10px 0 0 0; - } -} diff --git a/openslides/motions/static/css/motions/_inline-editing.scss b/openslides/motions/static/css/motions/_inline-editing.scss deleted file mode 100644 index 6583585ee..000000000 --- a/openslides/motions/static/css/motions/_inline-editing.scss +++ /dev/null @@ -1,45 +0,0 @@ -/* Toolbar to save motion in inline editing mode */ -.motion-save-toolbar { - position: fixed; - bottom: 0; - left: 50%; - height: 75px; - width: 300px; - background: #d3d3d3; - color: black; - text-align: center; - padding: 5px; - z-index: 1000001; - display: none; - border: 1px solid #d3d3d3; - margin-left: -150px; - border-bottom: none; - -webkit-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75); - -moz-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75); - box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75); - - &.visible { - display: block; - } - - .changed-hint { - display: block; - line-height: 16px; - text-align: center; - margin-bottom: 10px; - font-weight: bold; - } - - label { - font-weight: normal; - line-height: 16px; - text-align: left; - padding-left: 16px; - margin-top: 5px; - margin-left: 15px; - - input { - margin-left: -16px; - } - } -} diff --git a/openslides/motions/static/css/motions/_line-numbering.scss b/openslides/motions/static/css/motions/_line-numbering.scss deleted file mode 100644 index 5317ee7b9..000000000 --- a/openslides/motions/static/css/motions/_line-numbering.scss +++ /dev/null @@ -1,82 +0,0 @@ -/* Line numbers */ -.motion-text { - ins { - color: green; - text-decoration: underline; - } - - del { - color: red; - text-decoration: line-through; - } - - li { - padding-bottom: 10px; - } - - ol, ul { - margin-left: 15px; - margin-bottom: 0; - } - - .highlight { - background-color: #ff0; - } - &.line-numbers-outside { - padding-left: 40px; - position: relative; - - .os-line-number { - display: inline-block; - font-size: 0; - line-height: 0; - width: 22px; - height: 22px; - position: absolute; - left: -20px; - padding-right: 55px; - - &:after { - content: attr(data-line-number); - position: absolute; - top: 10px; - vertical-align: top; - color: gray; - font-size: 12px; - font-weight: normal; - } - } - } - - &.line-numbers-inline { - .os-line-break { - display: none; - } - - .os-line-number { - display: inline-block; - - &:after { - display: inline-block; - content: attr(data-line-number); - vertical-align: top; - font-size: 10px; - font-weight: normal; - color: gray; - margin-top: -3px; - margin-left: 0; - margin-right: 0; - } - } - } - - &.line-numbers-none { - .os-line-break { - display: none; - } - - .os-line-number { - display: none; - } - } -} diff --git a/openslides/motions/static/css/motions/_personal-note.scss b/openslides/motions/static/css/motions/_personal-note.scss deleted file mode 100644 index 6cadb3f50..000000000 --- a/openslides/motions/static/css/motions/_personal-note.scss +++ /dev/null @@ -1,25 +0,0 @@ -/* Motion personal note */ -#personalNote.pinned { - position: fixed; - z-index: 1000; - bottom: 0; - margin: 0px 20px; - -webkit-box-shadow: 0px 0px 10px 1px rgba(0,0,0,0.5); - -moz-box-shadow: 0px 0px 10px 1px rgba(0,0,0,0.5); - box-shadow: 0px 0px 10px 1px rgba(0,0,0,0.5); - - #personal-note-inline-editor { - overflow-y: scroll; - min-height: 100px; - max-height: 200px; - } -} - -#personalNoteSpacer { - display: none; - height: 220px; - - &.activeSpace { - display: block; - } -} diff --git a/openslides/motions/static/css/motions/_pollresults.scss b/openslides/motions/static/css/motions/_pollresults.scss deleted file mode 100644 index 43d0c5164..000000000 --- a/openslides/motions/static/css/motions/_pollresults.scss +++ /dev/null @@ -1,28 +0,0 @@ -.pollresults { - table { - margin-bottom: 0; - - td { - border: none !important; - padding: 5px 2px !important; - vertical-align: middle !important; - } - - .icon { - color: #636363; - } - - tr.total td { - border-top: 1px solid #444 !important; - } - } - - .result-label { - margin-top: 5px; - } - - .progress { - height: 12px; - margin-bottom: 0; - } -} diff --git a/openslides/motions/static/css/motions/_projector.scss b/openslides/motions/static/css/motions/_projector.scss deleted file mode 100644 index 21c509e85..000000000 --- a/openslides/motions/static/css/motions/_projector.scss +++ /dev/null @@ -1,69 +0,0 @@ -@import "line-numbering"; -@import "diff"; -@import "pollresults"; -@import "sidebox"; - -/* TODO: why do the projector has these extra diff related classes? - * -> Unify the site and projector in the _diff.scss and _line-numbers.scss*/ -ol.os-split-after, ul.os-split-after { - margin-bottom: 0; - padding-bottom: 0; -} -p.os-split-after { - margin-bottom: 0; -} -.motion-text-diff p.os-split-before { - padding-top: 0; - margin-top: 0; -} -.motion-text-diff p.os-split-after { - margin-top: 0; - margin-bottom: 0; -} -.diff-box { - padding-top: 0; -} -.diff-box-title { - margin-bottom: 10px; - .description { - font-weight: bold; - } -} - -.motion-text.line-numbers-outside { - padding-left: 0; - margin-left: 35px; - position: relative; -} -.motion-text.line-numbers-outside .os-line-number { - left: -33px; -} -.motion-text.line-numbers-outside .os-line-number:after { - content: attr(data-line-number); - position: absolute; - top: 17px; - vertical-align: top; - color: gray; - font-size: 14px; - font-weight: normal; -} - -/* Motion blocks */ -.motion-block { - display: flex; - flex-wrap: wrap; - - & > div { - width: 50%; - font-size: 1.1em; - margin-bottom: 15px; - padding-right: 15px; - line-height: 1em; - } - - .label { - text-align: left; - padding: 5px 10px; - margin-top: 3px; - } -} diff --git a/openslides/motions/static/css/motions/_sidebox.scss b/openslides/motions/static/css/motions/_sidebox.scss deleted file mode 100644 index 37fc2c07c..000000000 --- a/openslides/motions/static/css/motions/_sidebox.scss +++ /dev/null @@ -1,19 +0,0 @@ -#sidebox { - width: 260px; - right: 0; - margin-top: 75px; - background: #d3d3d3; - border-radius: 7px 0 0 7px; - padding: 3px 7px 10px 10px; - position: fixed; - z-index: 5; - - h3 { - margin-top: 10px; - margin-bottom: 0px; - } -} -#sidebox .pollresults td { - white-space: nowrap; - font-size: 19px; -} diff --git a/openslides/motions/static/css/motions/_site.scss b/openslides/motions/static/css/motions/_site.scss deleted file mode 100644 index 0c809459c..000000000 --- a/openslides/motions/static/css/motions/_site.scss +++ /dev/null @@ -1,268 +0,0 @@ -@import "amendments"; -@import "diff"; -@import "change-recommendation-overview"; -@import "inline-editing"; -@import "line-numbering"; -@import "personal-note"; -@import "pollresults"; - -/* Motion */ -.motion-toolbar, .speakers-toolbar { - background-color: #f5f5f5; - border-bottom: 1px solid #ddd; - padding: 12px 0 10px 0; - height: 54px; - margin: -20px -5px 50px -5px; -} - -.motion-toolbar:first-child { - margin-bottom: 20px; -} - -/* Styles for annotating the original motion text with change recommendations */ -#content .motion-text-holder { - position: relative; - - .change-recommendation-list { - position: absolute; - top: 0; - left: -10px; - width: 4px; - list-style-type: none; - margin: 0; - - & > li { - position: absolute; - width: 4px; - cursor: pointer; - - &.insert { - background-color: #00aa00; - } - - &.delete { - background-color: #aa0000; - } - - &.replace { - background-color: #0333ff; - } - - &.other { - background-color: #777777; - } - } - - .tooltip { - display: none; - } - } -} - -.import-preview p { - margin: 0; - padding: 0; -} - -.motion-toolbar .toolbar-left { - margin-top: 0; - margin-bottom: 55px; - margin-left: 15px; - - > * { - margin-right: 5px; - } - - ng-include { - display: inline-block; - } - - .btn.disabled { - cursor: default; - opacity: 1; - background-color: #eee; - } -} - -.inline-editing-activator { - margin-right: 13px; -} - -/* Linenumbering specific site things */ -.os-line-number { - position: relative; - user-select: none; - -moz-user-select: none; - -khtml-user-select: none; - -webkit-user-select: none; - -o-user-select: none; -} -.os-line-number:after { - position: relative; - user-select: none; - -moz-user-select: none; - -khtml-user-select: none; - -webkit-user-select: none; - -o-user-select: none; -} - -/* TODO: This rule should only apply to the site. Isn't this inconsistent?? */ -.motion-text.line-numbers-outside .os-line-number:after { - left: 20px; -} - -.motion-text.line-numbers-none li > br { - margin-top: 6px; - content: " "; - display: block; - &.os-line-break { - margin-top: 0; - content: ""; - display: inline; - } -} - -@mixin addChangeRecommendationBtn { - cursor: pointer; - content: "\f067"; - width: 14px; - height: 14px; - border-radius: 0.25em; - font-family: FontAwesome; - font-size: 12px; - color: white; - line-height: 16px; - text-align: center; - background-color: #337ab7; -} - -.line-numbers-outside .os-line-number.selectable:hover:before, .line-numbers-outside .os-line-number.selected:before { - position: absolute; - top: 4px; - left: 43px; - display: inline-block; - @include addChangeRecommendationBtn(); -} - -.motion-header { - .submenu { - position: relative; - z-index: 2; - } - .motion-title { - position: relative; - z-index: 1; - - // Grab the left padding of the parent element to catch hover-events for the :before element - margin-left: -20px; - padding-left: 20px; - - .change-title { - position: relative; - width: 0; - height: 0; - } - .change-title:before { - position: absolute; - top: 18px; - left: -17px; - @include addChangeRecommendationBtn(); - display: none; - } - &:hover .change-title.selectable:before { - display: block; - } - .title-change-indicator { - background-color: #0333ff; - position: absolute; - width: 4px; - height: 32px; - left: 10px; - top: 5px; - cursor: pointer; - } - } -} - - -.tt_change_recommendation_create_help { - display: none; - max-width: 150px; - left: -45px; - margin-top: -15px !important; - z-index: 10000; -} - -/* special hack for firefox only (see issue#2967) */ -@-moz-document url-prefix() { - .tt_change_recommendation_create_help { - margin-top: -20px !important; - } -} - -.tt_change_recommendation_create_help.opened { - display: inherit; - opacity: 0.8; -} - -/* Diffbox */ -.diff-box { - background-color: #f9f9f9; - border: solid 1px #eee; - border-radius: 3px; - margin-bottom: 0; - padding-top: 0; - padding-right: 155px; - - &:hover { - background-color: #f0f0f0; - - .action-row { - opacity: 1; - } - } - - .action-row { - font-size: 0.8em; - padding-top: 5px; - padding-bottom: 5px; - float: right; - width: 150px; - text-align: right; - margin-right: -150px; - opacity: 0.5; - - .btn-delete { - margin-left: 5px; - color: red; - } - - .btn-edit { - margin-left: 5px; - } - - .btn-amend-info { - margin-left: 5px; - min-width: 68px; - } - } - .status-row { - font-style: italic; - color: gray; - - & > *:after { - content: ':'; - } - } -} - -.diff-box-title { - margin-bottom: 10px; - .description { - font-weight: bold; - } -} - -.motion-text-with-diffs.line-numbers-inline .diff-box, .motion-text-with-diffs.line-numbers-none .diff-box { - margin-right: -220px; -} diff --git a/openslides/motions/static/js/motions/base.js b/openslides/motions/static/js/motions/base.js deleted file mode 100644 index 8b9d4e8d9..000000000 --- a/openslides/motions/static/js/motions/base.js +++ /dev/null @@ -1,1646 +0,0 @@ -(function () { - -"use strict"; - -angular.module('OpenSlidesApp.motions', [ - 'OpenSlidesApp.motions.motionBlock', - 'OpenSlidesApp.motions.lineNumbering', - 'OpenSlidesApp.motions.diff', - 'OpenSlidesApp.poll.majority', - 'OpenSlidesApp.users', -]) - -.factory('MotionState', [ - 'DS', - function (DS) { - return DS.defineResource({ - name: 'motions/state', - methods: { - getNextStates: function () { - return _.map(this.next_states_id, function (stateId) { - return DS.get('motions/state', stateId); - }); - }, - getRecommendations: function () { - var params = { - where: { - 'workflow_id': { - '==': this.workflow_id - }, - 'recommendation_label': { - '!=': null - } - } - }; - return DS.filter('motions/state', params); - } - }, - relations: { - hasOne: { - 'motions/workflow': { - localField: 'workflow', - localKey: 'workflow_id', - } - } - }, - }); - } -]) - -.factory('Workflow', [ - 'DS', - function (DS) { - return DS.defineResource({ - name: 'motions/workflow', - methods: { - getFirstState: function () { - return DS.get('motions/state', this.first_state_id); - }, - }, - relations: { - hasMany: { - 'motions/state': { - localField: 'states', - foreignKey: 'workflow_id', - } - } - } - }); - } -]) - -.factory('MotionPoll', [ - 'DS', - 'gettextCatalog', - 'Config', - 'MajorityMethods', - function (DS, gettextCatalog, Config, MajorityMethods) { - return DS.defineResource({ - name: 'motions/motion-poll', - relations: { - belongsTo: { - 'motions/motion': { - localField: 'motion', - localKey: 'motion_id', - } - } - }, - beforeInject: function (resource, instance) { - var attrs = ['yes', 'no', 'abstain', 'votescast', 'votesinvalid', 'votesvalid']; - _.forEach(attrs, function (attr) { - if (instance[attr] !== null) { - instance[attr] = parseFloat(instance[attr]); - } - }); - }, - methods: { - // Returns percent base. Returns undefined if calculation is not possible in general. - getPercentBase: function (config, type) { - var base; - switch (config) { - case 'CAST': - if (this.votescast <= 0 || this.votesinvalid < 0) { - // It would be OK to check only this.votescast < 0 because 0 - // is checked again later but this is a little bit faster. - break; - } - base = this.votescast; - /* falls through */ - case 'VALID': - if (this.votesvalid < 0) { - base = void 0; - break; - } - if (typeof base === 'undefined' && type !== 'votescast' && type !== 'votesinvalid') { - base = this.votesvalid; - } - /* falls through */ - case 'YES_NO_ABSTAIN': - if (this.abstain < 0) { - base = void 0; - break; - } - if (typeof base === 'undefined' && type !== 'votescast' && type !== 'votesinvalid' && type !== 'votesvalid') { - base = this.yes + this.no + this.abstain; - } - /* falls through */ - case 'YES_NO': - if (this.yes < 0 || this.no < 0 || this.abstain === -1 ) { - // It is not allowed to set 'Abstain' to 'majority' but exclude it from calculation. - // Setting 'Abstain' to 'undocumented' is possible, of course. - base = void 0; - break; - } - if (typeof base === 'undefined' && (type === 'yes' || type === 'no')) { - base = this.yes + this.no; - } - } - return base; - }, - - // Returns object with value and percent for this poll. - getVote: function (vote, type) { - if (!this.has_votes) { - // Return undefined if this poll has no votes. - return; - } - - // Initial values - var value = '', - percentStr = '', - percentNumber, - config = Config.get('motions_poll_100_percent_base').value; - - // Check special values - switch (vote) { - case -1: - value = gettextCatalog.getString('majority'); - break; - case -2: - value = gettextCatalog.getString('undocumented'); - break; - default: - if (vote >= 0) { - value = vote; - } else { - value = 0; // Vote was not defined. Set value to 0. - } - } - - // Calculate percent value - var base = this.getPercentBase(config, type); - if (base) { - percentNumber = Math.round(vote * 100 / (base) * 100) / 100; - percentStr = '(' + percentNumber + ' %)'; - } - return { - 'value': value, - 'percentStr': percentStr, - 'percentNumber': percentNumber, - 'display': value + ' ' + percentStr - }; - }, - - // Returns 0 or positive integer if quorum is reached or surpassed. - // Returns negativ integer if quorum is not reached. - // Returns undefined if we can not calculate the quorum. - isReached: function (method) { - if (!this.has_votes) { - // Return undefined if this poll has no votes. - return; - } - - var isReached; - var config = Config.get('motions_poll_100_percent_base').value; - var base = this.getPercentBase(config, 'yes'); - if (base) { - // Provide result only if base is not undefined and not 0. - isReached = MajorityMethods[method](this.yes, base); - } - return isReached; - } - } - }); - } -]) - -.provider('MotionPollDecimalPlaces', [ - function () { - this.$get = ['$q', function ($q) { - return { - getPlaces: function (poll, find) { - if (find) { - return $q(function (resolve) { - resolve(0); - }); - } else { - return 0; - } - }, - }; - }]; - } -]) - -.factory('MotionStateAndRecommendationParser', [ - 'DS', - 'gettextCatalog', - function (DS, gettextCatalog) { - return { - formatMotion: function (motion) { - return '[motion:' + motion.id + ']'; - }, - parse: function (recommendation) { - return recommendation.replace(/\[motion:(\d+)\]/g, function (match, id) { - var motion = DS.get('motions/motion', id); - if (motion) { - return motion.identifier ? motion.identifier : motion.getTitle(); - } else { - return gettextCatalog.getString(''); - } - }); - }, - }; - } -]) - -.factory('Submitter', [ - 'DS', - function (DS) { - return DS.defineResource({ - name: 'motions/submitter', - relations: { - belongsTo: { - 'users/user': { - localField: 'user', - localKey: 'user_id', - } - } - } - }); - } -]) - -.factory('Motion', [ - 'DS', - '$http', - '$cacheFactory', - 'MotionPoll', - 'MotionStateAndRecommendationParser', - 'MotionChangeRecommendation', - 'MotionComment', - 'jsDataModel', - 'gettext', - 'gettextCatalog', - 'Config', - 'lineNumberingService', - 'diffService', - 'OpenSlidesSettings', - 'Projector', - 'ProjectHelper', - 'operator', - 'UnifiedChangeObjectCollission', - function(DS, $http, $cacheFactory, MotionPoll, MotionStateAndRecommendationParser, MotionChangeRecommendation, - MotionComment, jsDataModel, gettext, gettextCatalog, Config, lineNumberingService, - diffService, OpenSlidesSettings, Projector, ProjectHelper, operator, UnifiedChangeObjectCollission) { - - var diffCache = $cacheFactory('motion.service'); - - var name = 'motions/motion'; - return DS.defineResource({ - name: name, - useClass: jsDataModel, - verboseName: gettext('Motion'), - verboseNamePlural: gettext('Motions'), - validate: function (resource, data, callback) { - MotionComment.populateFieldsReverse(data); - callback(null, data); - }, - computed: { - isAmendment: function () { - return this.parent_id !== null; - }, - }, - methods: { - getResourceName: function () { - return name; - }, - getVersion: function (versionId) { - versionId = versionId || this.active_version; - var index; - if (versionId == -1) { - index = this.versions.length - 1; - } else { - index = _.findIndex(this.versions, function (element) { - return element.id == versionId; - }); - } - return this.versions[index] || {}; - }, - isParagraphBasedAmendment: function () { - var version = this.getVersion(); - return this.isAmendment && version.amendment_paragraphs; - }, - getTitle: function (versionId) { - return this.getVersion(versionId).title; - }, - getAgendaTitle: function () { - var title = gettextCatalog.getString('Motion'); - if (this.identifier) { - title += ' ' + this.identifier; - } else { - title += ' (' + this.getTitle() + ')'; - } - return title; - }, - getListOfSpeakersTitle: function () { - var title = gettextCatalog.getString('Motion'); - if (this.identifier) { - title += ' ' + this.identifier; - } else { - title += ' (' + this.getTitle() + ')'; - } - return title; - }, - getTitleWithChanges: function (changeRecommendationMode, versionId) { - var titleChange = this.getTitleChangeRecommendation(versionId); - var title; - if (titleChange) { - if (changeRecommendationMode === "changed") { - title = titleChange.text; - } else if ((changeRecommendationMode === 'agreed' || - changeRecommendationMode === 'modified_agreed') && !titleChange.rejected) { - title = titleChange.text; - } else { - title = this.getTitle(); - } - } else { - title = this.getTitle(); - } - return title; - }, - getSequentialNumber: function () { - var id = this.id + ''; - var zeros = Math.max(0, OpenSlidesSettings.MOTION_IDENTIFIER_MIN_DIGITS - id.length); - for (var i = 0; i < zeros; i++) { - id = '0' + id; - } - return id; - }, - getText: function (versionId) { - return this.getVersion(versionId).text; - }, - getTextWithLineBreaks: function (versionId, highlight, callback) { - var lineLength = Config.get('motions_line_length').value, - html = this.getVersion(versionId).text; - - return lineNumberingService.insertLineNumbers(html, lineLength, highlight, callback); - }, - getModifiedFinalVersionWithLineBreaks: function (versionId) { - var lineLength = Config.get('motions_line_length').value, - html = this.getVersion(versionId).modified_final_version; - - return lineNumberingService.insertLineNumbers(html, lineLength); - }, - getTextBetweenChanges: function (versionId, change1, change2, highlight) { - var line_from = (change1 ? change1.line_to : 1), - line_to = (change2 ? change2.line_from : null); - - if (line_from > line_to) { - throw 'Invalid call of getTextBetweenChanges: change1 needs to be before change2'; - } - if (line_from === line_to) { - return ''; - } - - return this.getTextInLineRange(versionId, line_from, line_to, highlight); - }, - getTextInLineRange: function (versionId, line_from, line_to, highlight) { - var lineLength = Config.get('motions_line_length').value, - htmlRaw = this.getVersion(versionId).text; - - var cacheKey = 'getTextInLineRange ' + line_from + ' ' + line_to + ' ' + highlight + ' ' + - lineNumberingService.djb2hash(htmlRaw), - cached = diffCache.get(cacheKey); - if (!angular.isUndefined(cached)) { - return cached; - } - - var html = lineNumberingService.insertLineNumbers(htmlRaw, lineLength), - data; - - try { - data = diffService.extractRangeByLineNumbers(html, line_from, line_to); - } catch (e) { - // This only happens (as far as we know) when the motion text has been altered (shortened) - // without modifying the change recommendations accordingly. - // That's a pretty serious inconsistency that should not happen at all, - // we're just doing some basic damage control here. - var msg = 'Inconsistent data. A change recommendation is probably referring to a non-existant line number.'; - return '' + msg + ''; - } - - // Add "merge-before"-css-class if the first line begins in the middle of a paragraph. Used for PDF. - html = diffService.addCSSClassToFirstTag(data.outerContextStart + data.innerContextStart, "merge-before") + - data.html + data.innerContextEnd + data.outerContextEnd; - html = lineNumberingService.insertLineNumbers(html, lineLength, highlight, null, line_from); - - diffCache.put(cacheKey, html); - - return html; - }, - getTextRemainderAfterLastChange: function(versionId, changes, highlight) { - var maxLine = 0; - for (var i = 0; i < changes.length; i++) { - if (changes[i].line_to > maxLine) { - maxLine = changes[i].line_to; - } - } - - var lineLength = Config.get('motions_line_length').value, - html = lineNumberingService.insertLineNumbers(this.getVersion(versionId).text, lineLength), - data; - - try { - data = diffService.extractRangeByLineNumbers(html, maxLine, null); - } catch (e) { - // This only happens (as far as we know) when the motion text has been altered (shortened) - // without modifying the change recommendations accordingly. - // That's a pretty serious inconsistency that should not happen at all, - // we're just doing some basic damage control here. - var msg = 'Inconsistent data. A change recommendation is probably referring to a non-existant line number.'; - return '' + msg + ''; - } - - if (data.html !== '') { - // Add "merge-before"-css-class if the first line begins in the middle of a paragraph. Used for PDF. - html = diffService.addCSSClassToFirstTag(data.outerContextStart + data.innerContextStart, "merge-before") + - data.html + data.innerContextEnd + data.outerContextEnd; - html = lineNumberingService.insertLineNumbers(html, lineLength, highlight, null, maxLine); - } else { - // Prevents empty lines at the end of the motion - html = ''; - } - return html; - }, - _getTextWithChanges: function (versionId, highlight, lineBreaks, recommendation_filter, amendment_filter) { - var lineLength = Config.get('motions_line_length').value, - html = this.getVersion(versionId).text, - change_recommendations = this.getTextChangeRecommendations(versionId, 'DESC'), - amendments = this.getParagraphBasedAmendments(); - - var allChanges = []; - change_recommendations.filter(recommendation_filter).forEach(function(change) { - allChanges.push({"text": change.text, "line_from": change.line_from, "line_to": change.line_to}); - }); - amendments.filter(amendment_filter).forEach(function(amend) { - var change = amend.getAmendmentsAffectedLinesChanged(); - allChanges.push({"text": change.text, "line_from": change.line_from, "line_to": change.line_to}); - }); - - // Changes need to be applied from the bottom up, to prevent conflicts with changing line numbers. - allChanges.sort(function(change1, change2) { - if (change1.line_from < change2.line_from) { - return 1; - } else if (change1.line_from > change2.line_from) { - return -1; - } else { - return 0; - } - }); - - - allChanges.forEach(function(change) { - html = lineNumberingService.insertLineNumbers(html, lineLength, null, null, 1); - html = diffService.replaceLines(html, change.text, change.line_from, change.line_to); - }); - - if (lineBreaks) { - html = lineNumberingService.insertLineNumbers(html, lineLength, highlight, null, 1); - } - - return html; - }, - getTextWithAllChangeRecommendations: function (versionId, highlight, lineBreaks) { - return this._getTextWithChanges(versionId, highlight, lineBreaks, function() { - return true; // All change recommendations - }, function() { - return false; // No amendments - }); - }, - getTextWithAgreedChanges: function (versionId, highlight, lineBreaks) { - return this._getTextWithChanges(versionId, highlight, lineBreaks, function(recommendation) { - return !recommendation.rejected; - }, function(amendment) { - if (amendment.state && amendment.state.name === 'rejected') { - return false; - } - if (amendment.state && amendment.state.name === 'accepted') { - return true; - } - return (amendment.recommendation && amendment.recommendation.name === 'accepted'); - }); - }, - getTextByMode: function(mode, versionId, highlight, lineBreaks) { - /* - * @param mode ['original', 'diff', 'changed', 'agreed', 'modified_agreed'] - * @param versionId [if undefined, active_version will be used] - * @param highlight [the line number to highlight] - * @param lineBreaks [if line numbers / breaks should be included in the result] - */ - - lineBreaks = (lineBreaks === undefined ? true : lineBreaks); - - var text; - switch (mode) { - case 'original': - if (lineBreaks) { - text = this.getTextWithLineBreaks(versionId, highlight); - } else { - text = this.getVersion(versionId).text; - } - break; - case 'diff': - var amendments_crs = this.getTextChangeRecommendations(versionId, 'ASC').map(function (cr) { - return cr.getUnifiedChangeObject(); - }).concat( - this.getParagraphBasedAmendmentsForDiffView().map(function (amendment) { - return amendment.getUnifiedChangeObject(); - }) - ); - amendments_crs.sort(function (change1, change2) { - if (change1.line_from > change2.line_from) { - return 1; - } else if (change1.line_from < change2.line_from) { - return -1; - } else { - return 0; - } - }); - - text = ''; - for (var i = 0; i < amendments_crs.length; i++) { - if (i===0) { - text += this.getTextBetweenChanges(versionId, null, amendments_crs[0], highlight); - } else if (amendments_crs[i - 1].line_to < amendments_crs[i].line_from) { - text += this.getTextBetweenChanges(versionId, amendments_crs[i - 1], amendments_crs[i], highlight); - - } - text += amendments_crs[i].getDiff(this, versionId, highlight); - } - text += this.getTextRemainderAfterLastChange(versionId, amendments_crs); - - if (!lineBreaks) { - text = lineNumberingService.stripLineNumbers(text); - } - break; - case 'changed': - text = this.getTextWithAllChangeRecommendations(versionId, highlight, lineBreaks); - break; - case 'agreed': - text = this.getTextWithAgreedChanges(versionId, highlight, lineBreaks); - break; - case 'modified_agreed': - text = this.getModifiedFinalVersion(versionId); - if (text) { - // Insert line numbers - var lineLength = Config.get('motions_line_length').value; - text = lineNumberingService.insertLineNumbers(text, lineLength); - } else { - // Use the agreed version as fallback - text = this.getTextByMode('agreed', versionId, highlight, lineBreaks); - } - break; - } - return text; - }, - getTextParagraphs: function(versionId, lineBreaks) { - /* - * @param versionId [if undefined, active_version will be used] - * @param lineBreaks [if line numbers / breaks should be included in the result] - */ - var text; - if (lineBreaks) { - text = this.getTextWithLineBreaks(versionId); - } else { - text = this.getVersion(versionId).text; - } - - return lineNumberingService.splitToParagraphs(text); - }, - getTextHeadings: function(versionId) { - var html = this.getTextWithLineBreaks(versionId); - return lineNumberingService.getHeadingsWithLineNumbers(html); - }, - getAmendmentParagraphsByMode: function (mode, versionId, lineBreaks) { - /* - * @param mode ['original', 'diff', 'changed'] - * @param versionId [if undefined, active_version will be used] - * @param lineBreaks [if line numbers / breaks should be included in the result] - * - * Structure of the return array elements: - * { - * "paragraphNo": paragraph number, starting with 0 - * "lineFrom": First line number of the affected paragraph - * "lineTo": Last line number of the affected paragraph; - * refers to the line breaking element at the end, i.e. the start of the following line - * "text": the actual text - * } - */ - - lineBreaks = (lineBreaks === undefined ? true : lineBreaks); - - var cacheKey = 'getAmendmentParagraphsByMode ' + mode + ' ' + versionId + ' ' + lineBreaks + - lineNumberingService.djb2hash(JSON.stringify(this.getVersion(versionId).amendment_paragraphs)), - cached = diffCache.get(cacheKey); - if (!angular.isUndefined(cached)) { - return cached; - } - - var original_text = this.getParentMotion().getTextByMode('original', null, null, true); - var original_paragraphs = lineNumberingService.splitToParagraphs(original_text); - - var output = []; - - this.getVersion(versionId).amendment_paragraphs.forEach(function(paragraph_amend, paragraphNo) { - if (paragraph_amend === null) { - return; - } - if (original_paragraphs[paragraphNo] === undefined) { - throw "The amendment appears to have more paragraphs than the motion. This means, the data might be corrupt"; - } - var paragraph_orig = original_paragraphs[paragraphNo]; - var line_range = lineNumberingService.getLineNumberRange(paragraph_orig); - var line_length = Config.get('motions_line_length').value; - paragraph_orig = lineNumberingService.stripLineNumbers(paragraph_orig); - - var text = null; - - switch (mode) { - case "diff": - if (lineBreaks) { - text = diffService.diff(paragraph_orig, paragraph_amend, line_length, line_range.from); - } else { - text = diffService.diff(paragraph_orig, paragraph_amend); - } - break; - case "original": - text = paragraph_orig; - if (lineBreaks) { - text = lineNumberingService.insertLineNumbers(text, line_length, null, null, line_range.from); - } - break; - case "changed": - text = paragraph_amend; - if (lineBreaks) { - text = lineNumberingService.insertLineNumbers(text, line_length, null, null, line_range.from); - } - break; - default: - throw "Invalid text mode: " + mode; - } - output.push({ - "paragraphNo": paragraphNo, - "lineFrom": line_range.from, - "lineTo": line_range.to, - "text": text - }); - }); - - diffCache.put(cacheKey, output); - - return output; - }, - getAmendmentParagraphsLinesByMode: function (mode, versionId, lineBreaks) { - /* - * @param mode ['original', 'diff', 'changed'] - * @param versionId [if undefined, active_version will be used] - * @param lineBreaks [if line numbers / breaks should be included in the result] - * - * Structure of the return array elements: - * { - * "paragraphNo": paragraph number, starting with 0 - * "paragraphLineFrom": First line number of the affected paragraph - * "paragraphLineTo": End of the affected paragraph (line number + 1) - * "diffLineFrom": First line number of the affected lines - * "diffLineTo": End of the affected lines (line number + 1) - * "textPre": The beginning of the paragraph, before the diff - * "text": the diff - * "textPost": The end of the paragraph, after the diff - * } - */ - - if (!this.isParagraphBasedAmendment() || !this.getParentMotion()) { - return []; - } - - var cacheKey = 'getAmendmentParagraphsLinesByMode ' + mode + ' ' + versionId + ' ' + lineBreaks + - lineNumberingService.djb2hash(JSON.stringify(this.getVersion(versionId).amendment_paragraphs)), - cached = diffCache.get(cacheKey); - if (!angular.isUndefined(cached)) { - return cached; - } - - var original_text = this.getParentMotion().getTextByMode('original', null, null, true); - var original_paragraphs = lineNumberingService.splitToParagraphs(original_text); - - var output = []; - - this.getVersion(versionId).amendment_paragraphs.forEach(function(paragraph_amend, paragraphNo) { - if (paragraph_amend === null) { - return; - } - if (original_paragraphs[paragraphNo] === undefined) { - throw "The amendment appears to have more paragraphs than the motion. This means, the data might be corrupt"; - } - var line_length = Config.get('motions_line_length').value, - paragraph_orig = original_paragraphs[paragraphNo], - paragraph_line_range = lineNumberingService.getLineNumberRange(paragraph_orig), - diff = diffService.diff(paragraph_orig, paragraph_amend), - affected_lines = diffService.detectAffectedLineRange(diff); - - if (!affected_lines) { - return; - } - - // TODO: Make this work.. - var base_paragraph; - switch (mode) { - case 'original': - //base_paragraph = paragraph_orig; - //base_paragraph = diffService.diff(paragraph_orig, paragraph_orig, line_length, paragraph_line_range.from); - base_paragraph = diff; - break; - case 'diff': - base_paragraph = diff; - break; - case 'changed': - //base_paragraph = paragraph_amend; - //base_paragraph = diffService.diff(paragraph_amend, paragraph_amend, line_length, paragraph_line_range.from); - base_paragraph = diff; - break; - } - - var textPre = ''; - var textPost = ''; - if (affected_lines.from > paragraph_line_range.from) { - textPre = diffService.extractRangeByLineNumbers(base_paragraph, paragraph_line_range.from, affected_lines.from); - if (lineBreaks) { - textPre = diffService.formatDiffWithLineNumbers(textPre, line_length, paragraph_line_range.from); - } - } - if (paragraph_line_range.to > affected_lines.to) { - textPost = diffService.extractRangeByLineNumbers(base_paragraph, affected_lines.to, paragraph_line_range.to); - if (lineBreaks) { - textPost = diffService.formatDiffWithLineNumbers(textPost, line_length, affected_lines.to); - } - } - - var text = diffService.extractRangeByLineNumbers(base_paragraph, affected_lines.from, affected_lines.to); - if (lineBreaks) { - text = diffService.formatDiffWithLineNumbers(text, line_length, affected_lines.from); - } - - output.push({ - "paragraphNo": paragraphNo, - "paragraphLineFrom": paragraph_line_range.from, - "paragraphLineTo": paragraph_line_range.to, - "diffLineFrom": affected_lines.from, - "diffLineTo": affected_lines.to, - "textPre": textPre, - "text": text, - "textPost": textPost - }); - }); - - diffCache.put(cacheKey, output); - - return output; - }, - getAmendmentParagraphsLinesDiff: function (versionId) { - /* - * @param versionId [if undefined, active_version will be used] - * - */ - return this.getAmendmentParagraphsLinesByMode('diff', versionId, true); - }, - getAmendmentsAffectedLinesChanged: function () { - var paragraph_diff = this.getAmendmentParagraphsByMode("diff")[0], - affected_lines = diffService.detectAffectedLineRange(paragraph_diff.text); - - var extracted_lines = diffService.extractRangeByLineNumbers(paragraph_diff.text, affected_lines.from, affected_lines.to); - - var diff_html = extracted_lines.outerContextStart + extracted_lines.innerContextStart + - extracted_lines.html + extracted_lines.innerContextEnd + extracted_lines.outerContextEnd; - diff_html = diffService.diffHtmlToFinalText(diff_html); - - return { - "line_from": affected_lines.from, - "line_to": affected_lines.to, - "text": diff_html - }; - }, - getUnifiedChangeObject: function () { - var paragraph = this.getAmendmentParagraphsByMode("diff")[0]; - var affected_lines = diffService.detectAffectedLineRange(paragraph.text); - - if (!affected_lines) { - // no changes, no object to use - return null; - } - - var extracted_lines = diffService.extractRangeByLineNumbers(paragraph.text, affected_lines.from, affected_lines.to); - var lineLength = Config.get('motions_line_length').value; - - var diff_html = diffService.formatDiffWithLineNumbers(extracted_lines, lineLength, affected_lines.from); - - var acceptance_state = null; - var rejection_state = null; - this.state.getRecommendations().forEach(function(state) { - if (state.name === "accepted") { - acceptance_state = state.id; - } - if (state.name === "rejected") { - rejection_state = state.id; - } - }); - - // The interface of this object needs to be synchronized with the same method in MotionChangeRecommendation - // - // The change object needs to be cached to prevent confusing Angular's change detection - // Otherwise, a new object would be created with every call, leading to flickering - var amendment = this; - - if (this._change_object === undefined) { - // Properties that are guaranteed to be constant - this._change_object = { - "type": "amendment", - "id": "amendment-" + amendment.id, - "original": amendment, - "saveStatus": function () { - // The status needs to be reset first, as the workflow does not allow changing from - // acceptance to rejection directly or vice-versa. - amendment.setState(null).then(function () { - if (amendment._change_object.accepted) { - amendment.setState(acceptance_state); - } - if (amendment._change_object.rejected) { - amendment.setState(rejection_state); - } - }); - }, - "getDiff": function (motion, version, highlight) { - if (highlight > 0) { - diff_html = lineNumberingService.highlightLine(diff_html, highlight); - } - return diff_html; - } - }; - } - - // Properties that might change when the Amendment is edited - this._change_object.line_from = affected_lines.from; - this._change_object.line_to = affected_lines.to; - - this._change_object.accepted = false; - this._change_object.rejected = false; - if (this.state && this.state.name === 'rejected') { - this._change_object.rejected = true; - } else if (this.state && this.state.name === 'accepted') { - this._change_object.accepted = true; - } else if (this.recommendation && this.recommendation.name === 'rejected') { - this._change_object.rejected = true; - } - - UnifiedChangeObjectCollission.populate(this._change_object); - - return this._change_object; - }, - setTextStrippingLineBreaks: function (text) { - this.text = lineNumberingService.stripLineNumbers(text); - }, - setModifiedFinalVersionStrippingLineBreaks: function (html) { - this.modified_final_version = lineNumberingService.stripLineNumbers(html); - }, - // Copies to final version to the modified_final_version field - copyModifiedFinalVersionStrippingLineBreaks: function () { - var finalVersion = this.getTextByMode('agreed'); - this.setModifiedFinalVersionStrippingLineBreaks(finalVersion); - }, - getModifiedFinalVersion: function (versionId) { - return this.getVersion(versionId).modified_final_version; - }, - getReason: function (versionId) { - return this.getVersion(versionId).reason; - }, - // full state name - optional with custom state name extension - // depended by state and provided by a custom comment field - getStateName: function () { - var name = ''; - if (this.state) { - name = gettextCatalog.getString(this.state.name); - if (this.state.show_state_extension_field) { - // check motion comment fields for flag 'forState' - var commentFieldForStateId = MotionComment.getFieldIdForFlag('forState'); - if (commentFieldForStateId > -1) { - name += ' ' + this.comments[commentFieldForStateId]; - } - } - } - return MotionStateAndRecommendationParser.parse(name); - }, - // ID of the state - or null, if to be reset - setState: function(state_id) { - if (state_id === null) { - return $http.put('/rest/motions/motion/' + this.id + '/set_state/', {}); - } else { - return $http.put('/rest/motions/motion/' + this.id + '/set_state/', {'state': state_id}); - } - }, - // full recommendation string - optional with custom recommendationextension - // depended by state and provided by a custom comment field - getRecommendationName: function () { - var recommendation = ''; - if (Config.get('motions_recommendations_by').value !== '' && this.recommendation) { - recommendation = gettextCatalog.getString(this.recommendation.recommendation_label); - if (this.recommendation.show_recommendation_extension_field) { - // check motion comment fields for flag 'forRecommendation' - var commentFieldForRecommendationId = MotionComment.getFieldIdForFlag('forRecommendation'); - if (commentFieldForRecommendationId > -1) { - recommendation += ' ' + this.comments[commentFieldForRecommendationId]; - } - } - } - return MotionStateAndRecommendationParser.parse(recommendation); - }, - // ID of the state - or null, if to be reset - setRecommendation: function(recommendation_id) { - if (recommendation_id === null) { - return $http.put('/rest/motions/motion/' + this.id + '/set_recommendation/', {}); - } else { - return $http.put('/rest/motions/motion/' + this.id + '/set_recommendation/', {'recommendation': recommendation_id}); - } - }, - // link name which is shown in search result - getSearchResultName: function () { - return this.getTitle(); - }, - // return true if a specific relation matches for given searchquery - // e.g. submitter, supporters or category - hasSearchResult: function (results, searchquery) { - var motion = this; - // search for submitters and supporters (check if any user.id from already found users matches) - var foundSomething = _.some(results, function(result) { - if (result.getResourceName() === "users/user") { - if (_.some(motion.submitters, {'id': result.id})) { - return true; - } else if (_.some(motion.supporters, { 'id': result.id })) { - return true; - } - } - }); - // search for category - if (!foundSomething && motion.category && motion.category.name.match(new RegExp(searchquery, 'i'))) { - foundSomething = true; - } - - // search for change recommendation - if (!foundSomething) { - var recommendations = MotionChangeRecommendation.filter({ - where: {motion_version_id: this.active_version} - }); - foundSomething = _.some(recommendations, function(recommendation) { - if (recommendation.text.match(new RegExp(searchquery, 'i'))) { - return true; - } - }); - } - return foundSomething; - }, - getTextChangeRecommendations: function (versionId, order) { - /* - * Returns all change recommendations for this given version, sorted by line - * @param versionId - * @param order ['DESC' or 'ASC' (default)] - * @returns {*} - */ - versionId = versionId || this.active_version; - order = order || 'ASC'; - return MotionChangeRecommendation.filter({ - where: { - motion_version_id: versionId - }, - orderBy: [ - ['line_from', order] - ] - }).filter(function(change) { - return change.isTextRecommendation(); - }); - }, - getTitleChangeRecommendation: function (versionId) { - /** - * Returns the change recommendation affecting the title, or null - * @param versionId - * @returns MotionChangeRecommendation|null - */ - versionId = versionId || this.active_version; - var changes = MotionChangeRecommendation.filter({ - where: { - motion_version_id: versionId, - line_from: 0, - line_to: 0 - } - }); - return (changes.length > 0 ? changes[0] : null); - }, - getAmendments: function () { - return DS.filter('motions/motion', {parent_id: this.id}); - }, - hasAmendments: function () { - return DS.filter('motions/motion', {parent_id: this.id}).length > 0; - }, - getParagraphBasedAmendments: function () { - return DS.filter('motions/motion', {parent_id: this.id}).filter(function(amendment) { - return (amendment.isParagraphBasedAmendment()); - }); - }, - getParagraphBasedAmendmentsForDiffView: function () { - return _.filter(this.getParagraphBasedAmendments(), function(amendment) { - // If no accepted/rejected status is given, only amendments that have a recommendation - // of "accepted" and have not been officially rejected are to be shown in the diff-view - if (amendment.state && amendment.state.name === 'rejected') { - return false; - } - if (amendment.state && amendment.state.name === 'accepted') { - return true; - } - return (amendment.recommendation && amendment.recommendation.name === 'accepted'); - }); - }, - getParentMotion: function () { - if (this.parent_id > 0) { - var parents = DS.filter('motions/motion', {id: this.parent_id}); - if (parents.length > 0) { - return parents[0]; - } else { - return null; - } - } else { - return null; - } - }, - isAllowed: function (action) { - /* - * Return true if the requested user is allowed to do the specific action. - * There are the following possible actions. - * - see - * - update - * - update_submitters - * - delete - * - create_poll - * - support - * - unsupport - * - change_state - * - reset_state - * - change_comments - * - change_recommendation - * - can_manage - * - can_see_amendments - * - can_create_amendments - * - * NOTE: If you update this function please think about - * server permissions, see motions/views.py. - */ - switch (action) { - case 'see': - return ( - operator.hasPerms('motions.can_see') && - ( - !this.state.required_permission_to_see || - operator.hasPerms(this.state.required_permission_to_see) || - (operator.user in this.submitters) - ) - ); - case 'update': - return ( - operator.hasPerms('motions.can_manage') || - ( - (_.indexOf(this.submitters, operator.user) !== -1) && - this.state.allow_submitter_edit - ) - ); - case 'update_submitters': - return operator.hasPerms('motions.can_manage'); - case 'delete': - return ( - operator.hasPerms('motions.can_manage') || - ( - (_.indexOf(this.submitters, operator.user) !== -1) && - this.state.allow_submitter_edit - ) - ); - case 'create_poll': - return ( - operator.hasPerms('motions.can_manage') && - this.state && - this.state.allow_create_poll - ); - case 'support': - return ( - operator.hasPerms('motions.can_support') && - this.state.allow_support && - Config.get('motions_min_supporters').value > 0 && - (_.indexOf(this.submitters, operator.user) === -1) && - (_.indexOf(this.supporters, operator.user) === -1) - ); - case 'unsupport': - return this.state.allow_support && _.indexOf(this.supporters, operator.user) !== -1; - case 'change_state': - return operator.hasPerms('motions.can_manage'); - case 'reset_state': - return operator.hasPerms('motions.can_manage'); - case 'change_comments': - return operator.hasPerms('motions.can_manage_comments'); - case 'change_recommendation': - return operator.hasPerms('motions.can_manage'); - case 'can_manage': - return operator.hasPerms('motions.can_manage'); - case 'can_see_amendments': - var result; - if (operator.hasPerms('motions.can_create')) { - result = Config.get('motions_amendments_enabled').value && - (this.hasAmendments() || this.isAllowed('can_create_amendment')); - } else if (operator.hasPerms('motions.can_see')) { - result = Config.get('motions_amendments_enabled').value && this.hasAmendments(); - } - return result; - case 'can_create_amendment': - return ( - operator.hasPerms('motions.can_create') && - Config.get('motions_amendments_enabled').value && - ( !this.isAmendment || - (this.isAmendment && OpenSlidesSettings.MOTIONS_ALLOW_AMENDMENTS_OF_AMENDMENTS)) - ); - default: - return false; - } - }, - /* Overrides from jsDataModel factory. - * Also sets the projection mode if given; If not it projects in 'original' mode. */ - project: function (projectorId, mode) { - // if this object is already projected on projectorId, delete this element from this projector - var requestData = { - clear_ids: this.isProjected(), - }; - // Was there a projector with the same id and mode as the given id and mode? - // If not, project the motion. - var wasProjectedBefore = _.some(this.isProjectedWithMode(), function (mapping) { - var value = (mapping.projectorId === projectorId); - if (mode) { - value = value && (mapping.mode === mode); - } - return value; - }); - mode = mode || Config.get('motions_recommendation_text_mode').value; - if (!wasProjectedBefore) { - requestData.prune = { - id: projectorId, - element: { - name: name, - id: this.id, - mode: mode, - }, - }; - } - return ProjectHelper.project(requestData); - }, - isProjected: function (mode) { - var self = this; - var predicate = function (element) { - var value = element.name === name && - element.id === self.id; - if (mode) { - value = value && (element.mode === mode); - } - return value; - }; - var projectorIds = []; - _.forEach(Projector.getAll(), function (projector) { - if (typeof _.findKey(projector.elements, predicate) === 'string') { - projectorIds.push(projector.id); - } - }); - return projectorIds; - }, - /* returns a list of mappings between projector id and mode: - * [ {projectorId: 2, mode: 'original'}, ... ] */ - isProjectedWithMode: function () { - var self = this; - var mapping = []; - _.forEach(Projector.getAll(), function (projector) { - _.forEach(projector.elements, function (element) { - if (element.name === name && element.id === self.id) { - mapping.push({ - projectorId: projector.id, - mode: element.mode || 'original', - }); - } - }); - }); - return mapping; - }, - isRelatedProjected: function () { - // A motion related object is the list of speakers (through the agenda item) - if (this.agenda_item) { - return this.agenda_item.isListOfSpeakersProjected(); - } else { - return []; - } - }, - }, - relations: { - belongsTo: { - 'motions/category': { - localField: 'category', - localKey: 'category_id', - }, - 'motions/motion-block': { - localField: 'motionBlock', - localKey: 'motion_block_id', - }, - 'agenda/item': { - localKey: 'agenda_item_id', - localField: 'agenda_item', - } - }, - hasMany: { - 'core/tag': { - localField: 'tags', - localKeys: 'tags_id', - }, - 'mediafiles/mediafile': { - localField: 'attachments', - localKeys: 'attachments_id', - }, - 'users/user': { - localField: 'supporters', - localKeys: 'supporters_id', - }, - 'motions/motion-poll': { - localField: 'polls', - foreignKey: 'motion_id', - }, - 'motions/submitter': { - localField: 'submitters', - foreignKey: 'motion_id', - }, - }, - hasOne: { - 'motions/state': [ - { - localField: 'state', - localKey: 'state_id', - }, - { - localField: 'recommendation', - localKey: 'recommendation_id', - } - ] - } - } - }); - } -]) - -// Service for generic comment fields -.factory('MotionComment', [ - '$filter', - 'Config', - 'operator', - 'Editor', - function ($filter, Config, operator, Editor) { - return { - isSpecialCommentField: function (field) { - if (field) { - return field.forState || field.forRecommendation; - } else { - return false; - } - }, - getCommentsFields: function () { - var fields = Config.get('motions_comments').value; - return $filter('excludeDeletedAndForbiddenCommentsFields')(fields); - }, - getNoSpecialCommentsFields: function () { - var fields = this.getCommentsFields(); - return $filter('excludeSpecialCommentsFields')(fields); - }, - getFormFields: function () { - var fields = this.getNoSpecialCommentsFields(); - return _.map(fields, function (field, id) { - return { - key: 'comment_' + id, - type: 'editor', - templateOptions: { - label: field.name, - }, - data: { - ckeditorOptions: Editor.getOptions() - }, - hide: !operator.hasPerms("motions.can_manage_comments") - }; - } - ); - }, - getFormField : function (id) { - var fields = this.getNoSpecialCommentsFields(); - var field = fields[id]; - if (field) { - return { - key: 'comment_' + id, - type: 'editor', - templateOptions: { - label: field.name, - }, - data: { - ckeditorOptions: Editor.getOptions() - }, - hide: !operator.hasPerms("motions.can_manage_comments") - }; - } - }, - populateFields: function (motion) { - // Populate content of motion.comments to the single comment - var fields = this.getCommentsFields(); - if (motion.comments) { - _.forEach(fields, function (field, id) { - motion['comment_' + id] = motion.comments[id]; - }); - } - }, - populateFieldsReverse: function (motion) { - // Reverse equivalent to populateFields. - var fields = this.getCommentsFields(); - motion.comments = {}; - _.forEach(fields, function (field, id) { - motion.comments[id] = motion['comment_' + id] || ''; - }); - }, - getFieldIdForFlag: function (flag) { - var fields = this.getCommentsFields(); - return _.findKey(fields, [flag, true]); - }, - }; - } -]) - -.filter('excludeSpecialCommentsFields', [ - 'MotionComment', - function (MotionComment) { - return function (commentsFields) { - var withoutSpecialCommentsFields = {}; - _.forEach(commentsFields, function (field, id) { - if (!MotionComment.isSpecialCommentField(field)) { - withoutSpecialCommentsFields[id] = field; - } - }); - return withoutSpecialCommentsFields; - }; - } -]) - -.filter('excludeDeletedAndForbiddenCommentsFields', [ - 'MotionComment', - 'operator', - function (MotionComment, operator) { - return function (commentsFields) { - var withoutDeletedAndForbiddenCommentsFields = {}; - _.forEach(commentsFields, function (field, id) { - if (field && (field.public || operator.hasPerms('motions.can_see_comments'))) { - withoutDeletedAndForbiddenCommentsFields[id] = field; - } - }); - return withoutDeletedAndForbiddenCommentsFields; - }; - } -]) - -.factory('Category', [ - 'DS', - function(DS) { - return DS.defineResource({ - name: 'motions/category', - }); - } -]) - -.factory('MotionChangeRecommendation', [ - 'DS', - 'Config', - 'jsDataModel', - 'diffService', - 'lineNumberingService', - 'UnifiedChangeObjectCollission', - 'gettextCatalog', - function (DS, Config, jsDataModel, diffService, lineNumberingService, - UnifiedChangeObjectCollission, gettextCatalog) { - return DS.defineResource({ - name: 'motions/motion-change-recommendation', - useClass: jsDataModel, - methods: { - saveStatus: function() { - this.DSSave(); - }, - isTitleRecommendation: function() { - return (this.line_from === 0 && this.line_to === 0); - }, - isTextRecommendation: function() { - return (this.line_from !== 0 || this.line_to !== 0); - }, - getDiff: function(motion, version, highlight) { - var lineLength = Config.get('motions_line_length').value, - html = lineNumberingService.insertLineNumbers(motion.getVersion(version).text, lineLength), - data, oldText; - - try { - data = diffService.extractRangeByLineNumbers(html, this.line_from, this.line_to); - oldText = data.outerContextStart + data.innerContextStart + - data.html + data.innerContextEnd + data.outerContextEnd; - } catch (e) { - // This only happens (as far as we know) when the motion text has been altered (shortened) - // without modifying the change recommendations accordingly. - // That's a pretty serious inconsistency that should not happen at all, - // we're just doing some basic damage control here. - var msg = 'Inconsistent data. A change recommendation is probably referring to a non-existant line number.'; - return '' + msg + ''; - } - oldText = lineNumberingService.insertLineNumbers(oldText, lineLength, null, null, this.line_from); - var diff = diffService.diff(oldText, this.text); - - // If an insertion makes the line longer than the line length limit, we need two line breaking runs: - // - First, for the official line numbers, ignoring insertions (that's been done some lines before) - // - Second, another one to prevent the displayed including insertions to exceed the page width - diff = lineNumberingService.insertLineBreaksWithoutNumbers(diff, lineLength, true); - - if (highlight > 0) { - diff = lineNumberingService.highlightLine(diff, highlight); - } - - var origBeginning = data.outerContextStart + data.innerContextStart; - if (diff.toLowerCase().indexOf(origBeginning.toLowerCase()) === 0) { - // Add "merge-before"-css-class if the first line begins in the middle of a paragraph. Used for PDF. - diff = diffService.addCSSClassToFirstTag(origBeginning, "merge-before") + diff.substring(origBeginning.length); - } - - return diff; - }, - getType: function(original_full_html) { - return this.type; - }, - getTitle: function(original_full_html) { - var title; - if (this.line_to > (this.line_from + 1)) { - title = gettextCatalog.getString('%TYPE% from line %FROM% to %TO%'); - } else { - title = gettextCatalog.getString('%TYPE% in line %FROM%'); - } - switch (this.getType(original_full_html)) { - case diffService.TYPE_INSERTION: - title = title.replace('%TYPE%', gettextCatalog.getString('Insertion')); - break; - case diffService.TYPE_DELETION: - title = title.replace('%TYPE%', gettextCatalog.getString('Deletion')); - break; - case diffService.TYPE_REPLACEMENT: - title = title.replace('%TYPE%', gettextCatalog.getString('Replacement')); - break; - case diffService.TYPE_OTHER: - title = title.replace('%TYPE%', this.other_description); - break; - } - title = title.replace('%FROM%', this.line_from).replace('%TO%', (this.line_to - 1)); - return title; - }, - getUnifiedChangeObject: function () { - // The interface of this object needs to be synchronized with the same method in Motion - // - // The change object needs to be cached to prevent confusing Angular's change detection - // Otherwise, a new object would be created with every call, leading to flickering - var recommendation = this; - - if (this._change_object === undefined) { - // Properties that are guaranteed to be constant - this._change_object = { - "type": "recommendation", - "other_description": recommendation.other_description, - "id": "recommendation-" + recommendation.id, - "original": recommendation, - "saveStatus": function () { - recommendation.rejected = recommendation._change_object.rejected; - recommendation.saveStatus(); - }, - "getDiff": function (motion, version, highlight) { - return recommendation.getDiff(motion, version, highlight); - } - }; - } - // Properties that might change when the Change Recommendation is edited - this._change_object.line_from = recommendation.line_from; - this._change_object.line_to = recommendation.line_to; - this._change_object.rejected = recommendation.rejected; - this._change_object.accepted = !recommendation.rejected; - - UnifiedChangeObjectCollission.populate(this._change_object); - - return this._change_object; - } - } - }); - } -]) - -.factory('UnifiedChangeObjectCollission', [ - function () { - return { - populate: function (obj) { - obj.otherChanges = []; - obj.setOtherChangesForCollission = function (changes) { - obj.otherChanges = changes; - }; - obj.getCollissions = function(onlyAccepted) { - return obj.otherChanges.filter(function(otherChange) { - if (onlyAccepted && !otherChange.accepted) { - return false; - } - return (otherChange.id !== obj.id && ( - (otherChange.line_from >= obj.line_from && otherChange.line_from < obj.line_to) || - (otherChange.line_to > obj.line_from && otherChange.line_to <= obj.line_to) || - (otherChange.line_from < obj.line_from && otherChange.line_to > obj.line_to) - )); - }); - }; - obj.getAcceptedCollissions = function() { - return obj.getCollissions().filter(function(colliding) { - return colliding.accepted; - }); - }; - obj.setAccepted = function($event) { - if (obj.getAcceptedCollissions().length > 0) { - $event.preventDefault(); - $event.stopPropagation(); - return; - } - obj.accepted = true; - obj.rejected = false; - obj.saveStatus(); - }; - obj.setRejected = function($event) { - obj.rejected = true; - obj.accepted = false; - obj.saveStatus(); - }; - }, - }; - } -]) - -.run([ - 'Motion', - 'Category', - 'Workflow', - 'MotionState', - 'MotionChangeRecommendation', - 'Submitter', - function(Motion, Category, Workflow, MotionState, MotionChangeRecommendation, Submitter) {} -]) - - -// Mark all motion workflow state strings for translation in JavaScript. -// (see motions/signals.py) -.config([ - 'gettext', - function (gettext) { - // workflow 1 - gettext('Simple Workflow'); - gettext('submitted'); - gettext('accepted'); - gettext('Accept'); - gettext('Acceptance'); - gettext('rejected'); - gettext('Reject'); - gettext('Rejection'); - gettext('not decided'); - gettext('Do not decide'); - gettext('No decision'); - // workflow 2 - gettext('Complex Workflow'); - gettext('published'); - gettext('permitted'); - gettext('Permit'); - gettext('Permission'); - gettext('accepted'); - gettext('Accept'); - gettext('Acceptance'); - gettext('rejected'); - gettext('Reject'); - gettext('Rejection'); - gettext('withdrawed'); - gettext('Withdraw'); - gettext('adjourned'); - gettext('Adjourn'); - gettext('Adjournment'); - gettext('not concerned'); - gettext('Do not concern'); - gettext('No concernment'); - gettext('refered to committee'); - gettext('Refer to committee'); - gettext('Referral to committee'); - gettext('needs review'); - gettext('Needs review'); - gettext('rejected (not authorized)'); - gettext('Reject (not authorized)'); - gettext('Rejection (not authorized)'); - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/csv.js b/openslides/motions/static/js/motions/csv.js deleted file mode 100644 index b236569bd..000000000 --- a/openslides/motions/static/js/motions/csv.js +++ /dev/null @@ -1,210 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.motions.csv', []) - -.factory('MotionCsvExport', [ - '$filter', - 'gettextCatalog', - 'Config', - 'CsvDownload', - 'lineNumberingService', - function ($filter, gettextCatalog, Config, CsvDownload, lineNumberingService) { - var makeHeaderline = function (params) { - var headerline = ['Identifier', 'Title']; - if (params.include.text) { - headerline.push('Text'); - } - if (params.include.reason) { - headerline.push('Reason'); - } - if (params.include.submitters) { - headerline.push('Submitter'); - } - headerline.push('Category'); - if (params.include.origin) { - headerline.push('Origin'); - } - if (params.include.motionBlock) { - headerline.push('Motion block'); - } - return _.map(headerline, function (entry) { - return gettextCatalog.getString(entry); - }); - }; - return { - export: function (motions, params) { - if (!params) { - params = {}; - } - _.defaults(params, { - changeRecommendationMode: Config.get('motions_recommendation_text_mode').value, - include: { - text: true, - reason: true, - submitters: true, - origin: true, - motionBlock: true, - state: true, - recommendation: true, - }, - }); - params.filename = gettextCatalog.getString('motions') + '.csv'; - if (!_.includes(['original', 'changed', 'agreed'], params.changeRecommendationMode)) { - params.changeRecommendationMode = 'original'; - } - - var csvRows = [ - makeHeaderline(params) - ]; - _.forEach(motions, function (motion) { - var text = motion.getTextByMode(params.changeRecommendationMode, null, null, false); - var row = []; - // Identifier and title - row.push('"' + motion.identifier !== null ? motion.identifier : '' + '"'); - row.push('"' + motion.getTitle() + '"'); - - // Text - if (params.include.text) { - row.push('"' + text + '"'); - } - - // Reason - if (params.include.reason) { - row.push('"' + motion.getReason() + '"'); - } - - // Submitters - if (params.include.submitters) { - var submitters = []; - _.forEach($filter('orderBy')(motion.submitters, 'weight'), function (user) { - var user_short_name = [ - user.user.title, - user.user.first_name, - user.user.last_name - ].join(' ').trim(); - submitters.push(user_short_name); - }); - row.push('"' + submitters.join('; ') + '"'); - } - - // Category - var category = motion.category ? motion.category.name : ''; - row.push('"' + category + '"'); - - // Origin - if (params.include.origin) { - row.push('"' + motion.origin + '"'); - } - - // Motion block - if (params.include.motionBlock) { - var blockTitle = motion.motionBlock ? motion.motionBlock.title : ''; - row.push('"' + blockTitle + '"'); - } - - csvRows.push(row); - }); - CsvDownload(csvRows, params.filename); - }, - downloadExample: function () { - var csvRows = [makeHeaderline({ include: { - text: true, - reason: true, - submitters: true, - origin: true, - motionBlock: true, - state: true, - recommendation: true, - }}), - // example entries - ['A1', 'Title 1', 'Text 1', 'Reason 1', 'Submitter A', 'Category A', 'Last Year Conference A', 'Block A'], - ['B1', 'Title 2', 'Text 2', 'Reason 2', 'Submitter B', 'Category B', '', 'Block A'], - ['' , 'Title 3', 'Text 3', '', '', '', '', ''], - ]; - CsvDownload(csvRows, gettextCatalog.getString('motions-example') + '.csv'); - }, - }; - } -]) - -.factory('AmendmentCsvExport', [ - 'gettextCatalog', - 'CsvDownload', - 'lineNumberingService', - function (gettextCatalog, CsvDownload, lineNumberingService) { - var makeHeaderline = function () { - var headerline = ['Identifier', 'Submitters', 'Category', 'Motion block', - 'Leadmotion', 'Line', 'Old text', 'New text']; - return _.map(headerline, function (entry) { - return gettextCatalog.getString(entry); - }); - }; - return { - export: function (amendments) { - var csvRows = [ - makeHeaderline() - ]; - _.forEach(amendments, function (amendment) { - var row = []; - // Identifier and title - row.push('"' + amendment.identifier !== null ? amendment.identifier : '' + '"'); - // Submitters - var submitters = []; - angular.forEach(amendment.submitters, function(user) { - var user_short_name = [user.title, user.first_name, user.last_name].join(' ').trim(); - submitters.push(user_short_name); - }); - row.push('"' + submitters.join('; ') + '"'); - - // Category - var category = amendment.category ? amendment.category.name : ''; - row.push('"' + category + '"'); - - // Motion block - var blockTitle = amendment.motionBlock ? amendment.motionBlock.title : ''; - row.push('"' + blockTitle + '"'); - - // Lead motion - var leadmotion = amendment.getParentMotion(); - if (leadmotion) { - var leadmotionTitle = leadmotion.identifier ? leadmotion.identifier + ': ' : ''; - leadmotionTitle += leadmotion.getTitle(); - row.push('"' + leadmotionTitle + '"'); - } else { - row.push('""'); - } - - // changed paragraph - if (amendment.isParagraphBasedAmendment()) { - // TODO: get old and new paragraphLine. Resolve todo - // in motion.getAmendmentParagraphsLinesByMode - var p_old = amendment.getAmendmentParagraphsLinesByMode('original', null, false)[0]; - //var p_new = amendment.getAmendmentParagraphsLinesByMode('changed', null, false)[0]; - var lineStr = p_old.diffLineFrom; - if (p_old.diffLineTo != p_old.diffLineFrom + 1) { - lineStr += '-' + p_old.diffLineTo; - } - row.push('"' + lineStr + '"'); - //row.push('"' + p_old.text.html + '"'); - //row.push('"' + p_new.text.html + '"'); - - // Work around: Export the full paragraphs instead of changed lines - row.push('"' + amendment.getAmendmentParagraphsByMode('original', null, false)[0].text + '"'); - row.push('"' + amendment.getAmendmentParagraphsByMode('changed', null, false)[0].text + '"'); - } else { - row.push('""'); - row.push('""'); - row.push('"' + amendment.getText() + '"'); - } - - csvRows.push(row); - }); - CsvDownload(csvRows, 'amendments-export.csv'); - }, - }; - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/diff.js b/openslides/motions/static/js/motions/diff.js deleted file mode 100644 index d6abfbf65..000000000 --- a/openslides/motions/static/js/motions/diff.js +++ /dev/null @@ -1,1589 +0,0 @@ -(function () { - -"use strict"; - -angular.module('OpenSlidesApp.motions.diff', ['OpenSlidesApp.motions.lineNumbering']) - -.service('diffService', [ - 'lineNumberingService', - '$cacheFactory', - function (lineNumberingService, $cacheFactory) { - var ELEMENT_NODE = 1, - TEXT_NODE = 3, - DOCUMENT_FRAGMENT_NODE = 11; - - var diffCache = $cacheFactory('diff.service'); - - this.TYPE_REPLACEMENT = 0; - this.TYPE_INSERTION = 1; - this.TYPE_DELETION = 2; - this.TYPE_OTHER = 3; - - this.getLineNumberNode = function(fragment, lineNumber) { - return fragment.querySelector('os-linebreak.os-line-number.line-number-' + lineNumber); - }; - - /** - * @param {Element} element - */ - this._getFirstLineNumberNode = function(element) { - if (element.nodeType === TEXT_NODE) { - return null; - } - if (element.nodeName === 'OS-LINEBREAK') { - return element; - } - var found = element.querySelectorAll('OS-LINEBREAK'); - if (found.length > 0) { - return found.item(0); - } else { - return null; - } - }; - - /** - * @param {Element} element - */ - this._getLastLineNumberNode = function(element) { - if (element.nodeType === TEXT_NODE) { - return null; - } - if (element.nodeName === 'OS-LINEBREAK') { - return element; - } - var found = element.querySelectorAll('OS-LINEBREAK'); - if (found.length > 0) { - return found.item(found.length - 1); - } else { - return null; - } - }; - - this._getNodeContextTrace = function(node) { - var context = [], - currNode = node; - while (currNode) { - context.unshift(currNode); - currNode = currNode.parentNode; - } - return context; - }; - - this._isFirstNonemptyChild = function(node, child) { - for (var i = 0; i < node.childNodes.length; i++) { - if (node.childNodes[i] === child) { - return true; - } - if (node.childNodes[i].nodeType !== TEXT_NODE || node.childNodes[i].nodeValue.match(/\S/)) { - return false; - } - } - return false; - }; - - // Adds elements like - this._insertInternalLineMarkers = function(fragment) { - if (fragment.querySelectorAll('OS-LINEBREAK').length > 0) { - // Prevent duplicate calls - return; - } - var lineNumbers = fragment.querySelectorAll('span.os-line-number'), - lineMarker, maxLineNumber; - - for (var i = 0; i < lineNumbers.length; i++) { - var insertBefore = lineNumbers[i]; - while (insertBefore.parentNode.nodeType !== DOCUMENT_FRAGMENT_NODE && - this._isFirstNonemptyChild(insertBefore.parentNode, insertBefore)) { - insertBefore = insertBefore.parentNode; - } - lineMarker = document.createElement('OS-LINEBREAK'); - lineMarker.setAttribute('data-line-number', lineNumbers[i].getAttribute('data-line-number')); - lineMarker.setAttribute('class', lineNumbers[i].getAttribute('class')); - insertBefore.parentNode.insertBefore(lineMarker, insertBefore); - maxLineNumber = lineNumbers[i].getAttribute('data-line-number'); - } - - // Add one more "fake" line number at the end and beginning, so we can select the last line as well - lineMarker = document.createElement('OS-LINEBREAK'); - lineMarker.setAttribute('data-line-number', (parseInt(maxLineNumber) + 1)); - lineMarker.setAttribute('class', 'os-line-number line-number-' + (parseInt(maxLineNumber) + 1)); - fragment.appendChild(lineMarker); - - lineMarker = document.createElement('OS-LINEBREAK'); - lineMarker.setAttribute('data-line-number', '0'); - lineMarker.setAttribute('class', 'os-line-number line-number-0'); - fragment.insertBefore(lineMarker, fragment.firstChild); - }; - - // @TODO Check if this is actually necessary - this._insertInternalLiNumbers = function(fragment) { - if (fragment.querySelectorAll('LI[os-li-number]').length > 0) { - // Prevent duplicate calls - return; - } - var ols = fragment.querySelectorAll('OL'); - for (var i = 0; i < ols.length; i++) { - var ol = ols[i], - liNo = 0; - for (var j = 0; j < ol.childNodes.length; j++) { - if (ol.childNodes[j].nodeName == 'LI') { - liNo++; - ol.childNodes[j].setAttribute('os-li-number', liNo); - } - } - } - }; - - this._addStartToOlIfNecessary = function(node) { - var firstLiNo = null; - for (var i = 0; i < node.childNodes.length && firstLiNo === null; i++) { - if (node.childNode[i].nodeName == 'LI') { - var lineNo = node.childNode[i].getAttribute('ol-li-number'); - if (lineNo) { - firstLiNo = parseInt(lineNo); - } - } - } - if (firstLiNo > 1) { - node.setAttribute('start', firstLiNo); - } - }; - - this._isWithinNthLIOfOL = function(olNode, descendantNode) { - var nthLIOfOL = null; - while (descendantNode.parentNode) { - if (descendantNode.parentNode === olNode) { - var lisBeforeOl = 0, - foundMe = false; - for (var i = 0; i < olNode.childNodes.length && !foundMe; i++) { - if (olNode.childNodes[i] === descendantNode) { - foundMe = true; - } else if (olNode.childNodes[i].nodeName === 'LI') { - lisBeforeOl++; - } - } - nthLIOfOL = lisBeforeOl + 1; - } - descendantNode = descendantNode.parentNode; - } - return nthLIOfOL; - }; - - /* - * Returns an array with the following values: - * 0: the most specific DOM-node that contains both line numbers - * 1: the context of node1 (an array of dom-elements; 0 is the document fragment) - * 2: the context of node2 (an array of dom-elements; 0 is the document fragment) - * 3: the index of [0] in the two arrays - */ - this._getCommonAncestor = function(node1, node2) { - var trace1 = this._getNodeContextTrace(node1), - trace2 = this._getNodeContextTrace(node2), - commonAncestor = null, - commonIndex = null, - childTrace1 = [], - childTrace2 = []; - - for (var i = 0; i < trace1.length && i < trace2.length; i++) { - if (trace1[i] == trace2[i]) { - commonAncestor = trace1[i]; - commonIndex = i; - } - } - for (i = commonIndex + 1; i < trace1.length; i++) { - childTrace1.push(trace1[i]); - } - for (i = commonIndex + 1; i < trace2.length; i++) { - childTrace2.push(trace2[i]); - } - return { - 'commonAncestor': commonAncestor, - 'trace1' : childTrace1, - 'trace2' : childTrace2, - 'index': commonIndex - }; - }; - - this._serializeTag = function(node) { - if (node.nodeType === DOCUMENT_FRAGMENT_NODE) { - // Fragments are only placeholders and do not have an HTML representation - return ''; - } - var html = '<' + node.nodeName; - for (var i = 0; i < node.attributes.length; i++) { - var attr = node.attributes[i]; - if (attr.name !== 'os-li-number') { - html += ' ' + attr.name + '="' + attr.value + '"'; - } - } - html += '>'; - return html; - }; - - this._serializeDom = function(node, stripLineNumbers) { - if (node.nodeType === TEXT_NODE) { - return node.nodeValue.replace(//g, ">"); - } - if (stripLineNumbers && ( - lineNumberingService._isOsLineNumberNode(node) || lineNumberingService._isOsLineBreakNode(node))) { - return ''; - } - if (node.nodeName === 'OS-LINEBREAK') { - return ''; - } - if (node.nodeName === 'BR') { - var br = ''; - } - - var html = this._serializeTag(node); - for (var i = 0; i < node.childNodes.length; i++) { - if (node.childNodes[i].nodeType === TEXT_NODE) { - html += node.childNodes[i].nodeValue.replace(/&/g, "&").replace(//g, ">"); - } else if (!stripLineNumbers || (!lineNumberingService._isOsLineNumberNode(node.childNodes[i]) && !lineNumberingService._isOsLineBreakNode(node.childNodes[i]))) { - html += this._serializeDom(node.childNodes[i], stripLineNumbers); - } - } - if (node.nodeType !== DOCUMENT_FRAGMENT_NODE) { - html += ''; - } - - return html; - }; - - /** - * Implementation hint: the first element of "toChildTrace" array needs to be a child element of "node" - */ - this._serializePartialDomToChild = function(node, toChildTrace, stripLineNumbers) { - if (lineNumberingService._isOsLineNumberNode(node) || lineNumberingService._isOsLineBreakNode(node)) { - return ''; - } - if (node.nodeName === 'OS-LINEBREAK') { - return ''; - } - - var html = this._serializeTag(node); - - for (var i = 0, found = false; i < node.childNodes.length && !found; i++) { - if (node.childNodes[i] === toChildTrace[0]) { - found = true; - var remainingTrace = toChildTrace; - remainingTrace.shift(); - if (!lineNumberingService._isOsLineNumberNode(node.childNodes[i])) { - html += this._serializePartialDomToChild(node.childNodes[i], remainingTrace, stripLineNumbers); - } - } else if (node.childNodes[i].nodeType === TEXT_NODE) { - html += node.childNodes[i].nodeValue; - } else { - if (!stripLineNumbers || (!lineNumberingService._isOsLineNumberNode(node.childNodes[i]) && - !lineNumberingService._isOsLineBreakNode(node.childNodes[i]))) { - html += this._serializeDom(node.childNodes[i], stripLineNumbers); - } - } - } - if (!found) { - console.trace(); - throw "Inconsistency or invalid call of this function detected (to)"; - } - return html; - }; - - /** - * Implementation hint: the first element of "toChildTrace" array needs to be a child element of "node" - */ - this._serializePartialDomFromChild = function(node, fromChildTrace, stripLineNumbers) { - if (lineNumberingService._isOsLineNumberNode(node) || lineNumberingService._isOsLineBreakNode(node)) { - return ''; - } - if (node.nodeName === 'OS-LINEBREAK') { - return ''; - } - - var html = ''; - for (var i = 0, found = false; i < node.childNodes.length; i++) { - if (node.childNodes[i] === fromChildTrace[0]) { - found = true; - var remainingTrace = fromChildTrace; - remainingTrace.shift(); - if (!lineNumberingService._isOsLineNumberNode(node.childNodes[i])) { - html += this._serializePartialDomFromChild(node.childNodes[i], remainingTrace, stripLineNumbers); - } - } else if (found) { - if (node.childNodes[i].nodeType === TEXT_NODE) { - html += node.childNodes[i].nodeValue; - } else { - if (!stripLineNumbers || (!lineNumberingService._isOsLineNumberNode(node.childNodes[i]) && - !lineNumberingService._isOsLineBreakNode(node.childNodes[i]))) { - html += this._serializeDom(node.childNodes[i], stripLineNumbers); - } - } - } - } - if (!found) { - console.trace(); - throw "Inconsistency or invalid call of this function detected (from)"; - } - if (node.nodeType !== DOCUMENT_FRAGMENT_NODE) { - html += ''; - } - return html; - }; - - /** - * @param {string} html - * @return {DocumentFragment} - */ - this.htmlToFragment = function(html) { - var fragment = document.createDocumentFragment(), - div = document.createElement('DIV'); - div.innerHTML = html; - while (div.childElementCount) { - var child = div.childNodes[0]; - div.removeChild(child); - fragment.appendChild(child); - } - return fragment; - }; - - /** - * When a
  • with a os-split-before-class (set by extractRangeByLineNumbers) is edited when creating a - * change recommendation and is split again in CKEditor, the second list items also gets that class. - * This is not correct however, as the second one actually is a new list item. So we need to remove it again. - * - * @param {string} html - * @returns {string} - */ - this.removeDuplicateClassesInsertedByCkeditor = function(html) { - var fragment = this.htmlToFragment(html); - var items = fragment.querySelectorAll('li.os-split-before'); - for (var i = 0; i < items.length; i++) { - if (!this._isFirstNonemptyChild(items[i].parentNode, items[i])) { - this.removeCSSClass(items[i], 'os-split-before'); - } - } - return this._serializeDom(fragment, false); - }; - - /** - * Returns the HTML snippet between two given line numbers. - * - * Hint: - * - The last line (toLine) is not included anymore, as the number refers to the line breaking element at the end of the line - * - if toLine === null, then everything from fromLine to the end of the fragment is returned - * - * In addition to the HTML snippet, additional information is provided regarding the most specific DOM element - * that contains the whole section specified by the line numbers (like a P-element if only one paragraph is selected - * or the most outer DIV, if multiple sections selected). - * - * This additional information is meant to render the snippet correctly without producing broken HTML - * - * The return object has the following fields: - * - html: The HTML between the two line numbers. - * Line numbers and automatically set line breaks are stripped. - * All HTML tags are converted to uppercase - * (e.g. Line 2
  • Line3
  • Line 4
    ) - * - ancestor: the most specific DOM element that contains the HTML snippet (e.g. a UL, if several LIs are selected) - * - outerContextStart: An HTML string that opens all necessary tags to get the browser into the rendering mode - * of the ancestor element (e.g.
      in the case of the multiple LIs) - * - outerContectEnd: An HTML string that closes all necessary tags from the ancestor element (e.g.
    - * - innerContextStart: A string that opens all necessary tags between the ancestor - * and the beginning of the selection (e.g.
  • ) - * - innerContextEnd: A string that closes all tags after the end of the selection to the ancestor (e.g.
  • ) - * - previousHtml: The HTML before the selected area begins (including line numbers) - * - previousHtmlEndSnippet: A HTML snippet that closes all open tags from previousHtml - * - followingHtml: The HTML after the selected area - * - followingHtmlStartSnippet: A HTML snippet that opens all HTML tags necessary to render "followingHtml" - * - * - * In some cases, the returned HTML tags receive additional CSS classes, providing information both for - * rendering it and for merging it again correctly. - * - os-split-*: These classes are set for all HTML Tags that have been split into two by this process, - * e.g. if the fromLine- or toLine-line-break was somewhere in the middle of this tag. - * If a tag is split, the first one receives "os-split-after", and the second one "os-split-before". - * For example, for the following string

    Line 1
    Line 2
    Line 3

    : - * - extracting line 1 to 2 results in

    Line 1

    - * - extracting line 2 to 3 results in

    Line 2

    - * - extracting line 3 to null/4 results in

    Line 3

    - */ - this.extractRangeByLineNumbers = function(htmlIn, fromLine, toLine) { - if (typeof(htmlIn) !== 'string') { - throw 'Invalid call - extractRangeByLineNumbers expects a string as first argument'; - } - - var cacheKey = fromLine + "-" + toLine + "-" + lineNumberingService.djb2hash(htmlIn), - cached = diffCache.get(cacheKey); - - if (!angular.isUndefined(cached)) { - return cached; - } - - var fragment = this.htmlToFragment(htmlIn); - - this._insertInternalLineMarkers(fragment); - this._insertInternalLiNumbers(fragment); - if (toLine === null) { - var internalLineMarkers = fragment.querySelectorAll('OS-LINEBREAK'); - toLine = parseInt(internalLineMarkers[internalLineMarkers.length - 1].getAttribute("data-line-number")); - } - - var fromLineNode = this.getLineNumberNode(fragment, fromLine), - toLineNode = (toLine ? this.getLineNumberNode(fragment, toLine) : null), - ancestorData = this._getCommonAncestor(fromLineNode, toLineNode); - - var fromChildTraceRel = ancestorData.trace1, - fromChildTraceAbs = this._getNodeContextTrace(fromLineNode), - toChildTraceRel = ancestorData.trace2, - toChildTraceAbs = this._getNodeContextTrace(toLineNode), - ancestor = ancestorData.commonAncestor, - htmlOut = '', - outerContextStart = '', - outerContextEnd = '', - innerContextStart = '', - innerContextEnd = '', - previousHtmlEndSnippet = '', - followingHtmlStartSnippet = '', - fakeOl, offset; - - fromChildTraceAbs.shift(); - var previousHtml = this._serializePartialDomToChild(fragment, fromChildTraceAbs, false); - toChildTraceAbs.shift(); - var followingHtml = this._serializePartialDomFromChild(fragment, toChildTraceAbs, false); - - var currNode = fromLineNode, - isSplit = false; - while (currNode.parentNode) { - if (!this._isFirstNonemptyChild(currNode.parentNode, currNode)) { - isSplit = true; - } - if (isSplit) { - this.addCSSClass(currNode.parentNode, 'os-split-before'); - } - if (currNode.nodeName !== 'OS-LINEBREAK') { - previousHtmlEndSnippet += ''; - } - currNode = currNode.parentNode; - } - - currNode = toLineNode; - isSplit = false; - while (currNode.parentNode) { - if (!this._isFirstNonemptyChild(currNode.parentNode, currNode)) { - isSplit = true; - } - if (isSplit) { - this.addCSSClass(currNode.parentNode, 'os-split-after'); - } - if (currNode.parentNode.nodeName === 'OL') { - fakeOl = currNode.parentNode.cloneNode(false); - offset = (currNode.parentNode.getAttribute("start") ? parseInt(currNode.parentNode.getAttribute("start")) - 1 : 0); - fakeOl.setAttribute('start', (this._isWithinNthLIOfOL(currNode.parentNode, toLineNode) + offset).toString()); - followingHtmlStartSnippet = this._serializeTag(fakeOl) + followingHtmlStartSnippet; - } else { - followingHtmlStartSnippet = this._serializeTag(currNode.parentNode) + followingHtmlStartSnippet; - } - currNode = currNode.parentNode; - } - - var found = false; - isSplit = false; - for (var i = 0; i < fromChildTraceRel.length && !found; i++) { - if (fromChildTraceRel[i].nodeName === 'OS-LINEBREAK') { - found = true; - } else { - if (!this._isFirstNonemptyChild(fromChildTraceRel[i], fromChildTraceRel[i + 1])) { - isSplit = true; - } - if (fromChildTraceRel[i].nodeName === 'OL') { - fakeOl = fromChildTraceRel[i].cloneNode(false); - offset = (fromChildTraceRel[i].getAttribute("start") ? parseInt(fromChildTraceRel[i].getAttribute("start")) - 1 : 0); - fakeOl.setAttribute('start', (offset + this._isWithinNthLIOfOL(fromChildTraceRel[i], fromLineNode)).toString()); - innerContextStart += this._serializeTag(fakeOl); - } else { - if (i < (fromChildTraceRel.length - 1) && isSplit) { - this.addCSSClass(fromChildTraceRel[i], 'os-split-before'); - } - innerContextStart += this._serializeTag(fromChildTraceRel[i]); - } - } - } - found = false; - for (i = 0; i < toChildTraceRel.length && !found; i++) { - if (toChildTraceRel[i].nodeName === 'OS-LINEBREAK') { - found = true; - } else { - innerContextEnd = '' + innerContextEnd; - } - } - - found = false; - for (i = 0; i < ancestor.childNodes.length; i++) { - if (ancestor.childNodes[i] === fromChildTraceRel[0]) { - found = true; - fromChildTraceRel.shift(); - htmlOut += this._serializePartialDomFromChild(ancestor.childNodes[i], fromChildTraceRel, true); - } else if (ancestor.childNodes[i] === toChildTraceRel[0]) { - found = false; - toChildTraceRel.shift(); - htmlOut += this._serializePartialDomToChild(ancestor.childNodes[i], toChildTraceRel, true); - } else if (found === true) { - htmlOut += this._serializeDom(ancestor.childNodes[i], true); - } - } - - currNode = ancestor; - while (currNode.parentNode) { - if (currNode.nodeName === 'OL') { - fakeOl = currNode.cloneNode(false); - offset = (currNode.getAttribute("start") ? parseInt(currNode.getAttribute("start")) - 1 : 0); - fakeOl.setAttribute('start', (this._isWithinNthLIOfOL(currNode, fromLineNode) + offset).toString()); - outerContextStart = this._serializeTag(fakeOl) + outerContextStart; - } else { - outerContextStart = this._serializeTag(currNode) + outerContextStart; - } - outerContextEnd += ''; - currNode = currNode.parentNode; - } - - var ret = { - 'html': htmlOut, - 'ancestor': ancestor, - 'outerContextStart': outerContextStart, - 'outerContextEnd': outerContextEnd, - 'innerContextStart': innerContextStart, - 'innerContextEnd': innerContextEnd, - 'previousHtml': previousHtml, - 'previousHtmlEndSnippet': previousHtmlEndSnippet, - 'followingHtml': followingHtml, - 'followingHtmlStartSnippet': followingHtmlStartSnippet - }; - - diffCache.put(cacheKey, ret); - return ret; - }; - - /* - * Convenience method that takes the html-attribute from an extractRangeByLineNumbers()-method, - * wraps it with the context and adds line numbers. - */ - this.formatDiffWithLineNumbers = function(diff, lineLength, firstLine) { - var text = diff.outerContextStart + diff.innerContextStart + diff.html + diff.innerContextEnd + diff.outerContextEnd; - text = lineNumberingService.insertLineNumbers(text, lineLength, null, null, firstLine); - return text; - }; - - /* - * This is a workardoun to prevent the last word of the inserted text from accidently being merged with the - * first word of the following line. - * - * This happens as trailing spaces in the change recommendation's text are frequently stripped, - * which is pretty nasty if the original text goes on after the affected line. So we insert a space - * if the original line ends with one. - */ - this._insertDanglingSpace = function(element) { - if (element.childNodes.length > 0) { - var lastChild = element.childNodes[element.childNodes.length - 1]; - if (lastChild.nodeType === TEXT_NODE && !lastChild.nodeValue.match(/[\S]/) && element.childNodes.length > 1) { - // If the text node only contains whitespaces, chances are high it's just space between block elmeents, - // like a line break between and - lastChild = element.childNodes[element.childNodes.length - 2]; - } - if (lastChild.nodeType === TEXT_NODE) { - if (lastChild.nodeValue === '' || lastChild.nodeValue.substr(-1) !== ' ') { - lastChild.nodeValue += ' '; - } - } else { - this._insertDanglingSpace(lastChild); - } - } - }; - - /* - * This functions merges to arrays of nodes. The last element of nodes1 and the first element of nodes2 - * are merged, if they are of the same type. - * - * This is done recursively until a TEMPLATE-Tag is is found, which was inserted in this.replaceLines. - * Using a TEMPLATE-Tag is a rather dirty hack, as it is allowed inside of any other element, including
      . - * - */ - this._replaceLinesMergeNodeArrays = function(nodes1, nodes2) { - if (nodes1.length === 0) { - return nodes2; - } - if (nodes2.length === 0) { - return nodes1; - } - - var out = []; - for (var i = 0; i < nodes1.length - 1; i++) { - out.push(nodes1[i]); - } - - var lastNode = nodes1[nodes1.length - 1], - firstNode = nodes2[0]; - if (lastNode.nodeType === TEXT_NODE && firstNode.nodeType === TEXT_NODE) { - var newTextNode = lastNode.ownerDocument.createTextNode(lastNode.nodeValue + firstNode.nodeValue); - out.push(newTextNode); - } else if (lastNode.nodeName === firstNode.nodeName) { - var newNode = lastNode.ownerDocument.createElement(lastNode.nodeName); - for (i = 0; i < lastNode.attributes.length; i++) { - var attr = lastNode.attributes[i]; - newNode.setAttribute(attr.name, attr.value); - } - - // Remove #text nodes inside of List elements (OL/UL), as they are confusing - var lastChildren, firstChildren; - if (lastNode.nodeName === 'OL' || lastNode.nodeName === 'UL') { - lastChildren = []; - firstChildren = []; - for (i = 0; i < firstNode.childNodes.length; i++) { - if (firstNode.childNodes[i].nodeType === ELEMENT_NODE) { - firstChildren.push(firstNode.childNodes[i]); - } - } - for (i = 0; i < lastNode.childNodes.length; i++) { - if (lastNode.childNodes[i].nodeType === ELEMENT_NODE) { - lastChildren.push(lastNode.childNodes[i]); - } - } - } else { - lastChildren = lastNode.childNodes; - firstChildren = firstNode.childNodes; - } - - var children = this._replaceLinesMergeNodeArrays(lastChildren, firstChildren); - for (i = 0; i < children.length; i++) { - newNode.appendChild(children[i]); - } - - out.push(newNode); - } else { - if (lastNode.nodeName !== 'TEMPLATE') { - out.push(lastNode); - } - if (firstNode.nodeName !== 'TEMPLATE') { - out.push(firstNode); - } - } - - for (i = 1; i < nodes2.length; i++) { - out.push(nodes2[i]); - } - - return out; - }; - - /** - * - * @param {string} html - * @returns {string} - * @private - */ - this._normalizeHtmlForDiff = function (html) { - // Convert all HTML tags to uppercase, but leave the values of attributes unchanged - // All attributes and CSS class names are sorted alphabetically - // If an attribute is empty, it is removed - html = html.replace(/<(\/?[a-z]*)( [^>]*)?>/ig, function (html, tag, attributes) { - var tagNormalized = tag.toUpperCase(); - if (attributes === undefined) { - attributes = ""; - } - var attributesList = [], - attributesMatcher = /( [^"'=]*)(= *((["'])(.*?)\4))?/gi, - match; - do { - match = attributesMatcher.exec(attributes); - if (match) { - var attrNormalized = match[1].toUpperCase(), - attrValue = match[5]; - if (match[2] !== undefined) { - if (attrNormalized === ' CLASS') { - attrValue = attrValue.split(' ').sort().join(' ').replace(/^\s+/, '').replace(/\s+$/, ''); - } - attrNormalized += "=" + match[4] + attrValue + match[4]; - } - if (attrValue !== '') { - attributesList.push(attrNormalized); - } - } - } while (match); - attributes = attributesList.sort().join(''); - return "<" + tagNormalized + attributes + ">"; - }); - - var entities = { - ' ': ' ', - '–': '-', - 'ä': 'ä', - 'ö': 'ö', - 'ü': 'ü', - 'Ä': 'Ä', - 'Ö': 'Ö', - 'Ü': 'Ü', - 'ß': 'ß', - '„': '„', - '“': '“', - '•': '•', - '§': '§', - 'é': 'é', - '€': '€' - }; - - html = html.replace(/\s+<\/P>/gi, '

      ').replace(/\s+<\/DIV>/gi, '
    ').replace(/\s+<\/LI>/gi, ''); - html = html.replace(/\s+
  • /gi, '
  • ').replace(/<\/LI>\s+/gi, '
  • '); - html = html.replace(/\u00A0/g, ' '); - html = html.replace(/\u2013/g, '-'); - for (var ent in entities) { - html = html.replace(new RegExp(ent, 'g'), entities[ent]); - } - - // Newline characters: after closing block-level-elements, but not after BR (which is inline) - html = html.replace(/(
    )\n/gi, "$1"); - html = html.replace(/[ \n\t]+/gi, ' '); - html = html.replace(/(<\/(div|p|ul|li|blockquote>)>) /gi, "$1\n"); - - return html; - }; - - this._getAllNextSiblings = function(element) { - var elements = []; - while (element.nextSibling) { - elements.push(element.nextSibling); - element = element.nextSibling; - } - return elements; - }; - - this._getAllPrevSiblingsReversed = function(element) { - var elements = []; - while (element.previousSibling) { - elements.push(element.previousSibling); - element = element.previousSibling; - } - return elements; - }; - - /** - * This returns the line number range in which changes (insertions, deletions) are encountered. - * As in extractRangeByLineNumbers(), "to" refers to the line breaking element at the end, i.e. the start of the following line. - * - * @param {string} diffHtml - */ - this.detectAffectedLineRange = function (diffHtml) { - var cacheKey = lineNumberingService.djb2hash(diffHtml), - cached = diffCache.get(cacheKey); - if (!angular.isUndefined(cached)) { - return cached; - } - - var fragment = this.htmlToFragment(diffHtml); - - this._insertInternalLineMarkers(fragment); - this._insertInternalLiNumbers(fragment); - - var changes = fragment.querySelectorAll('ins, del, .insert, .delete'), - firstChange = changes.item(0), - lastChange = changes.item(changes.length - 1), - i, j; - - if (!firstChange || !lastChange) { - // There are no changes - return null; - } - - var firstTrace = this._getNodeContextTrace(firstChange), - lastLineNumberBefore = null; - for (j = firstTrace.length - 1; j >= 0 && lastLineNumberBefore === null; j--) { - var prevSiblings = this._getAllPrevSiblingsReversed(firstTrace[j]); - for (i = 0; i < prevSiblings.length && lastLineNumberBefore === null; i++) { - lastLineNumberBefore = this._getLastLineNumberNode(prevSiblings[i]); - } - } - - var lastTrace = this._getNodeContextTrace(lastChange), - firstLineNumberAfter = null; - for (j = lastTrace.length - 1; j >= 0 && firstLineNumberAfter === null; j--) { - var nextSiblings = this._getAllNextSiblings(lastTrace[j]); - for (i = 0; i < nextSiblings.length && firstLineNumberAfter === null; i++) { - firstLineNumberAfter = this._getFirstLineNumberNode(nextSiblings[i]); - } - } - - var range = { - "from": parseInt(lastLineNumberBefore.getAttribute("data-line-number")), - "to": parseInt(firstLineNumberAfter.getAttribute("data-line-number")) - }; - - diffCache.put(cacheKey, range); - return range; - }; - - /** - * Removes .delete-nodes and -Tags (including content) - * Removes the .insert-classes and the wrapping -Tags (while maintaining content) - * @param html - */ - this.diffHtmlToFinalText = function(html) { - var fragment = this.htmlToFragment(html); - - var delNodes = fragment.querySelectorAll('.delete, del'); - for (var i = 0; i < delNodes.length; i++) { - delNodes[i].parentNode.removeChild(delNodes[i]); - } - - var insNodes = fragment.querySelectorAll('ins'); - for (i = 0; i < insNodes.length; i++) { - var ins = insNodes[i]; - while (ins.childNodes.length > 0) { - var child = ins.childNodes.item(0); - ins.removeChild(child); - ins.parentNode.insertBefore(child, ins); - } - ins.parentNode.removeChild(ins); - } - - var insertNodes = fragment.querySelectorAll('.insert'); - for (i = 0;i < insertNodes.length; i++) { - this.removeCSSClass(insertNodes[i], 'insert'); - } - - return this._serializeDom(fragment, false); - }; - - /** - * @param {string} htmlOld - * @param {string} htmlNew - * @returns {number} - */ - this.detectReplacementType = function (htmlOld, htmlNew) { - htmlOld = this._normalizeHtmlForDiff(htmlOld); - htmlNew = this._normalizeHtmlForDiff(htmlNew); - - if (htmlOld === htmlNew) { - return this.TYPE_REPLACEMENT; - } - - var i, foundDiff; - for (i = 0, foundDiff = false; i < htmlOld.length && i < htmlNew.length && foundDiff === false; i++) { - if (htmlOld[i] !== htmlNew[i]) { - foundDiff = true; - } - } - - var remainderOld = htmlOld.substr(i - 1), - remainderNew = htmlNew.substr(i - 1), - type = this.TYPE_REPLACEMENT; - - if (remainderOld.length > remainderNew.length) { - if (remainderOld.substr(remainderOld.length - remainderNew.length) === remainderNew) { - type = this.TYPE_DELETION; - } - } else if (remainderOld.length < remainderNew.length) { - if (remainderNew.substr(remainderNew.length - remainderOld.length) === remainderOld) { - type = this.TYPE_INSERTION; - } - } - - return type; - }; - - /** - * @param {string} oldHtml - * @param {string} newHTML - * @param {number} fromLine - * @param {number} toLine - */ - this.replaceLines = function (oldHtml, newHTML, fromLine, toLine) { - var data = this.extractRangeByLineNumbers(oldHtml, fromLine, toLine), - previousHtml = data.previousHtml + '' + data.previousHtmlEndSnippet, - previousFragment = this.htmlToFragment(previousHtml), - followingHtml = data.followingHtmlStartSnippet + '' + data.followingHtml, - followingFragment = this.htmlToFragment(followingHtml), - newFragment = this.htmlToFragment(newHTML); - - if (data.html.length > 0 && data.html.substr(-1) === ' ') { - this._insertDanglingSpace(newFragment); - } - - var merged = this._replaceLinesMergeNodeArrays(previousFragment.childNodes, newFragment.childNodes); - merged = this._replaceLinesMergeNodeArrays(merged, followingFragment.childNodes); - - var mergedFragment = document.createDocumentFragment(); - for (var i = 0; i < merged.length; i++) { - mergedFragment.appendChild(merged[i]); - } - - var forgottenTemplates = mergedFragment.querySelectorAll("TEMPLATE"); - for (i = 0; i < forgottenTemplates.length; i++) { - var el = forgottenTemplates[i]; - el.parentNode.removeChild(el); - } - - var forgottenSplitClasses = mergedFragment.querySelectorAll(".os-split-before, .os-split-after"); - for (i = 0; i < forgottenSplitClasses.length; i++) { - this.removeCSSClass(forgottenSplitClasses[i], 'os-split-before'); - this.removeCSSClass(forgottenSplitClasses[i], 'os-split-after'); - } - - return this._serializeDom(mergedFragment, true); - }; - - this.addCSSClass = function (node, className) { - if (node.nodeType !== ELEMENT_NODE) { - return; - } - var classes = node.getAttribute('class'); - classes = (classes ? classes.split(' ') : []); - if (classes.indexOf(className) === -1) { - classes.push(className); - } - node.setAttribute('class', classes.join(' ')); - }; - - this.removeCSSClass = function (node, className) { - if (node.nodeType !== ELEMENT_NODE) { - return; - } - var classes = node.getAttribute('class'), - newClasses = []; - classes = (classes ? classes.split(' ') : []); - for (var i = 0; i < classes.length; i++) { - if (classes[i] !== className) { - newClasses.push(classes[i]); - } - } - if (newClasses.length === 0) { - node.removeAttribute('class'); - } else { - node.setAttribute('class', newClasses.join(' ')); - } - }; - - this.addDiffMarkup = function (originalHTML, newHTML, fromLine, toLine, diffFormatterCb) { - var data = this.extractRangeByLineNumbers(originalHTML, fromLine, toLine), - previousHtml = data.previousHtml + '' + data.previousHtmlEndSnippet, - previousFragment = this.htmlToFragment(previousHtml), - followingHtml = data.followingHtmlStartSnippet + '' + data.followingHtml, - followingFragment = this.htmlToFragment(followingHtml), - newFragment = this.htmlToFragment(newHTML), - oldHTML = data.outerContextStart + data.innerContextStart + data.html + - data.innerContextEnd + data.outerContextEnd, - oldFragment = this.htmlToFragment(oldHTML), - el; - - var diffFragment = diffFormatterCb(oldFragment, newFragment); - - var mergedFragment = document.createDocumentFragment(); - while (previousFragment.firstChild) { - el = previousFragment.firstChild; - previousFragment.removeChild(el); - mergedFragment.appendChild(el); - } - while (diffFragment.firstChild) { - el = diffFragment.firstChild; - diffFragment.removeChild(el); - mergedFragment.appendChild(el); - } - while (followingFragment.firstChild) { - el = followingFragment.firstChild; - followingFragment.removeChild(el); - mergedFragment.appendChild(el); - } - - var forgottenTemplates = mergedFragment.querySelectorAll("TEMPLATE"); - for (var i = 0; i < forgottenTemplates.length; i++) { - el = forgottenTemplates[i]; - el.parentNode.removeChild(el); - } - - return this._serializeDom(mergedFragment, true); - }; - - /** - * Adapted from http://ejohn.org/projects/javascript-diff-algorithm/ - * by John Resig, MIT License - * @param {array} oldArr - * @param {array} newArr - * @returns {object} - */ - this._diff = function (oldArr, newArr) { - var ns = {}, - os = {}, - i; - - for (i = 0; i < newArr.length; i++) { - if (ns[newArr[i]] === undefined) - ns[newArr[i]] = {rows: [], o: null}; - ns[newArr[i]].rows.push(i); - } - - for (i = 0; i < oldArr.length; i++) { - if (os[oldArr[i]] === undefined) - os[oldArr[i]] = {rows: [], n: null}; - os[oldArr[i]].rows.push(i); - } - - for (i in ns) { - if (ns[i].rows.length === 1 && typeof(os[i]) !== "undefined" && os[i].rows.length === 1) { - newArr[ns[i].rows[0]] = {text: newArr[ns[i].rows[0]], row: os[i].rows[0]}; - oldArr[os[i].rows[0]] = {text: oldArr[os[i].rows[0]], row: ns[i].rows[0]}; - } - } - - for (i = 0; i < newArr.length - 1; i++) { - if (newArr[i].text !== null && newArr[i + 1].text === undefined && newArr[i].row + 1 < oldArr.length && - oldArr[newArr[i].row + 1].text === undefined && newArr[i + 1] == oldArr[newArr[i].row + 1]) { - newArr[i + 1] = {text: newArr[i + 1], row: newArr[i].row + 1}; - oldArr[newArr[i].row + 1] = {text: oldArr[newArr[i].row + 1], row: i + 1}; - } - } - - for (i = newArr.length - 1; i > 0; i--) { - if (newArr[i].text !== null && newArr[i - 1].text === undefined && newArr[i].row > 0 && - oldArr[newArr[i].row - 1].text === undefined && newArr[i - 1] == oldArr[newArr[i].row - 1]) { - newArr[i - 1] = {text: newArr[i - 1], row: newArr[i].row - 1}; - oldArr[newArr[i].row - 1] = {text: oldArr[newArr[i].row - 1], row: i - 1}; - } - } - - return {o: oldArr, n: newArr}; - }; - - this._tokenizeHtml = function (str) { - var splitArrayEntriesEmbedSeparator = function (arr, by, prepend) { - var newArr = []; - for (var i = 0; i < arr.length; i++) { - if (arr[i][0] === '<' && (by === " " || by === "\n")) { - // Don't split HTML tags - newArr.push(arr[i]); - continue; - } - - var parts = arr[i].split(by); - if (parts.length === 1) { - newArr.push(arr[i]); - } else { - var j; - if (prepend) { - if (parts[0] !== '') { - newArr.push(parts[0]); - } - for (j = 1; j < parts.length; j++) { - newArr.push(by + parts[j]); - } - } else { - for (j = 0; j < parts.length - 1; j++) { - newArr.push(parts[j] + by); - } - if (parts[parts.length - 1] !== '') { - newArr.push(parts[parts.length - 1]); - } - } - } - } - return newArr; - }; - var splitArrayEntriesSplitSeparator = function (arr, by) { - var newArr = []; - for (var i = 0; i < arr.length; i++) { - if (arr[i][0] === '<') { - newArr.push(arr[i]); - continue; - } - var parts = arr[i].split(by); - for (var j = 0; j < parts.length; j++) { - if (j > 0) { - newArr.push(by); - } - newArr.push(parts[j]); - } - } - return newArr; - }; - var arr = splitArrayEntriesEmbedSeparator([str], '<', true); - arr = splitArrayEntriesEmbedSeparator(arr, '>', false); - arr = splitArrayEntriesSplitSeparator(arr, " "); - arr = splitArrayEntriesSplitSeparator(arr, "."); - arr = splitArrayEntriesSplitSeparator(arr, ","); - arr = splitArrayEntriesSplitSeparator(arr, "!"); - arr = splitArrayEntriesSplitSeparator(arr, "-"); - arr = splitArrayEntriesEmbedSeparator(arr, "\n", false); - - var arrWithoutEmptes = []; - for (var i = 0; i < arr.length; i++) { - if (arr[i] !== '') { - arrWithoutEmptes.push(arr[i]); - } - } - - return arrWithoutEmptes; - }; - - /** - * @param {string} oldStr - * @param {string} newStr - * @returns {string} - */ - this._diffString = function (oldStr, newStr) { - oldStr = this._normalizeHtmlForDiff(oldStr.replace(/\s+$/, '').replace(/^\s+/, '')); - newStr = this._normalizeHtmlForDiff(newStr.replace(/\s+$/, '').replace(/^\s+/, '')); - - var out = this._diff(this._tokenizeHtml(oldStr), this._tokenizeHtml(newStr)); - - // This fixes the problem tested by "does not lose words when changes are moved X-wise" - var lastRow = 0; - for (var z = 0; z < out.n.length; z++) { - if (out.n[z].row && out.n[z].row > lastRow) { - lastRow = out.n[z].row; - } - if (out.n[z].row && out.n[z].row < lastRow) { - out.o[out.n[z].row] = out.o[out.n[z].row].text; - out.n[z] = out.n[z].text; - } - } - - var str = ""; - var i; - - if (out.n.length === 0) { - for (i = 0; i < out.o.length; i++) { - str += '' + out.o[i] + ""; - } - } else { - if (out.n[0].text === undefined) { - for (var k = 0; k < out.o.length && out.o[k].text === undefined; k++) { - str += '' + out.o[k] + ""; - } - } - - var currOldRow = 0; - for (i = 0; i < out.n.length; i++) { - if (out.n[i].text === undefined) { - if (out.n[i] !== "") { - str += '' + out.n[i] + ""; - } - } else if (out.n[i].row < currOldRow) { - str += '' + out.n[i].text + ""; - } else { - var pre = ""; - - if ((i + 1) < out.n.length && out.n[i + 1].row !== undefined && out.n[i + 1].row > out.n[i].row + 1) { - for (var n = out.n[i].row + 1; n < out.n[i + 1].row; n++) { - if (out.o[n].text === undefined) { - pre += '' + out.o[n] + ""; - } else { - pre += '' + out.o[n].text + ""; - } - } - } else { - for (var j = out.n[i].row + 1; j < out.o.length && out.o[j].text === undefined; j++) { - pre += '' + out.o[j] + ""; - } - } - str += out.n[i].text + pre; - - currOldRow = out.n[i].row; - } - } - } - - return str.replace(/^\s+/g, '').replace(/\s+$/g, '').replace(/ {2,}/g, ' '); - }; - - /** - * @param {string} html - * @return {boolean} - * @private - */ - this._isValidInlineHtml = function(html) { - // If there are no HTML tags, we assume it's valid and skip further checks - if (!html.match(/<[^>]*>/)) { - return true; - } - - // We check if this is a valid HTML that closes all its tags again using the innerHTML-Hack to correct - // the string and check if the number of HTML tags changes by this - var doc = document.createElement('div'); - doc.innerHTML = html; - var tagsBefore = (html.match(/ it was not valid - return false; - } - - // If there is any block element inside, we consider it as broken, as this string will be displayed - // inside of / tags - if (html.match(/<(div|p|ul|li|blockquote)\W/i)) { - return false; - } - - return true; - }; - - /** - * @param {string} html - * @returns {boolean} - * @private - */ - this._diffDetectBrokenDiffHtml = function(html) { - // If other HTML tags are contained within INS/DEL (e.g. "Test

    "), let's better be cautious - // The "!!(found=...)"-construction is only used to make jshint happy :) - var findDel = /(.*?)<\/del>/gi, - findIns = /(.*?)<\/ins>/gi, - found, inner; - while (!!(found = findDel.exec(html))) { - inner = found[1].replace(/]*>/gi, ''); - if (inner.match(/<[^>]*>/)) { - return true; - } - } - while (!!(found = findIns.exec(html))) { - inner = found[1].replace(/]*>/gi, ''); - if (!this._isValidInlineHtml(inner)) { - return true; - } - } - - // If non of the conditions up to now is met, we consider the diff as being sane - return false; - }; - - this._diffParagraphs = function(oldText, newText, lineLength, firstLineNumber) { - var oldTextWithBreaks, newTextWithBreaks, currChild; - - if (lineLength !== undefined) { - oldTextWithBreaks = lineNumberingService.insertLineNumbersNode(oldText, lineLength, null, firstLineNumber); - newTextWithBreaks = lineNumberingService.insertLineNumbersNode(newText, lineLength, null, firstLineNumber); - } else { - oldTextWithBreaks = document.createElement('div'); - oldTextWithBreaks.innerHTML = oldText; - newTextWithBreaks = document.createElement('div'); - newTextWithBreaks.innerHTML = newText; - } - - for (var i = 0; i < oldTextWithBreaks.childNodes.length; i++) { - currChild = oldTextWithBreaks.childNodes[i]; - if (currChild.nodeType === TEXT_NODE) { - var wrapDel = document.createElement('del'); - oldTextWithBreaks.insertBefore(wrapDel, currChild); - oldTextWithBreaks.removeChild(currChild); - wrapDel.appendChild(currChild); - } else { - this.addCSSClass(currChild, 'delete'); - this._removeColorStyles(currChild); - } - } - for (i = 0; i < newTextWithBreaks.childNodes.length; i++) { - currChild = newTextWithBreaks.childNodes[i]; - if (currChild.nodeType === TEXT_NODE) { - var wrapIns = document.createElement('ins'); - newTextWithBreaks.insertBefore(wrapIns, currChild); - newTextWithBreaks.removeChild(currChild); - wrapIns.appendChild(currChild); - } else { - this.addCSSClass(currChild, 'insert'); - this._removeColorStyles(currChild); - } - } - - var mergedFragment = document.createDocumentFragment(), - el; - while (oldTextWithBreaks.firstChild) { - el = oldTextWithBreaks.firstChild; - oldTextWithBreaks.removeChild(el); - mergedFragment.appendChild(el); - } - while (newTextWithBreaks.firstChild) { - el = newTextWithBreaks.firstChild; - newTextWithBreaks.removeChild(el); - mergedFragment.appendChild(el); - } - - return this._serializeDom(mergedFragment); - }; - - this.addCSSClassToFirstTag = function (html, className) { - return html.replace(/<[a-z][^>]*>/i, function (match) { - if (match.match(/class=["'][a-z0-9 _-]*["']/i)) { - return match.replace(/class=["']([a-z0-9 _-]*)["']/i, function (match2, previousClasses) { - return "class=\"" + previousClasses + " " + className + "\""; - }); - } else { - return match.substring(0, match.length - 1) + " class=\"" + className + "\">"; - } - }); - }; - - this._addClassToLastNode = function (html, className) { - var node = document.createElement('div'); - node.innerHTML = html; - var foundLast = false; - for (var i = node.childNodes.length - 1; i >= 0 && !foundLast; i--) { - if (node.childNodes[i].nodeType === ELEMENT_NODE) { - var classes = []; - if (node.childNodes[i].getAttribute("class")) { - classes = node.childNodes[i].getAttribute("class").split(" "); - } - classes.push(className); - node.childNodes[i].setAttribute("class", classes.sort().join(' ').replace(/^\s+/, '').replace(/\s+$/, '')); - foundLast = true; - } - } - return node.innerHTML; - }; - - /** - * This function removes color-Attributes from the styles of this node or a descendant, - * as they interfer with the green/red color in HTML and PDF - * - * For the moment, it is sufficient to do this only in paragraph diff mode, as we fall back to this mode anyway - * once we encounter SPANs or other tags inside of INS/DEL-tags - * - * @param {Element} node - * @private - */ - this._removeColorStyles = function (node) { - var styles = node.getAttribute('style'); - if (styles && styles.indexOf('color') > -1) { - var stylesNew = []; - styles.split(';').forEach(function(style) { - if (!style.match(/^\s*color\s*:/i)) { - stylesNew.push(style); - } - }); - if (stylesNew.join(";") === '') { - node.removeAttribute('style'); - } else { - node.setAttribute('style', stylesNew.join(";")); - } - } - for (var i = 0; i < node.childNodes.length; i++) { - if (node.childNodes[i].nodeType === ELEMENT_NODE) { - this._removeColorStyles(node.childNodes[i]); - } - } - }; - - /** - * This fixes a very specific, really weird bug that is tested in the test case "does not a change in a very specific case". - * - * @param {string}diffStr - * @return {string} - * @private - */ - this._fixWrongChangeDetection = function (diffStr) { - if (diffStr.indexOf('') === -1 || diffStr.indexOf('') === -1) { - return diffStr; - } - - var findDelGroupFinder = /(?:.*?<\/del>)+/gi, - found, - returnStr = diffStr; - - while (!!(found = findDelGroupFinder.exec(diffStr))) { - var del = found[0], - split = returnStr.split(del); - - var findInsGroupFinder = /^(?:.*?<\/ins>)+/gi, - foundIns = findInsGroupFinder.exec(split[1]); - if (foundIns) { - var ins = foundIns[0]; - - var delShortened = del.replace( - /((
    <\/del>)?(]+os-line-number[^>]+?>)(\s|<\/?del>)*<\/span>)<\/del>/gi, - '' - ).replace(/<\/del>/g, ''); - var insConv = ins.replace(//g, '').replace(/<\/ins>/g, '').replace(/<\/del>/g, ''); - if (delShortened.indexOf(insConv) !== -1) { - delShortened = delShortened.replace(insConv, ''); - if (delShortened === '') { - returnStr = returnStr.replace(del + ins, del.replace(//g, '').replace(/<\/del>/g, '')); - } - } - } - } - return returnStr; - }; - - /** - * This function calculates the diff between two strings and tries to fix problems with the resulting HTML. - * If lineLength and firstLineNumber is given, line numbers will be returned es well - * - * @param {string} htmlOld - * @param {string} htmlNew - * @param {number} lineLength - optional - * @param {number} firstLineNumber - optional - * @returns {string} - */ - this.diff = function (htmlOld, htmlNew, lineLength, firstLineNumber) { - var cacheKey = lineLength + ' ' + firstLineNumber + ' ' + - lineNumberingService.djb2hash(htmlOld) + lineNumberingService.djb2hash(htmlNew), - cached = diffCache.get(cacheKey); - if (!angular.isUndefined(cached)) { - return cached; - } - - // This fixes a really strange artefact with the diff that occures under the following conditions: - // - The first tag of the two texts is identical, e.g.

    - // - A change happens in the next tag, e.g. inserted text - // - The first tag occures a second time in the text, e.g. another

    - // In this condition, the first tag is deleted first and inserted afterwards again - // Test case: "does not break when an insertion followes a beginning tag occuring twice" - // The work around inserts to tags at the beginning and removes them afterwards again, - // to make sure this situation does not happen (and uses invisible pseudo-tags in case something goes wrong) - var workaroundPrepend = ""; - - // os-split-after should not be considered for detecting changes in paragraphs, so we strip it here - // and add it afterwards. - // We only do this for P for now, as for more complex types like UL/LI that tend to be nestend, - // information would get lost by this that we will need to recursively merge it again later on. - var oldIsSplitAfter = false, - newIsSplitAfter = false; - htmlOld = htmlOld.replace(/(\s*]+class\s*=\s*["'][^"']*)os-split-after/gi, function(match, beginning) { - oldIsSplitAfter = true; - return beginning; - }); - htmlNew = htmlNew.replace(/(\s*]+class\s*=\s*["'][^"']*)os-split-after/gi, function(match, beginning) { - newIsSplitAfter = true; - return beginning; - }); - - // Performing the actual diff - var str = this._diffString(workaroundPrepend + htmlOld, workaroundPrepend + htmlNew), - diffUnnormalized = str.replace(/^\s+/g, '').replace(/\s+$/g, '').replace(/ {2,}/g, ' '); - - - diffUnnormalized = this._fixWrongChangeDetection(diffUnnormalized); - - // Remove tags that only delete line numbers - // We need to do this before removing as done in one of the next statements - diffUnnormalized = diffUnnormalized.replace( - /((
    <\/del>)?(]+os-line-number[^>]+?>)(\s|<\/?del>)*<\/span>)<\/del>/gi, - function(found,tag,br,span) { - return (br !== undefined ? br : '') + span + ' '; - } - ); - - diffUnnormalized = diffUnnormalized.replace(/<\/ins>/gi, '').replace(/<\/del>/gi, ''); - - // Move whitespaces around inserted P's out of the INS-tag - diffUnnormalized = diffUnnormalized.replace( - /(\s*)(]*)?>[\s\S]*?<\/p>)(\s*)<\/ins>/gim, - function(match, whiteBefore, inner, tagInner, whiteAfter) { - return whiteBefore + - inner - .replace(/]*)?>/gi, function(match) { - return match + ""; - }) - .replace(/<\/p>/gi, "

    ") + - whiteAfter; - } - ); - - // Fixes HTML produced by the diff like this: - // from:

    Inserted Text

    \n

    More inserted text

    - // into: Inserted Text

    \n

    More inserted text

    - diffUnnormalized = diffUnnormalized.replace( - /<\/p><\/del>([\s\S]*?)<\/p><\/ins>/gim, - "$1

    " - ); - diffUnnormalized = diffUnnormalized.replace( - /[\s\S]*?<\/ins>/gim, - function(match) { - return match.replace(/(<\/p>\s*

    )/gi, "$1"); - } - ); - - // If only a few characters of a word have changed, don't display this as a replacement of the whole word, - // but only of these specific characters - diffUnnormalized = diffUnnormalized.replace(/([a-z0-9,_-]* ?)<\/del>([a-z0-9,_-]* ?)<\/ins>/gi, function (found, oldText, newText) { - var foundDiff = false, commonStart = '', commonEnd = '', - remainderOld = oldText, remainderNew = newText; - - while (remainderOld.length > 0 && remainderNew.length > 0 && !foundDiff) { - if (remainderOld[0] === remainderNew[0]) { - commonStart += remainderOld[0]; - remainderOld = remainderOld.substr(1); - remainderNew = remainderNew.substr(1); - } else { - foundDiff = true; - } - } - - foundDiff = false; - while (remainderOld.length > 0 && remainderNew.length > 0 && !foundDiff) { - if (remainderOld[remainderOld.length - 1] === remainderNew[remainderNew.length - 1]) { - commonEnd = remainderOld[remainderOld.length - 1] + commonEnd; - remainderNew = remainderNew.substr(0, remainderNew.length - 1); - remainderOld = remainderOld.substr(0, remainderOld.length - 1); - } else { - foundDiff = true; - } - } - - var out = commonStart; - if (remainderOld !== '') { - out += '' + remainderOld + ''; - } - if (remainderNew !== '') { - out += '' + remainderNew + ''; - } - out += commonEnd; - - return out; - }); - - // Replace spaces in line numbers by   - diffUnnormalized = diffUnnormalized.replace( - /]+os-line-number[^>]+?>\s*<\/span>/gi, - function(found) { - return found.toLowerCase().replace(/> <\/span/gi, "> .*?(\n.*?)*<\/ins>/gi, function (found) { - found = found.replace(/<(div|p|li)[^>]*>/gi, function(match) { return match + ''; }); - found = found.replace(/<\/(div|p|li)[^>]*>/gi, function(match) { return '' + match; }); - return found; - }); - diffUnnormalized = diffUnnormalized.replace(/.*?(\n.*?)*<\/del>/gi, function (found) { - found = found.replace(/<(div|p|li)[^>]*>/gi, function(match) { return match + ''; }); - found = found.replace(/<\/(div|p|li)[^>]*>/gi, function(match) { return '' + match; }); - return found; - }); - diffUnnormalized = diffUnnormalized.replace(/^

    (.*)<\/p><\/del>$/gi, function(match, inner) { return "

    " + inner + "

    "; }); - - var node = document.createElement('div'); - node.innerHTML = diffUnnormalized; - diff = node.innerHTML; - - if (lineLength !== undefined && firstLineNumber !== undefined) { - node = lineNumberingService.insertLineNumbersNode(diff, lineLength, null, firstLineNumber); - diff = node.innerHTML; - } - } - - if (oldIsSplitAfter || newIsSplitAfter) { - diff = this._addClassToLastNode(diff, "os-split-after"); - } - - diffCache.put(cacheKey, diff); - return diff; - }; - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/docx.js b/openslides/motions/static/js/motions/docx.js deleted file mode 100644 index dce23e97d..000000000 --- a/openslides/motions/static/js/motions/docx.js +++ /dev/null @@ -1,221 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.motions.docx', ['OpenSlidesApp.core.docx']) - -.factory('MotionDocxExport', [ - '$http', - '$q', - '$filter', - 'operator', - 'Config', - 'Category', - 'gettextCatalog', - 'FileSaver', - 'lineNumberingService', - 'Html2DocxConverter', - 'MotionComment', - function ($http, $q, $filter, operator, Config, Category, gettextCatalog, - FileSaver, lineNumberingService, Html2DocxConverter, MotionComment) { - - var PAGEBREAK = ''; - - var converter; - - var getData = function (motions, params) { - var data = {}; - // header - var headerline1 = [ - Config.translate(Config.get('general_event_name').value), - Config.translate(Config.get('general_event_description').value) - ].filter(Boolean).join(' – '); - var headerline2 = [ - Config.get('general_event_location').value, - Config.get('general_event_date').value - ].filter(Boolean).join(', '); - data.header = [headerline1, headerline2].join('\n'); - - // motion catalog title/preamble - data.title = Config.translate(Config.get('motions_export_title').value); - data.preamble = Config.get('motions_export_preamble').value; - - // categories - var categories = getCategoriesData(motions); - data.has_categories = categories.length === 0 ? false : true; - data.categories_translation = gettextCatalog.getString('Categories'); - data.categories = categories; - data.no_categories = gettextCatalog.getString('No categories available.'); - data.pagebreak_main = categories.length === 0 ? '' : PAGEBREAK; - - // motions - data.tableofcontents_translation = gettextCatalog.getString('Table of contents'); - data.motions_list = getMotionShortData(motions, params); - data.no_motions = gettextCatalog.getString('No motions available.'); - - return $q(function (resolve) { - getMotionFullData(motions, params).then(function (motionData) { - data.motions = motionData; - resolve(data); - }); - }); - }; - - var getCategoriesData = function (motions) { - var categories = _.map(motions, function (motion) { - if (motion.category) { - return { - prefix: motion.category.prefix, - name: motion.category.name, - }; - } - }); - // clear out 'undefined' and make the categories unique. - categories = _.uniqBy(_.filter(categories, function(category) { - return category; - }), 'prefix'); - var sortKey = Config.get('motions_export_category_sorting').value; - return _.orderBy(categories, [sortKey]); - }; - - var getMotionShortData = function (motions, params) { - return _.map(motions, function (motion) { - return { - identifier: motion.identifier || '', - title: motion.getTitleWithChanges(params.changeRecommendationMode), - }; - }); - }; - - var getMotionFullData = function (motions, params) { - // All translations - var translation = gettextCatalog.getString('Motion'), - sequential_translation = gettextCatalog.getString('Sequential number'), - submitters_translation = gettextCatalog.getString('Submitters'), - status_translation = gettextCatalog.getString('Status'), - reason_translation = gettextCatalog.getString('Reason'), - comment_translation = gettextCatalog.getString('Comments'); - var sequential_enabled = Config.get('motions_export_sequential_number').value; - // promises for create the actual motion data - var promises = _.map(motions, function (motion) { - var title = motion.getTitleWithChanges(params.changeRecommendationMode); - var text = params.include.text ? motion.getTextByMode(params.changeRecommendationMode, null, null, false) : ''; - var reason = params.include.reason ? motion.getReason() : ''; - var comments = getMotionComments(motion, params.includeComments); - - // Data for one motions. Must include translations, ... - var motionData = { - // Translations - motion_translation: translation, - sequential_translation: sequential_translation, - submitters_translation: submitters_translation, - reason_translation: reason.length === 0 ? '' : reason_translation, - status_translation: status_translation, - comment_translation: comments.length === 0 ? '' : comment_translation, - sequential_enabled: sequential_enabled, - // Actual data - id: motion.id, - identifier: motion.identifier || '', - title: title, - submitters: params.include.submitters ? _.map( - $filter('orderBy')(motion.submitters, 'weight'), function (submitter) { - return submitter.user.get_full_name(); - } - ).join(', ') : '', - status: motion.getStateName(), - // Miscellaneous stuff - preamble: gettextCatalog.getString(Config.get('motions_preamble').value), - pagebreak: PAGEBREAK, - }; - // converting html to docx is async, so text, reason and comments are inserted here. - return $q(function (resolve) { - var convertPromises = _.map(comments, function (comment) { - return converter.html2docx(comment.comment).then(function (commentAsDocx) { - comment.comment = commentAsDocx; - }); - }); - convertPromises.push(converter.html2docx(text).then(function (textAsDocx) { - motionData.text = textAsDocx; - })); - convertPromises.push(converter.html2docx(reason).then(function (reasonAsDocx) { - motionData.reason = reasonAsDocx; - })); - $q.all(convertPromises).then(function () { - motionData.comments = comments; - resolve(motionData); - }); - }); - }); - // resolve, if all motion data is fetched. - return $q(function (resolve) { - $q.all(promises).then(function (data) { - if (data.length) { - // clear pagebreak on last element - data[data.length - 1].pagebreak = ''; - } - resolve(data); - }); - }); - }; - - var getMotionComments = function (motion, fieldsIncluded) { - var fields = MotionComment.getNoSpecialCommentsFields(); - var comments = []; - _.forEach(fieldsIncluded, function (ok, id) { - if (ok && motion.comments[id]) { - var title = fields[id].name; - if (!fields[id].public) { - title += ' (' + gettextCatalog.getString('internal') + ')'; - } - var comment = motion.comments[id]; - if (comment.indexOf('

    ') !== 0) { - comment = '

    ' + comment + '

    '; - } - comments.push({ - title: title, - comment: comment, - }); - } - }); - return comments; - }; - - return { - export: function (motions, params) { - converter = Html2DocxConverter.createInstance(); - params = _.clone(params || {}); // Clone this to avoid sideeffects. - _.defaults(params, { - changeRecommendationMode: Config.get('motions_recommendation_text_mode').value, - include: { - text: true, - reason: true, - submitters: true, - }, - includeComments: {}, - }); - params.filename = gettextCatalog.getString('motions') + '.docx'; - if (!_.includes(['original', 'changed', 'agreed'], params.changeRecommendationMode)) { - params.changeRecommendationMode = 'original'; - } - - $http.get('/motions/docxtemplate/').then(function (success) { - var content = window.atob(success.data); - var doc = new Docxgen(content); - - getData(motions, params).then(function (data) { - doc.setData(data); - doc.render(); - - var zip = doc.getZip(); - zip = converter.updateZipFile(zip); - - var out = zip.generate({type: 'blob'}); - FileSaver.saveAs(out, params.filename); - }); - }); - }, - }; - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/linenumbering.js b/openslides/motions/static/js/motions/linenumbering.js deleted file mode 100644 index d978e2d01..000000000 --- a/openslides/motions/static/js/motions/linenumbering.js +++ /dev/null @@ -1,772 +0,0 @@ -(function () { - -"use strict"; - -angular.module('OpenSlidesApp.motions.lineNumbering', []) - -/** - * Current limitations of this implementation: - * - * Only the following inline elements are supported: - * - 'SPAN', 'A', 'EM', 'S', 'B', 'I', 'STRONG', 'U', 'BIG', 'SMALL', 'SUB', 'SUP', 'TT' - * - 'INS' and 'DEL' are supported, but line numbering does not affect the content of 'INS'-elements - * - * Only other inline elements are allowed within inline elements. - * No constructs like
    are allowed. CSS-attributes like 'display: block' are ignored. - */ - -.service('lineNumberingService', [ - '$cacheFactory', - function ($cacheFactory) { - var ELEMENT_NODE = 1, - TEXT_NODE = 3; - - // Counts the number of characters in the current line, beyond singe nodes. - // Needs to be resetted after each line break and after entering a new block node. - this._currentInlineOffset = null; - - // The last position of a point suitable for breaking the line. null or an object with the following values: - // - node: the node that contains the position. Guaranteed to be a TextNode - // - offset: the offset of the breaking characters (like the space) - // Needs to be resetted after each line break and after entering a new block node. - this._lastInlineBreakablePoint = null; - - // The line number counter - this._currentLineNumber = null; - - // Indicates that we just entered a block element and we want to add a line number without line break at the beginning. - this._prependLineNumberToFirstText = false; - - // A workaround to prevent double line numbers - this._ignoreNextRegularLineNumber = false; - - // Decides if the content of inserted nodes should count as well. This is used so we can use the algorithm on a - // text with inline diff annotations and get the same line numbering as with the original text (when set to false) - this._ignoreInsertedText = false; - - var lineNumberCache = $cacheFactory('linenumbering.service'); - - this.djb2hash = function(str) { - var hash = 5381, char; - for (var i = 0; i < str.length; i++) { - char = str.charCodeAt(i); - hash = ((hash << 5) + hash) + char; - } - return hash.toString(); - }; - - this._isInlineElement = function (node) { - var inlineElements = [ - 'SPAN', 'A', 'EM', 'S', 'B', 'I', 'STRONG', 'U', 'BIG', 'SMALL', 'SUB', 'SUP', 'TT', 'INS', 'DEL', - 'STRIKE' - ]; - return (inlineElements.indexOf(node.nodeName) > -1); - }; - - this._isIgnoredByLineNumbering = function (node) { - if (node.nodeName === 'INS') { - return this._ignoreInsertedText; - } else if (this._isOsLineNumberNode(node)) { - return true; - } else { - return false; - } - }; - - this._isOsLineBreakNode = function (node) { - var isLineBreak = false; - if (node && node.nodeType === ELEMENT_NODE && node.nodeName === 'BR' && node.hasAttribute('class')) { - var classes = node.getAttribute('class').split(' '); - if (classes.indexOf('os-line-break') > -1) { - isLineBreak = true; - } - } - return isLineBreak; - }; - - this._isOsLineNumberNode = function (node) { - var isLineNumber = false; - if (node && node.nodeType === ELEMENT_NODE && node.nodeName === 'SPAN' && node.hasAttribute('class')) { - var classes = node.getAttribute('class').split(' '); - if (classes.indexOf('os-line-number') > -1) { - isLineNumber = true; - } - } - return isLineNumber; - }; - - this._getLineNumberNode = function(fragment, lineNumber) { - return fragment.querySelector('.os-line-number.line-number-' + lineNumber); - }; - - this._htmlToFragment = function(html) { - var fragment = document.createDocumentFragment(), - div = document.createElement('DIV'); - div.innerHTML = html; - while (div.childElementCount) { - var child = div.childNodes[0]; - div.removeChild(child); - fragment.appendChild(child); - } - return fragment; - }; - - this._fragmentToHtml = function(fragment) { - var div = document.createElement('DIV'); - while (fragment.firstChild) { - var child = fragment.firstChild; - fragment.removeChild(child); - div.appendChild(child); - } - return div.innerHTML; - }; - - this._createLineBreak = function () { - var br = document.createElement('br'); - br.setAttribute('class', 'os-line-break'); - return br; - }; - - this._createLineNumber = function () { - if (this._ignoreNextRegularLineNumber) { - this._ignoreNextRegularLineNumber = false; - return; - } - var node = document.createElement('span'); - var lineNumber = this._currentLineNumber; - this._currentLineNumber++; - node.setAttribute('class', 'os-line-number line-number-' + lineNumber); - node.setAttribute('data-line-number', lineNumber + ''); - node.setAttribute('contenteditable', 'false'); - node.innerHTML = ' '; // Prevent ckeditor from stripping out empty span's - return node; - }; - - /** - * Splits a TEXT_NODE into an array of TEXT_NODEs and BR-Elements separating them into lines. - * Each line has a maximum length of 'length', with one exception: spaces are accepted to exceed the length. - * Otherwise the string is split by the last space or dash in the line. - * - * @param node - * @param length - * @param highlight - * @returns Array - * @private - */ - this._textNodeToLines = function (node, length, highlight) { - var out = [], - currLineStart = 0, - i = 0, - firstTextNode = true, - service = this; - var addLine = function (text, highlight) { - var node; - if (typeof highlight === 'undefined') { - highlight = -1; - } - if (firstTextNode) { - if (highlight === service._currentLineNumber - 1) { - node = document.createElement('span'); - node.setAttribute('class', 'highlight'); - node.innerHTML = text; - } else { - node = document.createTextNode(text); - } - firstTextNode = false; - } else { - if (service._currentLineNumber === highlight && highlight !== null) { - node = document.createElement('span'); - node.setAttribute('class', 'highlight'); - node.innerHTML = text; - } else { - node = document.createTextNode(text); - } - out.push(service._createLineBreak()); - if (service._currentLineNumber !== null) { - out.push(service._createLineNumber()); - } - } - out.push(node); - return node; - }; - var addLinebreakToPreviousNode = function (node, offset) { - var firstText = node.nodeValue.substr(0, offset + 1), - secondText = node.nodeValue.substr(offset + 1); - var lineBreak = service._createLineBreak(); - var firstNode = document.createTextNode(firstText); - node.parentNode.insertBefore(firstNode, node); - node.parentNode.insertBefore(lineBreak, node); - if (service._currentLineNumber !== null) { - node.parentNode.insertBefore(service._createLineNumber(), node); - } - node.nodeValue = secondText; - }; - - if (node.nodeValue === "\n") { - out.push(node); - } else { - - // This happens if a previous inline element exactly stretches to the end of the line - if (this._currentInlineOffset >= length) { - out.push(service._createLineBreak()); - if (this._currentLineNumber !== null) { - out.push(service._createLineNumber()); - } - this._currentInlineOffset = 0; - this._lastInlineBreakablePoint = null; - } else if (this._prependLineNumberToFirstText) { - if (this._ignoreNextRegularLineNumber) { - this._ignoreNextRegularLineNumber = false; - } else if (service._currentLineNumber !== null) { - out.push(service._createLineNumber()); - } - } - this._prependLineNumberToFirstText = false; - - while (i < node.nodeValue.length) { - var lineBreakAt = null; - if (this._currentInlineOffset >= length) { - if (this._lastInlineBreakablePoint !== null) { - lineBreakAt = this._lastInlineBreakablePoint; - } else { - lineBreakAt = { - 'node': node, - 'offset': i - 1 - }; - } - } - if (lineBreakAt !== null && (node.nodeValue[i] !== ' ' && node.nodeValue[i] !== "\n")) { - if (lineBreakAt.node === node) { - // The last possible breaking point is in this text node - var currLine = node.nodeValue.substring(currLineStart, lineBreakAt.offset + 1); - addLine(currLine, highlight); - - currLineStart = lineBreakAt.offset + 1; - this._currentInlineOffset = i - lineBreakAt.offset - 1; - this._lastInlineBreakablePoint = null; - } else { - // The last possible breaking point was not in this text not, but one we have already passed - var remainderOfPrev = lineBreakAt.node.nodeValue.length - lineBreakAt.offset - 1; - addLinebreakToPreviousNode(lineBreakAt.node, lineBreakAt.offset); - - this._currentInlineOffset = i + remainderOfPrev; - this._lastInlineBreakablePoint = null; - } - - } - - if (node.nodeValue[i] === ' ' || node.nodeValue[i] === '-' || node.nodeValue[i] === "\n") { - this._lastInlineBreakablePoint = { - 'node': node, - 'offset': i - }; - } - - this._currentInlineOffset++; - i++; - - } - var lastLine = addLine(node.nodeValue.substring(currLineStart), highlight); - if (this._lastInlineBreakablePoint !== null) { - this._lastInlineBreakablePoint.node = lastLine; - } - } - return out; - }; - - - /** - * Moves line breaking and line numbering markup before inline elements - * - * @param innerNode - * @param outerNode - * @private - */ - this._moveLeadingLineBreaksToOuterNode = function (innerNode, outerNode) { - if (this._isInlineElement(innerNode)) { - if (this._isOsLineBreakNode(innerNode.firstChild)) { - var br = innerNode.firstChild; - innerNode.removeChild(br); - outerNode.appendChild(br); - } - if (this._isOsLineNumberNode(innerNode.firstChild)) { - var span = innerNode.firstChild; - innerNode.removeChild(span); - outerNode.appendChild(span); - } - } - }; - - this._lengthOfFirstInlineWord = function (node) { - if (!node.firstChild) { - return 0; - } - if (node.firstChild.nodeType === TEXT_NODE) { - var parts = node.firstChild.nodeValue.split(' '); - return parts[0].length; - } else { - return this._lengthOfFirstInlineWord(node.firstChild); - } - }; - - this._insertLineNumbersToInlineNode = function (node, length, highlight) { - var oldChildren = [], i; - for (i = 0; i < node.childNodes.length; i++) { - oldChildren.push(node.childNodes[i]); - } - - while (node.firstChild) { - node.removeChild(node.firstChild); - } - - for (i = 0; i < oldChildren.length; i++) { - if (oldChildren[i].nodeType === TEXT_NODE) { - var ret = this._textNodeToLines(oldChildren[i], length, highlight); - for (var j = 0; j < ret.length; j++) { - node.appendChild(ret[j]); - } - } else if (oldChildren[i].nodeType === ELEMENT_NODE) { - var firstword = this._lengthOfFirstInlineWord(oldChildren[i]), - overlength = ((this._currentInlineOffset + firstword) > length && this._currentInlineOffset > 0); - if (overlength && this._isInlineElement(oldChildren[i])) { - this._currentInlineOffset = 0; - this._lastInlineBreakablePoint = null; - node.appendChild(this._createLineBreak()); - if (this._currentLineNumber !== null) { - node.appendChild(this._createLineNumber()); - } - } - var changedNode = this._insertLineNumbersToNode(oldChildren[i], length, highlight); - this._moveLeadingLineBreaksToOuterNode(changedNode, node); - node.appendChild(changedNode); - } else { - throw 'Unknown nodeType: ' + i + ': ' + oldChildren[i]; - } - } - - return node; - }; - - this._calcBlockNodeLength = function (node, oldLength) { - var newLength = oldLength; - switch (node.nodeName) { - case 'LI': - newLength -= 5; - break; - case 'BLOCKQUOTE': - newLength -= 20; - break; - case 'DIV': - case 'P': - var styles = node.getAttribute("style"), - padding = 0; - if (styles) { - var leftpad = styles.split("padding-left:"); - if (leftpad.length > 1) { - leftpad = parseInt(leftpad[1]); - padding += leftpad; - } - var rightpad = styles.split("padding-right:"); - if (rightpad.length > 1) { - rightpad = parseInt(rightpad[1]); - padding += rightpad; - } - newLength -= (padding / 5); - } - break; - case 'H1': - newLength *= 0.66; - break; - case 'H2': - newLength *= 0.75; - break; - case 'H3': - newLength *= 0.85; - break; - } - return Math.ceil(newLength); - }; - - this._insertLineNumbersToBlockNode = function (node, length, highlight) { - this._currentInlineOffset = 0; - this._lastInlineBreakablePoint = null; - this._prependLineNumberToFirstText = true; - - var oldChildren = [], i; - for (i = 0; i < node.childNodes.length; i++) { - oldChildren.push(node.childNodes[i]); - } - - while (node.firstChild) { - node.removeChild(node.firstChild); - } - - for (i = 0; i < oldChildren.length; i++) { - if (oldChildren[i].nodeType === TEXT_NODE) { - if (!oldChildren[i].nodeValue.match(/\S/)) { - // White space nodes between block elements should be ignored - var prevIsBlock = (i > 0 && !this._isInlineElement(oldChildren[i - 1])); - var nextIsBlock = (i < oldChildren.length - 1 && !this._isInlineElement(oldChildren[i + 1])); - if ((prevIsBlock && nextIsBlock) || (i === 0 && nextIsBlock) || (i === oldChildren.length - 1 && prevIsBlock)) { - node.appendChild(oldChildren[i]); - continue; - } - } - var ret = this._textNodeToLines(oldChildren[i], length, highlight); - for (var j = 0; j < ret.length; j++) { - node.appendChild(ret[j]); - } - } else if (oldChildren[i].nodeType === ELEMENT_NODE) { - var firstword = this._lengthOfFirstInlineWord(oldChildren[i]), - overlength = ((this._currentInlineOffset + firstword) > length && this._currentInlineOffset > 0); - if (overlength && this._isInlineElement(oldChildren[i]) && !this._isIgnoredByLineNumbering(oldChildren[i])) { - this._currentInlineOffset = 0; - this._lastInlineBreakablePoint = null; - node.appendChild(this._createLineBreak()); - if (this._currentLineNumber !== null) { - node.appendChild(this._createLineNumber()); - } - } - var changedNode = this._insertLineNumbersToNode(oldChildren[i], length, highlight); - this._moveLeadingLineBreaksToOuterNode(changedNode, node); - node.appendChild(changedNode); - } else { - throw 'Unknown nodeType: ' + i + ': ' + oldChildren[i]; - } - } - - this._currentInlineOffset = 0; - this._lastInlineBreakablePoint = null; - this._prependLineNumberToFirstText = true; - this._ignoreNextRegularLineNumber = false; - - return node; - }; - - this._insertLineNumbersToNode = function (node, length, highlight) { - if (node.nodeType !== ELEMENT_NODE) { - throw 'This method may only be called for ELEMENT-nodes: ' + node.nodeValue; - } - if (this._isIgnoredByLineNumbering(node)) { - if (this._currentInlineOffset === 0 && this._currentLineNumber !== null) { - var lineNumberNode = this._createLineNumber(); - if (lineNumberNode) { - node.insertBefore(lineNumberNode, node.firstChild); - this._ignoreNextRegularLineNumber = true; - } - } - return node; - } else if (this._isInlineElement(node)) { - return this._insertLineNumbersToInlineNode(node, length, highlight); - } else { - var newLength = this._calcBlockNodeLength(node, length); - return this._insertLineNumbersToBlockNode(node, newLength, highlight); - } - }; - - this._stripLineNumbers = function (node) { - for (var i = 0; i < node.childNodes.length; i++) { - if (this._isOsLineBreakNode(node.childNodes[i]) || this._isOsLineNumberNode(node.childNodes[i])) { - // If a newline character follows a line break, it's been very likely inserted by the WYSIWYG-editor - if (node.childNodes.length > (i + 1) && node.childNodes[i + 1].nodeType === TEXT_NODE) { - if (node.childNodes[i + 1].nodeValue[0] === "\n") { - node.childNodes[i + 1].nodeValue = " " + node.childNodes[i + 1].nodeValue.substring(1); - } - } - node.removeChild(node.childNodes[i]); - i--; - } else { - this._stripLineNumbers(node.childNodes[i]); - } - } - }; - - this._nodesToHtml = function (nodes) { - var root = document.createElement('div'); - for (var i in nodes) { - if (nodes.hasOwnProperty(i)) { - root.appendChild(nodes[i]); - } - } - return root.innerHTML; - }; - - /** - * - * @param {string} html - * @param {number|string} lineLength - * @param {number|null} highlight - optional - * @param {number|null} firstLine - */ - this.insertLineNumbersNode = function (html, lineLength, highlight, firstLine) { - // Removing newlines after BRs, as they lead to problems like #3410 - if (html) { - html = html.replace(/(]*>)[\n\r]+/gi, '$1'); - } - - var root = document.createElement('div'); - root.innerHTML = html; - - this._currentInlineOffset = 0; - this._lastInlineBreakablePoint = null; - if (firstLine) { - this._currentLineNumber = parseInt(firstLine); - } else { - this._currentLineNumber = 1; - } - if (highlight !== null) { - highlight = parseInt(highlight); - } - this._prependLineNumberToFirstText = true; - this._ignoreNextRegularLineNumber = false; - this._ignoreInsertedText = true; - - return this._insertLineNumbersToNode(root, lineLength, highlight); - }; - - /** - * - * @param {string} html - * @param {number} lineLength - * @param {number|null} highlight - optional - * @param {function} callback - * @param {number} firstLine - * @returns {string} - */ - this.insertLineNumbers = function (html, lineLength, highlight, callback, firstLine) { - var newHtml, newRoot; - - if (highlight > 0) { - // Caching versions with highlighted line numbers is probably not worth it - newRoot = this.insertLineNumbersNode(html, lineLength, highlight, firstLine); - newHtml = newRoot.innerHTML; - } else { - var firstLineStr = (firstLine === undefined || firstLine === null ? '' : firstLine.toString()); - var cacheKey = this.djb2hash(firstLineStr + "-" + lineLength.toString() + html); - newHtml = lineNumberCache.get(cacheKey); - - if (angular.isUndefined(newHtml)) { - newRoot = this.insertLineNumbersNode(html, lineLength, null, firstLine); - newHtml = newRoot.innerHTML; - lineNumberCache.put(cacheKey, newHtml); - } - } - - if (callback) { - callback(); - } - - return newHtml; - }; - - /** - * @param {string} html - * @param {number} lineLength - * @param {boolean} countInserted - */ - this.insertLineBreaksWithoutNumbers = function (html, lineLength, countInserted) { - var root = document.createElement('div'); - root.innerHTML = html; - - this._currentInlineOffset = 0; - this._lastInlineBreakablePoint = null; - this._currentLineNumber = null; - this._prependLineNumberToFirstText = true; - this._ignoreNextRegularLineNumber = false; - this._ignoreInsertedText = !countInserted; - - var newRoot = this._insertLineNumbersToNode(root, lineLength, null); - - return newRoot.innerHTML; - }; - - /** - * @param {string} html - * @returns {string} - */ - this.stripLineNumbers = function (html) { - var root = document.createElement('div'); - root.innerHTML = html; - this._stripLineNumbers(root); - return root.innerHTML; - }; - - /** - * @param {string} html - * @returns {object} - * {"from": 23, "to": 42} ; "to" refers to the line breaking element at the end of the last line, - * i.e. the line number of the following line - */ - this.getLineNumberRange = function (html) { - var fragment = this._htmlToFragment(html), - range = { - "from": null, - "to": null - }; - var lineNumbers = fragment.querySelectorAll('.os-line-number'); - for (var i = 0; i < lineNumbers.length; i++) { - var node = lineNumbers.item(i); - var number = parseInt(node.getAttribute("data-line-number")); - if (range.from === null || number < range.from) { - range.from = number; - } - if (range.to === null || (number + 1) > range.to) { - range.to = number + 1; - } - } - return range; - }; - - /** - * @param {string} html - */ - this.getHeadingsWithLineNumbers = function (html) { - var fragment = this._htmlToFragment(html), - headings = []; - var headingNodes = fragment.querySelectorAll('h1, h2, h3, h4, h5, h6'); - for (var i = 0; i < headingNodes.length; i++) { - var heading = headingNodes.item(i); - var linenumbers = heading.querySelectorAll('.os-line-number'); - if (linenumbers.length > 0) { - var number = parseInt(linenumbers.item(0).getAttribute("data-line-number")); - headings.push({ - "lineNumber": number, - "level": parseInt(heading.nodeName.substr(1)), - "text": heading.innerText.replace(/^\s/, "").replace(/\s$/, "") - }); - } - } - return headings.sort(function(heading1, heading2) { - if (heading1.lineNumber < heading2.lineNumber) { - return 0; - } else if (heading1.lineNumber > heading2.lineNumber) { - return 1; - } else { - return 0; - } - }); - }; - - /** - * @param {Element} node - * @returns {array} - * @private - */ - this._splitNodeToParagraphs = function (node) { - var elements = []; - for (var i = 0; i < node.childNodes.length; i++) { - var childNode = node.childNodes.item(i); - - if (childNode.nodeType === TEXT_NODE) { - continue; - } - if (childNode.nodeName === 'UL' || childNode.nodeName === 'OL') { - var start = 1; - if (childNode.getAttribute("start") !== null) { - start = parseInt(childNode.getAttribute("start")); - } - for (var j = 0; j < childNode.childNodes.length; j++) { - if (childNode.childNodes.item(j).nodeType === TEXT_NODE) { - continue; - } - var newParent = childNode.cloneNode(false); - if (childNode.nodeName === 'OL') { - newParent.setAttribute('start', start); - } - newParent.appendChild(childNode.childNodes.item(j).cloneNode(true)); - elements.push(newParent); - start++; - } - } else { - elements.push(childNode); - } - } - return elements; - }; - - /** - * Splitting the text into paragraphs: - * - Each root-level-element is considered as a paragraph. - * Inline-elements at root-level are not expected and treated as block elements. - * Text-nodes at root-level are not expected and ignored. Every text needs to be wrapped e.g. by

    or

    . - * - If a UL or OL is encountered, paragraphs are defined by the child-LI-elements. - * List items of nested lists are not considered as a paragraph of their own. - * - * @param {string} html - * @return {string[]} - */ - this.splitToParagraphs = function (html) { - var fragment = this._htmlToFragment(html); - return this._splitNodeToParagraphs(fragment).map(function(node) { return node.outerHTML; }); - }; - - /** - * Traverses up the DOM tree until it finds a node with a nextSibling, then returns that sibling - * - * @param node - * @private - */ - this._findNextAuntNode = function(node) { - if (node.nextSibling) { - return node.nextSibling; - } else if (node.parentNode) { - return this._findNextAuntNode(node.parentNode); - } else { - return null; - } - }; - - this._highlightUntilNextLine = function(lineNumberNode) { - var currentNode = lineNumberNode, - foundNextLineNumber = false; - - do { - var wasHighlighted = false; - if (currentNode.nodeType === TEXT_NODE) { - var node = document.createElement('span'); - node.setAttribute('class', 'highlight'); - node.innerHTML = currentNode.nodeValue; - currentNode.parentNode.insertBefore(node, currentNode); - currentNode.parentNode.removeChild(currentNode); - currentNode = node; - wasHighlighted = true; - } else { - wasHighlighted = false; - } - - if (currentNode.childNodes.length > 0 && !this._isOsLineNumberNode(currentNode) && !wasHighlighted) { - currentNode = currentNode.childNodes[0]; - } else if (currentNode.nextSibling) { - currentNode = currentNode.nextSibling; - } else { - currentNode = this._findNextAuntNode(currentNode); - } - - if (this._isOsLineNumberNode(currentNode)) { - foundNextLineNumber = true; - } - } while (!foundNextLineNumber && currentNode !== null); - }; - - /** - * @param {string} html - * @param {number} lineNumber - * @return {string} - */ - this.highlightLine = function (html, lineNumber) { - lineNumber = parseInt(lineNumber); - var fragment = this._htmlToFragment(html), - lineNumberNode = this._getLineNumberNode(fragment, lineNumber); - - if (lineNumberNode) { - this._highlightUntilNextLine(lineNumberNode); - html = this._fragmentToHtml(fragment); - } - - return html; - }; - } -]); - - -}()); diff --git a/openslides/motions/static/js/motions/motion-block-projector.js b/openslides/motions/static/js/motions/motion-block-projector.js deleted file mode 100644 index 5db18429c..000000000 --- a/openslides/motions/static/js/motions/motion-block-projector.js +++ /dev/null @@ -1,59 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.motions.motionBlockProjector', []) - - -// MotionBlock projector elements - -.config([ - 'slidesProvider', - function(slidesProvider) { - slidesProvider.registerSlide('motions/motion-block', { - template: 'static/templates/motions/slide_motion_block.html', - }); - } -]) - -.controller('SlideMotionBlockCtrl', [ - '$scope', - 'Motion', - 'MotionBlock', - function($scope, Motion, MotionBlock) { - // Attention! Each object that is used here has to be dealt on server side. - // Add it to the coresponding get_requirements method of the ProjectorElement - // class. - var id = $scope.element.id; - MotionBlock.bindOne(id, $scope, 'motionBlock'); - - // Returns a shortened motion title. If the title is longer then maxLength, it is - // split at the last whitespace that is in maxLength. Three dots are added then. - $scope.getShortTitle = function (motion) { - var maxLength = 40; - var title = motion.getTitle(); - - if (title.length <= maxLength) { - return title; - } - - // Find last whitespace that is before maxLength. Split the title - // there and append dots. - var whitespaceIndex = -1; - for (var i = 0; i < maxLength+1; i++) { - if (title[i] === ' ') { - whitespaceIndex = i; - } - } - - if (whitespaceIndex === -1) { - // just one long word.. split it :/ - return title.substr(0, maxLength) + '...'; - } else { - return title.substr(0, whitespaceIndex) + '...'; - } - }; - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/motion-block.js b/openslides/motions/static/js/motions/motion-block.js deleted file mode 100644 index eb71e1f82..000000000 --- a/openslides/motions/static/js/motions/motion-block.js +++ /dev/null @@ -1,262 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.motions.motionBlock', []) - - -// MotionBlock model - -.factory('MotionBlock', [ - 'DS', - 'jsDataModel', - 'gettext', - function(DS, jsDataModel, gettext) { - var name = 'motions/motion-block'; - return DS.defineResource({ - name: name, - useClass: jsDataModel, - verboseName: gettext('Motion block'), - methods: { - getResourceName: function () { - return name; - }, - getAgendaTitle: function () { - return this.title; - }, - }, - relations: { - belongsTo: { - 'agenda/item': { - localKey: 'agenda_item_id', - localField: 'agenda_item', - } - }, - hasMany: { - 'motions/motion': { - localField: 'motions', - foreignKey: 'motion_block_id', - osProtectedRelation: true, - } - }, - } - }); - } -]) - -.run(['MotionBlock', function(MotionBlock) {}]) - -// MotionBlock views (list view, create dialog, update dialog) -.factory('MotionBlockForm', [ - '$http', - 'operator', - 'gettextCatalog', - 'Agenda', - 'AgendaTree', - 'ShowAsAgendaItemField', - function ($http, operator, gettextCatalog, Agenda, AgendaTree, ShowAsAgendaItemField) { - return { - // Get ngDialog configuration. - getDialog: function (motionBlock) { - return { - template: 'static/templates/motions/motion-block-form.html', - controller: (motionBlock) ? 'MotionBlockUpdateCtrl' : 'MotionBlockCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - motionBlockId: function () {return motionBlock ? motionBlock.id : void 0;} - } - }; - }, - // Get angular-formly fields. - getFormFields: function (isCreateForm) { - var formFields = [ - { - key: 'title', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Title') - } - }, - ]; - - // show as agenda item + parent item - if (isCreateForm) { - formFields.push(ShowAsAgendaItemField('motions.can_manage')); - formFields.push({ - key: 'agenda_parent_id', - type: 'select-single', - templateOptions: { - label: gettextCatalog.getString('Parent item'), - options: AgendaTree.getFlatTree(Agenda.getAll()), - ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id', - placeholder: gettextCatalog.getString('Select a parent item ...') - }, - hide: !operator.hasPerms('agenda.can_manage') - }); - } - - return formFields; - } - }; - } -]) - -.controller('MotionBlockListCtrl', [ - '$scope', - 'ngDialog', - 'MotionBlock', - 'MotionBlockForm', - 'Projector', - 'ProjectionDefault', - function ($scope, ngDialog, MotionBlock, MotionBlockForm, Projector, ProjectionDefault) { - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - var projectiondefault = ProjectionDefault.filter({name: 'motionBlocks'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId = projectiondefault.projector_id; - } - }); - // Two-way data binding for all MotionBlock instances. - MotionBlock.bindAll({}, $scope, 'motionBlocks'); - - // Dialog with a form to create or update a MotionBlock instance. - $scope.openFormDialog = function (motionBlock) { - ngDialog.open(MotionBlockForm.getDialog(motionBlock)); - }; - - // Confirm dialog to delete a MotionBlock instance. - $scope.delete = function (motionBlock) { - MotionBlock.destroy(motionBlock.id); - }; - } -]) - -.controller('MotionBlockDetailCtrl', [ - '$scope', - '$http', - 'ngDialog', - 'Motion', - 'MotionBlockForm', - 'MotionBlock', - 'motionBlockId', - 'Projector', - 'ProjectionDefault', - 'WebpageTitle', - 'gettextCatalog', - 'ErrorMessage', - function($scope, $http, ngDialog, Motion, MotionBlockForm, MotionBlock, motionBlockId, Projector, - ProjectionDefault, WebpageTitle, gettextCatalog, ErrorMessage) { - $scope.$watch(function () { - return MotionBlock.lastModified(motionBlockId); - }, function () { - $scope.motionBlock = MotionBlock.get(motionBlockId); - WebpageTitle.updateTitle(gettextCatalog.getString('Motion block') + ' ' + - $scope.motionBlock.agenda_item.getTitle()); - }); - Motion.bindAll({}, $scope, 'motions'); - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - var projectiondefault = ProjectionDefault.filter({name: 'motionBlocks'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId = projectiondefault.projector_id; - } - }); - $scope.openDialog = function (motionBlock) { - ngDialog.open(MotionBlockForm.getDialog(motionBlock)); - }; - $scope.followRecommendations = function () { - $http.post('/rest/motions/motion-block/' + motionBlockId + '/follow_recommendations/').then( - function (success) { - $scope.alert = { type: 'success', msg: success.data.detail, show: true }; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - $scope.delete = function (motion) { - motion.motion_block_id = null; - motion.title = motion.getTitle(-1); - motion.text = motion.getText(-1); - motion.reason = motion.getReason(-1); - Motion.save(motion); - }; - } -]) - -.controller('MotionBlockCreateCtrl', [ - '$scope', - 'MotionBlock', - 'MotionBlockForm', - 'Config', - function($scope, MotionBlock, MotionBlockForm, Config) { - // Prepare form. - $scope.model = { - agenda_type: parseInt(Config.get('agenda_new_items_default_visibility').value), - }; - - // Get all form fields. - $scope.formFields = MotionBlockForm.getFormFields(true); - - // Save form. - $scope.save = function (motionBlock) { - MotionBlock.create(motionBlock).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - var message = ''; - for (var e in error.data) { - message += e + ': ' + error.data[e] + ' '; - } - $scope.alert = {type: 'danger', msg: message, show: true}; - } - ); - }; - } -]) - -.controller('MotionBlockUpdateCtrl', [ - '$scope', - '$state', - 'MotionBlock', - 'MotionBlockForm', - 'motionBlockId', - function($scope, $state, MotionBlock, MotionBlockForm, motionBlockId) { - $scope.alert = {}; - - // Prepare form. Set initial values by creating a deep copy of - // motionBlock object so list/detail view is not updated while editing. - var motionBlock = MotionBlock.get(motionBlockId); - $scope.model = angular.copy(motionBlock); - - // Get all form fields. - $scope.formFields = MotionBlockForm.getFormFields(); - - // Save form. - $scope.save = function (motionBlock) { - // inject the changed motionBlock (copy) object back into DS store - MotionBlock.inject(motionBlock); - // save changed motionBlock object on server - MotionBlock.create(motionBlock).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - // Save error: revert all changes by restore - // (refresh) original motionBlock object from server - MotionBlock.refresh(motionBlock); // TODO: Why do we need a refresh here? - var message = ''; - for (var e in error.data) { - message += e + ': ' + error.data[e] + ' '; - } - $scope.alert = {type: 'danger', msg: message, show: true}; - } - ); - }; - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/motion-services.js b/openslides/motions/static/js/motions/motion-services.js deleted file mode 100644 index f4b6a32ef..000000000 --- a/openslides/motions/static/js/motions/motion-services.js +++ /dev/null @@ -1,698 +0,0 @@ -(function () { - -"use strict"; - -angular.module('OpenSlidesApp.motions.motionservices', ['OpenSlidesApp.motions', 'OpenSlidesApp.motions.lineNumbering']) - -/* Generic inline editing factory. - * - * getOriginalData: Function that should return the editor data. The editor object is passed. - * saveData: Function that is called whith the editor object as argument. This function - * should prepare the save. If the function returns true, the save process won't be - * continued. Else a patch request is send. - */ -.factory('MotionInlineEditing', [ - 'Motion', - '$timeout', - 'gettextCatalog', - function (Motion, $timeout, gettextCatalog) { - var createInstance = function ($scope, motion, selector, versioning, ckeditorOptions, getOriginalData, saveData) { - var obj = { - active: false, - changed: false, - isEditable: false, - trivialChange: false, - originalHtml: null, - }; - ckeditorOptions.readOnly = true; - - obj.setVersion = function (_motion, versionId) { - motion = _motion; // If this is not updated, - obj.originalHtml = motion.getTextWithLineBreaks(versionId); - obj.changed = false; - if (obj.editor) { - obj.editor.setReadOnly(true); - obj.editor.setData(obj.originalHtml); - } - }; - - obj.enable = function () { - obj.active = true; - obj.isEditable = true; - ckeditorOptions.language = localStorage.getItem('language'); - obj.editor = CKEDITOR.inline(selector, ckeditorOptions); - obj.editor.on('change', function () { - $timeout(function() { - if (obj.editor.getData() !== obj.originalHtml) { - obj.changed = true; - } else { - obj.changed = false; - } - }); - }); - obj.revert(); - }; - - obj.disable = function () { - if (obj.editor) { - obj.editor.setReadOnly(true); - obj.editor.setData(obj.originalHtml, { - callback: function() { - obj.editor.destroy(); - } - }); - } - $timeout(function() { - obj.active = false; - obj.changed = false; - obj.isEditable = false; - }); - }; - - // sets editor content to the initial motion state - obj.revert = function(originalData) { - if (obj.editor) { - obj.originalHtml = getOriginalData(obj); - obj.editor.setData( - getOriginalData(obj), { - callback: function() { - obj.originalHtml = obj.editor.getData(); - obj.editor.setReadOnly(false); - $timeout(function() { - obj.changed = false; - }); - $timeout(function () { - obj.editor.focus(); - }, 100); - } - }); - } - }; - - obj.save = function () { - if (!saveData(obj)) { - obj.disable(); - - Motion.inject(motion); - // save change motion object on server - Motion.save(motion, {method: 'PATCH'}).then( - function (success) { - if (versioning) { - $scope.showVersion(motion.getVersion(-1)); - } - obj.revert(); - }, - function (error) { - // save error: revert all changes by restore - // (refresh) original motion object from server - Motion.refresh(motion); - obj.revert(); - var message = ''; - for (var e in error.data) { - message += e + ': ' + error.data[e] + ' '; - } - $scope.alert = {type: 'danger', msg: message, show: true}; - } - ); - } - }; - - return obj; - }; - return { - createInstance: createInstance - }; - } -]) - -.factory('MotionCommentsInlineEditing', [ - 'MotionInlineEditing', - 'Editor', - function (MotionInlineEditing, Editor) { - var createInstances = function ($scope, motion) { - var commentsInlineEditing = { - editors: {}, // Map comment id to editor instance. - }; - var options = Editor.getOptions('inline', 'YOffset'); - _.forEachRight($scope.noSpecialCommentsFields, function (field, id) { - var inlineEditing = MotionInlineEditing.createInstance($scope, motion, - 'view-original-comment-inline-editor-' + id, false, options, - function (obj) { - return motion['comment_' + id]; - }, - function (obj) { - if (obj.editor) { - motion['comment_' + id] = obj.editor.getData(); - } - } - ); - commentsInlineEditing.editors[id] = inlineEditing; - }); - commentsInlineEditing.saveToolbarVisible = function () { - return _.some(commentsInlineEditing.editors, function (instance) { - return instance.changed && instance.active; - }); - }; - commentsInlineEditing.active = function (commentId) { - return commentsInlineEditing.editors[commentId].active; - }; - commentsInlineEditing.save = function () { - _.forEach(commentsInlineEditing.editors, function (instance) { - instance.save(); - }); - }; - commentsInlineEditing.revert = function () { - _.forEach(commentsInlineEditing.editors, function (instance) { - instance.revert(); - }); - }; - commentsInlineEditing.enable = function (commentId) { - commentsInlineEditing.editors[commentId].enable(); - }; - commentsInlineEditing.disable = function (commentId) { - commentsInlineEditing.editors[commentId].disable(); - }; - - return commentsInlineEditing; - }; - return { - createInstances: createInstances, - }; - } -]) - -.factory('ChangeRecommendationCreate', [ - 'ngDialog', - 'ChangeRecommendationTitleForm', - 'ChangeRecommendationTextForm', - function(ngDialog, ChangeRecommendationTitleForm, ChangeRecommendationTextForm) { - var MODE_INACTIVE = 0, - MODE_SELECTING_FROM = 1, - MODE_SELECTING_TO = 2, - - TITLE_DUMMY_LINE_NUMBER = 0; - - var obj = { - mode: MODE_INACTIVE, - lineFrom: 1, - lineTo: 2, - html: '', - reviewingHtml: '' - }; - - var $scope, motion, version; - - obj._getAffectedLineNumbers = function () { - var changeRecommendations = motion.getTextChangeRecommendations(version.id), - affectedLines = []; - for (var i = 0; i < changeRecommendations.length; i++) { - var change = changeRecommendations[i]; - for (var j = change.line_from; j < change.line_to; j++) { - affectedLines.push(j); - } - } - return affectedLines; - }; - - // startCreating is called right at the beginning after the users interacts with the text for the first time. - // This ensures all necessary nodes have been initialized - obj.startCreating = function () { - if (obj.mode > MODE_SELECTING_FROM || !motion.isAllowed('can_manage')) { - return; - } - - $(".tt_change_recommendation_create_help").removeClass("opened"); - var $lineNumbers = $(".motion-text-original .os-line-number"), - $title = $(".motion-title .change-title"); - if ($lineNumbers.filter(".selectable").length === 0) { - obj.mode = MODE_SELECTING_FROM; - var alreadyAffectedLines = obj._getAffectedLineNumbers(); - $lineNumbers.each(function () { - var $this = $(this), - lineNumber = $this.data("line-number"); - if (alreadyAffectedLines.indexOf(lineNumber) === -1) { - $(this).addClass("selectable"); - } - }); - if (alreadyAffectedLines.indexOf(TITLE_DUMMY_LINE_NUMBER) === -1) { - $title.addClass("selectable"); - } - } - }; - - obj.cancelCreating = function (ev) { - var $target = $(ev.target), - query = ".line-numbers-outside .os-line-number.selectable"; - if (!$target.is(query) && $target.parents(query).length === 0) { - obj.mode = MODE_INACTIVE; - obj.lineFrom = 0; - obj.lineTo = 0; - $(".motion-text-original .os-line-number").removeClass("selected selectable"); - obj.startCreating(); - } - }; - - obj.setFromLine = function (line) { - obj.mode = MODE_SELECTING_TO; - obj.lineFrom = line; - - var alreadyAffectedLines = obj._getAffectedLineNumbers(), - foundCollission = false; - - $(".motion-text-original .os-line-number").each(function () { - var $this = $(this); - if ($this.data("line-number") >= line && !foundCollission) { - if (alreadyAffectedLines.indexOf($this.data("line-number")) === -1) { - $(this).addClass("selectable"); - } else { - $(this).removeClass("selectable"); - foundCollission = true; - } - } else { - $(this).removeClass("selectable"); - } - }); - - var tt_pos = $(".motion-text-original .line-number-" + line).position().top - 45; - $(".tt_change_recommendation_create_help").css("top", tt_pos).addClass("opened"); - }; - - obj.titleClicked = function () { - ngDialog.open(ChangeRecommendationTitleForm.getCreateDialog(motion, version)); - - obj.mode = MODE_INACTIVE; - obj.lineFrom = 0; - obj.lineTo = 0; - $(".motion-text-original .os-line-number").removeClass("selected selectable"); - obj.startCreating(); - }; - - obj.setToLine = function (line) { - if (line < obj.lineFrom) { - return; - } - obj.mode = MODE_INACTIVE; - ngDialog.open(ChangeRecommendationTextForm.getCreateDialog(motion, version, obj.lineFrom, line + 1)); - - obj.lineFrom = 0; - obj.lineTo = 0; - $(".motion-text-original .os-line-number").removeClass("selected selectable"); - obj.startCreating(); - }; - - obj.lineClicked = function (ev) { - if (obj.mode === MODE_INACTIVE) { - return; - } - if (obj.mode === MODE_SELECTING_FROM) { - obj.setFromLine($(ev.target).data("line-number")); - $(ev.target).addClass("selected"); - } else if (obj.mode === MODE_SELECTING_TO) { - obj.setToLine($(ev.target).data("line-number")); - } - }; - - obj.mouseOver = function (ev) { - if (obj.mode !== MODE_SELECTING_TO) { - return; - } - var hoverLine = $(ev.target).data("line-number"); - $(".motion-text-original .os-line-number").each(function () { - var line = $(this).data("line-number"); - if (line >= obj.lineFrom && line <= hoverLine) { - $(this).addClass("selected"); - } else { - $(this).removeClass("selected"); - } - }); - }; - - obj.setVersion = function (_motion, _version) { - motion = _motion; - version = motion.getVersion(_version); - }; - - obj.editTextDialog = function(change_recommendation) { - ngDialog.open(ChangeRecommendationTextForm.getEditDialog(change_recommendation)); - }; - - obj.editTitleDialog = function(change_recommendation) { - ngDialog.open(ChangeRecommendationTitleForm.getEditDialog(change_recommendation)); - }; - - obj.init = function (_scope, _motion) { - $scope = _scope; - motion = _motion; - version = motion.getVersion($scope.version); - - var $content = $("#content"); - $content.on("click", ".line-numbers-outside .os-line-number.selectable", obj.lineClicked); - $content.on("click", ".motion-title .change-title.selectable", obj.titleClicked); - $content.on("click", obj.cancelCreating); - $content.on("mouseover", ".line-numbers-outside .os-line-number.selectable", obj.mouseOver); - $content.on("mouseover", ".motion-text-original, .motion-title", obj.startCreating); - - $scope.$watch(function () { - return $scope.change_recommendations.length; - }, function () { - if (obj.mode === MODE_INACTIVE || obj.mode === MODE_SELECTING_FROM) { - // Recalculate the affected lines so we cannot select lines affected by a recommendation - // that has just been created - $(".motion-text-original .os-line-number").removeClass("selected selectable"); - $(".motion-title .change-title").removeClass("selected selectable"); - obj.startCreating(); - } - }); - - $scope.$on("$destroy", function () { - obj.destroy(); - }); - }; - - obj.destroy = function () { - var $content = $("#content"); - $content.off("click", ".line-numbers-outside .os-line-number.selectable", obj.lineClicked); - $content.off("click", ".motion-title .change-title.selectable", obj.titleClicked); - $content.off("click", obj.cancelCreating); - $content.off("mouseover", ".line-numbers-outside .os-line-number.selectable", obj.mouseOver); - $content.off("mouseover", ".motion-text-original, .motion-title", obj.startCreating); - }; - - return obj; - } -]) - -.factory('ChangeRecommendationView', [ - 'Motion', - 'MotionChangeRecommendation', - 'Config', - 'lineNumberingService', - 'diffService', - '$interval', - '$timeout', - function (Motion, MotionChangeRecommendation, Config, lineNumberingService, diffService, $interval, $timeout) { - var $scope, motion; - - var obj = { - mode: 'original', - context: null - }; - - obj.diffFormatterCb = function (change, oldFragment, newFragment) { - for (var i = 0; i < oldFragment.childNodes.length; i++) { - diffService.addCSSClass(oldFragment.childNodes[i], 'delete'); - } - for (i = 0; i < newFragment.childNodes.length; i++) { - diffService.addCSSClass(newFragment.childNodes[i], 'insert'); - } - var mergedFragment = document.createDocumentFragment(), - diffSection = document.createElement('SECTION'), - el; - - mergedFragment.appendChild(diffSection); - diffSection.setAttribute('class', 'diff'); - diffSection.setAttribute('data-change-id', change.id); - - while (oldFragment.firstChild) { - el = oldFragment.firstChild; - oldFragment.removeChild(el); - diffSection.appendChild(el); - } - while (newFragment.firstChild) { - el = newFragment.firstChild; - newFragment.removeChild(el); - diffSection.appendChild(el); - } - - return mergedFragment; - }; - - obj.delete = function (changeId) { - MotionChangeRecommendation.destroy(changeId); - }; - - obj.rejectAllChangeRecommendations = function (motion) { - var changeRecommendations = MotionChangeRecommendation.filter({ - 'where': {'motion_version_id': {'==': motion.active_version}} - }); - _.forEach(changeRecommendations, function(change) { - change.rejected = true; - change.saveStatus(); - }); - }; - - obj.repositionOriginalAnnotations = function () { - var $changeRecommendationList = $('.change-recommendation-list'), - $lineNumberReference = $('.motion-text-original'); - - $changeRecommendationList.children().each(function() { - var $this = $(this), - lineFrom = $this.data('line-from'), - lineTo = ($this.data('line-to') - 1), - $lineFrom = $lineNumberReference.find('.line-number-' + lineFrom), - $lineTo = $lineNumberReference.find('.line-number-' + lineTo), - fromTop = $lineFrom.position().top + 3, - toTop = $lineTo.position().top + 20, - height = (toTop - fromTop); - - if (height < 10) { - height = 10; - } - - // $lineFrom.position().top seems to depend on the scrolling position when the line numbers - // have position: absolute. Maybe a bug in the used version of jQuery? - // This cancels the effect. - /* - if ($lineNumberReference.hasClass('line-numbers-outside')) { - fromTop += window.scrollY; - } - */ - - $this.css({ 'top': fromTop, 'height': height }); - }); - }; - - obj.copyToModifiedFinalVersion = function (motion, version) { - if (!motion.isAllowed('update')) { - throw 'No permission to update motion'; - } - - motion.copyModifiedFinalVersionStrippingLineBreaks(); - - Motion.inject(motion); - // save change motion object on server - Motion.save(motion, {method: 'PATCH'}).then(null, function (error) { - // save error: revert all changes by restore - // (refresh) original motion object from server - Motion.refresh(motion); - var message = ''; - for (var e in error.data) { - message += e + ': ' + error.data[e] + ' '; - } - $scope.alert = {type: 'danger', msg: message, show: true}; - }); - }; - - obj.deleteModifiedFinalVersion = function (motion, version) { - if (!motion.isAllowed('update')) { - throw 'No permission to update motion'; - } - - if (!motion.getModifiedFinalVersion(version)) { - return; - } - - motion.modified_final_version = ''; - - Motion.inject(motion); - // save change motion object on server - Motion.save(motion, {method: 'PATCH'}).then(function (success) { - $scope.viewChangeRecommendations.mode = 'agreed'; - }, function (error) { - // save error: revert all changes by restore - // (refresh) original motion object from server - Motion.refresh(motion); - var message = ''; - for (var e in error.data) { - message += e + ': ' + error.data[e] + ' '; - } - $scope.alert = {type: 'danger', msg: message, show: true}; - }); - }; - - obj.newVersionIncludingChanges = function (motion, version) { - if (!motion.isAllowed('update')) { - throw 'No permission to update motion'; - } - - var newHtml = motion.getTextByMode('agreed'); - motion.setTextStrippingLineBreaks(newHtml); - - Motion.inject(motion); - // save change motion object on server - Motion.save(motion, {method: 'PATCH'}).then( - function (success) { - $scope.showVersion(motion.getVersion(-1)); - }, - function (error) { - // save error: revert all changes by restore - // (refresh) original motion object from server - Motion.refresh(motion); - var message = ''; - for (var e in error.data) { - message += e + ': ' + error.data[e] + ' '; - } - $scope.alert = {type: 'danger', msg: message, show: true}; - } - ); - }; - - obj.scrollToDiffBox = function (changeId) { - obj.mode = 'diff'; - $timeout(function() { - var $diffBox = $('.diff-box-' + changeId); - $('html, body').animate({ - scrollTop: $diffBox.offset().top - 50 - }, 300); - }, 0, false); - }; - - // $scope.amendments_crs holds the change objects of all change recommendations regarding the text, - // and all amendments with a "accepted"-recommendation, ordered by the first affected line number. - obj.set_amendments_crs_watcher = function($scope, motion) { - $scope.amendments_crs = []; - $scope.change_recommendations = []; - $scope.paragraph_amendments = []; - $scope.has_proposed_changes = false; - $scope.changed_version_has_collissions = false; - - var rebuild_amendments_crs = function () { - $scope.amendments_crs = $scope.change_recommendations.map(function (cr) { - return cr.getUnifiedChangeObject(); - }).concat( - $scope.paragraph_amendments.map(function (amendment) { - return amendment.getUnifiedChangeObject(); - }) - ); - $scope.amendments_crs.sort(function (change1, change2) { - if (change1.line_from > change2.line_from) { - return 1; - } else if (change1.line_from < change2.line_from) { - return -1; - } else { - return 0; - } - }); - - // Set all crs and amendments for collission detection. - _.forEach($scope.amendments_crs, function (change) { - change.setOtherChangesForCollission($scope.amendments_crs); - }); - - $scope.has_proposed_changes = ($scope.amendments_crs.length > 0); - $scope.changed_version_has_accepted_collissions = ($scope.amendments_crs.find(function(change) { - return (change.getCollissions(true).length !== 0); - }) !== undefined); - - if (obj.context === 'site') { - if (!$scope.has_proposed_changes) { - $scope.setProjectionMode($scope.projectionModes[0]); - } - if ($scope.has_proposed_changes) { - $scope.disableMotionInlineEditing(); - } - } - }; - - $scope.$watch(function () { - return MotionChangeRecommendation.lastModified(); - }, function () { - $scope.change_recommendations = []; - $scope.title_change_recommendation = null; - MotionChangeRecommendation.filter({ - 'where': {'motion_version_id': {'==': motion.active_version}} - }).forEach(function (change) { - if (change.isTextRecommendation()) { - $scope.change_recommendations.push(change); - } - if (change.isTitleRecommendation()) { - $scope.title_change_recommendation = change; - } - }); - rebuild_amendments_crs(); - }); - - $scope.$watch(function () { - return Motion.lastModified(); - }, function () { - if (motion) { - $scope.paragraph_amendments = motion.getParagraphBasedAmendmentsForDiffView(); - rebuild_amendments_crs(); - } - }); - }; - - obj.setVersion = function (_motion/*, _version*/) { - motion = _motion; - }; - - obj.initProjector = function (_scope, _motion, viewMode) { - obj.context = 'projector'; - $scope = _scope; - motion = _motion; - - obj.set_amendments_crs_watcher($scope, motion); - obj.mode = viewMode; - }; - - obj.initSite = function (_scope, _motion, viewMode) { - obj.context = 'site'; - $scope = _scope; - motion = _motion; - - obj.set_amendments_crs_watcher($scope, motion); - - $scope.$evalAsync(function() { - obj.repositionOriginalAnnotations(); - }); - $scope.$watch(function() { - return $('.change-recommendation-list').children().length; - }, obj.repositionOriginalAnnotations); - - var checkGotoOriginal = function () { - if ($scope.amendments_crs.length === 0 && $scope.title_change_recommendation === null) { - obj.mode = 'original'; - } - }; - $scope.$watch(function () { - return $scope.amendments_crs.length; - }, checkGotoOriginal); - $scope.$watch(function () { - return $scope.title_change_recommendation; - }, checkGotoOriginal); - - var sizeCheckerLastSize = null, - sizeCheckerLastClass = null, - sizeChecker = $interval(function() { - var $holder = $(".motion-text-original"), - newHeight = $holder.height(), - classes = $holder.attr("class"); - if (newHeight !== sizeCheckerLastSize || sizeCheckerLastClass !== classes) { - sizeCheckerLastSize = newHeight; - sizeCheckerLastClass = classes; - obj.repositionOriginalAnnotations(); - } - }, 100, 0, false); - - $scope.$on('$destroy', function() { - $interval.cancel(sizeChecker); - }); - - obj.mode = viewMode; - }; - - return obj; - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/pdf.js b/openslides/motions/static/js/motions/pdf.js deleted file mode 100644 index 2becb86f4..000000000 --- a/openslides/motions/static/js/motions/pdf.js +++ /dev/null @@ -1,1447 +0,0 @@ -(function () { - -"use strict"; - -angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf']) - -.factory('MotionContentProvider', [ - '$q', - '$filter', - 'operator', - 'gettextCatalog', - 'PDFLayout', - 'PdfMakeConverter', - 'ImageConverter', - 'HTMLValidizer', - 'Category', - 'Config', - 'Motion', - 'MotionComment', - 'MotionPollDecimalPlaces', - 'OpenSlidesSettings', - function($q, $filter, operator, gettextCatalog, PDFLayout, PdfMakeConverter, ImageConverter, - HTMLValidizer, Category, Config, Motion, MotionComment, MotionPollDecimalPlaces, OpenSlidesSettings) { - /** - * Provides the content as JS objects for Motions in pdfMake context - * @constructor - */ - - var createInstance = function(motion, motionVersion, params) { - params = _.clone(params || {}); // Clone this to avoid sideeffects. - _.defaults(params, { - changeRecommendationMode: Config.get('motions_recommendation_text_mode').value, - lineNumberMode: Config.get('motions_default_line_numbering').value, - include: { - text: true, - reason: true, - state: true, - category: true, - submitters: true, - votingresult: true, - motionBlock: true, - origin: true, - recommendation: true, - }, - includeComments: {}, - }); - - var converter, imageMap = {}; - - // Query all image sources from motion text and reason - var getImageSources = function () { - var text = motion.getTextByMode(params.changeRecommendationMode, null); - var reason = motion.getReason(); - var comments = ''; - _.forEach(params.includeComments, function (ok, id) { - if (ok && motion.comments[id]) { - comments += HTMLValidizer.validize(motion.comments[id]); - } - }); - var content = HTMLValidizer.validize(text) + HTMLValidizer.validize(motion.getReason()) + comments; - var map = Function.prototype.call.bind([].map); - return map($(content).find('img'), function(element) { - return element.getAttribute('src'); - }); - }; - - // title - var identifier = motion.identifier ? ' ' + motion.identifier : ''; - var titlePlain = motion.getTitleWithChanges(params.changeRecommendationMode, motionVersion); - var title = PDFLayout.createTitle(gettextCatalog.getString('Motion') + identifier + ': ' + titlePlain); - - // subtitle and sequential number - var subtitleLines = []; - if (motion.parent_id) { - var parentMotion = Motion.get(motion.parent_id); - subtitleLines.push( - gettextCatalog.getString('Amendment to motion') + ': ' + - (parentMotion.identifier ? parentMotion.identifier : parentMotion.getTitle()) - ); - } - if (Config.get('motions_export_sequential_number').value) { - subtitleLines.push(gettextCatalog.getString('Sequential number') + ': ' + - motion.getSequentialNumber()); - } - var subtitle = PDFLayout.createSubtitle(subtitleLines); - - // meta data table - var metaTable = function() { - var metaTableBody = []; - - // submitters - var submitters = _.map( - $filter('orderBy')(motion.submitters, 'weight'), function (submitter) { - return submitter.user.get_full_name(); - } - ).join(', '); - if (params.include.submitters) { - metaTableBody.push([ - { - text: gettextCatalog.getString('Submitters') + ':', - style: ['bold', 'grey'], - }, - { - text: submitters, - style: 'grey' - } - ]); - } - - // state - if (params.include.state) { - metaTableBody.push([ - { - text: gettextCatalog.getString('State') + ':', - style: ['bold', 'grey'] - }, - { - text: motion.getStateName(), - style: 'grey' - } - ]); - } - - // recommendation - if (params.include.recommendation && motion.getRecommendationName()) { - metaTableBody.push([ - { - text: Config.get('motions_recommendations_by').value + ':', - style: ['bold', 'grey'] - }, - { - text: motion.getRecommendationName(), - style: 'grey' - } - ]); - } - - // category - if (params.include.category && motion.category) { - metaTableBody.push([ - { - text: gettextCatalog.getString('Category') + ':', - style: ['bold', 'grey'] }, - { - text: motion.category.prefix + ' - ' + motion.category.name, - style: 'grey' - } - ]); - } - - // motion block - if (params.include.motionBlock && motion.motionBlock) { - metaTableBody.push([ - { - text: gettextCatalog.getString('Motion block') + ':', - style: ['bold', 'grey'] }, - { - text: motion.motionBlock.title, - style: 'grey' - } - ]); - } - - // origin - if (params.include.origin && motion.origin) { - metaTableBody.push([ - { - text: gettextCatalog.getString('Origin') + ':', - style: ['bold', 'grey'] }, - { - text: motion.origin, - style: 'grey' - } - ]); - } - - // voting result - if (params.include.votingresult && motion.polls.length > 0 && motion.polls[0].has_votes) { - var column1 = []; - var column2 = []; - var column3 = []; - motion.polls.map(function(poll, index) { - if (poll.has_votes) { - // votenumber - if (motion.polls.length > 1) { - column1.push(index + 1 + '. ' + gettextCatalog.getString('Vote')); - column2.push(''); - column3.push(''); - } - var precision = MotionPollDecimalPlaces.getPlaces(poll); - // yes - var yes = poll.getVote(poll.yes, 'yes'); - column1.push(gettextCatalog.getString('Yes') + ':'); - column2.push($filter('number')(yes.value, precision)); - column3.push(yes.percentStr); - // no - var no = poll.getVote(poll.no, 'no'); - column1.push(gettextCatalog.getString('No') + ':'); - column2.push($filter('number')(no.value, precision)); - column3.push(no.percentStr); - // abstain - var abstain = poll.getVote(poll.abstain, 'abstain'); - column1.push(gettextCatalog.getString('Abstain') + ':'); - column2.push($filter('number')(abstain.value, precision)); - column3.push(abstain.percentStr); - // votes valid - if (poll.votesvalid) { - var valid = poll.getVote(poll.votesvalid, 'votesvalid'); - column1.push(gettextCatalog.getString('Valid votes') + ':'); - column2.push($filter('number')(valid.value, precision)); - column3.push(valid.percentStr); - } - // votes invalid - if (poll.votesvalid) { - var invalid = poll.getVote(poll.votesinvalid, 'votesinvalid'); - column1.push(gettextCatalog.getString('Invalid votes') + ':'); - column2.push($filter('number')(invalid.value, precision)); - column3.push(invalid.percentStr); - } - // votes cast - if (poll.votescast) { - var cast = poll.getVote(poll.votescast, 'votescast'); - column1.push(gettextCatalog.getString('Votes cast') + ':'); - column2.push($filter('number')(cast.value, precision)); - column3.push(cast.percentStr); - } - } - }); - metaTableBody.push([ - { - text: gettextCatalog.getString('Voting result') + ':', - style: ['bold', 'grey'] - }, - { - columns: [ - { - text: column1.join('\n'), - width: 'auto' - }, - { - text: column2.join('\n'), - width: 'auto', - alignment: 'right' - }, - { - text: column3.join('\n'), - width: 'auto', - alignment: 'right' - }, - ], - columnGap: 7, - style: 'grey' - } - ]); - } - - // summary of change recommendations (for motion diff version only) - if (params.changeRecommendationMode === 'diff' && motion.changeRecommendations.length) { - var columnLineNumbers = []; - var columnChangeType = []; - angular.forEach(_.orderBy(motion.changeRecommendations, ['line_from']), function(change) { - if (change.isTitleRecommendation()) { - columnLineNumbers.push( - gettextCatalog.getString('Title') + ': ' - ); - } else { - // line numbers column - var line; - if (change.line_from >= change.line_to - 1) { - line = change.line_from; - } else { - line = change.line_from + ' - ' + (change.line_to - 1); - } - columnLineNumbers.push( - gettextCatalog.getString('Line') + ' ' + line + ': ' - ); - } - // change type column - if (change.getType(motion.getVersion(motionVersion).text) === 0) { - columnChangeType.push(gettextCatalog.getString("Replacement")); - } else if (change.getType(motion.getVersion(motionVersion).text) === 1) { - columnChangeType.push(gettextCatalog.getString("Insertion")); - } else if (change.getType(motion.getVersion(motionVersion).text) === 2) { - columnChangeType.push(gettextCatalog.getString("Deletion")); - } else if (change.getType(motion.getVersion(motionVersion).text) === 3) { - columnChangeType.push(change.other_description); - } - }); - metaTableBody.push([ - { - text: gettextCatalog.getString('Summary of change recommendations'), - style: ['bold', 'grey'] - }, - { - columns: [ - { - text: columnLineNumbers.join('\n'), - width: 'auto' - }, - { - text: columnChangeType.join('\n'), - width: 'auto' - } - ], - columnGap: 7, - style: 'grey' - } - ]); - } - - if (metaTableBody.length) { - // build table - // Used placeholder for 'layout' functions whiche are - // replaced by lineWitdh/lineColor function in pfd-worker.js. - // TODO: Remove placeholder and us static values for LineWidth and LineColor - // if pdfmake has fixed this. - var metaTable = { - table: { - widths: ['35%','65%'], - body: metaTableBody, - }, - margin: [0, 0, 0, 20], - layout: '{{motion-placeholder-to-insert-functions-here}}' - }; - params.include.metatable = true; - return metaTable; - } else { - return {}; - } - }; - - // motion title - var motionTitle = function() { - if (params.include.metatable && params.include.text && !motion.isParagraphBasedAmendment()) { - return [{ - text: titlePlain, - style: 'heading3' - }]; - } else { - return {}; - } - }; - - // motion preamble - var motionPreamble = function () { - return { - text: Config.translate(Config.get('motions_preamble').value), - margin: [0, 10, 0, 0] - }; - }; - - var escapeHtml = function(text) { - return text.replace(/&/, '&').replace(//, '>'); - }; - - // motion text (with line-numbers) - var motionText = function() { - var content = []; - if (params.include.text) { - var motionTextContent = ''; - if (motion.isParagraphBasedAmendment()) { - // paragraph based amendment - var diffs = motion.getAmendmentParagraphsLinesDiff(); - if (diffs.length) { - content.push(motionPreamble()); - _.forEach(diffs, function (diff) { - motionTextContent += diff.textPre + diff.text + diff.textPost; - }); - } else { - motionTextContent += gettextCatalog.getString('No changes at the text.'); - } - } else { - // lead motion or normal amendment - content.push(motionPreamble()); - var titleChange = motion.getTitleChangeRecommendation(); - if (params.changeRecommendationMode === 'diff' && titleChange) { - motionTextContent += '

    ' + gettextCatalog.getString('New title') + ': ' + - escapeHtml(titleChange.text) + '

    '; - } - motionTextContent += motion.getTextByMode(params.changeRecommendationMode, motionVersion); - } - content.push(converter.convertHTML(motionTextContent, params.lineNumberMode)); - } - return content; - }; - - // motion reason heading - var motionReason = function() { - if (params.include.reason) { - var reason = []; - if (motion.getReason(motionVersion)) { - reason.push({ - text: gettextCatalog.getString('Reason'), - style: 'heading3', - marginTop: 25, - }); - var width; - if (params.lineNumberMode == 'outside') { - width = '80%'; - } else { - width = '100%'; - } - reason.push({ - columns: [ - { - width: width, - stack: converter.convertHTML(motion.getReason(motionVersion), 'none'), - }, - ] - }); - } - return reason; - } - }; - - // motion comments handling - var motionComments = function () { - if (_.keys(params.includeComments).length !== 0) { - var fields = MotionComment.getNoSpecialCommentsFields(); - var comments = []; - _.forEach(params.includeComments, function (ok, id) { - if (ok && motion.comments[id]) { - var title = fields[id].name; - if (!fields[id].public) { - title += ' (' + gettextCatalog.getString('internal') + ')'; - } - comments.push({ - text: title, - style: 'heading3', - marginTop: 25, - }); - comments.push(converter.convertHTML(motion.comments[id])); - } - }); - return comments; - } - }; - - // Generates content as a pdfmake consumable - var getContent = function() { - var content = [ - title, - subtitle, - metaTable(), - motionTitle() - ]; - content = content.concat(motionText()); - - var reason = motionReason(); - if (reason) { - content.push(reason); - } - var comments = motionComments(); - if (comments) { - content.push(comments); - } - return content; - }; - - // getters - var getTitle = function() { - return motion.getTitle(motionVersion); - }; - - var getIdentifier = function() { - return motion.identifier ? motion.identifier : ''; - }; - - var getId = function() { - return motion.id; - }; - - var getCategory = function() { - return motion.category; - }; - - var getImageMap = function() { - return imageMap; - }; - - return $q(function (resolve, reject) { - ImageConverter.toBase64(getImageSources()).then(function (_imageMap) { - imageMap = _imageMap; - converter = PdfMakeConverter.createInstance(_imageMap); - resolve({ - getContent: getContent, - getTitle: getTitle, - getIdentifier: getIdentifier, - getId: getId, - getCategory: getCategory, - getImageMap: getImageMap, - }); - }, reject); - }); - }; - - return { - createInstance: createInstance - }; - } -]) - -.factory('MotionPartialContentProvider', [ - '$q', - 'gettextCatalog', - 'Config', - 'PDFLayout', - 'PdfMakeConverter', - 'ImageConverter', - 'HTMLValidizer', - function ($q, gettextCatalog, Config, PDFLayout, PdfMakeConverter, ImageConverter, HTMLValidizer) { - /* - * content should be an array of content blocks. Each content is an object providing a - * heading and a text. E.g. - * [{heading: 'comment1', text: ''}, {heading: ...}, ...] - * */ - var createInstance = function (motion, content) { - - var converter, imageMap = {}; - - // Query all image sources from the content - var getImageSources = function () { - var imageSources = []; - _.forEach(content, function (contentBlock) { - var html = HTMLValidizer.validize(contentBlock.text); - imageSources = imageSources.concat(_.map($(html).find('img'), function(element) { - return element.getAttribute('src'); - })); - }); - return imageSources; - }; - - // title - var identifier = motion.identifier ? ' ' + motion.identifier : ''; - var title = PDFLayout.createTitle( - gettextCatalog.getString('Motion') + identifier + ': ' + motion.getTitle() - ); - - // subtitle and sequential number - var subtitleLines = []; - if (motion.parent_id) { - var parentMotion = Motion.get(motion.parent_id); - subtitleLines.push( - gettextCatalog.getString('Amendment to motion') + ': ' + - (parentMotion.identifier ? parentMotion.identifier : parentMotion.getTitle()) - ); - } - if (Config.get('motions_export_sequential_number').value) { - subtitleLines.push(gettextCatalog.getString('Sequential number') + ': ' + motion.id); - } - var subtitle = PDFLayout.createSubtitle(subtitleLines); - - // meta data table - var metaTable = function() { - var metaTableBody = []; - - // submitters - var submitters = _.map(motion.submitters, function (submitter) { - return submitter.user.get_full_name(); - }).join(', '); - metaTableBody.push([ - { - text: gettextCatalog.getString('Submitters') + ':', - style: ['bold', 'grey'], - }, - { - text: submitters, - style: 'grey' - } - ]); - - // state - metaTableBody.push([ - { - text: gettextCatalog.getString('State') + ':', - style: ['bold', 'grey'] - }, - { - text: motion.getStateName(), - style: 'grey' - } - ]); - - // recommendation - if (motion.getRecommendationName()) { - metaTableBody.push([ - { - text: Config.get('motions_recommendations_by').value + ':', - style: ['bold', 'grey'] - }, - { - text: motion.getRecommendationName(), - style: 'grey' - } - ]); - } - - // category - if (motion.category) { - metaTableBody.push([ - { - text: gettextCatalog.getString('Category') + ':', - style: ['bold', 'grey'] }, - { - text: motion.category.prefix + ' - ' + motion.category.name, - style: 'grey' - } - ]); - } - - // build table - // Used placeholder for 'layout' functions whiche are - // replaced by lineWitdh/lineColor function in pfd-worker.js. - // TODO: Remove placeholder and us static values for LineWidth and LineColor - // if pdfmake has fixed this. - var metaTableJsonString = { - table: { - widths: ['30%','70%'], - body: metaTableBody, - }, - margin: [0, 0, 0, 20], - layout: '{{motion-placeholder-to-insert-functions-here}}' - }; - return metaTableJsonString; - }; - - var getContentBlockData = function (block) { - var data = []; - data.push({ - text: block.heading, - style: 'heading3', - marginTop: 25, - }); - data.push(converter.convertHTML(block.text)); - return data; - }; - - // Generates content as a pdfmake consumable - var getContent = function() { - var pdfContent = [ - title, - subtitle, - metaTable(), - ]; - _.forEach(content, function (contentBlock) { - pdfContent.push(getContentBlockData(contentBlock)); - }); - return pdfContent; - }; - - var getImageMap = function () { - return imageMap; - }; - - return $q(function (resolve, reject) { - ImageConverter.toBase64(getImageSources()).then(function (_imageMap) { - imageMap = _imageMap; - converter = PdfMakeConverter.createInstance(_imageMap); - resolve({ - getContent: getContent, - getImageMap: getImageMap, - }); - }, reject); - }); - }; - - return { - createInstance: createInstance - }; - } -]) - -.factory('PollContentProvider', [ - '$q', - 'PDFLayout', - 'gettextCatalog', - 'Config', - 'User', - 'ImageConverter', - function($q, PDFLayout, gettextCatalog, Config, User, ImageConverter) { - /** - * Generates a content provider for polls - * @constructor - * @param {string} title - title of poll - * @param {string} id - if of poll - */ - var createInstance = function(title, id) { - - var logoBallotPaperUrl = Config.get('logo_pdf_ballot_paper').value.path; - var imageMap = {}; - - // PDF header - var header = function() { - var columns = []; - - var text = Config.get('general_event_name').value; - columns.push({ - text: text, - fontSize: 8, - alignment: 'left', - width: '60%' - }); - - // logo - if (logoBallotPaperUrl) { - columns.push({ - image: logoBallotPaperUrl, - fit: [90,25], - alignment: 'right', - width: '40%' - }); - } - return { - color: '#555', - fontSize: 10, - margin: [30, 10, 10, -10], // [left, top, right, bottom] - columns: columns, - columnGap: 5 - }; - }; - - /** - * Returns a single section on the ballot paper - * @function - */ - var createSection = function() { - var sheetend = 40; - return { - stack: [ - header(), - { - text: gettextCatalog.getString('Motion') + ' ' + id, - style: 'title', - }, - { - text: title, - style: 'description' - }, - PDFLayout.createBallotEntry(gettextCatalog.getString('Yes')), - PDFLayout.createBallotEntry(gettextCatalog.getString('No')), - PDFLayout.createBallotEntry(gettextCatalog.getString('Abstain')), - ], - margin: [0, 0, 0, sheetend], - }; - }; - - /** - * Returns Content for single motion - * @function - * @param {string} id - if of poll - */ - var getContent = function() { - var content = []; - var amount; - var amount_method = Config.get('motions_pdf_ballot_papers_selection').value; - switch (amount_method) { - case 'NUMBER_OF_ALL_PARTICIPANTS': - amount = User.getAll().length; - break; - case 'NUMBER_OF_DELEGATES': - //TODO: assumption that DELEGATES is always group id 2. This may not be true - var group_id = 2; - amount = User.filter({where: {'groups_id': {contains:group_id} }}).length; - break; - case 'CUSTOM_NUMBER': - amount = Config.get('motions_pdf_ballot_papers_number').value; - break; - default: - // should not happen. - amount = 0; - } - var fullpages = Math.floor(amount / 8); - - for (var i=0; i < fullpages; i++) { - content.push({ - table: { - headerRows: 1, - widths: ['*', '*'], - body: [ - [createSection(), createSection()], - [createSection(), createSection()], - [createSection(), createSection()], - [createSection(), createSection()] - ], - pageBreak: 'after' - }, - layout: PDFLayout.getBallotLayoutLines(), - rowsperpage: 4 - }); - } - amount = amount - (fullpages * 8); - if (amount > 0) { - var partialpagebody = []; - while (amount > 1) { - partialpagebody.push([createSection(), createSection()]); - amount -=2; - } - if (amount == 1) { - partialpagebody.push([createSection(), '']); - } - content.push({ - table: { - headerRows: 1, - widths: ['50%', '50%'], - body: partialpagebody - }, - layout: PDFLayout.getBallotLayoutLines(), - rowsperpage: 4 - }); - } - return content; - }; - - var getImageMap = function () { - return imageMap; - }; - - return $q(function (resolve, reject) { - var imageSources = [ - logoBallotPaperUrl, - ]; - ImageConverter.toBase64(imageSources).then(function (_imageMap) { - imageMap = _imageMap; - resolve({ - getContent: getContent, - getImageMap: getImageMap, - }); - }, reject); - }); - }; - return { - createInstance: createInstance - }; - } -]) - -.factory('MotionCatalogContentProvider', [ - 'gettextCatalog', - 'PDFLayout', - 'Category', - 'Config', - function(gettextCatalog, PDFLayout, Category, Config) { - /** - * Constructor - * @function - * @param {object} allMotions - A sorted array of all motions to parse - * @param {string} sorting - The way the catalog has been sorted. Necessary for ToC - */ - var createInstance = function(allMotions, sorting) { - - var title = PDFLayout.createTitle( - Config.translate(Config.get('motions_export_title').value) - ); - - var createPreamble = function() { - var preambleText = Config.get('motions_export_preamble').value; - if (preambleText) { - return { - text: preambleText, - style: "preamble" - }; - } else { - return ""; - } - }; - - var createTOContent = function() { - var toc = []; - var exportCategory = (sorting === 'identifier' || sorting === 'category.prefix'); - var uniqueCategories = getUniqueCategories(); - var tocTitle = { - text: gettextCatalog.getString('Table of contents'), - style: 'heading2' - }; - - // all motions need a page ID. We use the motion identifier for that - _.forEach(allMotions, function (motion) { - motion.getContent()[0].id = ''+motion.getId(); - }); - - if (exportCategory && uniqueCategories) { - // own table per category - var catTocBody = []; - _.forEach(uniqueCategories, function (category) { - // push the name of the category - // make a table for correct alignment - catTocBody.push({ - table: { - body: [ - [ - { - text: category.prefix + ' - ' + category.name, - style: 'tocCategoryTitle' - } - ], - ] - }, - layout: 'noBorders', - }); - - var tocBody = []; - _.forEach(allMotions, function (motion) { - if (motion.getCategory() && category.name === motion.getCategory().name) { - tocBody.push(tocLine(motion, 'tocCategoryEntry')); - } - }); - catTocBody.push(tocTable(tocBody)); - }); - - //handle thouse without category - var uncatTocBody = []; - _.forEach(allMotions, function (motion) { - if (!motion.getCategory()) { - uncatTocBody.push(tocLine(motion, 'tocEntry')); - } - }); - - // only push this array if there is at least one entry - if (uncatTocBody.length > 0) { - catTocBody.push(tocTable(uncatTocBody)); - } - - toc.push(catTocBody); - } else { - // all categories in the same table - var tocBody = []; - _.forEach(allMotions, function (motion) { - tocBody.push(tocLine(motion, 'tocEntry')); - }); - toc.push(tocTable(tocBody)); - } - - return [ - tocTitle, - toc, - PDFLayout.addPageBreak() - ]; - }; - - // creates a new table of contents table body - var tocTable = function (tocBody) { - return { - table: { - widths: ['auto', '*', 'auto'], - body: tocBody - }, - layout: 'noBorders', - style: 'tocCategorySection' - }; - }; - - // generates a line in the toc as list-object - var tocLine = function (motion, style) { - var firstColumn = ""; - if (motion.getIdentifier()) { - firstColumn = motion.getIdentifier(); - } - return [ - { - text: firstColumn, - style: style - }, - { - text: motion.getTitle(), - style: 'tocEntry' - }, - { - pageReference: ''+motion.getId(), - style: 'tocEntry', - alignment: 'right' - }, - ]; - }; - - // returns a list of unique category names - // necessary to create a ToC with categories - // if a motions without category is found, - // a corresponding entry should be added aswell - var getUniqueCategories = function() { - var categories = []; - _.forEach(allMotions, function (motion) { - if (motion.getCategory()) { - categories.push( - { - name: motion.getCategory().name, - prefix: motion.getCategory().prefix - } - ); - } - }); - return _.uniqBy(categories, 'name'); - }; - - // returns the pure content of the motion, parseable by pdfmake - var getContent = function() { - var motionContent = []; - _.forEach(allMotions, function(motion, key) { - motionContent.push(motion.getContent()); - if (key < allMotions.length - 1) { - motionContent.push(PDFLayout.addPageBreak()); - } - }); - var content = []; - // print extra data (title, preamble, categories, toc) only for more than 1 motion - if (allMotions.length > 1) { - content.push( - title, - createPreamble(), - createTOContent() - ); - } - content.push(motionContent); - return content; - }; - - var getImageMap = function () { - var imageMap = {}; - _.forEach(allMotions, function (motion) { - _.forEach(motion.getImageMap(), function (data, path) { - if (!imageMap[path]) { - imageMap[path] = data; - } - }); - }); - return imageMap; - }; - - return { - getContent: getContent, - getImageMap: getImageMap, - }; - }; - - return { - createInstance: createInstance - }; - } -]) - -.factory('AmendmentContentProvider', [ - '$q', - 'ImageConverter', - 'PdfMakeConverter', - 'HTMLValidizer', - 'PDFLayout', - 'Config', - 'gettextCatalog', - function ($q, ImageConverter, PdfMakeConverter, HTMLValidizer, PDFLayout, Config, gettextCatalog) { - var createInstance = function (motions) { - motions = _.filter(motions, function (motion) { - return motion.parent_id; - }); - - var converter, imageMap = {}; - - // Query all image sources from motion text and reason - var getImageSources = function () { - var sources = []; - _.forEach(motions, function (motion) { - var text = motion.getText(); - var reason = motion.getReason(); - var content = HTMLValidizer.validize(text) + HTMLValidizer.validize(motion.getReason()); - _.forEach($(content).find('img'), function (element) { - sources.push(element.getAttribute('src')); - }); - }); - return _.uniq(sources); - }; - - var createBundleContent = function (bundle) { - return _.flatten(_.map(bundle, function (motion) { - var content = []; - - // get diffs and title of the changed motions - var motionText; - var title = motion.identifier ? gettextCatalog.getString('Motion') + ' ' + motion.identifier : motion.getTitle(); - if (motion.isParagraphBasedAmendment()) { - // get changed parts - var paragraphs = motion.getAmendmentParagraphsLinesDiff(); - if (paragraphs.length) { - // Put the changed lines into the info column - var p = paragraphs[0]; - title += ' (' + gettextCatalog.getString('Line') + ' '; - if (p.diffLineTo === p.diffLineFrom + 1) { - title += p.diffLineFrom; - } else { - title += p.diffLineFrom + '-' + p.diffLineTo; - } - title += ')'; - - // get the diff - motionText = p.text; - } else { - motionText = gettextCatalog.getString('No changes at the text.'); - } - } else { // 'normal' amendment - motionText = motion.getText(); - } - content.push({ - text: title, - style: 'heading3', - marginTop: 15, - }); - - // submitters - var submitters = _.map(motion.submitters, function (submitter) { - return submitter.user.get_full_name(); - }).join(', '); - content.push({ - text: gettextCatalog.getString('Submitters') + ': ' + submitters, - }); - - // state - content.push({ - text: gettextCatalog.getString('State') + ': ' + motion.getStateName(), - }); - - // recommendation - var recommendations_by = Config.get('motions_recommendations_by').value; - var recommendation = motion.getRecommendationName(); - if (recommendations_by && recommendation) { - content.push({ - text: recommendations_by + ': ' + recommendation, - }); - } - - return _.concat(content, converter.convertHTML(motionText, 'outside')); - })); - }; - - var getBundleContent = function (bundle) { - var leadMotion = bundle[0].getParentMotion(); - // title - var title = leadMotion.identifier ? ' ' + leadMotion.identifier : ''; - title += ': ' + leadMotion.getTitle(); - title = PDFLayout.createTitle(gettextCatalog.getString('Amendments to motion') + title); - - var content = [title], - foundAmendments = []; - - var headings = leadMotion.getTextHeadings().map(function(heading) { - heading.amendments = []; - return heading; - }); - bundle.forEach(function(amendment) { - var headingIdx = null; - var changes = amendment.getAmendmentParagraphsByMode('diff'); - if (changes.length === 0) { - return; - } - var amendmentLineNumber = changes[0].lineFrom; - for (var i = 0; i < headings.length; i++) { - if (headings[i].lineNumber <= amendmentLineNumber) { - headingIdx = i; - } - } - if (headingIdx !== null) { - headings[headingIdx].amendments.push(amendment); - foundAmendments.push(amendment.id); - } - }); - - headings.forEach(function(heading) { - if (heading.amendments.length === 0) { - return; - } - content.push({ - text: heading.text, - style: "heading2", - marginTop: 25, - }); - content = _.concat(content, createBundleContent(heading.amendments)); - }); - - // If there was an amendment that did not have a heading, we append it at the bottom - var missedAmendments = []; - bundle.forEach(function(amendment) { - if (foundAmendments.indexOf(amendment.id) === -1) { - missedAmendments.push(amendment); - } - }); - if (missedAmendments.length > 0) { - content = _.concat(content, createBundleContent(missedAmendments)); - } - - return content; - }; - - // Generates content as a pdfmake consumable - var getContent = function() { - if (motions.length === 0) { - return []; - } - - // Creates bundles of motions. All motions with the same parent are bundled together - // respecting the order, in which they are sorted. - // motionBundles is an array containing Arrays of motions with the same parent. - var parentId = motions[0].parent_id; - var motionBundles = []; - var currentBundle = []; - _.forEach(motions, function (motion) { - if (motion.parent_id === parentId) { - currentBundle.push(motion); - } else { - motionBundles.push(currentBundle); - currentBundle = [motion]; - parentId = motion.parent_id; - } - }); - motionBundles.push(currentBundle); - - // Make the amendment table for each motion bundle. - return _.map(motionBundles, function (bundle, index) { - var content = getBundleContent(bundle); - if (index < motionBundles.length - 1) { - content.push(PDFLayout.addPageBreak()); - } - return content; - }); - }; - - var getImageMap = function() { - return imageMap; - }; - - return $q(function (resolve) { - ImageConverter.toBase64(getImageSources()).then(function (_imageMap) { - imageMap = _imageMap; - converter = PdfMakeConverter.createInstance(_imageMap); - resolve({ - getContent: getContent, - getImageMap: getImageMap, - }); - }); - }); - }; - - return { - createInstance: createInstance, - }; - } -]) - -.factory('MotionPdfExport', [ - '$http', - '$q', - 'operator', - 'Config', - 'gettextCatalog', - 'MotionChangeRecommendation', - 'HTMLValidizer', - 'PdfMakeConverter', - 'MotionContentProvider', - 'MotionCatalogContentProvider', - 'PdfMakeDocumentProvider', - 'PollContentProvider', - 'PdfMakeBallotPaperProvider', - 'MotionPartialContentProvider', - 'AmendmentContentProvider', - 'PdfCreate', - 'PDFLayout', - 'PersonalNoteManager', - 'MotionComment', - 'Messaging', - 'FileSaver', - function ($http, $q, operator, Config, gettextCatalog, MotionChangeRecommendation, HTMLValidizer, - PdfMakeConverter, MotionContentProvider, MotionCatalogContentProvider, PdfMakeDocumentProvider, - PollContentProvider, PdfMakeBallotPaperProvider, MotionPartialContentProvider, AmendmentContentProvider, - PdfCreate, PDFLayout, PersonalNoteManager, MotionComment, Messaging, FileSaver) { - return { - getDocumentProvider: function (motions, params, singleMotion) { - params = _.clone(params || {}); // Clone this to avoid sideeffects. - - if (singleMotion) { - _.defaults(params, { - version: motions.active_version, - }); - motions = [motions]; - } - - //save the arrays of all motions to an array - angular.forEach(motions, function (motion) { - if (singleMotion) { - motion.changeRecommendations = MotionChangeRecommendation.filter({ - 'where': {'motion_version_id': {'==': params.version}} - }); - } else { - motion.changeRecommendations = MotionChangeRecommendation.filter({ - 'where': {'motion_version_id': {'==': motion.active_version}} - }); - } - }); - - var motionContentProviderArray = []; - var motionContentProviderPromises = _.map(motions, function (motion) { - var version = (singleMotion ? params.version : motion.active_version); - return $q(function (resolve, reject) { - MotionContentProvider.createInstance( - motion, version, params - ).then(function (contentProvider) { - motionContentProviderArray.push(contentProvider); - resolve(); - }, reject); - }); - }); - - return $q(function (resolve, reject) { - $q.all(motionContentProviderPromises).then(function() { - var documentProviderPromise; - if (singleMotion) { - documentProviderPromise = PdfMakeDocumentProvider.createInstance(motionContentProviderArray[0]); - } else { - var motionCatalogContentProvider = MotionCatalogContentProvider.createInstance(motionContentProviderArray, params.column); - documentProviderPromise = PdfMakeDocumentProvider.createInstance(motionCatalogContentProvider); - } - documentProviderPromise.then(function (documentProvider) { - resolve(documentProvider); - }, reject); - }, reject); - }); - }, - export: function (motions, params, singleMotion) { - params = params || {}; - params.filename = gettextCatalog.getString('motions') + '.pdf'; - this.getDocumentProvider(motions, params, singleMotion).then( - function (documentProvider) { - PdfCreate.download(documentProvider, params.filename); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - } - ); - }, - exportZip: function (motions, params) { - var messageId = Messaging.addMessage('' + - gettextCatalog.getString('Generating PDFs and ZIP archive') + ' ...', 'info'); - var zipFilename = params.filename || gettextCatalog.getString('motions') + '.zip'; - params.filename = void 0; // clear this, so we do not override the default filenames for each pdf. - - var self = this; - var usedFilenames = []; - var docMap = {}; - var docPromises = _.map(motions, function (motion) { - var identifier = motion.identifier ? '-' + motion.identifier : ''; - var filename = gettextCatalog.getString('Motion') + identifier; - - // If the filename is already in use, try to append a number to it (like '(2)') - if (_.includes(usedFilenames, filename)) { - var i = 1; - var filenameWithNumber = filename; - while(_.includes(usedFilenames, filenameWithNumber)) { - filenameWithNumber = filename + ' (' + i + ')'; - i++; - } - filename = filenameWithNumber; - } - usedFilenames.push(filename); - filename += '.pdf'; - - return $q(function (resolve, reject) { - // get documentProvider for every motion. - self.getDocumentProvider(motion, params, true).then(function (documentProvider) { - docMap[filename] = documentProvider; - resolve(); - }, reject); - }); - }); - $q.all(docPromises).then(function () { - PdfCreate.getBase64FromMultipleDocuments(docMap).then(function (pdfMap) { - var zip = new JSZip(); - _.forEach(pdfMap, function (data, filename) { - zip.file(filename, data, {base64: true}); - }); - Messaging.createOrEditMessage(messageId, '' + - gettextCatalog.getString('ZIP successfully generated.'), 'success', {timeout: 3000}); - zip.generateAsync({type: 'blob'}).then(function (content) { - FileSaver.saveAs(content, zipFilename); - }); - }, function (error) { - Messaging.createOrEditMessage(messageId, '' + gettextCatalog.getString('Error while generating ZIP file') + - ': ' + error + '', 'error'); - }); - }, function (error) { - Messaging.createOrEditMessage(messageId, error.msg, 'error'); - }); - }, - createPollPdf: function (motion, version) { - var id = motion.identifier.replace(' ', ''); - var title = motion.getTitle(version); - var filename = gettextCatalog.getString('Motion') + '-' + id + '-' + gettextCatalog.getString('ballot-paper') + '.pdf'; - PollContentProvider.createInstance(title, id).then(function (pollContentProvider) { - var documentProvider = PdfMakeBallotPaperProvider.createInstance(pollContentProvider); - PdfCreate.download(documentProvider, filename); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - }); - }, - exportPersonalNote: function (motion, filename) { - var personalNote = PersonalNoteManager.getNote(motion); - var content = [{ - heading: gettextCatalog.getString('Personal note'), - text: personalNote ? personalNote.note : '', - }]; - MotionPartialContentProvider.createInstance(motion, content).then(function (contentProvider) { - PdfMakeDocumentProvider.createInstance(contentProvider).then(function (documentProvider) { - PdfCreate.download(documentProvider, filename); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - }); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - }); - }, - exportComment: function (motion, commentId, filename) { - var field = MotionComment.getNoSpecialCommentsFields()[commentId]; - if (field && motion.comments[commentId]) { - var title = field.name; - if (!field.public) { - title += ' (' + gettextCatalog.getString('internal') + ')'; - } - var content = [{ - heading: title, - text: motion.comments[commentId], - }]; - MotionPartialContentProvider.createInstance(motion, content).then(function (contentProvider) { - PdfMakeDocumentProvider.createInstance(contentProvider).then(function (documentProvider) { - PdfCreate.download(documentProvider, filename); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - }); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - }); - } - }, - exportAmendments: function (motions, filename) { - AmendmentContentProvider.createInstance(motions).then(function (contentProvider) { - PdfMakeDocumentProvider.createInstance(contentProvider).then(function (documentProvider) { - PdfCreate.download(documentProvider, filename); - }); - }); - }, - }; - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/projector.js b/openslides/motions/static/js/motions/projector.js deleted file mode 100644 index f4af95f03..000000000 --- a/openslides/motions/static/js/motions/projector.js +++ /dev/null @@ -1,98 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.motions.projector', [ - 'OpenSlidesApp.motions', - 'OpenSlidesApp.motions.motionservices', - 'OpenSlidesApp.motions.motionBlockProjector', -]) - -.config([ - 'slidesProvider', - function(slidesProvider) { - slidesProvider.registerSlide('motions/motion', { - template: 'static/templates/motions/slide_motion.html', - }); - } -]) - -.controller('SlideMotionCtrl', [ - '$scope', - '$timeout', - 'Config', - 'Motion', - 'MotionChangeRecommendation', - 'ChangeRecommendationView', - 'User', - 'Notify', - 'ProjectorID', - 'MotionPollDecimalPlaces', - function($scope, $timeout, Config, Motion, MotionChangeRecommendation, - ChangeRecommendationView, User, Notify, ProjectorID, MotionPollDecimalPlaces) { - // Attention! Each object that is used here has to be dealt on server side. - // Add it to the coresponding get_requirements method of the ProjectorElement - // class. - var motionId = $scope.element.id; - $scope.mode = $scope.element.mode || 'original'; - $scope.lineNumberMode = Config.get('motions_default_line_numbering').value; - - var notifyNamePrefix = 'projector_' + ProjectorID() + '_motion_line_'; - var callbackId = Notify.registerCallback(notifyNamePrefix + 'request', function (params) { - var line = params.params.line; - if (!line) { - return; - } - $scope.highlight = line; - $timeout(function () { - $scope.highlight = 0; - }, 4000); - - var scrollTop = null; - $('.line-number-' + line).each(function() { - var top = $(this).offset().top; - if (scrollTop === null || top < scrollTop) { - scrollTop = top; - } - }); - if (scrollTop) { - scrollTop += (-$scope.scroll); // Add the (reversed) scrolling ontop - var scroll = Math.floor((scrollTop/250) - 0.2); - var channel = params.senderReplyChannelName; - Notify.notify(notifyNamePrefix + 'answer', {scroll: scroll}, null, [channel], null); - } - }); - $scope.$on('$destroy', function () { - Notify.deregisterCallback(callbackId); - }); - - User.bindAll({}, $scope, 'users'); - - $scope.$watch(function () { - return Motion.lastModified(motionId); - }, function () { - $scope.motion = Motion.get(motionId); - $scope.amendment_diff_paragraphs = $scope.motion.getAmendmentParagraphsLinesDiff(); - $scope.viewChangeRecommendations.setVersion($scope.motion, $scope.motion.active_version); - _.forEach($scope.motion.polls, function (poll) { - MotionPollDecimalPlaces.getPlaces(poll, true).then(function (decimalPlaces) { - precisionCache[poll.id] = decimalPlaces; - }); - }); - }); - - var precisionCache = {}; - $scope.getPollVotesPrecision = function (poll) { - if (!precisionCache[poll.id]) { - return 0; - } - return precisionCache[poll.id]; - }; - - // Change recommendation viewing - $scope.viewChangeRecommendations = ChangeRecommendationView; - $scope.viewChangeRecommendations.initProjector($scope, Motion.get(motionId), $scope.mode); - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/site.js b/openslides/motions/static/js/motions/site.js deleted file mode 100644 index de91f8bcf..000000000 --- a/openslides/motions/static/js/motions/site.js +++ /dev/null @@ -1,3326 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.motions.site', [ - 'OpenSlidesApp.motions', - 'OpenSlidesApp.motions.motionservices', - 'OpenSlidesApp.poll.majority', - 'OpenSlidesApp.core.pdf', - 'OpenSlidesApp.motions.docx', - 'OpenSlidesApp.motions.pdf', - 'OpenSlidesApp.motions.csv', - 'OpenSlidesApp.motions.workflow', -]) - -.config([ - 'mainMenuProvider', - 'gettext', - function (mainMenuProvider, gettext) { - mainMenuProvider.register({ - 'ui_sref': 'motions.motion.list', - 'img_class': 'file-text', - 'title': gettext('Motions'), - 'weight': 300, - 'perm': 'motions.can_see', - }); - } -]) - -.config([ - 'SearchProvider', - 'gettext', - function (SearchProvider, gettext) { - SearchProvider.register({ - 'verboseName': gettext('Motions'), - 'collectionName': 'motions/motion', - 'urlDetailState': 'motions.motion.detail', - 'weight': 300, - }); - } -]) - -.config([ - '$stateProvider', - 'gettext', - function($stateProvider, gettext) { - $stateProvider - .state('motions', { - url: '/motions', - abstract: true, - template: "", - data: { - title: gettext('Motions'), - basePerm: 'motions.can_see', - }, - }) - .state('motions.motion', { - abstract: true, - template: "", - }) - .state('motions.motion.list', {}) - .state('motions.motion.detail', { - resolve: { - motionId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }], - } - }) - // redirects to motion detail and opens motion edit form dialog, uses edit url, - // used by ui-sref links from agenda only - // (from motion controller use MotionForm factory instead to open dialog in front of - // current view without redirect) - .state('motions.motion.detail.update', { - onEnter: ['$stateParams', '$state', 'ngDialog', 'Motion', - function($stateParams, $state, ngDialog, Motion) { - ngDialog.open({ - template: 'static/templates/motions/motion-form.html', - controller: 'MotionUpdateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - motionId: function () {return $stateParams.id;}, - }, - preCloseCallback: function () { - $state.go('motions.motion.detail', {motion: $stateParams.id}); - return true; - } - }); - } - ] - }) - .state('motions.motion.submitters', { - url: '/submitters/{id:int}', - controller: 'MotionSubmitterCtrl', - resolve: { - motionId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }], - }, - data: { - title: gettext('Submitters'), - basePerm: 'motions.can_manage', - }, - }) - .state('motions.motion.amendment-list', { - url: '/{id:int}/amendments', - controller: 'MotionAmendmentListStateCtrl', - params: { - motionId: null, - }, - resolve: { - motionId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }], - } - }) - .state('motions.motion.allamendments', { - url: '/amendments', - templateUrl: 'static/templates/motions/motion-amendment-list.html', - controller: 'MotionAmendmentListStateCtrl', - resolve: { - motionId: function() { return void 0; }, - } - }) - .state('motions.motion.import', { - url: '/import', - controller: 'MotionImportCtrl', - }) - // categories - .state('motions.category', { - url: '/category', - abstract: true, - template: "", - data: { - title: gettext('Categories'), - }, - }) - .state('motions.category.list', {}) - .state('motions.category.sort', { - url: '/sort/{id}', - controller: 'CategorySortCtrl', - templateUrl: 'static/templates/motions/category-sort.html', - resolve: { - categoryId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }], - }, - }) - // MotionBlock - .state('motions.motionBlock', { - url: '/blocks', - abstract: true, - template: '', - data: { - title: gettext('Motion blocks'), - }, - }) - .state('motions.motionBlock.list', {}) - .state('motions.motionBlock.detail', { - resolve: { - motionBlockId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }], - } - }) - // redirects to motionBlock detail and opens motionBlock edit form dialog, uses edit url, - // used by ui-sref links from agenda only - // (from motionBlock controller use MotionBlockForm factory instead to open dialog in front - // of current view without redirect) - .state('motions.motionBlock.detail.update', { - onEnter: ['$stateParams', '$state', 'ngDialog', - function($stateParams, $state, ngDialog) { - ngDialog.open({ - template: 'static/templates/motions/motion-block-form.html', - controller: 'MotionBlockUpdateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - motionBlockId: function () { - return $stateParams.id; - } - }, - preCloseCallback: function() { - $state.go('motions.motionBlock.detail', {motionBlock: $stateParams.id}); - return true; - } - }); - } - ], - }) - // Workflows and states - .state('motions.workflow', { - url: '/workflow', - abstract: true, - template: '', - data: { - title: gettext('Workflows'), - basePerm: 'motions.can_manage', - }, - }) - .state('motions.workflow.list', {}) - .state('motions.workflow.detail', { - resolve: { - workflowId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }], - } - }); - } -]) - -.factory('ChangeRecommendationTitleForm', [ - 'gettextCatalog', - 'Editor', - 'Config', - function(gettextCatalog) { - return { - // ngDialog for motion form - getCreateDialog: function (motion, version) { - return { - template: 'static/templates/motions/change-recommendation-form.html', - controller: 'ChangeRecommendationTitleCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - motion: function() { - return motion; - }, - version: function() { - return version; - } - } - }; - }, - getEditDialog: function(change) { - return { - template: 'static/templates/motions/change-recommendation-form.html', - controller: 'ChangeRecommendationTitleUpdateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - change: function() { - return change; - } - } - }; - }, - // angular-formly fields for motion form - getFormFields: function () { - return [ - { - key: 'identifier', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Identifier') - }, - hide: true - }, - { - key: 'motion_version_id', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Motion') - }, - hide: true - }, - { - key: 'text', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('New title'), - required: false - } - } - ]; - } - }; - } -]) - -.factory('ChangeRecommendationTextForm', [ - 'gettextCatalog', - 'Editor', - 'Config', - function(gettextCatalog, Editor) { - return { - // ngDialog for motion form - getCreateDialog: function (motion, version, lineFrom, lineTo) { - return { - template: 'static/templates/motions/change-recommendation-form.html', - controller: 'ChangeRecommendationTextCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - motion: function() { - return motion; - }, - version: function() { - return version; - }, - lineFrom: function() { - return lineFrom; - }, - lineTo: function() { - return lineTo; - } - } - }; - }, - getEditDialog: function(change) { - return { - template: 'static/templates/motions/change-recommendation-form.html', - controller: 'ChangeRecommendationTextUpdateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - change: function() { - return change; - } - } - }; - }, - // angular-formly fields for motion form - getFormFields: function (line_from, line_to) { - return [ - { - key: 'identifier', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Identifier') - }, - hide: true - }, - { - key: 'motion_version_id', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Motion') - }, - hide: true - }, - { - key: 'line_from', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('From Line') - }, - hide: true - }, - { - key: 'line_to', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('To Line') - }, - hide: true - }, - { - key: 'type', - type: 'radio-buttons', - templateOptions: { - label: 'Type', - options: [ - {name: gettextCatalog.getString('Replacement'), value: 0}, - {name: gettextCatalog.getString('Insertion'), value: 1}, - {name: gettextCatalog.getString('Deletion'), value: 2}, - {name: gettextCatalog.getString('Other'), value: 3}, - ] - } - }, - { - key: 'other_description', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Description'), - }, - hideExpression: "model.type !== 3", - }, - { - key: 'text', - type: 'editor', - templateOptions: { - label: ( - line_from == line_to - 1 ? - gettextCatalog.getString('Text in line %from%').replace(/%from%/, line_from) : - gettextCatalog.getString('Text from line %from% to %to%') - .replace(/%from%/, line_from).replace(/%to%/, line_to - 1) - ), - required: false - }, - data: { - ckeditorOptions: Editor.getOptions() - } - } - ]; - } - }; - } -]) - -// Service for choosing the paragraph of a given motion that is to be amended -.factory('AmendmentParagraphChooseForm', [ - function () { - return { - // ngDialog for motion form - getDialog: function (motion, successCb) { - return { - template: 'static/templates/motions/amendment-paragraph-choose-form.html', - controller: 'AmendmentParagraphChooseCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - motion: function () { return motion; }, - successCb: function() { return successCb; }, - } - }; - } - }; - } -]) - -// Service for generic motion form (create and update) -.factory('MotionForm', [ - '$filter', - 'gettextCatalog', - 'operator', - 'Editor', - 'MotionComment', - 'Category', - 'Config', - 'Mediafile', - 'MotionBlock', - 'Tag', - 'User', - 'Workflow', - 'Agenda', - 'AgendaTree', - 'ShowAsAgendaItemField', - function ($filter, gettextCatalog, operator, Editor, MotionComment, Category, Config, - Mediafile, MotionBlock, Tag, User, Workflow, Agenda, AgendaTree, ShowAsAgendaItemField) { - return { - // ngDialog for motion form - // If motion is given and not null, we're editing an already existing motion - // If parentMotion is give, we're dealing with an amendment - // If paragraphNo is given as well, the amendment is paragraph-based - // If paragraphTextPre is given, we're creating a modified version of another paragraph-based amendment - getDialog: function (motion, parentMotion, paragraphNo, paragraphTextPre) { - return { - template: 'static/templates/motions/motion-form.html', - controller: motion ? 'MotionUpdateCtrl' : 'MotionCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - motionId: function () {return motion ? motion.id : void 0;}, - parentMotion: function () {return parentMotion;}, - paragraphNo: function () {return paragraphNo;}, - paragraphTextPre: function () {return paragraphTextPre;} - } - }; - }, - // angular-formly fields for motion form - getFormFields: function (isCreateForm, isParagraphBasedAmendment) { - if (!isParagraphBasedAmendment) { // catch null and undefined. Angular formy doesn't like this. - isParagraphBasedAmendment = false; - } - - var workflows = Workflow.getAll(); - var images = Mediafile.getAllImages(); - var formFields = []; - formFields.push({ - key: 'identifier', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Identifier') - }, - hide: true - }); - - if (isCreateForm) { - formFields.push({ - key: 'submitters_id', - type: 'select-multiple', - templateOptions: { - label: gettextCatalog.getString('Submitters'), - options: User.getAll(), - ngOptions: 'option.id as option.full_name for option in to.options', - placeholder: gettextCatalog.getString('Select or search a submitter ...'), - }, - hide: !operator.hasPerms('motions.can_manage') - }); - } - - formFields = formFields.concat([ - { - key: 'title', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Title'), - required: true - }, - hide: isParagraphBasedAmendment && isCreateForm - }, - { - template: '

    ' + Config.translate(Config.get('motions_preamble').value) + '

    ' - }, - { - key: 'text', - type: 'editor', - templateOptions: { - label: gettextCatalog.getString('Text'), - required: !isParagraphBasedAmendment // Deleting the whole paragraph in an amendment should be possible - }, - data: { - ckeditorOptions: Editor.getOptions() - } - }, - { - key: 'reason', - type: 'editor', - templateOptions: { - label: gettextCatalog.getString('Reason'), - }, - data: { - ckeditorOptions: Editor.getOptions() - } - }, - { - key: 'disable_versioning', - type: 'checkbox', - templateOptions: { - label: gettextCatalog.getString('Trivial change'), - description: gettextCatalog.getString("Don't create a new version.") - }, - hide: true - } - ]); - - // show as agenda item + parent item - if (isCreateForm) { - formFields.push(ShowAsAgendaItemField('motions.can_manage')); - formFields.push({ - key: 'agenda_parent_id', - type: 'select-single', - templateOptions: { - label: gettextCatalog.getString('Parent item'), - options: AgendaTree.getFlatTree(Agenda.getAll()), - ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id', - placeholder: gettextCatalog.getString('Select a parent item ...') - }, - hide: !operator.hasPerms('agenda.can_manage') - }); - } - - // motion comments - formFields = formFields.concat(MotionComment.getFormFields()); - - // more - formFields.push( - { - key: 'more', - type: 'checkbox', - templateOptions: { - label: gettextCatalog.getString('Show extended fields') - }, - hide: !operator.hasPerms('motions.can_manage') - }, - { - template: '
    ', - hideExpression: '!model.more' - } - ); - // attachments - if (Mediafile.getAll().length > 0) { - formFields.push({ - key: 'attachments_id', - type: 'select-multiple', - templateOptions: { - label: gettextCatalog.getString('Attachment'), - options: $filter('orderBy')(Mediafile.getAll(), 'title_or_filename'), - ngOptions: 'option.id as option.title_or_filename for option in to.options', - placeholder: gettextCatalog.getString('Select or search an attachment ...') - }, - hideExpression: '!model.more' - }); - } - // category - if (Category.getAll().length > 0) { - formFields.push({ - key: 'category_id', - type: 'select-single', - templateOptions: { - label: gettextCatalog.getString('Category'), - options: Category.getAll(), - ngOptions: 'option.id as option.name for option in to.options', - placeholder: gettextCatalog.getString('Select or search a category ...') - }, - hideExpression: '!model.more' - }); - } - // motion block - if (MotionBlock.getAll().length > 0) { - formFields.push({ - key: 'motion_block_id', - type: 'select-single', - templateOptions: { - label: gettextCatalog.getString('Motion block'), - options: MotionBlock.getAll(), - ngOptions: 'option.id as option.title for option in to.options', - placeholder: gettextCatalog.getString('Select or search a motion block ...') - }, - hideExpression: '!model.more' - }); - } - // origin - formFields.push({ - key: 'origin', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Origin'), - }, - hideExpression: '!model.more' - }); - // tags - if (Tag.getAll().length > 0) { - formFields.push({ - key: 'tags_id', - type: 'select-multiple', - templateOptions: { - label: gettextCatalog.getString('Tags'), - options: Tag.getAll(), - ngOptions: 'option.id as option.name for option in to.options', - placeholder: gettextCatalog.getString('Select or search a tag ...') - }, - hideExpression: '!model.more' - }); - } - // supporters - if (Config.get('motions_min_supporters').value > 0) { - formFields.push({ - key: 'supporters_id', - type: 'select-multiple', - templateOptions: { - label: gettextCatalog.getString('Supporters'), - options: User.getAll(), - ngOptions: 'option.id as option.full_name for option in to.options', - placeholder: gettextCatalog.getString('Select or search a supporter ...') - }, - hideExpression: '!model.more' - }); - } - // workflows - if (workflows.length > 1) { - formFields.push({ - key: 'workflow_id', - type: 'select-single', - templateOptions: { - label: gettextCatalog.getString('Workflow'), - optionsAttr: 'bs-options', - options: workflows, - ngOptions: 'option.id as option.name | translate for option in to.options', - placeholder: gettextCatalog.getString('Select or search a workflow ...') - }, - hideExpression: '!model.more', - }); - } - - return formFields; - } - }; - } -]) - -.factory('MotionCommentForm', [ - 'MotionComment', - function (MotionComment) { - return { - // ngDialog for motion comment form - getDialog: function (motion, commentFieldId) { - return { - template: 'static/templates/motions/motion-comment-form.html', - controller: 'MotionCommentCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - motionId: function () {return motion.id;}, - commentFieldId: function () {return commentFieldId;}, - }, - }; - }, - // angular-formly fields for motion comment form - getFormFields: function (commentFieldId) { - return [ - MotionComment.getFormField(commentFieldId) - ]; - }, - }; - } -]) - -.factory('CategoryForm', [ - 'gettextCatalog', - function (gettextCatalog) { - return { - getDialog: function (category) { - return { - template: 'static/templates/motions/category-form.html', - controller: category ? 'CategoryUpdateCtrl' : 'CategoryCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - categoryId: function () {return category ? category.id : void 0;}, - }, - }; - - }, - getFormFields: function () { - return [ - { - key: 'prefix', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Prefix') - }, - }, - { - key: 'name', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Name') - }, - } - ]; - }, - }; - } -]) - -// Provide generic motionpoll form fields for poll update view -.factory('MotionPollForm', [ - 'gettextCatalog', - function (gettextCatalog) { - return { - getFormFields: function (precision) { - var step = Math.pow(10, -precision); - return [ - { - key: 'yes', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Yes'), - type: 'number', - step: step, - required: true - } - }, - { - key: 'no', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('No'), - type: 'number', - step: step, - required: true - } - }, - { - key: 'abstain', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Abstain'), - type: 'number', - step: step, - required: true - } - }, - { - key: 'votesvalid', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Valid votes'), - step: step, - type: 'number' - } - }, - { - key: 'votesinvalid', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Invalid votes'), - step: step, - type: 'number' - } - }, - { - key: 'votescast', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Votes cast'), - step: step, - type: 'number' - } - }]; - } - }; - } -]) - -.factory('MotionExportForm', [ - 'operator', - 'gettextCatalog', - 'Config', - 'MotionComment', - function (operator, gettextCatalog, Config, MotionComment) { - var noSpecialCommentsFields = MotionComment.getNoSpecialCommentsFields(); - return { - getDialog: function (motions, params, singleMotion) { - return { - template: 'static/templates/motions/motion-export-form.html', - controller: 'MotionExportCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - motions: function () {return motions;}, - params: function () {return params;}, - singleMotion: function () {return singleMotion;}, - }, - }; - }, - getFormFields: function (singleMotion, motions, formatChangeCallback) { - var fields = []; - var commentsAvailable = _.keys(noSpecialCommentsFields).length !== 0; - var someMotionsHaveAmendments = _.some(motions, function (motion) { - return motion.hasAmendments(); - }); - // if amendments amendments are already included. We owudl have them twice, if the option is enabled. - if (Config.get('motions_amendments_main_table').value) { - someMotionsHaveAmendments = false; - } - var getMetaInformationOptions = function (disabled) { - if (!disabled) { - disabled = {}; - } - var options = [ - {name: gettextCatalog.getString('Submitters'), id: 'submitters', disabled: disabled.submitters}, - {name: gettextCatalog.getString('State'), id: 'state', disabled: disabled.state}, - ]; - if (Config.get('motions_recommendations_by').value) { - options.push({ - name: gettextCatalog.getString('Recommendation'), - id: 'recommendation', - disabled: disabled.recommendation - }); - } - if (_.some(motions, function (motion) { return motion.category; })) { - options.push({ - name: gettextCatalog.getString('Category'), - id: 'category', - disabled: disabled.category, - }); - } - if (_.some(motions, function (motion) { return motion.motionBlock; })) { - options.push({ - name: gettextCatalog.getString('Motion block'), - id: 'motionBlock', - disabled: disabled.motionBlock, - }); - } - if (_.some(motions, function (motion) { return motion.origin; })) { - options.push({ - name: gettextCatalog.getString('Origin'), - id: 'origin', - disabled: disabled.origin, - }); - } - options.push({ - name: gettextCatalog.getString('Voting result'), - id: 'votingresult', - disabled: disabled.votingResult - }); - return options; - }; - if (!singleMotion) { - fields = [ - { - key: 'format', - type: 'radio-buttons', - templateOptions: { - label: gettextCatalog.getString('Format'), - options: [ - {name: 'PDF', value: 'pdf'}, - {name: 'CSV', value: 'csv'}, - {name: 'DOCX', value: 'docx'}, - ], - change: formatChangeCallback, - }, - } - ]; - } - if (someMotionsHaveAmendments) { - fields.push({ - key: 'amendments', - type: 'radio-buttons', - templateOptions: { - label: gettextCatalog.getString('Amendments'), - options: [ - {name: gettextCatalog.getString('Include'), value: true}, - {name: gettextCatalog.getString('Exclude'), value: false}, - ], - }, - }); - } - if (operator.hasPerms('motions.can_manage')) { - fields.push.apply(fields, [ - { - key: 'lineNumberMode', - type: 'radio-buttons', - templateOptions: { - label: gettextCatalog.getString('Line numbering'), - options: [ - {name: gettextCatalog.getString('None'), value: 'none'}, - {name: gettextCatalog.getString('inline'), value: 'inline'}, - {name: gettextCatalog.getString('outside'), value: 'outside'}, - ], - }, - hideExpression: "model.format !== 'pdf'", - }, - { - key: 'lineNumberMode', - type: 'radio-buttons', - templateOptions: { - label: gettextCatalog.getString('Line numbering'), - options: [ - {name: gettextCatalog.getString('None'), value: 'none'}, - {name: gettextCatalog.getString('inline'), value: 'inline', disabled: true}, - {name: gettextCatalog.getString('outside'), value: 'outside', disabled: true}, - ], - }, - hideExpression: "model.format === 'pdf'", - }, - { - key: 'changeRecommendationMode', - type: 'radio-buttons', - templateOptions: { - label: gettextCatalog.getString('Change recommendations'), - options: [ - {name: gettextCatalog.getString('Original version'), value: 'original'}, - {name: gettextCatalog.getString('Changed version'), value: 'changed'}, - {name: gettextCatalog.getString('Diff version'), value: 'diff'}, - {name: gettextCatalog.getString('Final version'), value: 'modified_agreed'}, - ], - }, - hideExpression: "model.format !== 'pdf'", - }, - { - key: 'changeRecommendationMode', - type: 'radio-buttons', - templateOptions: { - label: gettextCatalog.getString('Change recommendations'), - options: [ - {name: gettextCatalog.getString('Original version'), value: 'original'}, - {name: gettextCatalog.getString('Changed version'), value: 'changed'}, - {name: gettextCatalog.getString('Diff version'), value: 'diff', disabled: true}, - {name: gettextCatalog.getString('Final version'), value: 'modified_agreed'}, - ], - }, - hideExpression: "model.format === 'pdf'", - }, - { - key: 'include', - type: 'checkbox-buttons', - templateOptions: { - label: gettextCatalog.getString('Content'), - options: [ - {name: gettextCatalog.getString('Text'), id: 'text'}, - {name: gettextCatalog.getString('Reason'), id: 'reason'}, - ], - }, - }, - { - key: 'include', - type: 'checkbox-buttons', - templateOptions: { - label: gettextCatalog.getString('Meta information'), - options: getMetaInformationOptions(), - }, - hideExpression: "model.format !== 'pdf'", - }, - { - key: 'include', - type: 'checkbox-buttons', - templateOptions: { - label: gettextCatalog.getString('Meta information'), - options: getMetaInformationOptions({votingResult: true}), - }, - hideExpression: "model.format !== 'csv'", - }, - ]); - if (commentsAvailable) { - fields.push({ - key: 'includeComments', - type: 'checkbox-buttons', - templateOptions: { - label: gettextCatalog.getString('Comments'), - options: _.map(noSpecialCommentsFields, function (field, id) { - return { - name: gettextCatalog.getString(field.name), - id: id, - }; - }), - }, - hideExpression: "model.format === 'csv'", - }); - } - } - if (!singleMotion) { - fields.push({ - key: 'pdfFormat', - type: 'radio-buttons', - templateOptions: { - label: gettextCatalog.getString('PDF format'), - options: [ - {name: gettextCatalog.getString('One PDF'), value: 'pdf'}, - {name: gettextCatalog.getString('Multiple PDFs in a zip arcive'), value: 'zip'}, - ], - }, - hideExpression: "model.format !== 'pdf'", - }); - } - return fields; - }, - }; - } -]) - -.controller('MotionExportCtrl', [ - '$scope', - 'Config', - 'MotionExportForm', - 'MotionPdfExport', - 'MotionCsvExport', - 'MotionDocxExport', - 'motions', - 'params', - 'singleMotion', - function ($scope, Config, MotionExportForm, MotionPdfExport, MotionCsvExport, - MotionDocxExport, motions, params, singleMotion) { - $scope.formFields = MotionExportForm.getFormFields(singleMotion, motions, function () { - if ($scope.params.format !== 'pdf') { - $scope.params.changeRecommendationMode = 'original'; - $scope.params.lineNumberMode = 'none'; - $scope.params.include.votingresult = false; - } - if ($scope.params.format === 'docx') { - $scope.params.include.state = false; - $scope.params.include.submitter = true; - $scope.params.include.motionBlock = false; - $scope.params.include.origin = false; - $scope.params.include.recommendation = false; - } else { - $scope.params.include.state = true; - $scope.params.include.motionBlock = true; - $scope.params.include.origin = true; - $scope.params.include.recommendation = true; - } - if ($scope.params.format === 'pdf') { - $scope.params.include.state = true; - $scope.params.include.votingresult = true; - } - }); - $scope.params = params || {}; - _.defaults($scope.params, { - format: 'pdf', - pdfFormat: 'pdf', - changeRecommendationMode: Config.get('motions_recommendation_text_mode').value, - lineNumberMode: Config.get('motions_default_line_numbering').value, - amendments: false, - include: { - text: true, - reason: true, - state: true, - category: true, - submitters: true, - votingresult: true, - motionBlock: true, - origin: true, - recommendation: true, - }, - includeComments: {}, - }); - // Always change the mode from agreed to modified_agreed. If a motion does not have a modified - // final version, the agreed will be taken. - if ($scope.params.changeRecommendationMode === 'agreed') { - $scope.params.changeRecommendationMode = 'modified_agreed'; - } - $scope.motions = motions; - $scope.singleMotion = singleMotion; - - // Add amendments to motions. The amendments are sorted by their identifier - var prepareAmendments = function (motions) { - var allMotions = []; - _.forEach(motions, function (motion) { - allMotions.push(motion); - allMotions = allMotions.concat( - _.sortBy(motion.getAmendments(), function (amendment) { - return amendment.identifier; - }) - ); - }); - return allMotions; - }; - - $scope.export = function () { - if ($scope.params.amendments) { - motions = prepareAmendments(motions); - } - switch ($scope.params.format) { - case 'pdf': - if ($scope.params.pdfFormat === 'pdf') { - MotionPdfExport.export(motions, $scope.params, singleMotion); - } else { - MotionPdfExport.exportZip(motions, $scope.params); - } - break; - case 'csv': - MotionCsvExport.export(motions, $scope.params); - break; - case 'docx': - MotionDocxExport.export(motions, $scope.params); - break; - } - $scope.closeThisDialog(); - }; - } -]) - -// Cache for MotionPollDetailCtrl so that users choices are keeped during user actions (e. g. save poll form). -.value('MotionPollDetailCtrlCache', {}) - -// Child controller of MotionDetailCtrl for each single poll. -.controller('MotionPollDetailCtrl', [ - '$scope', - 'MajorityMethodChoices', - 'Config', - 'MotionPollDetailCtrlCache', - 'MotionPollDecimalPlaces', - function ($scope, MajorityMethodChoices, Config, MotionPollDetailCtrlCache, MotionPollDecimalPlaces) { - // Define choices. - $scope.methodChoices = MajorityMethodChoices; - // TODO: Get $scope.baseChoices from config_variables.py without copying them. - - $scope.votesPrecision = MotionPollDecimalPlaces.getPlaces($scope.poll); - - // Setup empty cache with default values. - if (typeof MotionPollDetailCtrlCache[$scope.poll.id] === 'undefined') { - MotionPollDetailCtrlCache[$scope.poll.id] = { - method: $scope.config('motions_poll_default_majority_method'), - }; - } - - // Fetch users choices from cache. - $scope.method = MotionPollDetailCtrlCache[$scope.poll.id].method; - - // Define result function. - $scope.isReached = function () { - return $scope.poll.isReached($scope.method); - }; - - // Define template controll function - $scope.hideMajorityCalculation = function () { - return typeof $scope.isReached() === 'undefined' && $scope.method !== 'disabled'; - }; - - // Save current values to cache on detroy of this controller. - $scope.$on('$destroy', function() { - MotionPollDetailCtrlCache[$scope.poll.id] = { - method: $scope.method, - }; - }); - } -]) - -.controller('MotionListCtrl', [ - '$scope', - '$state', - '$http', - 'gettext', - 'gettextCatalog', - 'operator', - 'ngDialog', - 'MotionForm', - 'Motion', - 'MotionComment', - 'Category', - 'Config', - 'Tag', - 'Workflow', - 'User', - 'Agenda', - 'MotionBlock', - 'Projector', - 'ProjectionDefault', - 'osTableFilter', - 'osTableSort', - 'osTablePagination', - 'MotionExportForm', - 'MotionPdfExport', - 'PersonalNoteManager', - function($scope, $state, $http, gettext, gettextCatalog, operator, ngDialog, MotionForm, Motion, - MotionComment, Category, Config, Tag, Workflow, User, Agenda, MotionBlock, Projector, - ProjectionDefault, osTableFilter, osTableSort, osTablePagination, MotionExportForm, - MotionPdfExport, PersonalNoteManager) { - Category.bindAll({}, $scope, 'categories'); - MotionBlock.bindAll({}, $scope, 'motionBlocks'); - Tag.bindAll({}, $scope, 'tags'); - Workflow.bindAll({}, $scope, 'workflows'); - User.bindAll({}, $scope, 'users'); - Projector.bindAll({}, $scope, 'projectors'); - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - var projectiondefault = ProjectionDefault.filter({name: 'motions'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId = projectiondefault.projector_id; - } - }); - $scope.$watch(function () { - return Motion.lastModified(); - }, function () { - // get all main motions and order by identifier (after custom ordering) - var motions; - if (Config.get('motions_amendments_main_table').value) { - motions = Motion.getAll(); - } else { - motions = Motion.filter({parent_id: undefined}); - } - - $scope.motions = _.orderBy(motions, ['identifier']); - _.forEach($scope.motions, function (motion) { - MotionComment.populateFields(motion); - motion.personalNote = PersonalNoteManager.getNote(motion); - // For filtering, we cannot filter for .personalNote.star - motion.star = motion.personalNote ? motion.personalNote.star : false; - motion.hasPersonalNote = motion.personalNote ? !!motion.personalNote.note : false; - if (motion.star === undefined) { - motion.star = false; - } - }); - $scope.collectStatesAndRecommendations(); - }); - $scope.alert = {}; - - // Motion comments - $scope.noSpecialCommentsFields = MotionComment.getNoSpecialCommentsFields(); - $scope.showCommentsFilter = function () { - return _.keys($scope.noSpecialCommentsFields).length > 0; - }; - - // collect all states and all recommendations of all workflows - $scope.collectStatesAndRecommendations = function () { - // Special case: If it is the first time updated, update the state filter. - // This causes to set the done/undone states correct on page load. - var doStateFilterUpdate = !$scope.states; - $scope.states = []; - $scope.recommendations = []; - var workflows = $scope.collectAllUsedWorkflows(); - _.forEach(workflows, function (workflow) { - if (workflows.length > 1) { - var workflowHeader = { - headername: workflow.name, - workflowHeader: true, - }; - $scope.states.push(workflowHeader); - $scope.recommendations.push(workflowHeader); - } - - var firstEndStateSeen = false; - _.forEach(_.orderBy(workflow.states, 'id'), function (state) { - if (state.next_states_id.length === 0 && !firstEndStateSeen) { - $scope.states.push({divider: true}); - firstEndStateSeen = true; - } - $scope.states.push(state); - if (state.recommendation_label) { - $scope.recommendations.push(state); - } - }); - }); - if (doStateFilterUpdate) { - updateStateFilter(); - } - }; - $scope.collectAllUsedWorkflows = function () { - return _.filter(Workflow.getAll(), function (workflow) { - return _.some($scope.motions, function (motion) { - return motion.state.workflow_id === workflow.id; - }); - }); - }; - - $scope.stateFilter = []; - var updateStateFilter = function () { - $scope.stateFilter = _.clone($scope.filter.multiselectFilters.state); - - var doneIndex = _.indexOf($scope.stateFilter, -1); - if (doneIndex > -1) { // contains -1 (done) - $scope.stateFilter.splice(doneIndex, 1); // remove -1 - _.forEach($scope.states, function (state) { - if (!state.workflowHeader && !state.divider) { - if (state.next_states_id.length === 0) { // add all done state - $scope.stateFilter.push(state.id); - } - } - }); - } - - var undoneIndex = _.indexOf($scope.stateFilter, -2); - if (undoneIndex > -1) { // contains -2 (undone) - $scope.stateFilter.splice(undoneIndex, 1); // remove -2 - _.forEach($scope.states, function (state) { - if (!state.workflowHeader && !state.divider) { - if (state.next_states_id.length !== 0) { // add all undone state - $scope.stateFilter.push(state.id); - } - } - }); - } - $scope.stateFilter = _.uniq($scope.stateFilter); - }; - - // This value may be overritten, so the filters, sorting and pagination in an - // derived view are independent to this view. - var osTablePrefix = $scope.osTablePrefix || 'MotionTable'; - - // Filtering - $scope.filter = osTableFilter.createInstance(osTablePrefix + 'Filter'); - - if (!$scope.filter.existsStorageEntry()) { - $scope.filter.multiselectFilters = { - state: [], - category: [], - motionBlock: [], - tag: [], - recommendation: [], - comment: [], - }; - $scope.filter.booleanFilters = { - isFavorite: { - value: undefined, - choiceYes: gettext('Marked as favorite'), - choiceNo: gettext('Not marked as favorite'), - }, - hasPersonalNote: { - value: undefined, - choiceYes: gettext('Personal note set'), - choiceNo: gettext('Personal note not set'), - }, - }; - } - $scope.filter.propertyList = ['identifier', 'origin']; - $scope.filter.propertyFunctionList = [ - function (motion) {return motion.getTitle();}, - function (motion) {return motion.category ? motion.category.name : '';}, - function (motion) {return motion.motionBlock ? motion.motionBlock.name : '';}, - function (motion) {return motion.recommendation ? motion.getRecommendationName() : '';}, - ]; - $scope.filter.propertyDict = { - 'submitters': function (submitter) { - return submitter.user.get_short_name(); - }, - 'supporters': function (supporter) { - return supporter.get_short_name(); - }, - 'tags': function (tag) { - return tag.name; - }, - }; - $scope.getItemId = { - state: function (motion) {return motion.state_id;}, - comment: function (motion) { - var ids = []; - _.forEach(motion.comments, function (comment, id) { - if (comment) { - ids.push(id); - } - }); - return ids; - }, - category: function (motion) {return motion.category_id;}, - motionBlock: function (motion) {return motion.motion_block_id;}, - tag: function (motion) {return motion.tags_id;}, - recommendation: function (motion) {return motion.recommendation_id;}, - }; - $scope.operateStateFilter = function (id, danger) { - $scope.filter.operateMultiselectFilter('state', id, danger); - updateStateFilter(); - }; - $scope.resetFilters = function (danger) { - $scope.filter.reset(danger); - updateStateFilter(); - }; - // Sorting - $scope.sort = osTableSort.createInstance(osTablePrefix + 'Sort'); - if (!$scope.sort.column) { - $scope.sort.column = 'identifier'; - } - $scope.sortOptions = [ - {name: 'identifier', - display_name: gettext('Identifier')}, - {name: 'getTitle()', - display_name: gettext('Title')}, - {name: 'submitters[0].user.get_short_name()', - display_name: gettext('Submitters')}, - {name: 'category.' + Config.get('motions_export_category_sorting').value, - display_name: gettext('Category')}, - {name: 'motionBlock.title', - display_name: gettext('Motion block')}, - {name: 'state.name', - display_name: gettext('State')}, - {name: 'log_messages[log_messages.length-1].time', - display_name: gettext('Creation date')}, - {name: 'log_messages[0].time', - display_name: gettext('Last modified')}, - ]; - - // pagination - $scope.pagination = osTablePagination.createInstance(osTablePrefix + 'Pagination'); - - $scope.hasTag = function (motion, tag) { - return _.indexOf(motion.tags_id, tag.id) > -1; - }; - - $scope.save = function (motion) { - Motion.save(motion, {method: 'PATCH'}); - }; - // delete single motion - $scope.delete = function (motion) { - Motion.destroy(motion.id); - }; - $scope.toggleTag = function (motion, tag) { - if ($scope.hasTag(motion, tag)) { - // remove - motion.tags_id = _.filter(motion.tags_id, function (tag_id){ - return tag_id != tag.id; - }); - } else { - motion.tags_id.push(tag.id); - } - $scope.save(motion); - }; - $scope.toggleCategory = function (motion, category) { - if (motion.category_id == category.id) { - motion.category_id = null; - } else { - motion.category_id = category.id; - } - $scope.save(motion); - }; - $scope.toggleMotionBlock = function (motion, block) { - if (motion.motion_block_id == block.id) { - motion.motion_block_id = null; - } else { - motion.motion_block_id = block.id; - } - $scope.save(motion); - }; - $scope.toggleStar = function (motion) { - if (motion.personalNote) { - motion.personalNote.star = !motion.personalNote.star; - } else { - motion.personalNote = {star: true}; - } - PersonalNoteManager.saveNote(motion, motion.personalNote); - }; - - // open new/edit dialog - $scope.openDialog = function (motion) { - ngDialog.open(MotionForm.getDialog(motion)); - }; - // Export dialog - $scope.openExportDialog = function (motions) { - ngDialog.open(MotionExportForm.getDialog(motions, $scope.sort)); - }; - $scope.pdfExport = function (motions) { - MotionPdfExport.export(motions); - }; - - // *** select mode functions *** - $scope.isSelectMode = false; - // check all checkboxes from filtered motions - $scope.checkAll = function (motions) { - $scope.selectedAll = !$scope.selectedAll; - _.forEach(motions, function (motion) { - motion.selected = $scope.selectedAll; - }); - }; - // uncheck all checkboxes if isSelectMode is closed - $scope.uncheckAll = function () { - if (!$scope.isSelectMode) { - $scope.selectedAll = false; - _.forEach($scope.motions, function (motion) { - motion.selected = false; - }); - } - }; - var selectModeAction = function (motions, predicate) { - angular.forEach(motions, function (motion) { - if (motion.selected) { - predicate(motion); - } - }); - $scope.isSelectMode = false; - $scope.uncheckAll(); - }; - // delete selected motions - $scope.deleteMultiple = function (motions) { - selectModeAction(motions, function (motion) { - $scope.delete(motion); - }); - }; - // set status for selected motions - $scope.setStatusMultiple = function (motions, stateId) { - selectModeAction(motions, function (motion) { - $http.put('/rest/motions/motion/' + motion.id + '/set_state/', {'state': stateId}); - }); - }; - // set category for selected motions - $scope.setCategoryMultiple = function (motions, categoryId) { - selectModeAction(motions, function (motion) { - motion.category_id = categoryId === 'no_category_selected' ? null : categoryId; - $scope.save(motion); - }); - }; - // set status for selected motions - $scope.setMotionBlockMultiple = function (motions, motionBlockId) { - selectModeAction(motions, function (motion) { - motion.motion_block_id = motionBlockId === 'no_motionBlock_selected' ? null : motionBlockId; - $scope.save(motion); - }); - }; - } -]) - -.controller('MotionDetailCtrl', [ - '$scope', - '$http', - '$timeout', - '$window', - '$filter', - 'operator', - 'ngDialog', - 'gettextCatalog', - 'MotionForm', - 'AmendmentParagraphChooseForm', - 'ChangeRecommendationCreate', - 'ChangeRecommendationView', - 'MotionStateAndRecommendationParser', - 'MotionChangeRecommendation', - 'Motion', - 'MotionComment', - 'Category', - 'Mediafile', - 'Tag', - 'User', - 'Workflow', - 'Config', - 'motionId', - 'MotionInlineEditing', - 'MotionCommentsInlineEditing', - 'Editor', - 'Projector', - 'ProjectionDefault', - 'MotionBlock', - 'MotionPdfExport', - 'PersonalNoteManager', - 'Notify', - 'WebpageTitle', - 'EditingWarning', - function($scope, $http, $timeout, $window, $filter, operator, ngDialog, gettextCatalog, - MotionForm, AmendmentParagraphChooseForm, ChangeRecommendationCreate, ChangeRecommendationView, - MotionStateAndRecommendationParser, MotionChangeRecommendation, Motion, MotionComment, - Category, Mediafile, Tag, User, Workflow, Config, motionId, MotionInlineEditing, - MotionCommentsInlineEditing, Editor, Projector, ProjectionDefault, MotionBlock, - MotionPdfExport, PersonalNoteManager, Notify, WebpageTitle, EditingWarning) { - var motion = Motion.get(motionId); - Category.bindAll({}, $scope, 'categories'); - Mediafile.bindAll({}, $scope, 'mediafiles'); - Tag.bindAll({}, $scope, 'tags'); - User.bindAll({}, $scope, 'users'); - Workflow.bindAll({}, $scope, 'workflows'); - MotionBlock.bindAll({}, $scope, 'motionBlocks'); - Motion.bindAll({}, $scope, 'motions'); - - - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - $scope.projectors = Projector.getAll(); - var projectiondefault = ProjectionDefault.filter({name: 'motions'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId = projectiondefault.projector_id; - } - }); - $scope.$watch(function () { - return Motion.lastModified(motionId); - }, function () { - $scope.motion = Motion.get(motionId); - $scope.amendment_diff_paragraphs = $scope.motion.getAmendmentParagraphsLinesDiff(); - MotionComment.populateFields($scope.motion); - if (motion.comments) { - $scope.stateExtension = $scope.motion.comments[$scope.commentFieldForStateId]; - $scope.recommendationExtension = $scope.motion.comments[$scope.commentFieldForRecommendationId]; - } - $scope.motion.personalNote = PersonalNoteManager.getNote($scope.motion); - $scope.navigation.evaluate(); - - var webpageTitle = gettextCatalog.getString('Motion') + ' '; - if ($scope.motion.identifier) { - webpageTitle += $scope.motion.identifier + ' - '; - } - webpageTitle += $scope.motion.getTitle(); - WebpageTitle.updateTitle(webpageTitle); - - $scope.createChangeRecommendation.setVersion(motion, motion.active_version); - $scope.viewChangeRecommendations.setVersion(motion, motion.active_version); - }); - $scope.$watch(function () { - return Motion.lastModified(); - }, function () { - $scope.motions = Motion.getAll(); - $scope.amendments = Motion.filter({parent_id: motion.id}); - }); - $scope.projectionModes = [ - {mode: 'original', - label: 'Original version'}, - {mode: 'changed', - label: 'Changed version'}, - {mode: 'diff', - label: 'Diff version'}, - {mode: 'agreed', - label: 'Final version'}, - {mode: 'modified_agreed', - label: 'Final print template'}, - ]; - var motionDefaultRecommendationTextMode = Config.get('motions_recommendation_text_mode').value; - // Change to the modified final version, if exists - if (motionDefaultRecommendationTextMode === 'agreed' && motion.getModifiedFinalVersion()) { - motionDefaultRecommendationTextMode = 'modified_agreed'; - } - $scope.projectionMode = _.find($scope.projectionModes, function (mode) { - return mode.mode == motionDefaultRecommendationTextMode; - }); - if (motion.isProjected().length) { - var modeMapping = motion.isProjectedWithMode(); - _.forEach($scope.projectionModes, function (mode) { - if (mode.mode === modeMapping[0].mode) { - $scope.projectionMode = mode; - } - }); - } - $scope.setProjectionMode = function (mode) { - $scope.projectionMode = mode; - var isProjected = motion.isProjectedWithMode(); - if (isProjected.length) { - _.forEach(isProjected, function (mapping) { - if (mapping.mode != mode.mode) { // change the mode if it is different - motion.project(mapping.projectorId, mode.mode); - } - }); - } - }; - $scope.commentsFields = MotionComment.getCommentsFields(); - $scope.noSpecialCommentsFields = MotionComment.getNoSpecialCommentsFields(); - $scope.commentFieldForStateId = MotionComment.getFieldIdForFlag('forState'); - $scope.commentFieldForRecommendationId = MotionComment.getFieldIdForFlag('forRecommendation'); - $scope.version = motion.active_version; - $scope.isCollapsed = true; - $scope.lineNumberMode = Config.get('motions_default_line_numbering').value; - $scope.setLineNumberMode = function(mode) { - $scope.lineNumberMode = mode; - }; - - $scope.showAmendmentContext = false; - $scope.setShowAmendmentContext = function($event) { - $event.preventDefault(); - $event.stopPropagation(); - $scope.showAmendmentContext = !$scope.showAmendmentContext; - }; - - if (motion.parent_id) { - Motion.bindOne(motion.parent_id, $scope, 'parent'); - } - - $scope.scrollToLine = 0; - $scope.highlight = 0; - $scope.linesForProjector = false; - $scope.scrollToAndHighlight = function (line) { - $scope.scrollToLine = line; - $scope.highlight = line; - - // The same line number can occur twice in diff view; we scroll to the first one in this case - var scrollTop = null; - $('.line-number-' + line).each(function() { - var top = $(this).offset().top; - if (top > 0 && (scrollTop === null || top < scrollTop)) { - scrollTop = top; - } - }); - - if (scrollTop) { - // Scroll local; 50 pixel above the line, so it's not completely squeezed to the screen border - $('html, body').animate({ - 'scrollTop': scrollTop - 50 - }, 1000); - // remove the line highlight after 2 seconds. - $timeout(function () { - $scope.highlight = 0; - }, 2000); - } - - $scope.scrollProjectorToLine(line); - }; - $scope.scrollProjectorToLine = function (line) { - var projectorIds = $scope.motion.isProjected(); - if (!$scope.linesForProjector || !line || !projectorIds.length) { - return; - } - var projectorId = projectorIds[0]; - var notifyNamePrefix = 'projector_' + projectorId + '_motion_line_'; - - // register callback - var callbackId = Notify.registerCallback(notifyNamePrefix + 'answer', function (params) { - Notify.deregisterCallback(callbackId); - $http.post('/rest/core/projector/' + projectorId + '/set_scroll/', params.params.scroll); - }); - - // Query all projectors - Notify.notify(notifyNamePrefix + 'request', {line: line}, null, null, [projectorId]); - }; - $scope.toggleLinesForProjector = function () { - $scope.linesForProjector = !$scope.linesForProjector; - $scope.scrollProjectorToLine($scope.scrollToLine); - }; - - // open edit dialog - $scope.openDialog = function (motion) { - if ($scope.inlineEditing.active) { - $scope.disableMotionInlineEditing(); - } - ngDialog.open(MotionForm.getDialog(motion)); - }; - $scope.save = function (motion) { - Motion.save(motion, {method: 'PATCH'}); - }; - // Navigation buttons - $scope.navigation = { - evaluate: function () { - var motions = $filter('orderByEmptyLast')(Motion.getAll(), 'identifier'); - var thisIndex = _.findIndex(motions, function (motion) { - return motion.id === $scope.motion.id; - }); - this.count = motions.length; - this.nextMotion = thisIndex < motions.length-1 ? motions[thisIndex+1] : _.head(motions); - this.previousMotion = thisIndex > 0 ? motions[thisIndex-1] : _.last(motions); - }, - }; - // support - $scope.support = function () { - $http.post('/rest/motions/motion/' + motion.id + '/support/'); - }; - // unsupport - $scope.unsupport = function () { - $http.delete('/rest/motions/motion/' + motion.id + '/support/'); - }; - // open dialog for new amendment - $scope.newAmendment = function () { - var openMainDialog = function (paragraphNo) { - var dialog = MotionForm.getDialog(null, motion, paragraphNo); - dialog.scope = $scope; - ngDialog.open(dialog); - }; - - if (Config.get('motions_amendments_text_mode').value === 'paragraph') { - var dialog = AmendmentParagraphChooseForm.getDialog($scope.motion, openMainDialog); - dialog.scope = $scope; - ngDialog.open(dialog); - } else { - openMainDialog(); - } - }; - // follow recommendation - $scope.followRecommendation = function () { - $http.post('/rest/motions/motion/' + motion.id + '/follow_recommendation/', { - 'recommendationExtension': $scope.recommendationExtension - }); - }; - // toggle functions for meta information - $scope.toggleCategory = function (category) { - if ($scope.motion.category_id == category.id) { - $scope.motion.category_id = null; - } else { - $scope.motion.category_id = category.id; - } - $scope.save($scope.motion); - }; - $scope.toggleMotionBlock = function (block) { - if ($scope.motion.motion_block_id == block.id) { - $scope.motion.motion_block_id = null; - } else { - $scope.motion.motion_block_id = block.id; - } - $scope.save($scope.motion); - - }; - $scope.toggleTag = function (tag) { - if (_.indexOf($scope.motion.tags_id, tag.id) > -1) { - // remove - $scope.motion.tags_id = _.filter($scope.motion.tags_id, - function (tag_id){ - return tag_id != tag.id; - } - ); - } else { - $scope.motion.tags_id.push(tag.id); - } - $scope.save($scope.motion); - }; - // save additional state field - $scope.saveAdditionalStateField = function (stateExtension) { - motion['comment_' + $scope.commentFieldForStateId] = stateExtension; - $scope.save(motion); - }; - // save additional recommendation field - $scope.saveAdditionalRecommendationField = function (recommendationExtension) { - motion['comment_' + $scope.commentFieldForRecommendationId] = recommendationExtension; - $scope.save(motion); - }; - $scope.addMotionToRecommendationField = function (motion) { - $scope.recommendationExtension += MotionStateAndRecommendationParser.formatMotion(motion); - }; - // create poll - $scope.create_poll = function () { - $http.post('/rest/motions/motion/' + motion.id + '/create_poll/', {}); - }; - // open poll update dialog - $scope.openPollDialog = function (poll, voteNumber) { - ngDialog.open({ - template: 'static/templates/motions/motion-poll-form.html', - controller: 'MotionPollUpdateCtrl', - className: 'ngdialog-theme-default', - closeByEscape: false, - closeByDocument: false, - resolve: { - motionpollId: function () { - return poll.id; - }, - voteNumber: function () { - return voteNumber; - } - } - }); - }; - // delete poll - $scope.delete_poll = function (poll) { - poll.DSDestroy(); - }; - // show specific version - $scope.showVersion = function (version) { - $scope.version = version.id; - $scope.inlineEditing.setVersion(motion, version.id); - $scope.reasonInlineEditing.setVersion(motion, version.id); - $scope.createChangeRecommendation.setVersion(motion, version.id); - $scope.viewChangeRecommendations.setVersion(motion, motion.active_version); - }; - // permit specific version - $scope.permitVersion = function (version) { - $http.put('/rest/motions/motion/' + motion.id + '/manage_version/', - {'version_number': version.version_number}) - .then(function(success) { - $scope.showVersion(version); - }); - }; - // delete specific version - $scope.deleteVersion = function (version) { - $http.delete('/rest/motions/motion/' + motion.id + '/manage_version/', - {headers: {'Content-Type': 'application/json'}, - data: JSON.stringify({version_number: version.version_number})}) - .then(function(success) { - $scope.showVersion({id: motion.active_version}); - }); - }; - // check if there is at least one comment field - $scope.commentFieldsAvailable = function () { - return _.keys($scope.noSpecialCommentsFields).length > 0; - }; - // personal note - // For pinning the personal note container we need to adjust the width with JS. We - // do not use angular here, because on every window resize a digist cycle would trigger. - // This costs too much performance. We use JQuery here, because it is fast for DOM - // manipulation and very responsive. - $scope.toggleStar = function () { - if ($scope.motion.personalNote) { - $scope.motion.personalNote.star = !$scope.motion.personalNote.star; - } else { - $scope.motion.personalNote = {star: true}; - } - PersonalNoteManager.saveNote($scope.motion, $scope.motion.personalNote); - }; - $scope.personalNotePinned = false; - $scope.pinPersonalNote = function () { - $scope.personalNotePinned = !$scope.personalNotePinned; - if ($scope.personalNotePinned) { - resizePersonalNoteContainer(); - } else { - $('#personalNote').css('width', ''); - } - }; - $scope.gotoPersonalNote = function () { - var pos = $('#personalNote').offset(); - $window.scrollTo(pos.left, pos.top); - }; - var resizePersonalNoteContainer = function () { - if ($scope.personalNotePinned) { - var width = $('#main-column').width() - 40; // Subtract 2x20px margin - $('#personalNote').css('width', width + 'px'); - } - }; - $(window).resize(resizePersonalNoteContainer); - - // Inline editing functions - $scope.inlineEditing = MotionInlineEditing.createInstance($scope, motion, - 'view-original-text-inline-editor', true, Editor.getOptions('inline'), - function (obj) { - return motion.getTextWithLineBreaks($scope.version); - }, - function (obj) { - motion.setTextStrippingLineBreaks(obj.editor.getData()); - motion.disable_versioning = (obj.trivialChange && - Config.get('motions_allow_disable_versioning').value); - } - ); - $scope.reasonInlineEditing = MotionInlineEditing.createInstance($scope, motion, - 'reason-inline-editor', true, Editor.getOptions('inline'), - function (obj) { - return motion.getReason($scope.version); - }, - function (obj) { - motion.reason = obj.editor.getData(); - motion.disable_versioning = (obj.trivialChange && - Config.get('motions_allow_disable_versioning').value); - } - ); - $scope.modifiedFinalVersionInlineEditing = MotionInlineEditing.createInstance($scope, motion, - 'view-modified-agreed-inline-editor', true, Editor.getOptions('inline'), - function (obj) { - return motion.getModifiedFinalVersionWithLineBreaks($scope.version); - }, - function (obj) { - motion.setModifiedFinalVersionStrippingLineBreaks(obj.editor.getData()); - motion.disable_versioning = (obj.trivialChange && - Config.get('motions_allow_disable_versioning').value); - } - ); - // Wrapper functions for $scope.inlineEditing, to warn other users. - var editingStoppedCallback; - $scope.enableMotionInlineEditing = function () { - editingStoppedCallback = EditingWarning.editingStarted('motion_update_' + motion.id); - if ($scope.motion.getReason($scope.version)) { - $scope.reasonInlineEditing.enable(); - } - $scope.inlineEditing.enable(); - }; - $scope.disableMotionInlineEditing = function () { - if (editingStoppedCallback) { - editingStoppedCallback(); - } - if ($scope.motion && $scope.motion.getReason($scope.version)) { - $scope.reasonInlineEditing.disable(); - } - $scope.inlineEditing.disable(); - }; - $scope.textReasonSaveToolbarVisible = function () { - return ($scope.inlineEditing.changed && $scope.inlineEditing.active) || - ($scope.reasonInlineEditing.changed && $scope.reasonInlineEditing.active); - }; - $scope.textReasonSave = function () { - if ($scope.motion.getReason($scope.version)) { - $scope.reasonInlineEditing.save(); - } - $scope.inlineEditing.save(); - }; - $scope.textReasonRevert = function () { - if ($scope.motion.getReason($scope.version)) { - $scope.reasonInlineEditing.revert(); - } - $scope.inlineEditing.revert(); - }; - $scope.commentsInlineEditing = MotionCommentsInlineEditing.createInstances($scope, motion); - $scope.personalNoteInlineEditing = MotionInlineEditing.createInstance($scope, motion, - 'personal-note-inline-editor', false, Editor.getOptions('inline'), - function (obj) { - return motion.personalNote ? motion.personalNote.note : ''; - }, - function (obj) { - if (motion.personalNote) { - motion.personalNote.note = obj.editor.getData(); - } else { - motion.personalNote = {note: obj.editor.getData()}; - } - PersonalNoteManager.saveNote(motion, motion.personalNote); - obj.revert(); - obj.disable(); - return true; // Do not update the motion via patch request. - } - ); - - // Change recommendation creation functions - $scope.createChangeRecommendation = ChangeRecommendationCreate; - $scope.createChangeRecommendation.init($scope, motion); - - // Change recommendation and amendment viewing - $scope.viewChangeRecommendations = ChangeRecommendationView; - $scope.viewChangeRecommendations.initSite($scope, motion, motionDefaultRecommendationTextMode); - - // PDF creating functions - $scope.pdfExport = function () { - var identifier = $scope.motion.identifier ? '-' + $scope.motion.identifier : ''; - var params = { - filename: gettextCatalog.getString('Motion') + identifier + '.pdf', - version: $scope.version, - changeRecommendationMode: $scope.viewChangeRecommendations.mode, - lineNumberMode: $scope.lineNumberMode, - }; - MotionPdfExport.export(motion, params, true); - }; - $scope.createPollPdf = function () { - MotionPdfExport.createPollPdf($scope.motion, $scope.version); - }; - $scope.exportComment = function (commentId) { - var identifier = $scope.motion.identifier ? '-' + $scope.motion.identifier : ''; - var commentsString = ' - ' + gettextCatalog.getString('Comments'); - var filename = gettextCatalog.getString('Motion') + identifier + commentsString + '.pdf'; - MotionPdfExport.exportComment($scope.motion, commentId, filename); - }; - $scope.exportPersonalNote = function () { - var identifier = $scope.motion.identifier ? '-' + $scope.motion.identifier : ''; - var personalNoteString = ' - ' + gettextCatalog.getString('personal note'); - var filename = gettextCatalog.getString('Motion') + identifier + personalNoteString + '.pdf'; - MotionPdfExport.exportPersonalNote($scope.motion, filename); - }; - } -]) - -.controller('ChangeRecommendationTitleUpdateCtrl', [ - '$scope', - 'MotionChangeRecommendation', - 'ChangeRecommendationTitleForm', - 'diffService', - 'change', - 'ErrorMessage', - function ($scope, MotionChangeRecommendation, ChangeRecommendationTitleForm, diffService, change, ErrorMessage) { - $scope.alert = {}; - $scope.model = angular.copy(change); - - // get all form fields - $scope.formFields = ChangeRecommendationTitleForm.getFormFields(); - // save motion - $scope.save = function (change) { - // inject the changed change recommendation (copy) object back into DS store - MotionChangeRecommendation.inject(change); - // save changed change recommendation object on server - MotionChangeRecommendation.save(change).then( - function() { - $scope.closeThisDialog(); - }, - function (error) { - MotionChangeRecommendation.refresh(change); - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('ChangeRecommendationTitleCreateCtrl', [ - '$scope', - 'Motion', - 'MotionChangeRecommendation', - 'ChangeRecommendationTitleForm', - 'Config', - 'diffService', - 'motion', - 'version', - function($scope, Motion, MotionChangeRecommendation, ChangeRecommendationTitleForm, Config, diffService, motion, - version) { - $scope.alert = {}; - - $scope.model = { - text: version.title, - motion_version_id: version.id - }; - - // get all form fields - $scope.formFields = ChangeRecommendationTitleForm.getFormFields(); - // save motion - $scope.save = function (change) { - change.line_from = 0; - change.line_to = 0; - MotionChangeRecommendation.create(change).then( - function() { - $scope.closeThisDialog(); - } - ); - }; - } -]) - -.controller('ChangeRecommendationTextUpdateCtrl', [ - '$scope', - 'MotionChangeRecommendation', - 'ChangeRecommendationTextForm', - 'diffService', - 'change', - 'ErrorMessage', - function ($scope, MotionChangeRecommendation, ChangeRecommendationTextForm, diffService, change, ErrorMessage) { - $scope.alert = {}; - $scope.model = angular.copy(change); - $scope.model._change_object = undefined; - - // get all form fields - $scope.formFields = ChangeRecommendationTextForm.getFormFields(change.line_from, change.line_to); - // save motion - $scope.save = function (change) { - change.text = diffService.removeDuplicateClassesInsertedByCkeditor(change.text); - // inject the changed change recommendation (copy) object back into DS store - MotionChangeRecommendation.inject(change); - // save changed change recommendation object on server - MotionChangeRecommendation.save(change).then( - function() { - $scope.closeThisDialog(); - }, - function (error) { - MotionChangeRecommendation.refresh(change); - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('ChangeRecommendationTextCreateCtrl', [ - '$scope', - 'Motion', - 'MotionChangeRecommendation', - 'ChangeRecommendationTextForm', - 'Config', - 'diffService', - 'motion', - 'version', - 'lineFrom', - 'lineTo', - function($scope, Motion, MotionChangeRecommendation, ChangeRecommendationTextForm, Config, diffService, motion, - version, lineFrom, lineTo) { - $scope.alert = {}; - - var html = motion.getTextWithLineBreaks(version.id), - lineData = diffService.extractRangeByLineNumbers(html, lineFrom, lineTo); - - $scope.model = { - text: lineData.outerContextStart + lineData.innerContextStart + - lineData.html + lineData.innerContextEnd + lineData.outerContextEnd, - line_from: lineFrom, - line_to: lineTo, - motion_version_id: version.id, - type: 0 - }; - - // get all form fields - $scope.formFields = ChangeRecommendationTextForm.getFormFields(lineFrom, lineTo); - // save motion - $scope.save = function (motion) { - motion.text = diffService.removeDuplicateClassesInsertedByCkeditor(motion.text); - MotionChangeRecommendation.create(motion).then( - function() { - $scope.closeThisDialog(); - } - ); - }; - } -]) - -.controller('AmendmentParagraphChooseCtrl', [ - '$scope', - '$state', - 'Motion', - 'motion', - 'successCb', - function($scope, $state, Motion, motion, successCb) { - $scope.model = angular.copy(motion); - $scope.model.paragraph_selected = null; - - $scope.paragraphs = motion.getTextParagraphs(motion.active_version, true).map(function(text, index) { - // This prevents an error in ng-repeater's duplication detection if two identical paragraphs occur - return { - "paragraphNo": index, - "text": text - }; - }); - - $scope.gotoMotionForm = function() { - var paragraphNo = parseInt($scope.model.paragraph_selected); - successCb(paragraphNo); - $scope.closeThisDialog(); - }; - } -]) - -.controller('MotionCreateCtrl', [ - '$scope', - '$state', - 'gettext', - 'gettextCatalog', - 'operator', - 'Motion', - 'MotionForm', - 'parentMotion', - 'paragraphNo', - 'paragraphTextPre', - 'Category', - 'Config', - 'Mediafile', - 'Tag', - 'User', - 'Workflow', - 'Agenda', - 'ErrorMessage', - function($scope, $state, gettext, gettextCatalog, operator, Motion, MotionForm, parentMotion, - paragraphNo, paragraphTextPre, Category, Config, Mediafile, Tag, User, Workflow, - Agenda, ErrorMessage) { - Category.bindAll({}, $scope, 'categories'); - Mediafile.bindAll({}, $scope, 'mediafiles'); - Tag.bindAll({}, $scope, 'tags'); - User.bindAll({}, $scope, 'users'); - Workflow.bindAll({}, $scope, 'workflows'); - - $scope.model = { - agenda_type: parseInt(Config.get('agenda_new_items_default_visibility').value), - }; - - $scope.alert = {}; - - // Check whether this is a new amendment. - var isAmendment = parentMotion && parentMotion.id, - isParagraphBasedAmendment = false; - - // Set default values for create form - // ... for amendments add parent_id - if (isAmendment) { - if (Config.get('motions_amendments_text_mode').value === 'fulltext') { - $scope.model.text = parentMotion.getText(); - } - if (Config.get('motions_amendments_text_mode').value === 'paragraph' && - paragraphNo !== undefined) { - var paragraphs = parentMotion.getTextParagraphs(parentMotion.active_version, false); - $scope.model.text = paragraphs[paragraphNo]; - isParagraphBasedAmendment = true; - } - if (paragraphTextPre !== undefined) { - $scope.model.text = paragraphTextPre; - } - if (parentMotion.identifier) { - $scope.model.title = gettextCatalog.getString('Amendment to') + - ' ' + parentMotion.identifier; - } else { - $scope.model.title = gettextCatalog.getString('Amendment to motion ') + - ' ' + parentMotion.getTitle(); - } - $scope.model.paragraphNo = paragraphNo; - $scope.model.parent_id = parentMotion.id; - $scope.model.category_id = parentMotion.category_id; - $scope.model.motion_block_id = parentMotion.motion_block_id; - Motion.bindOne($scope.model.parent_id, $scope, 'parent'); - } - // ... preselect default workflow if exist - var workflow = Workflow.get(Config.get('motions_workflow').value); - if (!workflow) { - workflow = _.first(Workflow.getAll()); - } - if (workflow) { - $scope.model.workflow_id = workflow.id; - } else { - $scope.alert = { - type: 'danger', - msg: gettextCatalog.getString('No workflows exists. You will not ' + - 'be able to create a motion.'), - show: true, - }; - } - - // get all form fields - $scope.formFields = MotionForm.getFormFields(true, isParagraphBasedAmendment); - - // save motion - $scope.save = function (motion, gotoDetailView) { - if (isAmendment && motion.paragraphNo !== undefined) { - var orig_paragraphs = parentMotion.getTextParagraphs(parentMotion.active_version, false); - motion.amendment_paragraphs = orig_paragraphs.map(function (_, idx) { - return (idx === motion.paragraphNo ? motion.text : null); - }); - } - - // The attribute motion.agenda_parent_id is set by the form, see form definition. - Motion.create(motion).then( - function(success) { - if (isAmendment || gotoDetailView) { - $state.go('motions.motion.detail', {id: success.id}); - } - $scope.closeThisDialog(); - }, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('MotionUpdateCtrl', [ - '$scope', - '$state', - 'Motion', - 'Category', - 'Config', - 'Mediafile', - 'MotionForm', - 'Tag', - 'User', - 'Workflow', - 'Agenda', - 'motionId', - 'operator', - 'ErrorMessage', - 'EditingWarning', - function ($scope, $state, Motion, Category, Config, Mediafile, MotionForm, - Tag, User, Workflow, Agenda, motionId, operator, ErrorMessage, - EditingWarning) { - Category.bindAll({}, $scope, 'categories'); - Mediafile.bindAll({}, $scope, 'mediafiles'); - Tag.bindAll({}, $scope, 'tags'); - User.bindAll({}, $scope, 'users'); - Workflow.bindAll({}, $scope, 'workflows'); - $scope.alert = {}; - - // set initial values for form model by create deep copy of motion object - // so list/detail view is not updated while editing - var motion = Motion.get(motionId); - // We need to clone this by hand. angular and lodash are not capable of keeping - // crossreferences out. - $scope.model = { - id: motion.id, - parent_id: motion.parent_id, - identifier: motion.identifier, - title: motion.getTitle(), - text: motion.getText(), - reason: motion.getReason(), - submitters_id: _.map(motion.submitters_id), - supporters_id: _.map(motion.supporters_id), - tags_id: _.map(motion.tags_id), - state_id: motion.state_id, - recommendation_id: motion.recommendation_id, - origin: motion.origin, - workflow_id: motion.workflow_id, - comments: _.clone(motion.comments), - attachments_id: _.map(motion.attachments_id), - active_version: motion.active_version, - agenda_item_id: motion.agenda_item_id, - category_id: motion.category_id, - motion_block_id: motion.motion_block_id, - }; - // Clone comments - _.forEach(motion.comments, function (comment, index) { - $scope.model['comment_' + index] = comment; - }); - $scope.model.disable_versioning = false; - $scope.model.more = false; - if (motion.isParagraphBasedAmendment()) { - motion.getVersion(motion.active_version).amendment_paragraphs.forEach(function(paragraph_amend, paragraphNo) { - // Hint: this assumes there is only one modified paragraph - if (paragraph_amend !== null) { - $scope.model.text = paragraph_amend; - $scope.model.paragraphNo = paragraphNo; - } - }); - $scope.model.title = motion.getTitle(); - } - - // get all form fields - $scope.formFields = MotionForm.getFormFields(false, motion.isParagraphBasedAmendment()); - // override default values for update form - for (var i = 0; i < $scope.formFields.length; i++) { - if ($scope.formFields[i].key == "identifier") { - // show identifier field if the operator has manage permissions - $scope.formFields[i].hide = !operator.hasPerms('motions.can_manage'); - } - if ($scope.formFields[i].key == "title") { - // get title of latest version - $scope.formFields[i].defaultValue = motion.getTitle(-1); - } - if ($scope.formFields[i].key == "text") { - // get text of latest version - $scope.formFields[i].defaultValue = motion.getText(-1); - } - if ($scope.formFields[i].key == "reason") { - // get reason of latest version - $scope.formFields[i].defaultValue = motion.getReason(-1); - } - if ($scope.formFields[i].key == "disable_versioning") { - if (Config.get('motions_allow_disable_versioning').value && motion.state.versioning) { - // check current state if versioning is active - $scope.formFields[i].hide = false; - } - } - if ($scope.formFields[i].key == "workflow_id") { - // get saved workflow id from state - $scope.formFields[i].defaultValue = motion.state.workflow_id; - } - } - - // Displaying a warning, if other users edit this motion too - var editingStoppedCallback = EditingWarning.editingStarted('motion_update_' + motionId); - $scope.$on('$destroy', editingStoppedCallback); - - // Save motion - $scope.save = function (model, gotoDetailView) { - if ($scope.model.paragraphNo !== undefined) { - var parentMotion = motion.getParentMotion(); - var orig_paragraphs = parentMotion.getTextParagraphs(parentMotion.active_version, false); - $scope.model.amendment_paragraphs = orig_paragraphs.map(function (_, idx) { - return (idx === $scope.model.paragraphNo ? $scope.model.text : null); - }); - } - - // inject the changed motion (copy) object back into DS store - Motion.inject(model); - // save changed motion object on server - Motion.save(model).then( - function(success) { - if (gotoDetailView) { - $state.go('motions.motion.detail', {id: success.id}); - } - $scope.closeThisDialog(); - }, - function (error) { - // save error: revert all changes by restore - // (refresh) original motion object from server - Motion.refresh(model); - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('MotionCommentCtrl', [ - '$scope', - 'Motion', - 'MotionComment', - 'MotionCommentForm', - 'motionId', - 'commentFieldId', - 'gettextCatalog', - 'ErrorMessage', - function ($scope, Motion, MotionComment, MotionCommentForm, motionId, commentFieldId, - gettextCatalog, ErrorMessage) { - $scope.alert = {}; - - // set initial values for form model by create deep copy of motion object - // so list/detail view is not updated while editing - var motion = Motion.get(motionId); - $scope.model = angular.copy(motion); - $scope.formFields = MotionCommentForm.getFormFields(commentFieldId); - - var fields = MotionComment.getNoSpecialCommentsFields(); - var title = gettextCatalog.getString('Edit comment %%comment%% of motion %%motion%%'); - title = title.replace('%%comment%%', fields[commentFieldId].name); - $scope.title = title.replace('%%motion%%', motion.getTitle()); - - $scope.model.title = motion.getTitle(-1); - $scope.model.text = motion.getText(-1); - $scope.model.reason = motion.getReason(-1); - - if (motion.isParagraphBasedAmendment()) { - motion.getVersion(motion.active_version).amendment_paragraphs.forEach(function(paragraph_amend, paragraphNo) { - // Hint: this assumes there is only one modified paragraph - if (paragraph_amend !== null) { - $scope.model.text = paragraph_amend; - $scope.model.paragraphNo = paragraphNo; - } - }); - } - - $scope.save = function (motion) { - if (motion.isParagraphBasedAmendment()) { - motion.getVersion(motion.active_version).amendment_paragraphs.forEach(function(paragraph_amend, paragraphNo) { - // Hint: this assumes there is only one modified paragraph - if (paragraph_amend !== null) { - $scope.model.text = paragraph_amend; - $scope.model.paragraphNo = paragraphNo; - } - }); - } - - // inject the changed motion (copy) object back into DS store - Motion.inject(motion); - // save changed motion object on server - Motion.save(motion).then( - function(success) { - $scope.closeThisDialog(); - }, - function (error) { - // save error: revert all changes by restore - // (refresh) original motion object from server - Motion.refresh(motion); - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('MotionPollUpdateCtrl', [ - '$scope', - 'gettextCatalog', - 'MotionPoll', - 'MotionPollForm', - 'MotionPollDecimalPlaces', - 'motionpollId', - 'voteNumber', - 'ErrorMessage', - function ($scope, gettextCatalog, MotionPoll, MotionPollForm, MotionPollDecimalPlaces, - motionpollId, voteNumber, ErrorMessage) { - // set initial values for form model by create deep copy of motionpoll object - // so detail view is not updated while editing poll - var motionpoll = MotionPoll.get(motionpollId); - $scope.model = angular.copy(motionpoll); - $scope.voteNumber = voteNumber; - var precision = MotionPollDecimalPlaces.getPlaces(motionpoll); - $scope.formFields = MotionPollForm.getFormFields(precision); - $scope.alert = {}; - - // save motionpoll - $scope.save = function (poll) { - poll.DSUpdate({ - motion_id: poll.motion_id, - votes: {"Yes": poll.yes, "No": poll.no, "Abstain": poll.abstain}, - votesvalid: poll.votesvalid, - votesinvalid: poll.votesinvalid, - votescast: poll.votescast - }) - .then(function (success) { - $scope.alert.show = false; - $scope.closeThisDialog(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('MotionSubmitterCtrl', [ - '$scope', - '$filter', - '$http', - 'User', - 'Motion', - 'motionId', - 'ErrorMessage', - function ($scope, $filter, $http, User, Motion, motionId, ErrorMessage) { - User.bindAll({}, $scope, 'users'); - $scope.submitterSelectBox = {}; - $scope.alert = {}; - - $scope.$watch(function () { - return Motion.lastModified(motionId); - }, function () { - $scope.motion = Motion.get(motionId); - $scope.submitters = $filter('orderBy')($scope.motion.submitters, 'weight'); - }); - - $scope.addSubmitter = function (userId) { - $scope.submitterSelectBox = {}; - $http.post('/rest/motions/motion/' + $scope.motion.id + '/manage_submitters/', { - 'user': userId - }).then( - function (success) { - $scope.alert.show = false; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - - $scope.removeSubmitter = function (userId) { - $http.delete('/rest/motions/motion/' + $scope.motion.id + '/manage_submitters/', { - headers: {'Content-Type': 'application/json'}, - data: JSON.stringify({user: userId}) - }).then( - function (success) { - $scope.alert.show = false; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - - // save reordered list of submitters - $scope.treeOptions = { - dropped: function (event) { - var submitterIds = _.map($scope.submitters, function (submitter) { - return submitter.id; - }); - $http.post('/rest/motions/motion/' + $scope.motion.id + '/sort_submitters/', { - submitters: submitterIds, - }); - } - }; - } -]) - -.controller('MotionAmendmentListStateCtrl', [ - '$scope', - 'motionId', - function ($scope, motionId) { - $scope.motionId = motionId; - $scope.osTablePrefix = 'AmendmentTable'; - } -]) - -.controller('MotionAmendmentListCtrl', [ - '$scope', - '$sessionStorage', - '$state', - 'Motion', - 'MotionComment', - 'MotionForm', - 'PersonalNoteManager', - 'ngDialog', - 'MotionCommentForm', - 'MotionChangeRecommendation', - 'MotionPdfExport', - 'AmendmentCsvExport', - 'gettextCatalog', - 'gettext', - function ($scope, $sessionStorage, $state, Motion, MotionComment, MotionForm, - PersonalNoteManager, ngDialog, MotionCommentForm, MotionChangeRecommendation, - MotionPdfExport, AmendmentCsvExport, gettextCatalog, gettext) { - if ($scope.motionId) { - $scope.leadMotion = Motion.get($scope.motionId); - } - - var updateMotions = function () { - // check, if lead motion is given - var amendments; - if ($scope.leadMotion) { - amendments = Motion.filter({parent_id: $scope.leadMotion.id}); - } else { - amendments = _.filter(Motion.getAll(), function (motion) { - return motion.parent_id; - }); - } - // always order by identifier (after custom ordering) - $scope.amendments = _.orderBy(amendments, ['identifier']); - - _.forEach($scope.amendments, function (amendment) { - MotionComment.populateFields(amendment); - amendment.personalNote = PersonalNoteManager.getNote(amendment); - // For filtering, we cannot filter for .personalNote.star - amendment.star = amendment.personalNote ? amendment.personalNote.star : false; - amendment.hasPersonalNote = amendment.personalNote ? !!amendment.personalNote.note : false; - if (amendment.star === undefined) { - amendment.star = false; - } - - // add a custom sort attribute - var parentMotion = amendment.getParentMotion(); - amendment.parentMotionAndLineNumber = parentMotion.identifier; - if (amendment.isParagraphBasedAmendment()) { - var paragraphs = amendment.getAmendmentParagraphsLinesDiff(); - var diffLine = '0'; - if (paragraphs.length) { - diffLine = '' + paragraphs[0].diffLineFrom; - } - while (diffLine.length < 6) { - diffLine = '0' + diffLine; - } - amendment.parentMotionAndLineNumber += ' ' + diffLine; - } - }); - - // Get all lead motions - $scope.leadMotions = _.orderBy(Motion.filter({parent_id: undefined}), ['identifier']); - - //updateCollissions(); - }; - - var updateCollissions = function () { - $scope.collissions = {}; - _.forEach($scope.amendments, function (amendment) { - if (amendment.isParagraphBasedAmendment()) { - var parentMotion = amendment.getParentMotion(); - // get all change recommendations _and_ changes by amendments from the - // parent motion. From all get the unified change object. - var parentChangeRecommendations = _.filter( - MotionChangeRecommendation.filter({ - 'where': {'motion_version_id': {'==': parentMotion.active_version}} - }), function (change) { - return change.isTextRecommendation(); - } - ); - var parentChanges = parentChangeRecommendations.map(function (cr) { - return cr.getUnifiedChangeObject(); - }).concat( - _.map(parentMotion.getParagraphBasedAmendmentsForDiffView(), function (amendment) { - return amendment.getUnifiedChangeObject(); - }) - ); - var change = amendment.getUnifiedChangeObject(); - if (change) { - change.setOtherChangesForCollission(parentChanges); - $scope.collissions[amendment.id] = !!change.getCollissions().length; - } - } - }); - }; - - //$scope.$watch(function () { - // return MotionChangeRecommendation.lastModified(); - //}, updateCollissions); - - $scope.$watch(function () { - return Motion.lastModified(); - }, updateMotions); - - $scope.selectLeadMotion = function (motion) { - $scope.leadMotion = motion; - updateMotions(); - if ($scope.leadMotion) { - $state.transitionTo('motions.motion.amendment-list', - {id: $scope.leadMotion.id}, - {notify: false} - ); - } else { - $state.transitionTo('motions.motion.allamendments', {}, - {notify: false} - ); - } - }; - - // Save expand state so the session - if ($sessionStorage.amendmentTableExpandState) { - $scope.toggleExpandContent(); - } - $scope.saveExpandState = function (state) { - $sessionStorage.amendmentTableExpandState = state; - }; - - // add custom sorting - $scope.sortOptions.unshift({ - name: 'parentMotionAndLineNumber', - display_name: gettext('Parent motion and line number'), - }); - if (!$scope.sort.column || $scope.sort.column === 'identifier') { - $scope.sort.column = 'parentMotionAndLineNumber'; - } - - $scope.isTextExpandable = function (comment, characters) { - comment = $(comment).text(); - return comment.length > characters; - }; - $scope.getTextPreview = function (comment, characters) { - comment = $(comment).text(); - if (comment.length > characters) { - comment = comment.substr(0, characters) + '...'; - } - return comment; - }; - $scope.editComment = function (motion, fieldId) { - ngDialog.open(MotionCommentForm.getDialog(motion, fieldId)); - }; - - $scope.createModifiedAmendment = function (amendment) { - var paragraphNo, - paragraphText; - if (amendment.isParagraphBasedAmendment()) { - // We assume there is only one affected paragraph - amendment.getVersion(amendment.active_version).amendment_paragraphs.forEach(function(parText, parNo) { - if (parText !== null) { - paragraphNo = parNo; - paragraphText = parText; - } - }); - } else { - paragraphText = amendment.getText(); - } - ngDialog.open(MotionForm.getDialog(null, amendment.getParentMotion(), paragraphNo, paragraphText)); - }; - - $scope.amendmentPdfExport = function (motions) { - var filename; - if ($scope.leadMotion) { - filename = gettextCatalog.getString('Amendments to') + ' ' + - $scope.leadMotion.getTitle(); - } else { - filename = gettextCatalog.getString('Amendments'); - } - filename += '.pdf'; - MotionPdfExport.exportAmendments(motions, filename); - }; - - $scope.exportCsv = function (motions) { - AmendmentCsvExport.export(motions); - }; - } -]) - -.controller('MotionImportCtrl', [ - '$scope', - '$q', - 'gettext', - 'Category', - 'Motion', - 'MotionBlock', - 'User', - 'MotionCsvExport', - function ($scope, $q, gettext, Category, Motion, MotionBlock, User, MotionCsvExport) { - // set initial data for csv import - $scope.motions = []; - - // set csv - $scope.csvConfig = { - accept: '.csv, .txt', - encodingOptions: ['UTF-8', 'ISO-8859-1'], - parseConfig: { - skipEmptyLines: true, - }, - }; - - var FIELDS = ['identifier', 'title', 'text', 'reason', 'submitter', 'category', 'origin', 'motionBlock']; - $scope.motions = []; - $scope.onCsvChange = function (csv) { - $scope.motions = []; - var motions = []; - _.forEach(csv.data, function (row) { - if (row.length >= 3) { - var filledRow = _.zipObject(FIELDS, row); - motions.push(filledRow); - } - }); - - _.forEach(motions, function (motion) { - motion.selected = true; - // identifier - if (motion.identifier !== '') { - // All motion objects are already loaded via the resolve statement from ui-router. - var motions = Motion.getAll(); - if (_.find(motions, function (item) { - return item.identifier === motion.identifier; - })) { - motion.importerror = true; - motion.identifier_error = gettext('Error: Identifier already exists.'); - } - } - // title - if (!motion.title) { - motion.importerror = true; - motion.title_error = gettext('Error: Title is required.'); - } - // text - if (!motion.text) { - motion.importerror = true; - motion.text_error = gettext('Error: Text is required.'); - } else if (!motion.text.startsWith('

    ')) { - motion.text = '

    ' + motion.text + '

    '; - } - // Reason - if (motion.reason && !motion.reason.startsWith('

    ')) { - motion.reason = '

    ' + motion.reason + '

    '; - } - // submitter - if (motion.submitter && motion.submitter !== '') { - _.forEach(User.getAll(), function (user) { - var user_short_name = [user.title, user.first_name, user.last_name].join(' ').trim(); - if (user_short_name == motion.submitter.trim()) { - motion.submitters_id = [user.id]; - motion.submitter = user.full_name; - } - }); - if (!motion.submitters_id) { - motion.submitter_create = gettext('New participant will be created.'); - } - } - // category - if (motion.category && motion.category !== '') { - angular.forEach(Category.getAll(), function (category) { - // search for existing category - if (category.name == motion.category.trim()) { - motion.category_id = category.id; - motion.category = category.name; - } - }); - if (!motion.category_id) { - motion.category_create = gettext('New category will be created.'); - } - } - // Motion block - if (motion.motionBlock && motion.motionBlock !== '') { - angular.forEach(MotionBlock.getAll(), function (block) { - // search for existing block - if (block.title == motion.motionBlock.trim()) { - motion.motion_block_id = block.id; - motion.motionBlock = block.title; - } - }); - if (!motion.motion_block_id) { - motion.motionBlock_create = gettext('New motion block will be created.'); - } - } - - $scope.motions.push(motion); - }); - $scope.calcStats(); - }; - - $scope.calcStats = function () { - $scope.motionsWillNotBeImported = 0; - $scope.motionsWillBeImported = 0; - - $scope.motions.forEach(function(motion) { - if (!motion.importerror && motion.selected) { - $scope.motionsWillBeImported++; - } else { - $scope.motionsWillNotBeImported++; - } - }); - }; - - // Counter for creations - $scope.usersCreated = 0; - $scope.categoriesCreated = 0; - - // import from csv file - $scope.import = function () { - $scope.csvImporting = true; - - // Reset counters - $scope.usersCreated = 0; - $scope.categoriesCreated = 0; - $scope.motionBlocksCreated = 0; - - var importedUsers = []; - var importedCategories = []; - var importedMotionBlocks = []; - // collect users, categories and motion blocks - angular.forEach($scope.motions, function (motion) { - if (motion.selected && !motion.importerror) { - // collect user if not exists - if (!motion.submitters_id && motion.submitter) { - var index = motion.submitter.indexOf(' '); - var user = { - first_name: motion.submitter.substr(0, index), - last_name: motion.submitter.substr(index+1), - groups_id: [] - }; - importedUsers.push(user); - } - // collect category if not exists - if (!motion.category_id && motion.category) { - var category = { - name: motion.category, - prefix: motion.category.charAt(0) - }; - importedCategories.push(category); - } - // collect motion block if not exists - if (!motion.motion_block_id && motion.motionBlock) { - var motionBlock = { - title: motion.motionBlock, - }; - importedMotionBlocks.push(motionBlock); - } - } - }); - - // unique users, categories and motion blocks - var importedUsersUnique = _.uniqWith(importedUsers, function (u1, u2) { - return u1.first_name == u2.first_name && - u1.last_name == u2.last_name; - }); - var importedCategoriesUnique = _.uniqWith(importedCategories, function (c1, c2) { - return c1.name == c2.name; - }); - var importedMotionBlocksUnique = _.uniqWith(importedMotionBlocks, function (c1, c2) { - return c1.title == c2.title; - }); - - // Promises for users and categories - var createPromises = []; - - // create users and categories - _.forEach(importedUsersUnique, function (user) { - createPromises.push(User.create(user).then( - function (success) { - user.id = success.id; - $scope.usersCreated++; - } - )); - }); - _.forEach(importedCategoriesUnique, function (category) { - createPromises.push(Category.create(category).then( - function (success) { - category.id = success.id; - $scope.categoriesCreated++; - } - )); - }); - _.forEach(importedMotionBlocksUnique, function (motionBlock) { - createPromises.push(MotionBlock.create(motionBlock).then( - function (success) { - motionBlock.id = success.id; - $scope.motionBlocksCreated++; - } - )); - }); - - // wait for users and categories to create - $q.all(createPromises).then( function() { - angular.forEach($scope.motions, function (motion) { - if (motion.selected && !motion.importerror) { - // now, add user - if (!motion.submitters_id && motion.submitter) { - var index = motion.submitter.indexOf(' '); - var first_name = motion.submitter.substr(0, index); - var last_name = motion.submitter.substr(index+1); - - // search for user, set id. - _.forEach(importedUsersUnique, function (user) { - if (user.first_name == first_name && - user.last_name == last_name) { - motion.submitters_id = [user.id]; - } - }); - } - // add category - if (!motion.category_id && motion.category) { - var name = motion.category; - - // search for category, set id. - _.forEach(importedCategoriesUnique, function (category) { - if (category.name == name) { - motion.category_id = category.id; - } - }); - } - // add motion block - if (!motion.motion_block_id && motion.motionBlock) { - var title = motion.motionBlock; - - // search for motion block - _.forEach(importedMotionBlocksUnique, function (motionBlock) { - if (motionBlock.title == title) { - motion.motion_block_id = motionBlock.id; - } - }); - } - - - // finally create motion - Motion.create(motion).then( - function(success) { - motion.imported = true; - } - ); - } - }); - }); - $scope.csvimported = true; - }; - $scope.clear = function () { - $scope.motions = []; - }; - // download CSV example file - $scope.downloadCSVExample = function () { - MotionCsvExport.downloadExample(); - }; - } -]) - -.controller('CategoryListCtrl', [ - '$scope', - 'Category', - 'ngDialog', - 'CategoryForm', - function($scope, Category, ngDialog, CategoryForm) { - Category.bindAll({}, $scope, 'categories'); - - // setup table sorting - $scope.sortColumn = 'name'; - $scope.reverse = false; - // function to sort by clicked column - $scope.toggleSort = function (column) { - if ($scope.sortColumn === column) { - $scope.reverse = !$scope.reverse; - } - $scope.sortColumn = column; - }; - - // delete selected category - $scope.delete = function (category) { - Category.destroy(category.id); - }; - $scope.editOrCreate = function (category) { - ngDialog.open(CategoryForm.getDialog(category)); - }; - } -]) - -.controller('CategoryCreateCtrl', [ - '$scope', - 'Category', - 'CategoryForm', - 'ErrorMessage', - function($scope, Category, CategoryForm, ErrorMessage) { - $scope.model = {}; - $scope.alert = {}; - $scope.formFields = CategoryForm.getFormFields(); - $scope.save = function (category) { - Category.create(category).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('CategoryUpdateCtrl', [ - '$scope', - 'Category', - 'categoryId', - 'CategoryForm', - 'ErrorMessage', - function ($scope, Category, categoryId, CategoryForm, ErrorMessage) { - $scope.alert = {}; - $scope.model = angular.copy(Category.get(categoryId)); - $scope.formFields = CategoryForm.getFormFields(); - $scope.save = function (category) { - Category.inject(category); - Category.save(category).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - // save error: revert all changes by restore - // (refresh) original category object from server - Category.refresh(category); - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('CategorySortCtrl', [ - '$scope', - '$stateParams', - '$http', - 'Category', - 'categoryId', - 'Motion', - 'ErrorMessage', - function ($scope, $stateParams, $http, Category, categoryId, Motion, ErrorMessage) { - Category.bindOne(categoryId, $scope, 'category'); - Motion.bindAll({}, $scope, 'motions'); - $scope.filter = { category_id: categoryId, - parent_id: null, - orderBy: 'identifier' }; - - $scope.$watch(function () { - return Motion.lastModified(); - }, function () { - var motions = Motion.filter($scope.filter); - $scope.items = _.map(motions, function (motion) { - return { - id: motion.id, - item: motion - }; - }); - }); - - $scope.alert = {}; - // Numbers all motions in this category by the given order in $scope.items - $scope.numbering = function () { - // Create a list of all motion ids in the current order. - var sorted_motions = []; - $scope.items.forEach(function (item) { - sorted_motions.push(item.item.id); - }); - - // renumber them - $http.post('/rest/motions/category/' + $scope.category.id + '/numbering/', - {'motions': sorted_motions} ).then( - function (success) { - $scope.alert = { type: 'success', msg: success.data.detail, show: true }; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -//mark all motions config strings for translation in javascript -.config([ - 'gettext', - function (gettext) { - gettext('Motions'); - - // subgroup General - gettext('General'); - gettext('Workflow of new motions'); - gettext('Identifier'); - gettext('Numbered per category'); - gettext('Serially numbered'); - gettext('Set it manually'); - gettext('Motion preamble'); - gettext('The assembly may decide:'); - gettext('Default line numbering'); - /// Line numbering: Outside - gettext('Outside'); - /// Line numbering: Inline - gettext('Inline'); - /// Line numbering: None - gettext('None'); - gettext('Line length'); - gettext('The maximum number of characters per line. Relevant when line numbering is enabled. Min: 40'); - gettext('Hide reason on projector'); - gettext('Hide meta information box on projector'); - gettext('Hide recommendation on projector'); - gettext('Stop submitting new motions by non-staff users'); - gettext('Allow to disable versioning'); - gettext('Name of recommender'); - gettext('Default text version for change recommendations'); - gettext('Will be displayed as label before selected recommendation. Use an empty value to disable the recommendation system.'); - gettext('Edit comment %%comment%% of motion %%motion%%'); - - // subgroup Amendments - gettext('Amendments'); - gettext('Activate amendments'); - gettext('Show amendments together with motions'); - gettext('Prefix for the identifier for amendments'); - gettext('Apply text for new amendments'); - gettext('The title of the motion is always applied.'); - gettext('Amendment to'); - gettext('How to create new amendments'); - gettext('Empty text field'); - gettext('Edit the whole motion text'); - gettext('Paragraph-based, Diff-enabled'); - - // subgroup Supporters - gettext('Supporters'); - gettext('Number of (minimum) required supporters for a motion'); - gettext('Choose 0 to disable the supporting system.'); - gettext('Remove all supporters of a motion if a submitter edits his ' + - 'motion in early state'); - - // subgroup Supporters - gettext('Comments'); - gettext('Comment fields for motions'); - gettext('Public'); - gettext('Private'); - - // subgroup Voting and ballot papers - gettext('Voting and ballot papers'); - gettext('The 100 % base of a voting result consists of'); - gettext('Yes/No/Abstain'); - gettext('Yes/No'); - gettext('All valid ballots'); - gettext('All casted ballots'); - gettext('Disabled (no percents)'); - gettext('Required majority'); - gettext('Default method to check whether a motion has reached the required majority.'); - gettext('Simple majority'); - gettext('Two-thirds majority'); - gettext('Three-quarters majority'); - gettext('Disabled'); - gettext('Number of ballot papers (selection)'); - gettext('Number of all delegates'); - gettext('Number of all participants'); - gettext('Use the following custom number'); - gettext('Custom number of ballot papers'); - - // subgroup PDF and DOCX - gettext('Title for PDF and DOCX documents (all motions)'); - gettext('Preamble text for PDF and DOCX documents (all motions)'); - gettext('Sort categories by'); - gettext('Include the sequential number in PDF and DOCX'); - - // misc strings (used dynamically in templates by translate filter) - gettext('needed'); - gettext('Amendment'); - } -]); - -}()); diff --git a/openslides/motions/static/js/motions/workflow.js b/openslides/motions/static/js/motions/workflow.js deleted file mode 100644 index 79ace06ed..000000000 --- a/openslides/motions/static/js/motions/workflow.js +++ /dev/null @@ -1,253 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.motions.workflow', []) - -.controller('WorkflowListCtrl', [ - '$scope', - 'Workflow', - 'ngDialog', - 'ErrorMessage', - function ($scope, Workflow, ngDialog, ErrorMessage) { - $scope.alert = {}; - Workflow.bindAll({}, $scope, 'workflows'); - $scope.create = function () { - ngDialog.open({ - template: 'static/templates/motions/workflow-edit.html', - controller: 'WorkflowCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - }); - }; - $scope.delete = function (workflow) { - Workflow.destroy(workflow).then(null, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('WorkflowDetailCtrl', [ - '$scope', - '$sessionStorage', - 'permissions', - 'Workflow', - 'MotionState', - 'workflowId', - 'ngDialog', - 'gettext', - 'gettextCatalog', - 'ErrorMessage', - function ($scope, $sessionStorage, permissions, Workflow, MotionState, workflowId, - ngDialog, gettext, gettextCatalog, ErrorMessage) { - $scope.permissions = permissions; - $scope.alert = {}; - - $scope.$watch(function () { - return Workflow.lastModified(workflowId); - }, function () { - $scope.workflow = Workflow.get(workflowId); - $scope.states = $scope.workflow.states; - $scope.states = _.orderBy($scope.states, 'id'); - _.forEach($scope.states, function (state) { - state.newActionWord = gettextCatalog.getString(state.action_word); - state.newRecommendationLabel = gettextCatalog.getString(state.recommendation_label); - }); - }); - - $scope.booleanMembers = [ - {name: 'allow_support', - displayName: gettext('Allow support'),}, - {name: 'allow_create_poll', - displayName: gettext('Allow create poll'),}, - {name: 'allow_submitter_edit', - displayName: gettext('Allow submitter edit'),}, - {name: 'versioning', - displayName: gettext('Versioning'),}, - {name: 'leave_old_version_active', - displayName: gettext('Leave old version active'),}, - {name: 'dont_set_identifier', - displayName: gettext('Set identifier'), - inverse: true,}, - {name: 'show_state_extension_field', - displayName: gettext('Show state extension field'),}, - {name: 'show_recommendation_extension_field', - displayName: gettext('Show recommendation extension field'),} - ]; - $scope.cssClasses = { - 'danger': gettext('Red'), - 'success': gettext('Green'), - 'warning': gettext('Yellow'), - 'default': gettext('Grey'), - 'primary': gettext('Blue'), - }; - $scope.getPermissionDisplayName = function (permission) { - if (permission) { - return _.find($scope.permissions, function (perm) { - return perm.value === permission; - }).display_name; - } - }; - $scope.clickPermission = function (state, permission) { - state.required_permission_to_see = - state.required_permission_to_see === permission.value ? '' : permission.value; - $scope.save(state); - }; - $scope.xor = function (a, b) { - return (a && !b) || (!a && b); - }; - - $scope.changeBooleanMember = function (state, memberName) { - state[memberName] = !state[memberName]; - $scope.save(state); - }; - $scope.setMember = function (state, member, value) { - state[member] = value; - $scope.save(state); - }; - $scope.clickNextStateEntry = function (state, clickedStateId) { - var index = state.next_states_id.indexOf(clickedStateId); - if (index > -1) { // remove now - state.next_states_id.splice(index, 1); - } else { // add - state.next_states_id.push(clickedStateId); - } - $scope.save(state); - }; - $scope.save = function (state) { - MotionState.save(state).then(null, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - - // Save expand state so the session - if ($sessionStorage.motionStateTableExpandState) { - $scope.toggleExpandContent(); - } - $scope.saveExpandState = function (state) { - $sessionStorage.motionStateTableExpandState = state; - }; - - $scope.openStateDialog = function (state) { - ngDialog.open({ - template: 'static/templates/motions/state-edit.html', - controller: state ? 'StateRenameCtrl' : 'StateCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - state: function () {return state;}, - workflow: function () {return $scope.workflow;}, - } - }); - }; - $scope.openWorkflowDialog = function () { - ngDialog.open({ - template: 'static/templates/motions/workflow-edit.html', - controller: 'WorkflowRenameCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - workflow: function () {return $scope.workflow;}, - } - }); - }; - - $scope.delete = function (state) { - MotionState.destroy(state).then(null, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('WorkflowCreateCtrl', [ - '$scope', - 'Workflow', - 'ErrorMessage', - function ($scope, Workflow, ErrorMessage) { - $scope.save = function () { - var workflow = { - name: $scope.newName, - }; - Workflow.create(workflow).then(function (success) { - $scope.closeThisDialog(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('WorkflowRenameCtrl', [ - '$scope', - 'workflow', - 'Workflow', - 'gettextCatalog', - 'ErrorMessage', - function ($scope, workflow, Workflow, gettextCatalog, ErrorMessage) { - $scope.workflow = workflow; - $scope.newName = gettextCatalog.getString(workflow.name); - $scope.save = function () { - workflow.name = $scope.newName; - Workflow.save(workflow).then(function (success) { - $scope.closeThisDialog(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('StateCreateCtrl', [ - '$scope', - 'workflow', - 'MotionState', - 'ErrorMessage', - function ($scope, workflow, MotionState, ErrorMessage) { - $scope.newName = ''; - $scope.actionWord = ''; - $scope.save = function () { - var state = { - name: $scope.newName, - action_word: $scope.actionWord, - workflow_id: workflow.id, - allow_create_poll: true, - allow_support: true, - allow_submitter_edit: true, - }; - MotionState.create(state).then(function () { - $scope.closeThisDialog(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]) - -.controller('StateRenameCtrl', [ - '$scope', - 'MotionState', - 'state', - 'gettextCatalog', - 'ErrorMessage', - function ($scope, MotionState, state, gettextCatalog, ErrorMessage) { - $scope.state = state; - $scope.newName = gettextCatalog.getString(state.name); - $scope.actionWord = gettextCatalog.getString(state.action_word); - $scope.save = function () { - state.name = $scope.newName; - state.action_word = $scope.actionWord; - MotionState.save(state).then(function () { - $scope.closeThisDialog(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - } -]); - -}()); diff --git a/openslides/motions/static/templates/motions/amendment-paragraph-choose-form.html b/openslides/motions/static/templates/motions/amendment-paragraph-choose-form.html deleted file mode 100644 index c4108f1a3..000000000 --- a/openslides/motions/static/templates/motions/amendment-paragraph-choose-form.html +++ /dev/null @@ -1,27 +0,0 @@ -

    Please choose the paragraph to amend

    - -
    - {{ alert.msg }} -
    - -
    - -
    -
    -
    - -
    -
    -
    -
    - - - -
    diff --git a/openslides/motions/static/templates/motions/category-form.html b/openslides/motions/static/templates/motions/category-form.html deleted file mode 100644 index 50683e1d8..000000000 --- a/openslides/motions/static/templates/motions/category-form.html +++ /dev/null @@ -1,17 +0,0 @@ -

    Edit category

    -

    New category

    - -
    - {{ alert.msg }} -
    - -
    - - - - -
    diff --git a/openslides/motions/static/templates/motions/category-list.html b/openslides/motions/static/templates/motions/category-list.html deleted file mode 100644 index 489c98c1c..000000000 --- a/openslides/motions/static/templates/motions/category-list.html +++ /dev/null @@ -1,55 +0,0 @@ -
    - -
    - -
    -
    -
    - -
    -
    - - - - - - -
    - Name - - - - Prefix - - -
    - {{ category.name }} -
    - - Sort · - - Edit · - - Delete -
    -
    {{ category.prefix }} -
    -
    diff --git a/openslides/motions/static/templates/motions/category-sort.html b/openslides/motions/static/templates/motions/category-sort.html deleted file mode 100644 index 2d9e08cac..000000000 --- a/openslides/motions/static/templates/motions/category-sort.html +++ /dev/null @@ -1,33 +0,0 @@ -
    -
    - -

    Category {{ category.name }}

    -
    -
    - -
    -
    -

    - Drag and drop motions to reorder the category. Then click the button to renumber. -

    - - - Numbering - -
    -
    - {{ alert.msg }} -
    - -
    diff --git a/openslides/motions/static/templates/motions/change-recommendation-form.html b/openslides/motions/static/templates/motions/change-recommendation-form.html deleted file mode 100644 index 7ed470e85..000000000 --- a/openslides/motions/static/templates/motions/change-recommendation-form.html +++ /dev/null @@ -1,17 +0,0 @@ -

    Edit change recommendation

    -

    New change recommendation

    - -
    - {{ alert.msg }} -
    - -
    - - - - -
    diff --git a/openslides/motions/static/templates/motions/motion-amendment-list.html b/openslides/motions/static/templates/motions/motion-amendment-list.html deleted file mode 100644 index 3de76eb7a..000000000 --- a/openslides/motions/static/templates/motions/motion-amendment-list.html +++ /dev/null @@ -1,414 +0,0 @@ -
    -
    - - -
    - -
    -
    - - - -
    - - -
    -
    -
    - -
    -
    - - - - - - - Set status - - - - - - Set category - - - - - - Set motion block - - - - - Delete selected amendments - -
    -
    - -
    -
    - {{ amendmentsFiltered.length }} / - {{ amendments.length }} - amendments, - {{(amendments|filter:{selected:true}).length}} {{ "selected" | translate }} -
    -
    - - Page {{ pagination.currentPage }} / - {{ Math.ceil(amendmentsFiltered.length/pagination.itemsPerPage) }} - -
    -
    - -
    -
    -
    - -
    -
    - -
    -
    - - -
    - - -
    - -
    - -
    - - -
    -
    - -
    -
    - - - - -
    - - {{ amendment.getStateName() }} - -
    - - - - - -
    - - by - - {{ submitter.user.get_full_name() }},, - ... [+{{ amendment.submitters.length - 1 }}] - - -
    - -
    - - Edit - · - - Edit submitters - - - · - Delete - - -
    -
    -
    -
    - -
    -
    - No changes at the text. -
    -
    -
    -
    -
    -
    - {{ getTextPreview(amendment.getText(), 400) }} -
    -
    - -
    -
    -
    -
    - - {{ field.name }} -
    -
    - {{ getTextPreview(amendment.comments[id], 30) }} -
    -
    -
    - -
    - -
    -
    - -
    -
    - - {{ amendment.supporters.length }} - -
    -
    - -
    -
    - -
      -
    - -
    -
    -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-block-detail.html b/openslides/motions/static/templates/motions/motion-block-detail.html deleted file mode 100644 index 2a8c119e2..000000000 --- a/openslides/motions/static/templates/motions/motion-block-detail.html +++ /dev/null @@ -1,76 +0,0 @@ -
    -
    - -

    {{ motionBlock.agenda_item.getTitle() }}

    -

    Motion block

    -
    -
    - -
    - - - Follow recommendations for all motions - - -
    -
    - -
    -
    - {{ motionsFiltered.length }} / - {{ motionBlock.motions.length }} {{ "motions" | translate }} -
    -
    - - - - - - -
    Motion - State - Recommendation -
    - - {{ motion.identifier }} {{ motion.getTitle() }} - - - -
    - {{ motion.getStateName() }} -
    -
    -
    - {{ motion.getRecommendationName() }} -
    -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-block-form.html b/openslides/motions/static/templates/motions/motion-block-form.html deleted file mode 100644 index f069e703f..000000000 --- a/openslides/motions/static/templates/motions/motion-block-form.html +++ /dev/null @@ -1,17 +0,0 @@ -

    Edit motion block

    -

    New motion block

    - -
    - {{ alert.msg }} -
    - -
    - - - - -
    diff --git a/openslides/motions/static/templates/motions/motion-block-list.html b/openslides/motions/static/templates/motions/motion-block-list.html deleted file mode 100644 index 58362a7a3..000000000 --- a/openslides/motions/static/templates/motions/motion-block-list.html +++ /dev/null @@ -1,52 +0,0 @@ -
    -
    - -

    Motion blocks

    -
    -
    - -
    -
    -
    - -
    -
    - - - - - - -
    - Name - Motions -
    - - - - - {{ motionBlock.title }} - -
    - - Edit | - - Delete -
    -
    - {{ motionBlock.motions.length }} -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-comment-form.html b/openslides/motions/static/templates/motions/motion-comment-form.html deleted file mode 100644 index 8f65843db..000000000 --- a/openslides/motions/static/templates/motions/motion-comment-form.html +++ /dev/null @@ -1,16 +0,0 @@ -

    {{ title }}

    - -
    - {{ alert.msg }} -
    - -
    - - - - -
    diff --git a/openslides/motions/static/templates/motions/motion-detail.html b/openslides/motions/static/templates/motions/motion-detail.html deleted file mode 100644 index 078582ef2..000000000 --- a/openslides/motions/static/templates/motions/motion-detail.html +++ /dev/null @@ -1,666 +0,0 @@ -
    -
    - - -

    - - - - - - {{ motion.getTitleWithChanges(viewChangeRecommendations.mode) }} - - -

    - -
    -
    -

    - Motion {{ motion.identifier }} - | Version {{ motion.getVersion(version).version_number }} - - - This version is not permitted. - - - Sequential number {{ motion.getSequentialNumber() }} - - - · - - Amendment to - {{ motion.getParentMotion().identifier || motion.getParentMotion().getTitle() }} - - - -

    -
    - -
    -
    -
    - -
    -
    -
    - - Meta information -
    -
    - -
    -
    -
    -
    -
    - -

    Submitters

    -

    - Submitters - - - -

    -
    - {{ submitter.user.get_full_name() }} -
    - - -
    -

    Supporters

    -
      -
    1. - {{ supporters.get_full_name() }} -
    - - - - -
    - - - -
    -
    - -

    State

    - -
    - {{ motion.getStateName() }} -
    -
    - - - - - -
    - - -
    -

    - {{ config('motions_recommendations_by') }} -

    -
    - - - {{ config('motions_recommendations_by') | translate }} - - - - -
    -
    - {{ motion.getRecommendationName() }} -
    -
    - - - - - - - - - - Motion - - - - -
    - -
    - - -

    Category

    - -
    - {{ motion.category.prefix }} – {{ motion.category.name }} -
    - - -

    Motion block

    -
    - - - Motion block - - - - -
    - {{ motion.motionBlock.title }} - {{ motion.motionBlock.title }} - - -

    Tags

    -
    - - - Tags - - - - -
    - - {{ tag.name }}{{$last ? '' : ', '}} - - - -

    Origin

    - {{ motion.origin }} - -
    -
    -

    Voting result

    - -
      -
    1. - Vote - - - - - - - - - - - - - - - - -
      - - - - - - - - - - -
      - - - Yes: - - {{ voteYes.value | number:votesPrecision }} {{ voteYes.percentStr }} - -
      - -
      - -
      - - - No: - - {{ voteNo.value | number:votesPrecision }} {{ voteNo.percentStr }} - -
      - -
      - -
      - - - Abstain: - - {{ voteAbstain.value | number:votesPrecision }} {{ voteAbstain.percentStr }} - -
      - -
      - -
      - - - Valid votes: - - {{ votesValid.value | number:votesPrecision }} {{ votesValid.percentStr }} - - -
      - - - Invalid votes: - - {{ votesInvalid.value | number:votesPrecision }} {{ votesInvalid.percentStr }} - - -
      - - - Votes cast: - - {{ votesCast.value | number:votesPrecision }} {{ votesCast.percentStr }} - - - -
      - -
      -
      - Required majority: -
      - -
      - - Quorum ({{ (voteYes.value - isReached()) | number:votesPrecision }}) reached. - - - Quorum ({{ (voteYes.value - isReached()) | number:votesPrecision }}) not reached. - -
      - -
      -
    - - - - - -
    - Personal note - - - -
    -
    -
    -
    -
    - -
    -
    - - - - -
    - - - -

    {{ config('motions_preamble') | translate }}

    - -
    - - - - - - - - -
    -
    - -
    - -
    -
    - - -
    -
    - - - At least two amendments or change recommendations affecting the same line are to be integrated. - This leads to undeterministic results. - Please resolve this conflict by not accepting multiple changes affecting the same line. - -
    - -
    - -
    - -
    - -
    - -
    -
    - - - - -
    -
    - - - - -
    -
    -
    - -
    -

    Reason

    -
    -
    -
    - - -

    Attachments

    - - - -
    -

    Version history

    - -
    - - -
    - -
    -
    -
      -
    • - {{ message.message }} -
    • -
    -
    -
    -
    -
    - -
    -
    - - - - - - - - diff --git a/openslides/motions/static/templates/motions/motion-detail/amendment-paragraph-diff.html b/openslides/motions/static/templates/motions/motion-detail/amendment-paragraph-diff.html deleted file mode 100644 index 5af81f791..000000000 --- a/openslides/motions/static/templates/motions/motion-detail/amendment-paragraph-diff.html +++ /dev/null @@ -1,43 +0,0 @@ -
    -
    -
    - -
    - -
    -
    - -
    -
    -
    - No changes at the text. -
    -
    - -
    -
    -
    - -

    - Line {{ paragraph.diffLineFrom }}: -

    -

    - Line {{ paragraph.diffLineFrom }} - {{ paragraph.diffLineTo - 1 }}: -

    - -
    -
    -
    - -
    -
    -
    -
    -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-detail/change-summary.html b/openslides/motions/static/templates/motions/motion-detail/change-summary.html deleted file mode 100644 index 59491881d..000000000 --- a/openslides/motions/static/templates/motions/motion-detail/change-summary.html +++ /dev/null @@ -1,55 +0,0 @@ - -
    - - Summary of changes: - - - - - - -
    - No change recommendations yet -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-detail/comments.html b/openslides/motions/static/templates/motions/motion-detail/comments.html deleted file mode 100644 index ed12dcf35..000000000 --- a/openslides/motions/static/templates/motions/motion-detail/comments.html +++ /dev/null @@ -1,45 +0,0 @@ -
    -
    - -
    -
    - - - - - -
    -

    {{ field.name }} - internal -

    -
    - -
    -
    -
    -
    -
    - - -
    -
    A comment has been changed.
    - - -
    diff --git a/openslides/motions/static/templates/motions/motion-detail/personal-note.html b/openslides/motions/static/templates/motions/motion-detail/personal-note.html deleted file mode 100644 index 34f1e685a..000000000 --- a/openslides/motions/static/templates/motions/motion-detail/personal-note.html +++ /dev/null @@ -1,45 +0,0 @@ -
    -
    - -
    -
    - - - - -
    -

    Personal note

    -
    - - -
    -
    - -
    -
    The personal note has been changed.
    - - -
    -
    -
    -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-detail/toolbar-line-numbering.html b/openslides/motions/static/templates/motions/motion-detail/toolbar-line-numbering.html deleted file mode 100644 index 16bff7cd4..000000000 --- a/openslides/motions/static/templates/motions/motion-detail/toolbar-line-numbering.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - diff --git a/openslides/motions/static/templates/motions/motion-detail/toolbar.html b/openslides/motions/static/templates/motions/motion-detail/toolbar.html deleted file mode 100644 index 5986ab95a..000000000 --- a/openslides/motions/static/templates/motions/motion-detail/toolbar.html +++ /dev/null @@ -1,154 +0,0 @@ -
    - -
    - - - -
    - - -
    - - -
    - -
    - - - - -
    - - -
    - - -
    -
    -
    -
    - - -
    -
    - - - - - - -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-detail/view-diff.html b/openslides/motions/static/templates/motions/motion-detail/view-diff.html deleted file mode 100644 index 5575abcf0..000000000 --- a/openslides/motions/static/templates/motions/motion-detail/view-diff.html +++ /dev/null @@ -1,111 +0,0 @@ -
    - -
    -
    -
    - - -
    - - - - -
    -
    - Rejected: -
    - -
    -
    New title:
    -
    {{ title_change_recommendation.text }}
    -
    -
    - - -
    -
    -
    - -
    -
    - -
    -
    - -
    - - -
    - - -
    - - - - {{ change.original.identifier }} - -
    -
    - Rejected: -
    - -
    -
    -
    - -
    -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-detail/view-modified-agreed.html b/openslides/motions/static/templates/motions/motion-detail/view-modified-agreed.html deleted file mode 100644 index 957269966..000000000 --- a/openslides/motions/static/templates/motions/motion-detail/view-modified-agreed.html +++ /dev/null @@ -1,28 +0,0 @@ - -
    -
    - -
    -
    -
    -
    -
    The final print template have been changed.
    - - - -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-detail/view-original.html b/openslides/motions/static/templates/motions/motion-detail/view-original.html deleted file mode 100644 index 8f2531878..000000000 --- a/openslides/motions/static/templates/motions/motion-detail/view-original.html +++ /dev/null @@ -1,46 +0,0 @@ - -
    -
    -
    -
    -
    The text or reason have been changed.
    - - - -
    -
    - -
      -
    • -
      -
      - - - -
      -
    • -
    - - diff --git a/openslides/motions/static/templates/motions/motion-export-form.html b/openslides/motions/static/templates/motions/motion-export-form.html deleted file mode 100644 index f0fefa826..000000000 --- a/openslides/motions/static/templates/motions/motion-export-form.html +++ /dev/null @@ -1,14 +0,0 @@ -

    Export motions

    -

    Export motion

    -

    Export motion "{{ motions.getTitle() }}"

    - -
    - - - - -
    diff --git a/openslides/motions/static/templates/motions/motion-form.html b/openslides/motions/static/templates/motions/motion-form.html deleted file mode 100644 index 32f862997..000000000 --- a/openslides/motions/static/templates/motions/motion-form.html +++ /dev/null @@ -1,23 +0,0 @@ -

    Edit motion

    -

    New motion

    -

    New amendment to motion {{ parent.identifier || parent.getTitle() }}

    - -
    - {{ alert.msg }} -
    - -
    - -
    -
    - - Open motion detail view after save. -
    - - -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-import.html b/openslides/motions/static/templates/motions/motion-import.html deleted file mode 100644 index 8af7b7560..000000000 --- a/openslides/motions/static/templates/motions/motion-import.html +++ /dev/null @@ -1,143 +0,0 @@ -
    -
    - -

    Import motions

    -
    -
    - -
    - -

    Select a CSV file

    - - -

    Please note:

    -
      -
    • Required comma or semicolon separated values with these column header names in the first row:
      - - Identifier, - Title, - Text, - Reason, - Submitter, - Category, - Origin, - Motion block - -
    • Identifier, reason, submitter, category, origin and motion block are optional and may be empty. -
    • Additional columns after the required ones may be present and won't affect the import. -
    • Only double quotes are accepted as text delimiter (no single quotes). -
    • Download CSV example file -
    - -
    -

    Preview

    -
    - - - - - - -
    - # - Identifier - Title - Text - Reason - Submitter - Category - Origin - Motion block
    - - - - - - - - - - - - {{ $index + 1 }} - - - - - {{ motion.identifier }} - - - - - {{ motion.title }} - - - - - - {{ motion.text.length > 90 ? '...' : '' }} - - - {{ motion.reason.length > 90 ? '...' : '' }} - - - - - {{ motion.submitter }} - - - - - {{ motion.category }} - {{ motion.origin | limitTo:30 }}{{ motion.origin.length > 30 ? '...' : '' }} - - - - - {{ motion.motionBlock }} -
    -
    - -
    - - {{ motionsWillNotBeImported }} - motions will be not imported. -
    -
    - - {{ motionsWillBeImported }} - motions will be imported. -
    -
    -
    -
    - - {{ motionsImported.length }} - motions were successfully imported. - (Users created: {{ usersCreated }}, - Categories created: {{ categoriesCreated }}, - Motion blocks created: {{ motionBlocksCreated }}) -
    - -
    - - -
    - -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-list.html b/openslides/motions/static/templates/motions/motion-list.html deleted file mode 100644 index 4a7863656..000000000 --- a/openslides/motions/static/templates/motions/motion-list.html +++ /dev/null @@ -1,466 +0,0 @@ - - -
    -
    -
    - - - - - -
    -
    - -
    -
    - - - - - - - Set status - - - - - - Set category - - - - - - Set motion block - - - - - Delete selected motions - -
    -
    - -
    -
    - {{ motionsFiltered.length }} / - {{ motions.length }} - motions, - {{(motions|filter:{selected:true}).length}} {{ "selected" | translate }} -
    -
    - - - « - - Page {{ pagination.currentPage }} / - {{ pagination.getPageCount(motionsFiltered) }} - - » - - -
    -
    - -
    -
    -
    - -
    -
    - -
    -
    - - -
    - - -
    - -
    - -
    - - -
    -
    - -
    -
    - - {{ motion.identifier }} - -
    -
    - - - -
    - - - {{ motion.getStateName() }} - - - - - - -
    - -
    - - - {{ motion.getRecommendationName() }} - - - - - - -
    - -
    - - by - - {{ submitter.user.get_full_name() }},, - ... [+{{ motion.submitters.length - 1 }}] - - -
    - -
    - - - Edit - - · - - Edit submitters - - - · - Delete - - -
    - -
    -
    - -
    -
    - - -
    - - - - - - - - - {{ motion.category.prefix }} – {{ motion.category.name }} - - - - - -
    - -
    - - {{ motion.category.prefix }} – {{ motion.category.name }} -
    - - -
    - - - - - - - - - {{ motion.motionBlock.title }} - - - - - -
    - -
    - - {{ motion.motionBlock.title }} -
    - - -
    - - - - - - - - - - {{ tag.name }}, - - - - - - -
    - -
    - - - {{ tag.name }}, - -
    - - -
    - -
    - - {{ motion.origin | limitTo:25 }}{{ motion.origin.length > 25 ? '...' : '' }} - -
    -
    - -
    -
    -
    -
    - - {{ motion.supporters.length }} - -
    -
    - -
    -
    {{ motion.agenda_item.getItemNumberWithAncestors() }}
    -
    -
    -
    - -
      -
    - -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-poll-form.html b/openslides/motions/static/templates/motions/motion-poll-form.html deleted file mode 100644 index 9e790e3c1..000000000 --- a/openslides/motions/static/templates/motions/motion-poll-form.html +++ /dev/null @@ -1,22 +0,0 @@ -

    Vote {{ voteNumber }}

    - -
    - {{ alert.msg }} -
    - -

    - Special values: - -1 = majority - -2 = undocumented - -

    - - - - - -
    diff --git a/openslides/motions/static/templates/motions/motion-submitters.html b/openslides/motions/static/templates/motions/motion-submitters.html deleted file mode 100644 index a5e4d0a36..000000000 --- a/openslides/motions/static/templates/motions/motion-submitters.html +++ /dev/null @@ -1,44 +0,0 @@ -
    -
    - -

    {{ motion.getAgendaTitle() }}

    -

    Sort submitters

    -
    -
    - -
    - -
    -
      -
    1. - - {{ $index + 1 }}. - {{ submitter.user.get_full_name() }} - -
    -
    - - -
    -
    - {{ alert.msg }} -
    - -
    -
    diff --git a/openslides/motions/static/templates/motions/motion-table-filters.html b/openslides/motions/static/templates/motions/motion-table-filters.html deleted file mode 100644 index 82a21c507..000000000 --- a/openslides/motions/static/templates/motions/motion-table-filters.html +++ /dev/null @@ -1,357 +0,0 @@ - - -
    - - - - All Filters - - - - - - {{ state.name | translate }} - - - - - done - - - - undone - - - - - - {{ category.prefix }} – {{ category.name }} - - - - - No category set - - - - - - {{ motionBlock.title }} - - - - - - - {{ commentsField.name }} - - - - - No comments set - - - - - - {{ recommendation.recommendation_label | translate }} - - - - - No motion block set - - - - - - {{ tag.name }} - - - - - No tag set - - - - - - {{ booleanFilter.value ? booleanFilter.choiceYes : booleanFilter.choiceNo | translate }} - - -
    diff --git a/openslides/motions/static/templates/motions/slide_motion.html b/openslides/motions/static/templates/motions/slide_motion.html deleted file mode 100644 index 564d655ed..000000000 --- a/openslides/motions/static/templates/motions/slide_motion.html +++ /dev/null @@ -1,171 +0,0 @@ -
    - - -
    - -
    -

    {{ motion.getTitleWithChanges(mode) }}

    -

    - Motion {{ motion.identifier }} - | Version {{ motion.getVersion().version_number }} - - - - (Line {{ paragraph.diffLineFrom }}) - - - (Line {{ paragraph.diffLineFrom }}-{{ paragraph.diffLineTo }}) - - - -

    -
    - -
    - -

    {{ config('motions_preamble') | translate }}


    - - - -
    -
    -
    - - -
    -
    -
    -
    - New title: -
    -
    {{ title_change_recommendation.text }}
    -
    -
    - - -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -

    Reason

    -
    -
    -
    - - - -
    -
    -
    -
    - - -
    -

    Reason

    -
    -
    -
    -
    -
    diff --git a/openslides/motions/static/templates/motions/slide_motion_block.html b/openslides/motions/static/templates/motions/slide_motion_block.html deleted file mode 100644 index 6ccdc555c..000000000 --- a/openslides/motions/static/templates/motions/slide_motion_block.html +++ /dev/null @@ -1,19 +0,0 @@ -
    - -
    -

    {{ motionBlock.agenda_item.getTitle() }}

    -

    Motion block — {{motionBlock.motions.length }} Motions

    -
    - - -
    -
    - {{ motion.identifier }}: - {{ getShortTitle(motion) }} -
    - - {{ motion.getRecommendationName() }} - -
    -
    -
    diff --git a/openslides/motions/static/templates/motions/state-edit.html b/openslides/motions/static/templates/motions/state-edit.html deleted file mode 100644 index 37a4f960b..000000000 --- a/openslides/motions/static/templates/motions/state-edit.html +++ /dev/null @@ -1,28 +0,0 @@ -

    Edit state

    -

    Create new state

    -
    - {{ alert.msg }} -
    - -
    - -
    - -
    - -
    - -
    -
    - - -
    -
    diff --git a/openslides/motions/static/templates/motions/workflow-detail.html b/openslides/motions/static/templates/motions/workflow-detail.html deleted file mode 100644 index c59e31dd9..000000000 --- a/openslides/motions/static/templates/motions/workflow-detail.html +++ /dev/null @@ -1,199 +0,0 @@ -
    -
    - -

    - {{ workflow.name | translate }} - -

    -
    -
    -

    - First state: - {{ workflow.getFirstState().name | translate }} -

    -
    -
    - -
    -
    - {{ alert.msg }} -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    -

    Permissions

    -
    - - {{ state.name | translate }} - - - {{ state.name | translate | limitTo: 1 }}... - -
    - - - -   - - - - -
    -
    - Action word - -
    - -
    - {{ state.action_word | translate }} -
    - - — - - -
    -
    -
    - Recommendation label - -
    - -
    - {{ state.recommendation_label | translate }} -
    - - — - - -
    -
    -
    - {{ member.displayName | translate }} - - - -
    - Label color - - - - - {{ cssClasses[state.css_class] | translate }} - - - - - -
    - Required permission to see - - - -
    - - {{ getPermissionDisplayName(state.required_permission_to_see) | translate }} - - - — - - -
    -
    - -
    -
    - Next states - - - — - -
    - - {{ nextState.name | translate }},
    -
    -
    - - - - - - -
    -
    diff --git a/openslides/motions/static/templates/motions/workflow-edit.html b/openslides/motions/static/templates/motions/workflow-edit.html deleted file mode 100644 index 4772757ba..000000000 --- a/openslides/motions/static/templates/motions/workflow-edit.html +++ /dev/null @@ -1,29 +0,0 @@ -

    Edit name

    -

    Create new workflow

    -
    - {{ alert.msg }} -
    - -
    - -
    -
    - -
    -
    - - -
    -
    -
    diff --git a/openslides/motions/static/templates/motions/workflow-list.html b/openslides/motions/static/templates/motions/workflow-list.html deleted file mode 100644 index 5abb8a7bd..000000000 --- a/openslides/motions/static/templates/motions/workflow-list.html +++ /dev/null @@ -1,46 +0,0 @@ -
    - -
    - -
    -
    - {{ alert.msg }} -
    - - - - - - - - - - - -
    - Name -
    - {{ workflow.name | translate }} -
    - - Edit · - - Delete -
    -
    -
    diff --git a/openslides/motions/urls.py b/openslides/motions/urls.py deleted file mode 100644 index 277e22dce..000000000 --- a/openslides/motions/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf.urls import url - -from . import views - - -urlpatterns = [ - url(r'^docxtemplate/$', - views.MotionDocxTemplateView.as_view(), - name='motions_docx_template'), -] diff --git a/openslides/motions/views.py b/openslides/motions/views.py index 1e1ca0f6a..af0132f03 100644 --- a/openslides/motions/views.py +++ b/openslides/motions/views.py @@ -26,7 +26,6 @@ from ..utils.rest_api import ( detail_route, list_route, ) -from ..utils.views import BinaryTemplateView from .access_permissions import ( CategoryAccessPermissions, MotionAccessPermissions, @@ -1016,12 +1015,3 @@ class StateViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Generi msg = self.getProtectedErrorMessage('workflow', e) raise ValidationError({'detail': msg}) return result - - -# Special views - -class MotionDocxTemplateView(BinaryTemplateView): - """ - Returns the template for motions docx export - """ - template_name = 'templates/docx/motions.docx' diff --git a/openslides/old_urls.py b/openslides/old_urls.py deleted file mode 100644 index 428e0347b..000000000 --- a/openslides/old_urls.py +++ /dev/null @@ -1,37 +0,0 @@ -from django.conf import settings -from django.conf.urls import include, url -from django.contrib.staticfiles.urls import urlpatterns -from django.views.generic import RedirectView - -from openslides.core import views as core_views -from openslides.mediafiles.views import protected_serve -from openslides.utils.plugins import get_all_plugin_urlpatterns -from openslides.utils.rest_api import router - - -urlpatterns += get_all_plugin_urlpatterns() - -urlpatterns += [ - url(r'^%s(?P.*)$' % settings.MEDIA_URL.lstrip('/'), protected_serve, {'document_root': settings.MEDIA_ROOT}), - url(r'^(?P.*[^/])$', RedirectView.as_view(url='/%(url)s/', permanent=True)), - url(r'^rest/', include(router.urls)), - url(r'^agenda/', include('openslides.agenda.urls')), - url(r'^motions/', include('openslides.motions.urls')), - url(r'^users/', include('openslides.users.urls')), - url(r'^core/', include('openslides.core.urls')), - # The old angular webclient - # TODO: Change me or at least my comment - url(r'^webclient/(?Psite|projector)/$', - core_views.WebclientJavaScriptView.as_view(), - name='core_webclient_javascript'), - - # View for the projectors are handled by angular. - url(r'^projector/(\d+)/$', core_views.ProjectorView.as_view()), - - # Original view without resolutioncontrol for the projectors are handled by angular. - url(r'^real-projector/(\d+)/$', core_views.RealProjectorView.as_view()), - - # Main entry point for all angular pages. - # Has to be the last entry in the urls.py - url(r'^.*$', core_views.IndexView.as_view(), name="index"), -] diff --git a/openslides/poll/static/js/poll/majority.js b/openslides/poll/static/js/poll/majority.js deleted file mode 100644 index f2461183e..000000000 --- a/openslides/poll/static/js/poll/majority.js +++ /dev/null @@ -1,41 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.poll.majority', []) - -.value('MajorityMethodChoices', [ - {'value': 'simple_majority', 'display_name': 'Simple majority'}, - {'value': 'two-thirds_majority', 'display_name': 'Two-thirds majority'}, - {'value': 'three-quarters_majority', 'display_name': 'Three-quarters majority'}, - {'value': 'disabled', 'display_name': 'Disabled'}, -]) - -.factory('MajorityMethods', [ - function () { - return { - 'simple_majority': function (vote, base) { - return Math.ceil(-(base / 2 - vote)) - 1; - }, - 'two-thirds_majority': function (vote, base) { - var result = -(base * 2 - vote * 3) / 3; - if (result % 1 !== 0) { - result = Math.ceil(result) - 1; - } - return result; - }, - 'three-quarters_majority': function (vote, base) { - var result = -(base * 3 - vote * 4) / 4; - if (result % 1 !== 0) { - result = Math.ceil(result) - 1; - } - return result; - }, - 'disabled': function () { - return undefined; - }, - }; - } -]); - -}()); diff --git a/openslides/topics/static/js/topics/base.js b/openslides/topics/static/js/topics/base.js deleted file mode 100644 index c0491dedf..000000000 --- a/openslides/topics/static/js/topics/base.js +++ /dev/null @@ -1,48 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.topics', []) - -.factory('Topic', [ - 'DS', - 'jsDataModel', - 'gettext', - function(DS, jsDataModel, gettext) { - var name = 'topics/topic'; - return DS.defineResource({ - name: name, - useClass: jsDataModel, - verboseName: gettext('Topic'), - methods: { - getResourceName: function () { - return name; - }, - getAgendaTitle: function () { - return this.title; - }, - getCSVExportText: function () { - return this.text; - }, - }, - relations: { - belongsTo: { - 'agenda/item': { - localKey: 'agenda_item_id', - localField: 'agenda_item', - } - }, - hasMany: { - 'mediafiles/mediafile': { - localField: 'attachments', - localKeys: 'attachments_id', - } - } - } - }); - } -]) - -.run(['Topic', function(Topic) {}]); - -}()); diff --git a/openslides/topics/static/js/topics/csv.js b/openslides/topics/static/js/topics/csv.js deleted file mode 100644 index 68fa7e9e8..000000000 --- a/openslides/topics/static/js/topics/csv.js +++ /dev/null @@ -1,32 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.topics.csv', []) - -.factory('TopicsCsvExample', [ - 'gettextCatalog', - 'CsvDownload', - function (gettextCatalog, CsvDownload) { - var makeHeaderline = function () { - var headerline = ['Title', 'Text', 'Duration', 'Comment', 'Internal item']; - return _.map(headerline, function (entry) { - return gettextCatalog.getString(entry); - }); - }; - return { - downloadExample: function () { - var csvRows = [makeHeaderline(), - // example entries - ['Demo 1', 'Demo text 1', '1:00', 'test comment', ''], - ['Break', '', '0:10', '', '1'], - ['Demo 2', 'Demo text 2', '1:30', '', ''] - - ]; - CsvDownload(csvRows, gettextCatalog.getString('agenda-example') + '.csv'); - }, - }; - } -]); - -}()); diff --git a/openslides/topics/static/js/topics/projector.js b/openslides/topics/static/js/topics/projector.js deleted file mode 100644 index e910351c1..000000000 --- a/openslides/topics/static/js/topics/projector.js +++ /dev/null @@ -1,28 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.topics.projector', ['OpenSlidesApp.topics']) - -.config([ - 'slidesProvider', - function (slidesProvider) { - slidesProvider.registerSlide('topics/topic', { - template: 'static/templates/topics/slide_topic.html' - }); - } -]) - -.controller('SlideTopicCtrl', [ - '$scope', - 'Topic', - function($scope, Topic) { - // Attention! Each object that is used here has to be dealt on server side. - // Add it to the coresponding get_requirements method of the ProjectorElement - // class. - var id = $scope.element.id; - Topic.bindOne(id, $scope, 'topic'); - } -]); - -})(); diff --git a/openslides/topics/static/js/topics/site.js b/openslides/topics/static/js/topics/site.js deleted file mode 100644 index 627bee0a7..000000000 --- a/openslides/topics/static/js/topics/site.js +++ /dev/null @@ -1,369 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlidesApp.topics.csv']) - -.config([ - '$stateProvider', - 'gettext', - function($stateProvider, gettext) { - $stateProvider - .state('topics', { - url: '/topics', - abstract: true, - template: "", - data: { - title: gettext('Topics'), - }, - }) - .state('topics.topic', { - abstract: true, - template: "", - }) - .state('topics.topic.detail', { - resolve: { - topicId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }], - } - }) - // redirects to topic detail and opens topic edit form dialog, uses edit url, - // used by ui-sref links from agenda only - // (from topic controller use TopicForm factory instead to open dialog in front - // of current view without redirect) - .state('topics.topic.detail.update', { - onEnter: ['$stateParams', '$state', 'ngDialog', - function($stateParams, $state, ngDialog) { - ngDialog.open({ - template: 'static/templates/topics/topic-form.html', - controller: 'TopicUpdateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - topicId: function() { - return $stateParams.id; - }, - }, - preCloseCallback: function() { - $state.go('topics.topic.detail', {topic: $stateParams.id}); - return true; - } - }); - }], - }) - .state('topics.topic.import', { - url: '/import', - controller: 'TopicImportCtrl', - }); - } -]) - -.factory('TopicForm', [ - '$filter', - 'gettextCatalog', - 'operator', - 'Editor', - 'Mediafile', - 'Agenda', - 'AgendaTree', - 'ShowAsAgendaItemField', - function ($filter, gettextCatalog, operator, Editor, Mediafile, Agenda, - AgendaTree, ShowAsAgendaItemField) { - return { - // ngDialog for topic form - getDialog: function (topic) { - return { - template: 'static/templates/topics/topic-form.html', - controller: (topic) ? 'TopicUpdateCtrl' : 'TopicCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - topicId: function () {return topic ? topic.id: void 0;} - }, - }; - }, - getFormFields: function (isCreateForm) { - var images = Mediafile.getAllImages(); - var formFields = [ - { - key: 'title', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Title'), - required: true - } - }, - { - key: 'text', - type: 'editor', - templateOptions: { - label: gettextCatalog.getString('Text') - }, - data: { - ckeditorOptions: Editor.getOptions(images) - } - }]; - // attachments - if (Mediafile.getAll().length > 0) { - formFields.push({ - key: 'attachments_id', - type: 'select-multiple', - templateOptions: { - label: gettextCatalog.getString('Attachment'), - options: $filter('orderBy')(Mediafile.getAll(), 'title_or_filename'), - ngOptions: 'option.id as option.title_or_filename for option in to.options', - placeholder: gettextCatalog.getString('Select or search an attachment ...') - } - }); - } - - // show as agenda item + parent item - if (isCreateForm) { - formFields.push(ShowAsAgendaItemField('agenda.can_manage')); - formFields.push({ - key: 'agenda_parent_id', - type: 'select-single', - templateOptions: { - label: gettextCatalog.getString('Parent item'), - options: AgendaTree.getFlatTree(Agenda.getAll()), - ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id', - placeholder: gettextCatalog.getString('Select a parent item ...') - }, - hide: !operator.hasPerms('agenda.can_manage') - }); - } - - return formFields; - } - }; - } -]) - -.controller('TopicDetailCtrl', [ - '$scope', - 'ngDialog', - 'TopicForm', - 'Topic', - 'topicId', - 'Projector', - 'ProjectionDefault', - 'WebpageTitle', - 'gettextCatalog', - function($scope, ngDialog, TopicForm, Topic, topicId, Projector, ProjectionDefault, WebpageTitle, - gettextCatalog) { - $scope.$watch(function () { - return Topic.lastModified(topicId); - }, function () { - $scope.topic = Topic.get(topicId); - WebpageTitle.updateTitle(gettextCatalog.getString('Topic') + ' ' + - $scope.topic.agenda_item.getTitle()); - }); - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - var projectiondefault = ProjectionDefault.filter({name: 'topics'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId = projectiondefault.projector_id; - } - }); - $scope.openDialog = function (topic) { - ngDialog.open(TopicForm.getDialog(topic)); - }; - } -]) - -.controller('TopicCreateCtrl', [ - '$scope', - '$state', - 'Topic', - 'TopicForm', - 'Agenda', - 'Config', - 'ErrorMessage', - function($scope, $state, Topic, TopicForm, Agenda, Config, ErrorMessage) { - $scope.model = { - agenda_type: 1, // Default is a public item. The config field - // 'agenda_new_items_default_visibility' is not used. - }; - // get all form fields - $scope.formFields = TopicForm.getFormFields(true); - // save form - $scope.save = function (topic) { - Topic.create(topic).then( - function (success) { - $scope.closeThisDialog(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('TopicUpdateCtrl', [ - '$scope', - '$state', - 'Topic', - 'TopicForm', - 'Agenda', - 'topicId', - 'ErrorMessage', - function($scope, $state, Topic, TopicForm, Agenda, topicId, ErrorMessage) { - var topic = Topic.get(topicId); - $scope.alert = {}; - // set initial values for form model by create deep copy of topic object - // so list/detail view is not updated while editing - $scope.model = angular.copy(topic); - // get all form fields - $scope.formFields = TopicForm.getFormFields(); - - // save form - $scope.save = function (topic) { - // inject the changed topic (copy) object back into DS store - Topic.inject(topic); - // save changed topic object on server - Topic.save(topic).then( - function(success) { - $scope.closeThisDialog(); - }, - function (error) { - // save error: revert all changes by restore - // (refresh) original topic object from server - Topic.refresh(topic); - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('TopicImportCtrl', [ - '$scope', - 'gettext', - 'Agenda', - 'Topic', - 'HumanTimeConverter', - 'TopicsCsvExample', - function($scope, gettext, Agenda, Topic, HumanTimeConverter, TopicsCsvExample) { - // Big TODO: Change wording from "item" to "topic". - // import from textarea - $scope.importByLine = function () { - if ($scope.itemlist) { - $scope.titleItems = _.filter($scope.itemlist[0].split("\n")); - $scope.importcounter = 0; - _.forEach($scope.titleItems, function(title, index) { - var item = {title: title}; - item.agenda_type = 1; // The new topic is not hidden. - item.agenda_weight = 1000 + index; - // TODO: create all items in bulk mode - Topic.create(item).then( - function(success) { - $scope.importcounter++; - } - ); - }); - } - }; - - // *** CSV import *** - $scope.csvConfig = { - accept: '.csv, .txt', - encodingOptions: ['UTF-8', 'ISO-8859-1'], - parseConfig: { - skipEmptyLines: true, - }, - }; - var FIELDS = ['title', 'text', 'duration', 'comment', 'is_hidden']; - $scope.items = []; - $scope.onCsvChange = function (csv) { - $scope.items = []; - - var items = []; - _.forEach(csv.data, function (row) { - if (row.length > 1) { - var filledRow = _.zipObject(FIELDS, row); - items.push(filledRow); - } - }); - - _.forEach(items, function (item, index) { - item.selected = true; - - if (!item.title) { - item.importerror = true; - item.title_error = gettext('Error: Title is required.'); - } - // duration - if (item.duration) { - var time = HumanTimeConverter.humanTimeToSeconds(item.duration, {hours: true})/60; - if (time <= 0) { // null instead of 0 or negative duration - time = null; - } - item.duration = time; - } else { - delete item.duration; - } - // is_hidden - if (item.is_hidden) { - if (item.is_hidden == '1') { - item.type = 2; - } else { - item.type = 1; - } - } else { - item.type = 1; - } - // set weight for right csv row order - // (Use 1000+ to protect existing items and prevent collision - // with new items which use weight 10000 as default.) - item.weight = 1000 + index; - $scope.items.push(item); - }); - $scope.calcStats(); - }; - - $scope.calcStats = function () { - $scope.itemsWillNotBeImported = 0; - $scope.itemsWillBeImported = 0; - - $scope.items.forEach(function(item) { - if (item.selected && !item.importerror) { - $scope.itemsWillBeImported++; - } else { - $scope.itemsWillNotBeImported++; - } - }); - }; - - // import from csv file - $scope.import = function () { - $scope.csvImporting = true; - angular.forEach($scope.items, function (item) { - if (item.selected && !item.importerror) { - item.agenda_type = item.type; - item.agenda_comment = item.comment; - item.agenda_duration = item.duration; - item.agenda_weight = item.weight; - Topic.create(item).then( - function(success) { - item.imported = true; - } - ); - } - }); - $scope.csvimported = true; - }; - $scope.clear = function () { - $scope.items = null; - }; - // download CSV example file - $scope.downloadCSVExample = function () { - TopicsCsvExample.downloadExample(); - }; - } -]); - -}()); diff --git a/openslides/topics/static/templates/topics/slide_topic.html b/openslides/topics/static/templates/topics/slide_topic.html deleted file mode 100644 index 29da04115..000000000 --- a/openslides/topics/static/templates/topics/slide_topic.html +++ /dev/null @@ -1,4 +0,0 @@ -
    -

    {{ topic.agenda_item.getTitle() }}

    -
    -
    diff --git a/openslides/topics/static/templates/topics/topic-detail.html b/openslides/topics/static/templates/topics/topic-detail.html deleted file mode 100644 index ec33b3741..000000000 --- a/openslides/topics/static/templates/topics/topic-detail.html +++ /dev/null @@ -1,40 +0,0 @@ -
    -
    - -

    {{ topic.agenda_item.getTitle() }}

    -

    Topic

    -
    -
    - - - - diff --git a/openslides/topics/static/templates/topics/topic-form.html b/openslides/topics/static/templates/topics/topic-form.html deleted file mode 100644 index a5329b05a..000000000 --- a/openslides/topics/static/templates/topics/topic-form.html +++ /dev/null @@ -1,17 +0,0 @@ -

    Edit topic

    -

    New topic

    - -
    - {{ alert.msg }} -
    - -
    - - - - -
    diff --git a/openslides/topics/static/templates/topics/topic-import.html b/openslides/topics/static/templates/topics/topic-import.html deleted file mode 100644 index 2d801dbcc..000000000 --- a/openslides/topics/static/templates/topics/topic-import.html +++ /dev/null @@ -1,134 +0,0 @@ -
    -
    - -

    Import topics

    -
    -
    - -
    -

    Import by copy/paste

    -

    Copy and paste your topic titles in this textbox. Keep each item in a single line.

    - -
    -
    - -
    -
    - -
    - -
    - - {{ importcounter }} / {{ titleItems.length }} {{ "imported" | translate }} - -
    -
    - - -
    - -

    Import by CSV file

    - -

    Select a CSV file

    - - -

    Please note:

    -
      -
    • Required comma or semicolon separated values with these column header names in the first row:
      - - Title, - Text, - Duration, - Comment, - Internal item - -
    • Title is required. All other fields are optional and may be empty. -
    • Only double quotes are accepted as text delimiter (no single quotes). -
    • Download CSV example file -
    - -
    -

    Preview

    -
    - - - - - - -
    - # - Title - Text - Duration - Comment - Internal item
    - - - - - - - - - - - {{ $index + 1 }} - - - - - {{ item.title }} - {{ item.text | limitTo:80 }}{{ item.text.length > 80 ? '...' : '' }} - {{ item.duration | osMinutesToTime }} - {{ item.comment }} - {{ item.is_hidden }} -
    -
    - -
    - - {{ itemsWillNotBeImported }} - topics will be not imported. -
    -
    - - {{ itemsWillBeImported }} - topics will be imported. -
    -
    -
    -
    - - {{ itemsImported.length }} - topics were successfully imported. -
    - -
    - - -
    - -
    -
    diff --git a/openslides/urls.py b/openslides/urls.py index d3da9d09b..2fbd665f1 100644 --- a/openslides/urls.py +++ b/openslides/urls.py @@ -1,6 +1,5 @@ from django.conf import settings from django.conf.urls import include, url -from django.contrib.staticfiles.urls import urlpatterns from django.views.generic import RedirectView from openslides.mediafiles.views import protected_serve @@ -9,9 +8,7 @@ from openslides.utils.rest_api import router from .core import views as core_views -# Urls for /static/ are already in urlpatterns - -urlpatterns += [ +urlpatterns = [ # URLs for /media/ url(r'^%s(?P.*)$' % settings.MEDIA_URL.lstrip('/'), protected_serve, {'document_root': settings.MEDIA_ROOT}), @@ -25,19 +22,7 @@ urlpatterns += [ # Other urls defined by modules and plugins url(r'^apps/', include('openslides.urls_apps')), - # The old angular webclient - # TODO: Change me or at least my comment - url(r'^webclient/(?Psite|projector)/$', - core_views.WebclientJavaScriptView.as_view(), - name='core_webclient_javascript'), - - # View for the projectors are handled by angular. - url(r'^projector/(\d+)/$', core_views.ProjectorView.as_view()), - - # Original view without resolutioncontrol for the projectors are handled by angular. - url(r'^real-projector/(\d+)/$', core_views.RealProjectorView.as_view()), - # Main entry point for all angular pages. # Has to be the last entry in the urls.py - url(r'^.*$', core_views.IndexView.as_view(), name="index"), + url(r'^(?P.*)$', core_views.IndexView.as_view(), name="index"), ] diff --git a/openslides/urls_apps.py b/openslides/urls_apps.py index 8ef1ef6ab..c5b719c24 100644 --- a/openslides/urls_apps.py +++ b/openslides/urls_apps.py @@ -7,7 +7,5 @@ urlpatterns = get_all_plugin_urlpatterns() urlpatterns += [ url(r'^core/', include('openslides.core.urls')), - url(r'^agenda/', include('openslides.agenda.urls')), - url(r'^motions/', include('openslides.motions.urls')), url(r'^users/', include('openslides.users.urls')), ] diff --git a/openslides/users/static/css/users/_site.scss b/openslides/users/static/css/users/_site.scss deleted file mode 100644 index b21f9e5c3..000000000 --- a/openslides/users/static/css/users/_site.scss +++ /dev/null @@ -1,18 +0,0 @@ -.user_details { - fieldset { - margin-bottom: 10px; - } - legend { - margin-bottom: 5px; - } - label { - margin: 10px 0 0 0; - display: block; - font-family: $font-medium; - font-family: $font-medium, sans-serif; - font-weight: normal; - &:after { - content: ":"; - } - } -} diff --git a/openslides/users/static/js/users/base.js b/openslides/users/static/js/users/base.js deleted file mode 100644 index eddde81c3..000000000 --- a/openslides/users/static/js/users/base.js +++ /dev/null @@ -1,178 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.users', []) - -.factory('User', [ - 'DS', - 'Group', - 'jsDataModel', - 'gettext', - 'gettextCatalog', - 'Config', - function(DS, Group, jsDataModel, gettext, gettextCatalog, Config) { - var name = 'users/user'; - return DS.defineResource({ - name: name, - useClass: jsDataModel, - verboseName: gettext('Participants'), - verboseNamePlural: gettext('Participants'), - computed: { - full_name: function () { - return this.get_full_name(); - }, - short_name: function () { - return this.get_short_name(); - }, - }, - methods: { - getResourceName: function () { - return name; - }, - /* - * Returns a short form of the name. - * - * Example: - * - Dr. Max Mustermann - * - Professor Dr. Enders, Christoph - */ - get_short_name: function() { - var title = _.trim(this.title), - firstName = _.trim(this.first_name), - lastName = _.trim(this.last_name), - name = ''; - if (Config.get('users_sort_by') && Config.get('users_sort_by').value == 'last_name') { - if (lastName && firstName) { - name += [lastName, firstName].join(', '); - } else { - name += lastName || firstName; - } - } else { - name += [firstName, lastName].join(' '); - } - if (name.trim() === '') { - name = this.username; - } - if (title !== '') { - name = title + ' ' + name; - } - return name.trim(); - }, - /* - * Returns a long form of the name. - * - * Example: - * - Dr. Max Mustermann (Villingen) - * - Professor Dr. Enders, Christoph (Leipzig) - */ - get_full_name: function() { - var name = this.get_short_name(), - structure_level = _.trim(this.structure_level), - number = _.trim(this.number), - addition = []; - - // addition: add number and structure level - if (structure_level) { - addition.push(structure_level); - } - if (number) { - addition.push( - /// abbreviation for number - gettextCatalog.getString('No.') + ' ' + number - ); - } - if (addition.length > 0) { - name += ' (' + addition.join(' · ') + ')'; - } - return name.trim(); - }, - getPerms: function() { - var allPerms = []; - var allGroups = []; - if (this.groups_id) { - allGroups = this.groups_id.slice(0); - } - if (allGroups.length === 0) { - allGroups.push(1); // add default group - } - _.forEach(allGroups, function(groupId) { - var group = Group.get(groupId); - if (group) { - _.forEach(group.permissions, function(perm) { - allPerms.push(perm); - }); - } - }); - return _.uniq(allPerms); - }, - // link name which is shown in search result - getSearchResultName: function () { - return this.get_full_name(); - }, - // subtitle of search result - getSearchResultSubtitle: function () { - return "Participant"; - }, - }, - relations: { - hasMany: { - 'users/group': { - localField: 'groups', - localKey: 'groups_id', - } - } - } - }); - } -]) - -.factory('Group', [ - 'DS', - function(DS) { - var name = 'users/group'; - var permissions; - return DS.defineResource({ - name: name, - }); - } -]) - -.factory('PersonalNote', [ - 'DS', - function (DS) { - var name = 'users/personal-note'; - return DS.defineResource({ - name: name, - relations: { - hasOne: { - 'users/user': { - localField: 'user', - localKey: 'user_id', - } - } - } - }); - } -]) - -.run([ - 'User', - 'Group', - 'PersonalNote', - function(User, Group) {} -]) - -// Mark strings for translation in JavaScript. -.config([ - 'gettext', - function (gettext) { - // default group names (from users/signals.py) - gettext('Default'); - gettext('Delegates'); - gettext('Staff'); - gettext('Committees'); - } -]); - -}()); diff --git a/openslides/users/static/js/users/csv.js b/openslides/users/static/js/users/csv.js deleted file mode 100644 index d0ac2cb84..000000000 --- a/openslides/users/static/js/users/csv.js +++ /dev/null @@ -1,74 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.users.csv', []) - -.factory('UserCsvExport', [ - '$filter', - 'Group', - 'gettextCatalog', - 'CsvDownload', - function ($filter, Group, gettextCatalog, CsvDownload) { - var makeHeaderline = function () { - var headerline = ['Title', 'Given name', 'Surname', 'Structure level', 'Participant number', 'Groups', - 'Comment', 'Is active', 'Is present', 'Is a committee', 'Initial password', 'Email']; - return _.map(headerline, function (entry) { - return gettextCatalog.getString(entry); - }); - }; - return { - export: function (users) { - var csvRows = [ - makeHeaderline() - ]; - _.forEach(users, function (user) { - var groups = _.map(user.groups_id, function (id) { - return gettextCatalog.getString(Group.get(id).name); - }).join(','); - var row = []; - row.push('"' + user.title + '"'); - row.push('"' + user.first_name + '"'); - row.push('"' + user.last_name + '"'); - row.push('"' + user.structure_level + '"'); - row.push('"' + user.number + '"'); - row.push('"' + groups + '"'); - row.push('"' + user.comment + '"'); - row.push(user.is_active ? '1' : '0'); - row.push(user.is_present ? '1' : '0'); - row.push(user.is_committee ? '1' : '0'); - row.push('"' + user.default_password + '"'); - row.push('"' + user.email + '"'); - csvRows.push(row); - }); - CsvDownload(csvRows, gettextCatalog.getString('participants') + '.csv'); - }, - - downloadExample: function () { - // try to get an example with two groups and one with one group - var groups = $filter('orderBy')(Group.getAll(), 'id'); - var csvGroups = ''; - var csvGroup = ''; - if (groups.length >= 3) { // do not pick groups[0], this is the default group - csvGroups = '"' + gettextCatalog.getString(groups[1].name) + - ', ' + gettextCatalog.getString(groups[2].name) + '"'; - } - if (groups.length >= 2) { - csvGroup = gettextCatalog.getString(groups[groups.length - 1].name); // take last group - } - - var csvRows = [makeHeaderline(), - // example entries - ['Dr.', 'Max', 'Mustermann', 'Berlin','1234567890', csvGroups, 'xyz', '1', '1', '', 'initialPassword', ''], - ['', 'John', 'Doe', 'Washington','75/99/8-2', csvGroup, 'abc', '1', '1', '', '', 'john.doe@email.com'], - ['', 'Fred', 'Bloggs', 'London', '', '', '', '', '', '', '', ''], - ['', '', 'Executive Board', '', '', '', '', '', '', '1', '', ''], - - ]; - CsvDownload(csvRows, gettextCatalog.getString('participants-example') + '.csv'); - } - }; - } -]); - -}()); diff --git a/openslides/users/static/js/users/pdf.js b/openslides/users/static/js/users/pdf.js deleted file mode 100644 index 2d647f681..000000000 --- a/openslides/users/static/js/users/pdf.js +++ /dev/null @@ -1,329 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.users.pdf', ['OpenSlidesApp.core.pdf']) - -.factory('UserListContentProvider', [ - 'gettextCatalog', - 'PDFLayout', - 'Group', - function(gettextCatalog, PDFLayout, Group) { - - var createInstance = function(userList) { - var groups = Group.getAll(); - - //use the Predefined Functions to create the title - var title = PDFLayout.createTitle(gettextCatalog.getString('List of participants')); - - //function to generate the user list - var createUserList = function() { - var userJsonList = []; - - angular.forEach(userList, function (user, counter) { - - //parse for the group names - var userGroups = []; - angular.forEach(user.groups_id, function (id) { - if (id) { - angular.forEach(groups, function(group) { - if (id == group.id) { - userGroups.push(gettextCatalog.getString(group.name)); - } - }); - } - }); - - var userJsonObj = [ - { - text: "" + (counter+1), - style: PDFLayout.flipTableRowStyle(userJsonList.length) - }, - { - text: user.short_name, - style: PDFLayout.flipTableRowStyle(userJsonList.length) - }, - { - text: user.structure_level, - style: PDFLayout.flipTableRowStyle(userJsonList.length) - }, - { - text: userGroups.join(', '), - style: PDFLayout.flipTableRowStyle(userJsonList.length) - } - ]; - userJsonList.push(userJsonObj); - }); - - var userTableBody = [ - [ - { - text: '#', - style: 'tableHeader' - }, - { - text: gettextCatalog.getString('Name'), - style: 'tableHeader' - }, - { - text: gettextCatalog.getString('Structure level'), - style: 'tableHeader' - }, - { - text: gettextCatalog.getString('Groups'), - style: 'tableHeader' - } - ] - ]; - userTableBody = userTableBody.concat((userJsonList)); - - var userTableJsonString = { - table: { - widths: ['auto', '*', 'auto', 'auto'], - headerRows: 1, - body: userTableBody - }, - layout: 'headerLineOnly' - }; - - return userTableJsonString; - }; - - var getContent = function() { - return [ - title, - createUserList() - ]; - }; - - return { - getContent: getContent - }; - }; - - return { - createInstance: createInstance - }; - } -]) - -.factory('UserAccessDataListContentProvider', [ - 'gettextCatalog', - 'PDFLayout', - 'Config', - function(gettextCatalog, PDFLayout, Config) { - - var createInstance = function(userList) { - - var createUserHeadLine = function(user) { - var titleLine = []; - titleLine.push({ - text: user.get_short_name(), - style: 'userDataTitle' - }); - if (user.structure_level) { - titleLine.push({ - text: user.structure_level, - style: 'userDataHeading' - }); - } - return titleLine; - }; - - var createAccessDataContent = function(user) { - // wlan access data - var columnWifi = [ - { - text: gettextCatalog.getString('WLAN access data'), - style: 'userDataHeading' - }, - { - text: gettextCatalog.getString('WLAN name (SSID)') + ':', - style: 'userDataTopic' - }, - { - text: Config.get('users_pdf_wlan_ssid').value || '-', - style: 'userDataValue' - }, - { - text: gettextCatalog.getString('WLAN password') + ':', - style: 'userDataTopic' - }, - { - text: Config.get('users_pdf_wlan_password').value || '-', - style: 'userDataValue' - }, - { - text: gettextCatalog.getString('WLAN encryption') + ':', - style: 'userDataTopic' - }, - { - text: Config.get('users_pdf_wlan_encryption').value || '-', - style: 'userDataValue' - }, - { - text: '\n' - } - ]; - // wifi qr code - if (Config.get('users_pdf_wlan_ssid').value && Config.get('users_pdf_wlan_encryption').value) { - var wifiQrCode = 'WIFI:S:' + Config.get('users_pdf_wlan_ssid').value + - ';T:' + Config.get('users_pdf_wlan_encryption').value + - ';P:' + Config.get('users_pdf_wlan_password').value + ';;'; - columnWifi.push( - { - qr: wifiQrCode, - fit: 120, - margin: [0, 0, 0, 8] - }, - { - text: gettextCatalog.getString('Scan this QR code to connect to WLAN.'), - style: 'small' - } - ); - } - - // openslides access data - var columnOpenSlides = [ - { - text: gettextCatalog.getString('OpenSlides access data'), - style: 'userDataHeading' - }, - { - text: gettextCatalog.getString('Username') + ':', - style: 'userDataTopic' - }, - { - text: user.username, - style: 'userDataValue' - }, - { - text: gettextCatalog.getString('Initial password') + ':', - style: 'userDataTopic' - }, - { - text: user.default_password, - style: 'userDataValue' - }, - { - text: 'URL:', - style: 'userDataTopic' - }, - { - text: Config.get('users_pdf_url').value || '-', - link: Config.get('users_pdf_url').value, - style: 'userDataValue' - }, - { - text: '\n' - } - ]; - // url qr code - if (Config.get('users_pdf_url').value) { - columnOpenSlides.push( - { - qr: Config.get('users_pdf_url').value, - fit: 120, - margin: [0, 0, 0, 8] - }, - { - text: gettextCatalog.getString('Scan this QR code to open URL.'), - style: 'small' - } - ); - } - - var accessDataColumns = { - columns: [ - columnWifi, - columnOpenSlides, - ], - margin: [0, 20] - }; - - return accessDataColumns; - }; - - var createWelcomeText = function() { - return [ - { - text: Config.translate(Config.get('users_pdf_welcometitle').value), - style: 'userDataHeading' - }, - { - text: Config.translate(Config.get('users_pdf_welcometext').value), - style: 'userDataTopic' - } - ]; - }; - - var getContent = function() { - var content = []; - angular.forEach(userList, function (user, index) { - content.push(createUserHeadLine(user)); - content.push(createAccessDataContent(user)); - content.push(createWelcomeText()); - // No pagebreak after the last user - if (index !== userList.length - 1) { - content.push({ - text: '', - pageBreak: 'after' - }); - } - }); - - return [ - content - ]; - }; - - return { - getContent: getContent - }; - }; - - return { - createInstance: createInstance - }; - } -]) - -.factory('UserPdfExport', [ - 'gettextCatalog', - 'UserListContentProvider', - 'UserAccessDataListContentProvider', - 'PdfMakeDocumentProvider', - 'PdfCreate', - 'Messaging', - function (gettextCatalog, UserListContentProvider, UserAccessDataListContentProvider, - PdfMakeDocumentProvider, PdfCreate, Messaging) { - return { - exportUserList: function (users) { - var filename = gettextCatalog.getString('List of participants') + '.pdf'; - var userListContentProvider = UserListContentProvider.createInstance(users); - PdfMakeDocumentProvider.createInstance(userListContentProvider).then( - function (documentProvider) { - PdfCreate.download(documentProvider, filename); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - } - ); - }, - exportUserAccessDataList: function (users) { - var filename = gettextCatalog.getString('List of access data') + '.pdf'; - var userAccessDataListContentProvider = UserAccessDataListContentProvider.createInstance( - users); - // no footer here - PdfMakeDocumentProvider.createInstance(userAccessDataListContentProvider, true).then( - function (documentProvider) { - PdfCreate.download(documentProvider, filename, true); - }, function (error) { - Messaging.addMessage(error.msg, 'error'); - } - ); - } - }; - } -]); - -}()); diff --git a/openslides/users/static/js/users/projector.js b/openslides/users/static/js/users/projector.js deleted file mode 100644 index 49866fa30..000000000 --- a/openslides/users/static/js/users/projector.js +++ /dev/null @@ -1,28 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.users.projector', ['OpenSlidesApp.users']) - -.config([ - 'slidesProvider', - function(slidesProvider) { - slidesProvider.registerSlide('users/user', { - template: 'static/templates/users/slide_user.html', - }); - } -]) - -.controller('SlideUserCtrl', [ - '$scope', - 'User', - function($scope, User) { - // Attention! Each object that is used here has to be dealt on server side. - // Add it to the coresponding get_requirements method of the ProjectorElement - // class. - var id = $scope.element.id; - User.bindOne(id, $scope, 'user'); - } -]); - -}()); diff --git a/openslides/users/static/js/users/site.js b/openslides/users/static/js/users/site.js deleted file mode 100644 index e9a3df953..000000000 --- a/openslides/users/static/js/users/site.js +++ /dev/null @@ -1,1898 +0,0 @@ -(function () { - -'use strict'; - -angular.module('OpenSlidesApp.users.site', [ - 'OpenSlidesApp.users', - 'OpenSlidesApp.core.pdf', - 'OpenSlidesApp.users.pdf', - 'OpenSlidesApp.users.csv', -]) - -.config([ - 'mainMenuProvider', - 'gettext', - function (mainMenuProvider, gettext) { - mainMenuProvider.register({ - 'ui_sref': 'users.user.list', - 'img_class': 'user', - 'title': gettext('Participants'), - 'weight': 500, - 'perm': 'users.can_see_name', - }); - } -]) -.config([ - 'SearchProvider', - 'gettext', - function (SearchProvider, gettext) { - SearchProvider.register({ - 'verboseName': gettext('Participants'), - 'collectionName': 'users/user', - 'urlDetailState': 'users.user.detail', - 'weight': 500, - }); - } -]) - -.config([ - '$stateProvider', - 'gettext', - function($stateProvider, gettext) { - $stateProvider - .state('users', { - url: '/users', - abstract: true, - template: "", - data: { - title: gettext('Participants'), - basePerm: 'users.can_see_name', - }, - }) - .state('users.user', { - abstract: true, - template: "", - }) - .state('users.user.list', {}) - .state('users.user.create', {}) - .state('users.user.detail', { - resolve: { - userId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }] - } - }) - .state('users.user.change-password', { - url: '/change-password/{id}', - controller: 'UserChangePasswordCtrl', - templateUrl: 'static/templates/users/user-change-password.html', - resolve: { - userId: ['$stateParams', function($stateParams) { - return $stateParams.id; - }] - } - }) - .state('users.user.import', { - url: '/import', - controller: 'UserImportCtrl', - }) - .state('users.user.presence', { - url: '/presence', - controller: 'UserPresenceCtrl', - }) - // groups - .state('users.group', { - url: '/groups', - abstract: true, - template: "", - data: { - title: gettext('Groups'), - }, - }) - .state('users.group.list', {}) - .state('login', { - template: null, - url: '/login', - params: { - guest_enabled: false, - msg: null, - }, - onEnter: ['$state', '$stateParams', 'ngDialog', 'LoginDialog', function($state, $stateParams, ngDialog, LoginDialog) { - LoginDialog.id = ngDialog.open({ - template: 'static/templates/core/login-form.html', - controller: 'LoginFormCtrl', - showClose: $stateParams.guest_enabled, - closeByEscape: $stateParams.guest_enabled, - closeByDocument: $stateParams.guest_enabled, - }).id; - }], - data: { - title: 'Login', - }, - }); - } -]) - -.value('LoginDialog', {}) - -/* - * Directive to check for permissions - * - * This is the Code from angular.js ngIf. - * - * TODO: find a way not to copy the code. -*/ -.directive('osPerms', [ - '$animate', - function($animate) { - return { - multiElement: true, - transclude: 'element', - priority: 600, - terminal: true, - restrict: 'A', - $$tlb: true, - link: function($scope, $element, $attr, ctrl, $transclude) { - var block, childScope, previousElements, perms; - if ($attr.osPerms[0] === '!') { - perms = _.trimStart($attr.osPerms, '!'); - } else { - perms = $attr.osPerms; - } - $scope.$watch( - function (scope) { - return scope.operator && scope.operator.hasPerms(perms); - }, - function (value) { - if ($attr.osPerms[0] === '!') { - value = !value; - } - if (value) { - if (!childScope) { - $transclude(function(clone, newScope) { - childScope = newScope; - clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' '); - // Note: We only need the first/last node of the cloned nodes. - // However, we need to keep the reference to the jqlite wrapper as it might be changed later - // by a directive with templateUrl when its template arrives. - block = { - clone: clone - }; - $animate.enter(clone, $element.parent(), $element); - }); - } - } else { - if (previousElements) { - previousElements.remove(); - previousElements = null; - } - if (childScope) { - childScope.$destroy(); - childScope = null; - } - if (block) { - previousElements = getBlockNodes(block.clone); - $animate.leave(previousElements).then(function() { - previousElements = null; - }); - block = null; - } - } - } - ); - } - }; - } -]) - -.factory('PasswordGenerator', [ - function () { - return { - generate: function (length) { - if (!length) { - length = 8; - } - var chars = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789', - pw = ''; - for (var i = 0; i < length; ++i) { - pw += chars.charAt(Math.floor(Math.random() * chars.length)); - } - return pw; - } - }; - } -]) - -.factory('PersonalNoteManager', [ - 'PersonalNote', - 'operator', - function (PersonalNote, operator) { - var _getPersonalNoteObject = function (resourceName) { - var personalNote = _.find(PersonalNote.getAll(), function (pn) { - return pn.user_id === operator.user.id; - }); - if (!personalNote) { - personalNote = { - notes: {}, - }; - } - if (!personalNote.notes[resourceName]) { - personalNote.notes[resourceName] = {}; - } - return personalNote; - }; - var get = function (resourceName, id) { - return _getPersonalNoteObject(resourceName).notes[resourceName][id]; - }; - var save = function (resourceName, id, note) { - var personalNote = _getPersonalNoteObject(resourceName); - personalNote.notes[resourceName][id] = note; - if (personalNote.id) { - return PersonalNote.save(personalNote); - } else { - return PersonalNote.create(personalNote); - } - }; - return { - getNote: function (obj) { - if (typeof obj.getResourceName === 'undefined') { - throw 'The Object has to be a js data model!'; - } - return get(obj.getResourceName(), obj.id); - }, - saveNote: function (obj, note) { - if (typeof obj.getResourceName === 'undefined') { - throw 'The Object has to be a js data model!'; - } - return save(obj.getResourceName(), obj.id, note); - }, - }; - } -]) - -// Service for generic assignment form (create and update) -.factory('UserForm', [ - '$http', - 'gettextCatalog', - 'Editor', - 'Group', - 'Mediafile', - 'PasswordGenerator', - function ($http, gettextCatalog, Editor, Group, Mediafile, PasswordGenerator) { - return { - // ngDialog for user form - getDialog: function (user) { - return { - template: 'static/templates/users/user-form.html', - controller: (user) ? 'UserUpdateCtrl' : 'UserCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - userId: function () {return user ? user.id : void 0;}, - } - }; - }, - // angular-formly fields for user form - getFormFields: function (hideOnCreateForm) { - var images = Mediafile.getAllImages(); - return [ - { - className: "row", - fieldGroup: [ - { - key: 'title', - type: 'input', - className: "col-xs-2 no-padding-left", - templateOptions: { - label: gettextCatalog.getString('Title') - } - }, - { - key: 'first_name', - type: 'input', - className: "col-xs-5 no-padding", - templateOptions: { - label: gettextCatalog.getString('Given name') - } - }, - { - key: 'last_name', - type: 'input', - className: "col-xs-5 no-padding-right", - templateOptions: { - label: gettextCatalog.getString('Surname') - } - } - ] - }, - { - key: 'email', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Email') - }, - }, - { - className: "row", - fieldGroup: [ - { - key: 'structure_level', - type: 'input', - className: "col-xs-9 no-padding-left", - templateOptions: { - label: gettextCatalog.getString('Structure level'), - } - }, - { key: 'number', - type: 'input', - className: "col-xs-3 no-padding-left no-padding-right", - templateOptions: { - label:gettextCatalog.getString('Participant number') - } - } - ] - }, - { - key: 'username', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Username') - }, - hide: hideOnCreateForm - }, - { - key: 'groups_id', - type: 'select-multiple', - templateOptions: { - label: gettextCatalog.getString('Groups'), - options: Group.filter({where: {id: {'>': 1}}}), - ngOptions: "option.id as option.name | translate for option in to.options | orderBy: 'id'", - placeholder: gettextCatalog.getString('Select or search a group ...') - } - }, - { - key: 'default_password', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Initial password'), - description: gettextCatalog.getString('Initial password can not be changed.'), - addonRight: { - text: gettextCatalog.getString('Generate'), - class: 'fa fa-magic', - onClick:function (options, scope) { - scope.$parent.model.default_password = PasswordGenerator.generate(); - } - } - }, - hide: !hideOnCreateForm - }, - { - key: 'comment', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Comment'), - description: gettextCatalog.getString('Only for internal notes.') - } - }, - { - key: 'more', - type: 'checkbox', - templateOptions: { - label: gettextCatalog.getString('Show extended fields') - } - }, - { - template: '
    ', - hideExpression: '!model.more' - }, - { - key: 'is_present', - type: 'checkbox', - templateOptions: { - label: gettextCatalog.getString('Is present'), - description: gettextCatalog.getString('Designates whether this user is in the room or not.') - }, - defaultValue: true, - hideExpression: '!model.more' - }, - { - key: 'is_active', - type: 'checkbox', - templateOptions: { - label: gettextCatalog.getString('Is active'), - description: gettextCatalog.getString( - 'Designates whether this user should be treated as ' + - 'active. Unselect this instead of deleting the account.') - }, - defaultValue: true, - hideExpression: '!model.more' - }, - { - key: 'is_committee', - type: 'checkbox', - templateOptions: { - label: gettextCatalog.getString('Is a committee'), - description: gettextCatalog.getString( - 'Designates whether this user should be treated as a committee.') - }, - defaultValue: false, - hideExpression: '!model.more' - }, - { - key: 'about_me', - type: 'editor', - templateOptions: { - label: gettextCatalog.getString('About me'), - }, - data: { - ckeditorOptions: Editor.getOptions(images) - }, - hideExpression: '!model.more' - } - ]; - } - }; - } -]) - -.factory('UserProfileForm', [ - 'gettextCatalog', - 'Editor', - 'Mediafile', - function (gettextCatalog, Editor, Mediafile) { - return { - // ngDialog for user form - getDialog: function () { - return { - template: 'static/templates/users/profile-password-form.html', - controller: 'UserProfileCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - }; - }, - // angular-formly fields for user form - getFormFields: function (hideOnCreateForm) { - var images = Mediafile.getAllImages(); - return [ - { - key: 'username', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Username'), - required: true - }, - }, - { - key: 'email', - type: 'input', - templateOptions: { - label: gettextCatalog.getString('Email') - }, - }, - { - key: 'about_me', - type: 'editor', - templateOptions: { - label: gettextCatalog.getString('About me'), - }, - data: { - ckeditorOptions: Editor.getOptions(images) - }, - } - ]; - } - }; - } -]) - -.factory('UserPasswordForm', [ - 'gettextCatalog', - function (gettextCatalog) { - return { - // ngDialog for user form - getDialog: function () { - return { - template: 'static/templates/users/profile-password-form.html', - controller: 'UserPasswordCtrl', - className: 'ngdialog-theme-default', - closeByEscape: false, - closeByDocument: false, - }; - }, - // angular-formly fields for user form - getFormFields: function (hideOnCreateForm) { - return [ - { - key: 'oldPassword', - type: 'password', - templateOptions: { - label: gettextCatalog.getString('Old password'), - required: true - }, - }, - { - key: 'newPassword', - type: 'password', - templateOptions: { - label: gettextCatalog.getString('New password'), - required: true - }, - }, - { - key: 'newPassword2', - type: 'password', - templateOptions: { - label: gettextCatalog.getString('Confirm new password'), - required: true - }, - }, - ]; - } - }; - } -]) - -.controller('UserListCtrl', [ - '$scope', - '$http', - 'ngDialog', - 'UserForm', - 'User', - 'Group', - 'PasswordGenerator', - 'Projector', - 'ProjectionDefault', - 'Config', - 'UserCsvExport', - 'osTableFilter', - 'osTableSort', - 'osTablePagination', - 'gettext', - 'UserPdfExport', - 'InvitationEmails', - 'ErrorMessage', - function($scope, $http, ngDialog, UserForm, User, Group, PasswordGenerator, - Projector, ProjectionDefault, Config, UserCsvExport, osTableFilter, osTableSort, - osTablePagination, gettext, UserPdfExport, InvitationEmails, ErrorMessage) { - $scope.$watch(function () { - return User.lastModified(); - }, function () { - $scope.users = _.orderBy(User.getAll(), ['first_name']); - _.forEach($scope.users, function (user) { - user.has_last_email_send = !!user.last_email_send; - }); - if ($scope.updateUsers) { - $scope.updateUsers(); - } - }); - Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups'); - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - var projectiondefault = ProjectionDefault.filter({name: 'users'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId = projectiondefault.projector_id; - } - }); - $scope.alert = {}; - - // Filtering - $scope.filter = osTableFilter.createInstance('UserTableFilter'); - - if (!$scope.filter.existsStorageEntry()) { - $scope.filter.multiselectFilters = { - group: [], - }; - $scope.filter.booleanFilters = { - isPresent: { - value: undefined, - displayName: gettext('Present'), - choiceYes: gettext('Is present'), - choiceNo: gettext('Is not present'), - needExtraPermission: true, - }, - isActive: { - value: undefined, - displayName: gettext('Active'), - choiceYes: gettext('Is active'), - choiceNo: gettext('Is not active'), - needExtraPermission: true, - }, - isCommittee: { - value: undefined, - displayName: gettext('Committee'), - choiceYes: gettext('Is a committee'), - choiceNo: gettext('Is not a committee'), - }, - hasLastEmailSend: { - value: undefined, - displayName: gettext('Last email send'), - choiceYes: gettext('Got an email'), - choiceNo: gettext("Didn't get an email"), - }, - }; - } - $scope.filter.propertyList = ['first_name', 'last_name', 'username', 'title', - 'number', 'comment', 'structure_level']; - $scope.filter.propertyDict = { - 'groups_id' : function (group_id) { - return Group.get(group_id).name; - }, - }; - $scope.getItemId = { - group: function (user) {return user.groups_id;}, - }; - // Sorting - $scope.sort = osTableSort.createInstance('UserTableSort'); - if (!$scope.sort.column) { - $scope.sort.column = $scope.config('users_sort_by'); - } - $scope.sortOptions = [ - {name: 'first_name', - display_name: gettext('Given name')}, - {name: 'last_name', - display_name: gettext('Surname')}, - {name: 'is_present', - display_name: gettext('Present')}, - {name: 'is_active', - display_name: gettext('Active')}, - {name: 'is_committee', - display_name: gettext('Committee')}, - {name: 'number', - display_name: gettext('Number')}, - {name: 'structure_level', - display_name: gettext('Structure level')}, - {name: 'comment', - display_name: gettext('Comment')}, - {name: 'last_email_send', - display_name: gettext('Last email send')}, - ]; - - // pagination - $scope.pagination = osTablePagination.createInstance('UserTablePagination'); - - // Toggle group from user - $scope.toggleGroup = function (user, group) { - if (_.indexOf(user.groups_id, group.id) > -1) { - user.groups_id = _.filter(user.groups_id, function (group_id) { - return group_id != group.id; - }); - } else { - user.groups_id.push(group.id); - } - $scope.save(user); - }; - // open new/edit dialog - $scope.openDialog = function (user) { - ngDialog.open(UserForm.getDialog(user)); - }; - // save changed user - $scope.save = function (user) { - User.save(user).then( - function(success) { - //user.quickEdit = false; - $scope.alert.show = false; - }, - function(error){ - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - // delete single user - $scope.delete = function (user) { - User.destroy(user.id); - }; - // *** select mode functions *** - $scope.isSelectMode = false; - // check all checkboxes - $scope.checkAll = function () { - $scope.selectedAll = !$scope.selectedAll; - _.forEach($scope.usersFiltered, function (user) { - user.selected = $scope.selectedAll; - }); - }; - // uncheck all checkboxes if isSelectMode is closed - $scope.uncheckAll = function () { - if (!$scope.isSelectMode) { - $scope.selectedAll = false; - angular.forEach($scope.users, function (user) { - user.selected = false; - }); - } - }; - var selectModeAction = function (predicate) { - angular.forEach($scope.usersFiltered, function (user) { - if (user.selected) { - predicate(user); - } - }); - $scope.isSelectMode = false; - $scope.uncheckAll(); - }; - // delete all selected users - $scope.deleteMultiple = function () { - selectModeAction(function (user) { - $scope.delete(user); - }); - }; - // add group for selected users - $scope.addGroupMultiple = function (group) { - if (group) { - selectModeAction(function (user) { - user.groups_id.push(group); - User.save(user); - }); - } - }; - // remove group for selected users - $scope.removeGroupMultiple = function (group) { - if (group) { - selectModeAction(function (user) { - var groupIndex = _.indexOf(user.groups_id, parseInt(group)); - if (groupIndex > -1) { - user.groups_id.splice(groupIndex, 1); - User.save(user); - } - }); - } - }; - // generate new passwords - $scope.generateNewPasswordsMultiple = function () { - selectModeAction(function (user) { - var newPassword = PasswordGenerator.generate(); - user.default_password = newPassword; - User.save(user); - $http.post( - '/rest/users/user/' + user.id + '/reset_password/', - {'password': newPassword} - ); - }); - }; - // set boolean properties (is_active, is_present, is_committee) - $scope.setBoolPropertyMultiple = function (property, value) { - selectModeAction(function (user) { - user[property] = value; - User.save(user); - }); - }; - // Send invitation emails - $scope.sendInvitationEmails = function () { - InvitationEmails.send($scope.usersFiltered).then(function (success) { - $scope.alert = success; - $scope.isSelectMode = false; - $scope.uncheckAll(); - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - $scope.isSelectMode = false; - $scope.uncheckAll(); - }); - }; - - // Export as PDF - $scope.pdfExportUserList = function () { - UserPdfExport.exportUserList($scope.usersFiltered); - }; - $scope.pdfExportUserAccessDataList = function () { - UserPdfExport.exportUserAccessDataList($scope.usersFiltered); - }; - // Export as a csv file - $scope.csvExport = function () { - UserCsvExport.export($scope.usersFiltered); - }; - } -]) - -.factory('InvitationEmails', [ - '$http', - 'User', - 'Config', - 'gettextCatalog', - function ($http, User, Config, gettextCatalog) { - return { - // Returns the request promise. If it was successfull, a nice message for - // an alert is generated and the alert-object is returned. - send: function (users) { - var user_ids = _ - .chain(users) - .filter(function (user) { - return user.selected; - }) - .map(function (user) { - return user.id; - }) - .value(); - var subject = gettextCatalog.getString(Config.get('users_email_subject').value); - var message = gettextCatalog.getString(Config.get('users_email_body').value); - - return $http.post('/rest/users/user/mass_invite_email/', { - user_ids: user_ids, - subject: subject, - message: message, - }).then(function (success) { - var numEmails = success.data.count; - var noEmailIds = success.data.no_email_ids; - var type = 'success', msg; - if (numEmails === 0) { - type = 'danger'; - msg = gettextCatalog.getString('No emails were send.'); - } else if (numEmails === 1) { - msg = gettextCatalog.getString('One email was send sucessfully.'); - } else { - msg = gettextCatalog.getString('%num% emails were send sucessfully.').replace('%num%', numEmails); - } - - if (noEmailIds.length) { - type = 'warning'; - msg += ' '; - - if (noEmailIds.length === 1) { - msg += gettextCatalog.getString('The user %user% has no email, so the invitation email could not be send.'); - } else { - msg += gettextCatalog.getString('The users %user% have no email, so the invitation emails could not be send.'); - } - - // This one builds a username string like "user1, user2 and user3" with the full names. - var lastUsername, userString = _ - .chain(noEmailIds) - .map(function (id) { - var user = User.get(id); - return user ? user.get_full_name() : ''; - }) - .filter(function (username) { - return username; - }) - .tap(function (names) { - if (names.length !== 1) { - lastUsername = names.pop(); - } - }) - .join(', ') - .thru(function (names) { - return lastUsername ? names + ' ' + gettextCatalog.getString('and') + ' ' + lastUsername : names; - }) - .value(); - msg = msg.replace('%user%', userString); - } - - return { - msg: msg, - type: type, - show: true, - }; - }); - }, - }; - } -]) - -.controller('UserDetailCtrl', [ - '$scope', - 'ngDialog', - 'UserForm', - 'User', - 'userId', - 'Group', - 'Projector', - 'ProjectionDefault', - 'gettextCatalog', - 'WebpageTitle', - function($scope, ngDialog, UserForm, User, userId, Group, Projector, ProjectionDefault, gettextCatalog, - WebpageTitle) { - Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups'); - $scope.$watch(function () { - return User.lastModified(userId); - }, function () { - $scope.user = User.get(userId); - WebpageTitle.updateTitle(gettextCatalog.getString('Participant') + ' ' + $scope.user.get_short_name()); - }); - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - var projectiondefault = ProjectionDefault.filter({name: 'users'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId = projectiondefault.projector_id; - } - }); - - // open edit dialog - $scope.openDialog = function (user) { - ngDialog.open(UserForm.getDialog(user)); - }; - } -]) - -.controller('UserCreateCtrl', [ - '$scope', - '$state', - 'User', - 'UserForm', - 'ErrorMessage', - function($scope, $state, User, UserForm, ErrorMessage) { - $scope.alert = {}; - // get all form fields - $scope.formFields = UserForm.getFormFields(true); - - // save user - $scope.save = function (user) { - if (!user.groups_id) { - user.groups_id = []; - } - User.create(user).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('UserUpdateCtrl', [ - '$scope', - '$state', - 'User', - 'UserForm', - 'userId', - 'ErrorMessage', - function($scope, $state, User, UserForm, userId, ErrorMessage) { - $scope.alert = {}; - // set initial values for form model by create deep copy of user object - // so list/detail view is not updated while editing - $scope.model = angular.copy(User.get(userId)); - - // get all form fields - $scope.formFields = UserForm.getFormFields(); - - // save user - $scope.save = function (user) { - if (!user.groups_id) { - user.groups_id = []; - } - // inject the changed user (copy) object back into DS store - User.inject(user); - // save change user object on server - User.save(user).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - // save error: revert all changes by restore - // (refresh) original user object from server - User.refresh(user); - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('UserProfileCtrl', [ - '$scope', - 'Editor', - 'User', - 'operator', - 'UserProfileForm', - 'gettext', - 'ErrorMessage', - function($scope, Editor, User, operator, UserProfileForm, gettext, ErrorMessage) { - $scope.model = angular.copy(operator.user); - $scope.title = gettext('Edit profile'); - $scope.formFields = UserProfileForm.getFormFields(); - $scope.save = function (user) { - User.inject(user); - User.save(user).then( - function(success) { - $scope.closeThisDialog(); - }, - function(error) { - // save error: revert all changes by restore - // (refresh) original user object from server - User.refresh(user); - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.controller('UserChangePasswordCtrl', [ - '$scope', - '$state', - '$http', - 'User', - 'userId', - 'gettextCatalog', - 'PasswordGenerator', - 'ErrorMessage', - function($scope, $state, $http, User, userId, gettextCatalog, PasswordGenerator, ErrorMessage) { - User.bindOne(userId, $scope, 'user'); - $scope.alert = {}; - $scope.generatePassword = function () { - $scope.new_password = PasswordGenerator.generate(); - }; - $scope.save = function (user) { - if ($scope.new_password !== '') { - $http.post( - '/rest/users/user/' + user.id + '/reset_password/', - {'password': $scope.new_password} - ).then( - function (success) { - $scope.alert = {type: 'success', msg: success.data.detail, show: true}; - $scope.new_password = ''; - }, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - } - }; - } -]) - -.directive("showPassword", function() { - return function linkFn(scope, elem, attrs) { - scope.$watch(attrs.showPassword, function(value) { - if (value) { - elem.attr("type", "text"); - } else { - elem.attr("type", "password"); - } - }); - }; -}) - -.controller('UserPasswordCtrl', [ - '$scope', - '$state', - '$http', - 'gettext', - 'UserPasswordForm', - 'ErrorMessage', - function($scope, $state, $http, gettext, UserPasswordForm, ErrorMessage) { - $scope.title = 'Change password'; - $scope.alert = {}; - $scope.model = {}; - $scope.formFields = UserPasswordForm.getFormFields(); - $scope.save = function (data) { - if (data.newPassword != data.newPassword2) { - data.newPassword = data.newPassword2 = ''; - $scope.alert = { - type: 'danger', - msg: gettext('Password confirmation does not match.'), - show: true, - }; - } else { - $http.post( - '/users/setpassword/', - {'old_password': data.oldPassword, 'new_password': data.newPassword} - ).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - // Error, e. g. wrong old password. - $scope.model = {}; - $scope.alert = ErrorMessage.forAlert(error); - } - ); - } - }; - } -]) - -.controller('UserPresenceCtrl', [ - '$scope', - 'User', - 'gettextCatalog', - 'ErrorMessage', - function ($scope, User, gettextCatalog, ErrorMessage) { - $scope.alert = {}; - $('#userNumber').focus(); - - $scope.changeState = function () { - if (!$scope.number) { - return; - } - var enteredNumber = $scope.number.trim(); - var user = _.find(User.getAll(), function (user) { - return user.number === enteredNumber; - }); - if (user) { - user.is_present = !user.is_present; - User.save(user).then(function (success) { - var messageText = user.full_name + ' ' + gettextCatalog.getString('is now') + ' '; - messageText += gettextCatalog.getString(user.is_present ? 'present' : 'not present') + '.'; - $scope.alert = { - msg: messageText, - show: true, - type: 'success', - }; - $scope.number = ''; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - } else { - $scope.alert = { - msg: gettextCatalog.getString('Cannot find the participant with the participant number') + ' "' + enteredNumber + '".', - show: true, - type: 'danger', - }; - } - $('#userNumber').focus(); - }; - } -]) - -.controller('UserImportCtrl', [ - '$scope', - '$http', - '$q', - 'gettext', - 'gettextCatalog', - 'User', - 'Group', - 'UserCsvExport', - 'osTablePagination', - 'ErrorMessage', - function($scope, $http, $q, gettext, gettextCatalog, User, Group, UserCsvExport, - osTablePagination, ErrorMessage) { - // import from textarea - $scope.importByLine = function () { - var usernames = $scope.userlist[0].split("\n"); - var users = _.map(usernames, function (name) { - // Split each full name in first and last name. - // The last word is set as last name, rest is the first name(s). - // (e.g.: "Max Martin Mustermann" -> last_name = "Mustermann") - var names = name.split(" "); - var last_name = names.slice(-1)[0]; - var first_name = names.slice(0, -1).join(" "); - return { - first_name: first_name, - last_name: last_name, - groups_id: [], - }; - }); - $http.post('/rest/users/user/mass_import/', { - users: users - }).then(function (success) { - $scope.alert = { - show: true, - type: 'success', - msg: success.data.detail, - }; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }; - - // pagination - $scope.pagination = osTablePagination.createInstance('UserImportTablePagination', 100); - - // Duplicates - $scope.duplicateActions = [ - gettext('keep original'), - gettext('override new'), - gettext('create duplicate') - ]; - - // *** csv import *** - $scope.csvConfig = { - accept: '.csv, .txt', - encodingOptions: ['UTF-8', 'ISO-8859-1'], - parseConfig: { - skipEmptyLines: true, - }, - }; - - var FIELDS = ['title', 'first_name', 'last_name', 'structure_level', 'number', - 'groups', 'comment', 'is_active', 'is_present', 'is_committee', 'default_password', 'email']; - $scope.users = []; - $scope.onCsvChange = function (csv) { - $scope.csvImporting = false; - // All user objects are already loaded via the resolve statement from ui-router. - var users = User.getAll(); - $scope.users = []; - - var csvUsers = []; - _.forEach(csv.data, function (row) { - if (row.length >= 2) { - var filledRow = _.zipObject(FIELDS, row); - csvUsers.push(filledRow); - } - }); - $scope.duplicates = 0; - _.forEach(csvUsers, function (user, index) { - user.importTrackId = index; - user.selected = true; - if (!user.first_name && !user.last_name) { - user.importerror = true; - user.name_error = gettext('Error: Given name or surname is required.'); - } - // number - if (!user.number) { - user.number = ""; - } - // groups - user.groups_id = []; // will be overwritten if there are groups - if (user.groups) { - user.groups = user.groups.split(','); - user.groups = _.map(user.groups, function (group) { - return _.trim(group); // remove whitespaces on start or end - }); - - // All group objects are already loaded via the resolve statement from ui-router. - var allGroups = Group.getAll(); - // in allGroupsNames ar all original group names and translated names if a - // translation exists (e.g. for default group Delegates) - var allGroupsNames = []; - _.forEach(allGroups, function (group) { - var groupTranslation = gettextCatalog.getString(group.name); - if (group.name !== groupTranslation) { - allGroupsNames.push(groupTranslation); - } - allGroupsNames.push(group.name); - }); - user.groupsToCreate = _.difference(user.groups, allGroupsNames); - - // for template: - user.groupsNotToCreate = _.difference(user.groups, user.groupsToCreate); - } else { - user.groups = []; - } - user.is_active = (user.is_active !== undefined && user.is_active === '1'); - user.is_present = (user.is_present !== undefined && user.is_present === '1'); - user.is_committee = (user.is_committee !== undefined && user.is_committee === '1'); - - // Check for duplicates - user.duplicate = false; - users.forEach(function(user_) { - user_.fullname = [ - user_.title, - user_.first_name, - user_.last_name, - user_.structure_level].join(' ').trim(); - user.fullname = [ - user.title, - user.first_name, - user.last_name, - user.structure_level].join(' ').trim(); - if (user_.fullname === user.fullname) { - if (user.duplicate) { - // there are multiple duplicates! - user.duplicate_info += '\n' + gettextCatalog.getString('There are more than one duplicates of this user!'); - } else { - user.duplicate = true; - user.duplicateAction = $scope.duplicateActions[1]; - user.duplicate_info = ''; - if (user_.title) - user.duplicate_info += user_.title + ' '; - if (user_.first_name) - user.duplicate_info += user_.first_name; - if (user_.first_name && user_.last_name) - user.duplicate_info += ' '; - if (user_.last_name) - user.duplicate_info += user_.last_name; - user.duplicate_info += ' ('; - if (user_.number) - user.duplicate_info += gettextCatalog.getString('Number') + ': ' + user_.number + ', '; - if (user_.structure_level) - user.duplicate_info += gettextCatalog.getString('Structure level') + ': ' + user_.structure_level + ', '; - user.duplicate_info += gettextCatalog.getString('Username') + ': ' + user_.username + ') '+ - gettextCatalog.getString('already exists.'); - - $scope.duplicates++; - } - } - }); - $scope.users.push(user); - }); - $scope.calcStats(); - }; - - // Stats - $scope.calcStats = function() { - // not imported: if importerror or duplicate->keep original - $scope.usersWillNotBeImported = 0; - // imported: all others - $scope.usersWillBeImported = 0; - - $scope.users.forEach(function(user) { - if (!user.selected || user.importerror || (user.duplicate && user.duplicateAction == $scope.duplicateActions[0])) { - $scope.usersWillNotBeImported++; - } else { - $scope.usersWillBeImported++; - } - }); - }; - - $scope.setGlobalAction = function (action) { - $scope.users.forEach(function (user) { - if (user.duplicate) - user.duplicateAction = action; - }); - $scope.calcStats(); - }; - - // import from csv file - $scope.import = function () { - $scope.csvImporting = true; - - // collect all needed groups and create non existing groups - var groupsToCreate = []; - _.forEach($scope.users, function (user) { - if (user.selected && !user.importerror && user.groups.length) { - _.forEach(user.groupsToCreate, function (group) { // Just append groups, that are not listed yet. - if (_.indexOf(groupsToCreate, group) == -1) { - groupsToCreate.push(group); - } - }); - } - }); - var createPromises = []; - $scope.groupsCreated = 0; - _.forEach(groupsToCreate, function (groupname) { - var group = { - name: groupname, - permissions: [] - }; - createPromises.push(Group.create(group).then( function (success) { - $scope.groupsCreated++; - })); - }); - - $q.all(createPromises).then(function () { - // reload allGroups, now all new groups are created - var allGroups = Group.getAll(); - var existingUsers = User.getAll(); - - // For option 'delete existing user' on duplicates - var deletePromises = []; - // Array of users for mass import - var usersToBeImported = []; - - _.forEach($scope.users, function (user) { - if (user.selected && !user.importerror) { - // Assign all groups - _.forEach(user.groups, function(csvGroup) { - allGroups.forEach(function (allGroup) { - // check with and without translation - if (csvGroup === allGroup.name || - csvGroup === gettextCatalog.getString(allGroup.name)) { - user.groups_id.push(allGroup.id); - } - }); - }); - - // Do nothing on duplicateAction==duplicateActions[0] (keep original) - if (user.duplicate && (user.duplicateAction == $scope.duplicateActions[1])) { - // delete existing user - existingUsers.forEach(function(user_) { - user_.fullname = [ - user_.title, - user_.first_name, - user_.last_name, - user_.structure_level].join(' ').trim(); - user.fullname = [ - user.title, - user.first_name, - user.last_name, - user.structure_level].join(' ').trim(); - if (user_.fullname === user.fullname) { - deletePromises.push(User.destroy(user_.id)); - } - }); - usersToBeImported.push(user); - } else if (!user.duplicate || - (user.duplicateAction == $scope.duplicateActions[2])) { - // create user - usersToBeImported.push(user); - } - } - }); - $q.all(deletePromises).then(function () { - $http.post('/rest/users/user/mass_import/', { - users: usersToBeImported - }).then(function (success) { - _.forEach(success.data.importedTrackIds, function (trackId) { - _.find($scope.users, function (user) { - return user.importTrackId === trackId; - }).imported = true; - }); - $scope.csvimported = true; - }, function (error) { - $scope.alert = ErrorMessage.forAlert(error); - }); - }); - }); - }; - $scope.clear = function () { - $scope.users = null; - }; - $scope.excludeImportedUsers = function () { - $scope.users = _.filter($scope.users, function (user) { - return !user.imported; - }); - $scope.csvImporting = false; - $scope.calcStats(); - }; - $scope.someImportedUsers = function () { - return _.some($scope.users, function (user) { - return user.imported; - }); - }; - // download CSV example file - $scope.downloadCSVExample = function () { - UserCsvExport.downloadExample(); - }; - } -]) - -.controller('GroupListCtrl', [ - '$scope', - '$http', - '$filter', - 'operator', - 'Group', - 'permissions', - 'gettext', - 'Agenda', - 'Assignment', - 'Mediafile', - 'Motion', - 'User', - 'ngDialog', - 'OpenSlidesPlugins', - function($scope, $http, $filter, operator, Group, permissions, gettext, Agenda, - Assignment, Mediafile, Motion, User, ngDialog, OpenSlidesPlugins) { - $scope.permissions = permissions; - - $scope.$watch(function() { - return Group.lastModified(); - }, function() { - $scope.groups = $filter('orderBy')(Group.getAll(), 'id'); - - // find all groups with the 2 dangerous permissions - var groups_danger = []; - $scope.groups.forEach(function (group) { - if ((_.indexOf(group.permissions, 'users.can_see_name') > -1) && - (_.indexOf(group.permissions, 'users.can_manage') > -1)){ - if (operator.isInGroup(group)){ - groups_danger.push(group); - } - } - }); - // if there is only one dangerous group, block it. - $scope.group_danger = groups_danger.length == 1 ? groups_danger[0] : null; - }); - - // Dict to map plugin name -> display_name - var pluginTranslation = {}; - _.forEach(OpenSlidesPlugins.getAll(), function (plugin) { - pluginTranslation[plugin.name] = plugin.display_name; - }); - $scope.apps = []; - // Create the main clustering with appname->permissions - angular.forEach(permissions, function(perm) { - var permissionApp = perm.value.split('.')[0]; // get appname - - // To insert perm in the right spot in $scope.apps - var insert = function (id, perm, verboseName) { - if (!$scope.apps[id]) { - $scope.apps[id] = { - app_name: verboseName, - app_visible: true, - permissions: [] - }; - } - $scope.apps[id].permissions.push(perm); - }; - - switch(permissionApp) { - case 'core': // id 0 (projector) and id 6 (general) - if (perm.value.indexOf('projector') > -1) { - insert(0, perm, gettext('Projector')); - } else { - insert(6, perm, gettext('General')); - } - break; - case 'agenda': // id 1 - insert(1, perm, Agenda.verboseName); - break; - case 'motions': // id 2 - insert(2, perm, Motion.verboseNamePlural); - break; - case 'assignments': // id 3 - insert(3, perm, Assignment.verboseNamePlural); - break; - case 'mediafiles': // id 4 - insert(4, perm, Mediafile.verboseNamePlural); - break; - case 'users': // id 5 - insert(5, perm, User.verboseNamePlural); - break; - default: // plugins: id>5 - var display_name = pluginTranslation[permissionApp] || permissionApp.charAt(0).toUpperCase() + - permissionApp.slice(1); - // does the app exists? - var result = -1; - angular.forEach($scope.apps, function (app, index) { - if (app.app_name === display_name) - result = index; - }); - var id = result == -1 ? $scope.apps.length : result; - insert(id, perm, display_name); - break; - } - }); - - // sort each app: first all permission with 'see', then 'manage', then the rest - // save the permissions in different lists an concat them in the right order together - // Special Users: the two "see"-permissions are normally swapped. To create the right - // order, we could simply reverse the whole permissions. - angular.forEach($scope.apps, function (app, index) { - if(index == 5) { // users - app.permissions.reverse(); - } else { // rest - var see = []; - var manage = []; - var others = []; - angular.forEach(app.permissions, function (perm) { - if (perm.value.indexOf('see') > -1) { - see.push(perm); - } else if (perm.value.indexOf('manage') > -1) { - manage.push(perm); - } else { - others.push(perm); - } - }); - app.permissions = see.concat(manage.concat(others)); - } - }); - - // check if the given group has the given permission - $scope.hasPerm = function (group, permission) { - return _.indexOf(group.permissions, permission.value) > -1; - }; - - // The current user is not allowed to lock himself out of the configuration: - // - if the permission is 'users.can_manage' or 'users.can_see' - // - if the user is in only one group with these permissions (group_danger is set) - $scope.danger = function (group, permission){ - if ($scope.group_danger){ - if (permission.value == 'users.can_see_name' || - permission.value == 'users.can_manage'){ - return $scope.group_danger == group; - } - } - return false; - }; - - // delete selected group - $scope.delete = function (group) { - Group.destroy(group.id); - }; - - // save changed permission - $scope.changePermission = function (group, perm) { - if (!$scope.danger(group, perm)) { - if (!$scope.hasPerm(group, perm)) { // activate perm - group.permissions.push(perm.value); - } else { - // delete perm in group.permissions - group.permissions = _.filter(group.permissions, function(value) { - return value != perm.value; // remove perm - }); - } - Group.save(group); - } - }; - - $scope.openDialog = function (group) { - ngDialog.open({ - template: 'static/templates/users/group-edit.html', - controller: group ? 'GroupRenameCtrl' : 'GroupCreateCtrl', - className: 'ngdialog-theme-default wide-form', - closeByEscape: false, - closeByDocument: false, - resolve: { - group: function () {return group;}, - } - }); - }; - } -]) - -.controller('GroupRenameCtrl', [ - '$scope', - 'Group', - 'group', - 'gettextCatalog', - 'ErrorMessage', - function($scope, Group, group, gettextCatalog, ErrorMessage) { - $scope.group = group; - $scope.new_name = gettextCatalog.getString(group.name); - - $scope.alert = {}; - $scope.save = function() { - var old_name = gettextCatalog.getString($scope.group.name); - $scope.group.name = $scope.new_name; - Group.save($scope.group).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - $scope.group.name = old_name; - } - ); - }; - } -]) - -.controller('GroupCreateCtrl', [ - '$scope', - 'Group', - 'ErrorMessage', - function($scope, Group, ErrorMessage) { - $scope.new_name = ''; - $scope.alert = {}; - - $scope.save = function() { - var group = { - name: $scope.new_name, - permissions: [] - }; - - Group.create(group).then( - function (success) { - $scope.closeThisDialog(); - }, - function (error) { - $scope.alert = ErrorMessage.forAlert(error); - } - ); - }; - } -]) - -.factory('UserMenu', [ - '$http', - 'OpenSlides', - 'ngDialog', - 'UserProfileForm', - 'UserPasswordForm', - function ($http, OpenSlides, ngDialog, UserProfileForm, UserPasswordForm) { - return { - logout: function () { - $http.post('/users/logout/').then(function (response) { - // Success: User logged out, so reboot OpenSlides. - OpenSlides.reboot(); - }); - }, - editProfile: function () { - ngDialog.open(UserProfileForm.getDialog()); - }, - changePassword: function () { - ngDialog.open(UserPasswordForm.getDialog()); - }, - }; - } -]) - -.controller('userMenu', [ - '$scope', - 'UserMenu', - function($scope, UserMenu) { - $scope.logout = UserMenu.logout; - $scope.editProfile = UserMenu.editProfile; - $scope.changePassword = UserMenu.changePassword; - - } -]) - -.controller('LoginFormCtrl', [ - '$rootScope', - '$scope', - '$http', - '$state', - '$stateParams', - '$q', - 'operator', - 'gettext', - 'autoupdate', - 'mainMenu', - 'DS', - 'ngDialog', - function ($rootScope, $scope, $http, $state, $stateParams, $q, operator, gettext, - autoupdate, mainMenu, DS, ngDialog) { - $scope.alerts = []; - - if ($stateParams.msg) { - $scope.alerts.push({ - type: 'danger', - msg: $stateParams.msg, - }); - } - - // check if guest login is allowed - $scope.guestAllowed = $rootScope.guest_enabled; - - // get login info-text from server - $http.get('/users/login/').then(function (success) { - if(success.data.info_text) { - $scope.alerts.push({ - type: 'success', - msg: success.data.info_text - }); - } - }); - // check if cookies are enabled - if (!navigator.cookieEnabled) { - $scope.alerts.push({ - type: 'danger', - msg: gettext('You have to enable cookies to use OpenSlides.'), - }); - } - - // close alert function - $scope.closeAlert = function(index) { - $scope.alerts.splice(index, 1); - }; - // login - $scope.login = function () { - $scope.closeThisDialog(); - $scope.alerts = []; - var data = { 'username': $scope.username, 'password': $scope.password }; - if (!navigator.cookieEnabled) { - data.cookies = false; - } - $http.post('/users/login/', data).then( - function (response) { - // Success: User logged in. - // Clear store and reset deferred first message, if guests was enabled before. - DS.clear(); - autoupdate.firstMessageDeferred = $q.defer(); - // The next lines are partly the same lines as in core/start.js - autoupdate.newConnect(); - autoupdate.firstMessageDeferred.promise.then(function () { - operator.setUser(response.data.user_id, response.data.user); - $rootScope.operator = operator; - mainMenu.updateMainMenu(); - $state.go('home'); - $rootScope.openslidesBootstrapDone = true; - }); - }, - function (error) { - // Error: Username or password is not correct. - $state.transitionTo($state.current, {msg: error.data.detail}, { - reload: true, inherit: false, notify: true - }); - } - ); - }; - // guest login - $scope.guestLogin = function () { - $scope.closeThisDialog(); - $state.go('home'); - }; - // quick fix, that we cannot change the state to the main privacy - // policy view. Display it in a dialog.. - $scope.openPrivacyPolicyDialog = function () { - ngDialog.open({ - template: 'static/templates/privacypolicydialog.html', - className: 'ngdialog-theme-default wide-form', - controller: 'PrivacyPolicyDialogCtrl', - showClose: true, - closeByEscape: true, - closeByDocument: true, - }); - }; - } -]) - -.controller('PrivacyPolicyDialogCtrl', [ - '$scope', - 'PrivacyPolicy', - function ($scope, PrivacyPolicy) { - $scope.privacyPolicy = PrivacyPolicy; - } -]) - -// Mark all users strings for translation in JavaScript. -.config([ - 'gettext', - function (gettext) { - // permission strings (see models.py of each Django app) - // agenda - gettext('Can see agenda'); - gettext('Can manage agenda'); - gettext('Can manage list of speakers'); - gettext('Can see internal items and time scheduling of agenda'); - gettext('Can put oneself on the list of speakers'); - // assignments - gettext('Can see elections'); - gettext('Can nominate another participant'); - gettext('Can nominate oneself'); - gettext('Can manage elections'); - // core - gettext('Can see the projector'); - gettext('Can manage the projector'); - gettext('Can see the front page'); - gettext('Can manage tags'); - gettext('Can manage configuration'); - gettext('Can use the chat'); - gettext('Can manage the chat'); - gettext('Can manage logos and fonts'); - // mediafiles - gettext('Can see the list of files'); - gettext('Can upload files'); - gettext('Can manage files'); - gettext('Can see hidden files'); - // motions - gettext('Can see motions'); - gettext('Can create motions'); - gettext('Can support motions'); - gettext('Can manage motions'); - gettext('Can see comments'); - gettext('Can manage comments'); - // users - gettext('Can see names of users'); - gettext('Can see extra data of users (e.g. present and comment)'); - gettext('Can manage users'); - - // config strings in users/config_variables.py - gettext('General'); - gettext('Sort name of participants by'); - gettext('Enable participant presence view'); - gettext('Participants'); - gettext('Given name'); - gettext('Surname'); - gettext('PDF'); - gettext('Welcome to OpenSlides'); - gettext('Title for access data and welcome PDF'); - gettext('[Place for your welcome and help text.]'); - gettext('Help text for access data and welcome PDF'); - gettext('System URL'); - gettext('Used for QRCode in PDF of access data.'); - gettext('WLAN name (SSID)'); - gettext('Used for WLAN QRCode in PDF of access data.'); - gettext('WLAN password'); - gettext('Used for WLAN QRCode in PDF of access data.'); - gettext('WLAN encryption'); - gettext('Used for WLAN QRCode in PDF of access data.'); - gettext('WEP'); - gettext('WPA/WPA2'); - gettext('No encryption'); - gettext('Email'); - gettext('Email sender'); - gettext('Email subject'); - gettext('Your login for {event_name}'); - gettext('You can use {event_name} as a placeholder.'); - gettext('Email body'); - gettext('Dear {name},\n\nthis is your OpenSlides login for the event {event_name}:\n\n {url}\n username: {username}\n password: {password}\n\nThis email was generated automatically.'); - gettext('Use these placeholders: {name}, {event_name}, {url}, {username}, {password}. The url referrs to the system url.'); - } -]); - - -// this is code from angular.js. Find a way to call this function from this file -function getBlockNodes(nodes) { - // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original - // collection, otherwise update the original collection. - var node = nodes[0]; - var endNode = nodes[nodes.length - 1]; - var blockNodes = [node]; - - do { - node = node.nextSibling; - if (!node) break; - blockNodes.push(node); - } while (node !== endNode); - - return $(blockNodes); -} - -}()); diff --git a/openslides/users/static/templates/users/group-edit.html b/openslides/users/static/templates/users/group-edit.html deleted file mode 100644 index 57d4e8342..000000000 --- a/openslides/users/static/templates/users/group-edit.html +++ /dev/null @@ -1,29 +0,0 @@ -

    Edit name

    -

    Create new group

    -
    - {{ alert.msg }} -
    - -
    - -
    -
    - -
    -
    - - -
    -
    -
    diff --git a/openslides/users/static/templates/users/group-list.html b/openslides/users/static/templates/users/group-list.html deleted file mode 100644 index 79f86cf38..000000000 --- a/openslides/users/static/templates/users/group-list.html +++ /dev/null @@ -1,74 +0,0 @@ - - -
    -

    - All your changes are saved immediately. Changes you make are only effective once you (or the users concerned) reload the page. -

    - - - - - - -
    -

    Permissions

    -
    - - {{ group.name | translate }} - - - {{ group.name | translate | limitTo: 1 }}... - - -
    - - - -   - - - - -
    -
    - {{ app.app_name | translate}} - - - -
    - {{ permission.display_name | translate }} - - - - - - - -
    -
    diff --git a/openslides/users/static/templates/users/profile-password-form.html b/openslides/users/static/templates/users/profile-password-form.html deleted file mode 100644 index 0150a84ba..000000000 --- a/openslides/users/static/templates/users/profile-password-form.html +++ /dev/null @@ -1,16 +0,0 @@ -

    {{ title | translate }}

    - -
    - {{ alert.msg }} -
    - -
    - - - - -
    diff --git a/openslides/users/static/templates/users/slide_user.html b/openslides/users/static/templates/users/slide_user.html deleted file mode 100644 index 41cfbc009..000000000 --- a/openslides/users/static/templates/users/slide_user.html +++ /dev/null @@ -1,3 +0,0 @@ -
    -

    {{ user.get_full_name() }}

    -
    diff --git a/openslides/users/static/templates/users/user-change-password.html b/openslides/users/static/templates/users/user-change-password.html deleted file mode 100644 index 09f824686..000000000 --- a/openslides/users/static/templates/users/user-change-password.html +++ /dev/null @@ -1,55 +0,0 @@ -
    -
    - -

    Change password for {{ user.get_short_name() }}

    -
    -
    - -
    -
    -

    - - You override the personally set password of {{ user.get_short_name() }}! -

    -
    - {{ alert.msg }} -
    -
    - -
    - - -
    - - Generate -
    -
    -
    - Initial password: {{ user.default_password }} - - -
    - Username: {{ user.username }} - - Show password - - -
    -
    - - -
    -
    diff --git a/openslides/users/static/templates/users/user-detail.html b/openslides/users/static/templates/users/user-detail.html deleted file mode 100644 index 39a257341..000000000 --- a/openslides/users/static/templates/users/user-detail.html +++ /dev/null @@ -1,52 +0,0 @@ -
    -
    - -

    {{ user.get_short_name() }}

    -

    Participant

    -
    -
    - -
    -
    -
    - Personal data - - {{ user.structure_level }} - -
    - {{ (groups | filter: {id: group})[0].name | translate }} -
    - - {{ user.number }} - - {{ user.email }} - - {{ user.last_email_send | date: 'yyyy-MM-dd HH:mm:ss' }} - -
    -
    - -
    - Administrative data - - {{ user.username }} - -
    {{ user.comment }}
    -
    -
    -
    diff --git a/openslides/users/static/templates/users/user-form.html b/openslides/users/static/templates/users/user-form.html deleted file mode 100644 index 7f8f16619..000000000 --- a/openslides/users/static/templates/users/user-form.html +++ /dev/null @@ -1,17 +0,0 @@ -

    Edit participant

    -

    New participant

    - -
    - {{ alert.msg }} -
    - -
    - - - - -
    diff --git a/openslides/users/static/templates/users/user-import.html b/openslides/users/static/templates/users/user-import.html deleted file mode 100644 index 1b7c25a94..000000000 --- a/openslides/users/static/templates/users/user-import.html +++ /dev/null @@ -1,248 +0,0 @@ -
    -
    - -

    Import participants

    -
    -
    - -
    -
    - {{ alert.msg }} -
    - -

    Import by copy/paste

    -

    Copy and paste your participant names in this textbox. - Keep each person in a single line.

    - -
    -
    - -
    -
    - -
    - -
    - - -
    - -

    Import by CSV file

    - -

    Select a CSV file

    - - -

    Please note:

    -
      -
    • Required comma or semicolon separated values with these column header names in the first row:
      - - Title, - Given name, - Surname, - Structure level, - Participant number, - Groups, - Comment, - Is active, - Is present, - Is committee, - Initial password - Email - -
    • At least given name or surname have to be filled in. All - other fields are optional and may be empty. -
    • Only double quotes are accepted as text delimiter (no single quotes). -
    • Download CSV example file -
    - -
    -

    Preview

    -
    - - Page {{ pagination.currentPage }} / - {{ Math.ceil(users.length/pagination.itemsPerPage) }} - -
    -
    - - - - - - -
    - # - Title - Given name - Surname - Structure level - Participant number - Groups - Comment - Is active - Is present - Is committee - Initial password - Email - - - 1 duplicate - {{ duplicates }} duplicates - -
    - - -
    -
    - - - - - - - - - - - - {{ (pagination.currentPage - 1) * pagination.itemsPerPage + $index + 1 }} - - {{ user.title }} - - - - - {{ user.first_name }} - - - - - {{ user.last_name }} - - {{ user.structure_level }} - - {{ user.number }} - -
    - {{ groupname | translate }} -
    -
    - - {{ groupname | translate }} -
    -
    - - {{ user.comment | limitTo: 30 }}{{ user.comment.length > 30 ? '...' : '' }} - - - - - - - - - {{ user.default_password }} - - {{ user.email }} - -
    - - -
    -
    -
      -
    - -
    -
    - - {{ usersWillNotBeImported }} - participants will be not imported. -
    -
    - - {{ duplicates }} - duplicates! -
    -
    - - {{ usersWillBeImported }} - participants will be imported. - - - -
    -
    -
    -
    - - {{ usersImported.length }} - participants were successfully imported. - (Groups created: {{ groupsCreated }}) -
    -
    - -
    - - - -
    - -
    -
    diff --git a/openslides/users/static/templates/users/user-list.html b/openslides/users/static/templates/users/user-list.html deleted file mode 100644 index 699e5a5a3..000000000 --- a/openslides/users/static/templates/users/user-list.html +++ /dev/null @@ -1,487 +0,0 @@ - - -
    -
    - {{ alert.msg }} -
    -
    -
    - - -
    -
    - - - -
    -
    -
    -
    - - - - - - Delete selected participants - - - - - - - Add group - - - - - Remove group - - - - - Generate new passwords - - - - - Is active - Is present - Is committee - - - - - Is not active - Is not present - Is not a committee - - - - Send invitation emails - -
    -
    - -
    -
    - {{ usersFiltered.length }} / - {{ users.length }} {{ "participants" | translate }}, - {{(users|filter:{selected:true}).length}} {{ "selected" | translate }} - -
    -
    - - - « - - Page {{ pagination.currentPage }} / - {{ pagination.getPageCount(usersFiltered) }} - - » - - -
    -
    - - -
    - -
    - -
    - -
    -
    -
    - -
    -
    - - - - - Filter - - - - - Groups - - - - - - - - {{ booleanFilter.displayName | translate }} - - - - - - - - Sort - - - - - - - - - - - - - - - - - - {{ group.name | translate }} - - - - - No group set - - - - - - {{ booleanFilter.value ? booleanFilter.choiceYes : booleanFilter.choiceNo | translate }} - - - -
    -
    - - -
    - - -
    - -
    - -
    - - -
    -
    - -
    -
    - - -
    -
    - -
    -
    - No. {{ user.number }} -
    -
    -
    - No. {{ user.number }} -
    -
    - - - - Edit · - - - Change password · - Delete - -
    -
    -
    - - -
    -
    - - -
    - - - - - - - - - - {{ (groups | filter: {id: group}:true)[0].name | translate }},, - ... [+{{ user.groups_id.length - 2}}] - - - - - -
    - -
    - - - {{ (groups | filter: {id: group})[0].name | translate }},, - ... [+{{ user.groups_id.length - 2}}] - -
    - -
    -
    - - - Set structure level ... - {{ user.structure_level }} - -
    -
    -
    -
    - - {{ user.structure_level }} -
    -
    - -
    -
    - - - Set comment ... - {{ user.comment }} - -
    -
    -
    -
    - - {{ user.comment | limitTo:25}}{{ user.comment.length > 25 ? '...' : '' }} -
    -
    - -
    -
    - - {{ user.last_email_send | date: 'yyyy-MM-dd HH:mm:ss' }} -
    -
    -
    -
    -
    -
    - - - Present - -
    -
    - - Present -
    - -
    -
    - -
    -
    - -
      -
    -
    diff --git a/openslides/users/static/templates/users/user-presence.html b/openslides/users/static/templates/users/user-presence.html deleted file mode 100644 index 1eeeac341..000000000 --- a/openslides/users/static/templates/users/user-presence.html +++ /dev/null @@ -1,31 +0,0 @@ -
    -
    - -

    Presence

    -
    -
    - -
    - -
    - {{ alert.msg }} -
    - -
    -
    -
    - - -
    - -
    -
    -
    diff --git a/openslides/utils/views.py b/openslides/utils/views.py index 02e4efd4f..dbc6178d6 100644 --- a/openslides/utils/views.py +++ b/openslides/utils/views.py @@ -1,27 +1,8 @@ -import base64 -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List -from django.contrib.staticfiles import finders -from django.core.exceptions import ImproperlyConfigured -from django.http import HttpResponse -from django.views.decorators.csrf import ensure_csrf_cookie -from django.views.generic.base import View from rest_framework.response import Response from rest_framework.views import APIView as _APIView -from .arguments import arguments - - -class CSRFMixin: - """ - Adds the csrf cookie to the response. - """ - - @classmethod - def as_view(cls, *args: Any, **kwargs: Any) -> View: - view = super().as_view(*args, **kwargs) # type: ignore - return ensure_csrf_cookie(view) - class APIView(_APIView): """ @@ -51,40 +32,3 @@ class APIView(_APIView): # Add the http-methods and delete the method "method_call" get = post = put = patch = delete = head = options = trace = method_call del method_call - - -class TemplateView(View): - """ - A view to serve a single cached template file. Subclasses have to provide 'template_name'. - The state dict is used to cache the template. The state variable is static, but the object ID - is not allowed to change. So the State has to be saved in this dict. Search for 'Borg design - pattern' for more information. - """ - template_name: Optional[str] = None - state: Dict[str, str] = {} - - def __init__(self, *args: Any, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - - if self.template_name is None: - raise ImproperlyConfigured("'template_name' is not provided.") - - no_caching = arguments.get('no_template_caching', False) - if self.template_name not in self.state or no_caching: - self.state[self.template_name] = self.load_template() - - def load_template(self) -> str: - with open(finders.find(self.template_name)) as template: - return template.read() - - def get(self, *args: Any, **kwargs: Any) -> HttpResponse: - return HttpResponse(self.state[self.template_name]) # type: ignore - - -class BinaryTemplateView(TemplateView): - """ - Loads the specified binary template and encode it with base64. - """ - def load_template(self) -> str: - with open(finders.find(self.template_name), 'rb') as template: - return base64.b64encode(template.read()).decode() diff --git a/package.json b/package.json deleted file mode 100644 index 06cd01ae9..000000000 --- a/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "openslides", - "private": true, - "scripts": { - "prepublish": "bower install && gulp", - "karma": "karma start tests/karma/karma.conf.js", - "karma:watch": "karma start tests/karma/karma.conf.js --single-run=false" - }, - "devDependencies": { - "angular-mocks": "~1.5.11", - "bower": "^1.8.0", - "gulp": "~4.0.0", - "gulp-angular-gettext": "^2.2.0", - "gulp-angular-templatecache": "^2.0.0", - "gulp-concat": "^2.6.1", - "gulp-cssnano": "^2.1.2", - "gulp-if": "^2.0.2", - "gulp-inject-string": "^1.1.0", - "gulp-jshint": "^2.0.4", - "gulp-rename": "^1.2.2", - "gulp-sass": "^3.1.0", - "gulp-sourcemaps": "^2.6.0", - "gulp-uglify": "^2.1.2", - "jasmine-core": "^2.6.1", - "jshint": "^2.9.4", - "karma": "^1.5.0", - "karma-chrome-launcher": "^2.0.0", - "karma-jasmine": "^1.1.0", - "karma-phantomjs-launcher": "^1.0.4", - "main-bower-files": "^2.13.1", - "sprintf-js": "^1.0.3", - "through2": "^2.0.3", - "yargs": "^7.1.0" - } -} diff --git a/tests/integration/core/test_views.py b/tests/integration/core/test_views.py index cb4eda5e8..ebc6af9ec 100644 --- a/tests/integration/core/test_views.py +++ b/tests/integration/core/test_views.py @@ -1,6 +1,5 @@ import json -from django.apps import apps from django.urls import reverse from rest_framework import status from rest_framework.test import APIClient @@ -85,29 +84,6 @@ class VersionView(TestCase): 'url': ''}]}) -class WebclientJavaScriptView(TestCase): - """ - Tests the generation of the JavaScript startup code. - """ - def test_angular_constants(self): - self.client.login(username='admin', password='admin') - response = self.client.get(reverse('core_webclient_javascript', args=['site'])) - content = response.content.decode() - constants = self.get_angular_constants_from_apps() - for key, constant in constants.items(): - self.assertTrue(json.dumps(constant) in content) - - def get_angular_constants_from_apps(self): - constants = {} - for app in apps.get_app_configs(): - try: - get_angular_constants = app.get_angular_constants - except AttributeError: - continue - constants.update(get_angular_constants()) - return constants - - class ConfigViewSet(TestCase): """ Tests requests to deal with config variables. diff --git a/tests/unit/utils/test_views.py b/tests/unit/utils/test_views.py index a47e2914e..21424bc0b 100644 --- a/tests/unit/utils/test_views.py +++ b/tests/unit/utils/test_views.py @@ -1,5 +1,4 @@ from unittest import TestCase -from unittest.mock import patch from openslides.utils import views @@ -17,16 +16,3 @@ class TestAPIView(TestCase): self.assertFalse( hasattr(views.APIView, 'method_call'), "The APIView should not have the method 'method_call'") - - -class TestCSRFMixin(TestCase): - @patch('builtins.super') - def test_as_view(self, mock_super): - """ - Tests, that ensure_csrf_cookie is called. - """ - mock_super().as_view.return_value = 'super_view' - with patch('openslides.utils.views.ensure_csrf_cookie') as ensure_csrf_cookie: - views.CSRFMixin.as_view() - - ensure_csrf_cookie.assert_called_once_with('super_view') diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 6a53663fa..000000000 --- a/yarn.lock +++ /dev/null @@ -1,5135 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@gulp-sourcemaps/identity-map@1.X": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz#1e6fe5d8027b1f285dc0d31762f566bccd73d5a9" - dependencies: - acorn "^5.0.3" - css "^2.2.1" - normalize-path "^2.1.1" - source-map "^0.6.0" - through2 "^2.0.3" - -"@gulp-sourcemaps/map-sources@1.X": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz#890ae7c5d8c877f6d384860215ace9d7ec945bda" - dependencies: - normalize-path "^2.0.1" - through2 "^2.0.3" - -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - -accepts@1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" - dependencies: - mime-types "~2.1.11" - negotiator "0.6.1" - -acorn@5.X, acorn@^5.0.3: - version "5.7.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" - -after@0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" - -ajv@^4.9.1: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - -ajv@^5.1.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - -alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - -angular-gettext-tools@^2.2.0: - version "2.3.11" - resolved "https://registry.yarnpkg.com/angular-gettext-tools/-/angular-gettext-tools-2.3.11.tgz#61920670eb3e34ee8f958dd8a4a3c327ee4e5de4" - dependencies: - babylon "^6.11.4" - binary-search "^1.2.0" - cheerio "~0.19.0" - lodash "^4.0.0" - pofile "~1.0.0" - typescript "~2.3.2" - typescript-eslint-parser "^3.0.0" - -angular-mocks@~1.5.11: - version "1.5.11" - resolved "https://registry.yarnpkg.com/angular-mocks/-/angular-mocks-1.5.11.tgz#a0e1dd0ea55fd77ee7a757d75536c5e964c86f81" - -ansi-colors@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" - dependencies: - ansi-wrap "^0.1.0" - -ansi-cyan@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-cyan/-/ansi-cyan-0.1.1.tgz#538ae528af8982f28ae30d86f2f17456d2609873" - dependencies: - ansi-wrap "0.1.0" - -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - dependencies: - ansi-wrap "0.1.0" - -ansi-red@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-red/-/ansi-red-0.1.1.tgz#8c638f9d1080800a353c9c28c8a81ca4705d946c" - dependencies: - ansi-wrap "0.1.0" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - -ansi-wrap@0.1.0, ansi-wrap@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - -anymatch@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" - dependencies: - micromatch "^2.1.5" - normalize-path "^2.0.0" - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -append-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" - dependencies: - buffer-equal "^1.0.0" - -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a" - dependencies: - arr-flatten "^1.0.1" - array-slice "^0.2.3" - -arr-diff@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - dependencies: - arr-flatten "^1.0.1" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - -arr-filter@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/arr-filter/-/arr-filter-1.1.2.tgz#43fdddd091e8ef11aa4c45d9cdc18e2dff1711ee" - dependencies: - make-iterator "^1.0.0" - -arr-flatten@^1.0.1, arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - -arr-map@^2.0.0, arr-map@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/arr-map/-/arr-map-2.0.2.tgz#3a77345ffc1cf35e2a91825601f9e58f2e24cac4" - dependencies: - make-iterator "^1.0.0" - -arr-union@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-2.1.0.tgz#20f9eab5ec70f5c7d215b1077b1c39161d292c7d" - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - -array-differ@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031" - -array-each@^1.0.0, array-each@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" - -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - -array-initial@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/array-initial/-/array-initial-1.1.0.tgz#2fa74b26739371c3947bd7a7adc73be334b3d795" - dependencies: - array-slice "^1.0.0" - is-number "^4.0.0" - -array-last@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array-last/-/array-last-1.3.0.tgz#7aa77073fec565ddab2493f5f88185f404a9d336" - dependencies: - is-number "^4.0.0" - -array-slice@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5" - -array-slice@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" - -array-sort@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" - dependencies: - default-compare "^1.0.0" - get-value "^2.0.6" - kind-of "^5.0.2" - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1, array-uniq@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - -arraybuffer.slice@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca" - -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - -asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - -assert-plus@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - -async-done@^1.2.0, async-done@^1.2.2: - version "1.3.1" - resolved "https://registry.yarnpkg.com/async-done/-/async-done-1.3.1.tgz#14b7b73667b864c8f02b5b253fc9c6eddb777f3e" - dependencies: - end-of-stream "^1.1.0" - once "^1.3.2" - process-nextick-args "^1.0.7" - stream-exhaust "^1.0.1" - -async-each@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" - -async-foreach@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" - -async-settle@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-1.0.0.tgz#1d0a914bb02575bec8a8f3a74e5080f72b2c0c6b" - dependencies: - async-done "^1.2.2" - -async@^1.2.1: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - -atob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" - -autoprefixer@^6.3.1: - version "6.7.7" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" - dependencies: - browserslist "^1.7.6" - caniuse-db "^1.0.30000634" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^5.2.16" - postcss-value-parser "^3.2.3" - -aws-sign2@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - -aws4@^1.2.1, aws4@^1.6.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" - -babylon@^6.11.4: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" - -bach@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" - dependencies: - arr-filter "^1.1.1" - arr-flatten "^1.0.1" - arr-map "^2.0.0" - array-each "^1.0.0" - array-initial "^1.0.0" - array-last "^1.1.1" - async-done "^1.2.2" - async-settle "^1.0.0" - now-and-later "^2.0.0" - -backo2@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" - -balanced-match@^0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - -base64-arraybuffer@0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" - -base64id@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6" - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - dependencies: - tweetnacl "^0.14.3" - -beeper@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/beeper/-/beeper-1.1.1.tgz#e6d5ea8c5dad001304a70b22638447f69cb2f809" - -better-assert@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" - dependencies: - callsite "1.0.0" - -binary-extensions@^1.0.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" - -binary-search@^1.2.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.4.tgz#d15f44ff9226ef309d85247fa0dbfbf659955f56" - -blob@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921" - -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - dependencies: - inherits "~2.0.0" - -bluebird@^3.3.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - -body-parser@^1.16.1: - version "1.18.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" - dependencies: - bytes "3.0.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "~1.6.3" - iconv-lite "0.4.23" - on-finished "~2.3.0" - qs "6.5.2" - raw-body "2.3.3" - type-is "~1.6.16" - -boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - -boom@2.x.x: - version "2.10.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" - dependencies: - hoek "2.x.x" - -bower@^1.8.0: - version "1.8.4" - resolved "https://registry.yarnpkg.com/bower/-/bower-1.8.4.tgz#e7876a076deb8137f7d06525dc5e8c66db82f28a" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^0.1.2: - version "0.1.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-0.1.5.tgz#c085711085291d8b75fdd74eab0f8597280711e6" - dependencies: - expand-range "^0.1.0" - -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -braces@^2.3.0, braces@^2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: - version "1.7.7" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" - dependencies: - caniuse-db "^1.0.30000639" - electron-to-chromium "^1.2.7" - -buffer-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" - -buffer-from@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" - -builtin-modules@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -callsite@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" - -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - -caniuse-api@^1.5.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" - dependencies: - browserslist "^1.3.6" - caniuse-db "^1.0.30000529" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - -caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000871" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000871.tgz#f1995c1fe31892649a7605957a80c92518423d4d" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -cheerio@~0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.19.0.tgz#772e7015f2ee29965096d71ea4175b75ab354925" - dependencies: - css-select "~1.0.0" - dom-serializer "~0.1.0" - entities "~1.1.1" - htmlparser2 "~3.8.1" - lodash "^3.2.0" - -chokidar@^1.4.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" - dependencies: - anymatch "^1.3.0" - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^2.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - optionalDependencies: - fsevents "^1.0.0" - -chokidar@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" - dependencies: - anymatch "^2.0.0" - async-each "^1.0.0" - braces "^2.3.0" - glob-parent "^3.1.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - lodash.debounce "^4.0.8" - normalize-path "^2.1.1" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - upath "^1.0.5" - optionalDependencies: - fsevents "^1.2.2" - -chownr@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" - -clap@^1.0.9: - version "1.2.3" - resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51" - dependencies: - chalk "^1.1.3" - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -cli@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cli/-/cli-1.0.1.tgz#22817534f24bfa4950c34d532d48ecbc621b8c14" - dependencies: - exit "0.1.2" - glob "^7.1.1" - -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - -cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - -clone-stats@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" - -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - -clone@^1.0.0, clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - -clone@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" - -cloneable-readable@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.2.tgz#d591dee4a8f8bc15da43ce97dceeba13d43e2a65" - dependencies: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - -coa@~1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" - dependencies: - q "^1.1.2" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - -collection-map@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" - dependencies: - arr-map "^2.0.2" - for-own "^1.0.0" - make-iterator "^1.0.0" - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.3.0: - version "1.9.2" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" - dependencies: - color-name "1.1.1" - -color-name@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" - -color-name@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - -color-string@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" - dependencies: - color-name "^1.0.0" - -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - -color@^0.11.0: - version "0.11.4" - resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" - dependencies: - clone "^1.0.2" - color-convert "^1.3.0" - color-string "^0.3.0" - -colormin@^1.0.5: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" - dependencies: - color "^0.11.0" - css-color-names "0.0.4" - has "^1.0.1" - -colors@^1.1.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.1.tgz#4accdb89cf2cabc7f982771925e9468784f32f3d" - -colors@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" - -combine-lists@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/combine-lists/-/combine-lists-1.0.1.tgz#458c07e09e0d900fc28b70a3fec2dacd1d2cb7f6" - dependencies: - lodash "^4.5.0" - -combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" - dependencies: - delayed-stream "~1.0.0" - -component-bind@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" - -component-emitter@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.1.2.tgz#296594f2753daa63996d2af08d15a95116c9aec3" - -component-emitter@1.2.1, component-emitter@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" - -component-inherit@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -concat-stream@1.6.2, concat-stream@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -concat-with-sourcemaps@*, concat-with-sourcemaps@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz#d4ea93f05ae25790951b99e7b3b09e3908a4082e" - dependencies: - source-map "^0.6.1" - -connect@^3.6.0: - version "3.6.6" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.6.tgz#09eff6c55af7236e137135a72574858b6786f524" - dependencies: - debug "2.6.9" - finalhandler "1.1.0" - parseurl "~1.3.2" - utils-merge "1.0.1" - -console-browserify@1.1.x: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" - dependencies: - date-now "^0.1.4" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - -convert-source-map@1.X, convert-source-map@^1.1.1, convert-source-map@^1.5.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" - -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - -copy-props@^2.0.1: - version "2.0.4" - resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.4.tgz#93bb1cadfafd31da5bb8a9d4b41f471ec3a72dfe" - dependencies: - each-props "^1.3.0" - is-plain-object "^2.0.1" - -core-js@^2.2.0: - version "2.5.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - -cross-spawn@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" - dependencies: - lru-cache "^4.0.1" - which "^1.2.9" - -cryptiles@2.x.x: - version "2.0.5" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" - dependencies: - boom "2.x.x" - -css-color-names@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - -css-select@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.0.0.tgz#b1121ca51848dd264e2244d058cee254deeb44b0" - dependencies: - boolbase "~1.0.0" - css-what "1.0" - domutils "1.4" - nth-check "~1.0.0" - -css-what@1.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-1.0.0.tgz#d7cc2df45180666f99d2b14462639469e00f736c" - -css@2.X, css@^2.2.1: - version "2.2.3" - resolved "https://registry.yarnpkg.com/css/-/css-2.2.3.tgz#f861f4ba61e79bedc962aa548e5780fd95cbc6be" - dependencies: - inherits "^2.0.1" - source-map "^0.1.38" - source-map-resolve "^0.5.1" - urix "^0.1.0" - -cssnano@^3.0.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" - dependencies: - autoprefixer "^6.3.1" - decamelize "^1.1.2" - defined "^1.0.0" - has "^1.0.1" - object-assign "^4.0.1" - postcss "^5.0.14" - postcss-calc "^5.2.0" - postcss-colormin "^2.1.8" - postcss-convert-values "^2.3.4" - postcss-discard-comments "^2.0.4" - postcss-discard-duplicates "^2.0.1" - postcss-discard-empty "^2.0.1" - postcss-discard-overridden "^0.1.1" - postcss-discard-unused "^2.2.1" - postcss-filter-plugins "^2.0.0" - postcss-merge-idents "^2.1.5" - postcss-merge-longhand "^2.0.1" - postcss-merge-rules "^2.0.3" - postcss-minify-font-values "^1.0.2" - postcss-minify-gradients "^1.0.1" - postcss-minify-params "^1.0.4" - postcss-minify-selectors "^2.0.4" - postcss-normalize-charset "^1.1.0" - postcss-normalize-url "^3.0.7" - postcss-ordered-values "^2.1.0" - postcss-reduce-idents "^2.2.2" - postcss-reduce-initial "^1.0.0" - postcss-reduce-transforms "^1.0.3" - postcss-svgo "^2.1.1" - postcss-unique-selectors "^2.0.2" - postcss-value-parser "^3.2.3" - postcss-zindex "^2.0.1" - -csso@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85" - dependencies: - clap "^1.0.9" - source-map "^0.5.3" - -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - dependencies: - array-find-index "^1.0.1" - -custom-event@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" - -d@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" - dependencies: - es5-ext "^0.10.9" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - dependencies: - assert-plus "^1.0.0" - -date-now@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" - -dateformat@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062" - -debug-fabulous@1.X: - version "1.1.0" - resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-1.1.0.tgz#af8a08632465224ef4174a9f06308c3c2a1ebc8e" - dependencies: - debug "3.X" - memoizee "0.4.X" - object-assign "4.X" - -debug@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" - dependencies: - ms "0.7.1" - -debug@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.3.3.tgz#40c453e67e6e13c901ddec317af8986cda9eff8c" - dependencies: - ms "0.7.2" - -debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - dependencies: - ms "2.0.0" - -debug@3.X, debug@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - dependencies: - ms "2.0.0" - -decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - -default-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" - dependencies: - kind-of "^5.0.2" - -default-resolution@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" - -define-properties@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" - dependencies: - foreach "^2.0.5" - object-keys "^1.0.8" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -defined@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - -detect-newline@2.X: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" - -di@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" - -dom-serialize@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" - dependencies: - custom-event "~1.0.0" - ent "~2.2.0" - extend "^3.0.0" - void-elements "^2.0.0" - -dom-serializer@0, dom-serializer@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" - dependencies: - domelementtype "~1.1.1" - entities "~1.1.1" - -domelementtype@1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" - -domelementtype@~1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" - -domhandler@2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" - dependencies: - domelementtype "1" - -domutils@1.4: - version "1.4.3" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.4.3.tgz#0865513796c6b306031850e175516baf80b72a6f" - dependencies: - domelementtype "1" - -domutils@1.5: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - dependencies: - dom-serializer "0" - domelementtype "1" - -duplexer2@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.0.2.tgz#c614dcf67e2fb14995a91711e5a617e8a60a31db" - dependencies: - readable-stream "~1.1.9" - -duplexer@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" - -duplexify@^3.2.0, duplexify@^3.5.0, duplexify@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.0.tgz#592903f5d80b38d037220541264d69a198fb3410" - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - -each-props@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333" - dependencies: - is-plain-object "^2.0.1" - object.defaults "^1.1.0" - -ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" - dependencies: - jsbn "~0.1.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - -electron-to-chromium@^1.2.7: - version "1.3.52" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.52.tgz#d2d9f1270ba4a3b967b831c40ef71fb4d9ab5ce0" - -encodeurl@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" - dependencies: - once "^1.4.0" - -engine.io-client@1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab" - dependencies: - component-emitter "1.2.1" - component-inherit "0.0.3" - debug "2.3.3" - engine.io-parser "1.3.2" - has-cors "1.1.0" - indexof "0.0.1" - parsejson "0.0.3" - parseqs "0.0.5" - parseuri "0.0.5" - ws "1.1.2" - xmlhttprequest-ssl "1.5.3" - yeast "0.1.2" - -engine.io-parser@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-1.3.2.tgz#937b079f0007d0893ec56d46cb220b8cb435220a" - dependencies: - after "0.8.2" - arraybuffer.slice "0.0.6" - base64-arraybuffer "0.1.5" - blob "0.0.4" - has-binary "0.1.7" - wtf-8 "1.0.0" - -engine.io@1.8.3: - version "1.8.3" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-1.8.3.tgz#8de7f97895d20d39b85f88eeee777b2bd42b13d4" - dependencies: - accepts "1.3.3" - base64id "1.0.0" - cookie "0.3.1" - debug "2.3.3" - engine.io-parser "1.3.2" - ws "1.1.2" - -ent@~2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" - -entities@1.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" - -entities@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" - -error-ex@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - dependencies: - is-arrayish "^0.2.1" - -es5-ext@^0.10.14, es5-ext@^0.10.30, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14, es5-ext@~0.10.2: - version "0.10.45" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.45.tgz#0bfdf7b473da5919d5adf3bd25ceb754fccc3653" - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.1" - next-tick "1" - -es6-iterator@^2.0.1, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-promise@^4.0.3: - version "4.2.4" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29" - -es6-symbol@^3.1.1, es6-symbol@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" - dependencies: - d "1" - es5-ext "~0.10.14" - -es6-weak-map@^2.0.1, es6-weak-map@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - -escape-string-regexp@^1.0.2: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - -esprima@^2.6.0: - version "2.7.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" - -event-emitter@^0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - dependencies: - d "1" - es5-ext "~0.10.14" - -event-stream@*, event-stream@3.3.4, event-stream@^3.1.7: - version "3.3.4" - resolved "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" - dependencies: - duplexer "~0.1.1" - from "~0" - map-stream "~0.1.0" - pause-stream "0.0.11" - split "0.3" - stream-combiner "~0.0.4" - through "~2.3.1" - -eventemitter3@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" - -exit@0.1.2, exit@0.1.x: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - -expand-braces@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea" - dependencies: - array-slice "^0.2.3" - array-unique "^0.2.1" - braces "^0.1.2" - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - dependencies: - is-posix-bracket "^0.1.0" - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expand-range@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044" - dependencies: - is-number "^0.1.1" - repeat-string "^0.2.2" - -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - dependencies: - fill-range "^2.1.0" - -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - dependencies: - homedir-polyfill "^1.0.1" - -extend-shallow@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-1.1.4.tgz#19d6bf94dfc09d76ba711f39b872d21ff4dd9071" - dependencies: - kind-of "^1.1.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-2.0.2.tgz#1b74985400171b85554894459c978de6ef453ab7" - -extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - dependencies: - is-extglob "^1.0.0" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extract-zip@^1.6.5: - version "1.6.7" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" - dependencies: - concat-stream "1.6.2" - debug "2.6.9" - mkdirp "0.5.1" - yauzl "2.4.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - -fancy-log@^1.1.0, fancy-log@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.2.tgz#f41125e3d84f2e7d89a43d06d958c8f78be16be1" - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - time-stamp "^1.0.0" - -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - -fd-slicer@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" - dependencies: - pend "~1.2.0" - -filename-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - -fill-range@^2.1.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^3.0.0" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -finalhandler@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" - dependencies: - debug "2.6.9" - encodeurl "~1.0.1" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.3.1" - unpipe "~1.0.0" - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -findup-sync@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" - dependencies: - detect-file "^1.0.0" - is-glob "^3.1.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -fined@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fined/-/fined-1.1.0.tgz#b37dc844b76a2f5e7081e884f7c0ae344f153476" - dependencies: - expand-tilde "^2.0.2" - is-plain-object "^2.0.3" - object.defaults "^1.1.0" - object.pick "^1.2.0" - parse-filepath "^1.0.1" - -first-chunk-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz#59bfb50cd905f60d7c394cd3d9acaab4e6ad934e" - -flagged-respawn@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.0.tgz#4e79ae9b2eb38bf86b3bb56bf3e0a56aa5fcabd7" - -flatten@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" - -flush-write-stream@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.4" - -follow-redirects@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.1.tgz#67a8f14f5a1f67f962c2c46469c79eaec0a90291" - dependencies: - debug "^3.1.0" - -for-in@^1.0.1, for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - -for-own@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - dependencies: - for-in "^1.0.1" - -for-own@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" - dependencies: - for-in "^1.0.1" - -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - -fork-stream@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/fork-stream/-/fork-stream-0.0.4.tgz#db849fce77f6708a5f8f386ae533a0907b54ae70" - -form-data@~2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" - -form-data@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" - dependencies: - asynckit "^0.4.0" - combined-stream "1.0.6" - mime-types "^2.1.12" - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - dependencies: - map-cache "^0.2.2" - -from@~0: - version "0.1.7" - resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" - -fs-access@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fs-access/-/fs-access-1.0.1.tgz#d6a87f262271cefebec30c553407fb995da8777a" - dependencies: - null-check "^1.0.0" - -fs-extra@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^2.1.0" - klaw "^1.0.0" - -fs-minipass@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" - dependencies: - minipass "^2.2.1" - -fs-mkdirp-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" - dependencies: - graceful-fs "^4.1.11" - through2 "^2.0.3" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -fsevents@^1.0.0, fsevents@^1.2.2: - version "1.2.4" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" - dependencies: - nan "^2.9.2" - node-pre-gyp "^0.10.0" - -fstream@^1.0.0, fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -gaze@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" - dependencies: - globule "^1.0.0" - -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - dependencies: - assert-plus "^1.0.0" - -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - dependencies: - is-glob "^2.0.0" - -glob-parent@^3.0.0, glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-stream@^5.3.2: - version "5.3.5" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-5.3.5.tgz#a55665a9a8ccdc41915a87c701e32d4e016fad22" - dependencies: - extend "^3.0.0" - glob "^5.0.3" - glob-parent "^3.0.0" - micromatch "^2.3.7" - ordered-read-streams "^0.3.0" - through2 "^0.6.0" - to-absolute-glob "^0.1.1" - unique-stream "^2.0.2" - -glob-stream@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" - dependencies: - extend "^3.0.0" - glob "^7.1.1" - glob-parent "^3.1.0" - is-negated-glob "^1.0.0" - ordered-read-streams "^1.0.0" - pumpify "^1.3.5" - readable-stream "^2.1.5" - remove-trailing-separator "^1.0.1" - to-absolute-glob "^2.0.0" - unique-stream "^2.0.2" - -glob-watcher@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.1.tgz#239aaa621b6bd843b288fdf6b155f50963c7d7ea" - dependencies: - async-done "^1.2.0" - chokidar "^2.0.0" - just-debounce "^1.0.0" - object.defaults "^1.1.0" - -glob@^5.0.3: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^6.0.4: - version "6.0.4" - resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@~7.1.1: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - -globby@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-2.1.0.tgz#9e9192bcd33f4ab6a4f894e5e7ea8b713213c482" - dependencies: - array-union "^1.0.1" - async "^1.2.1" - glob "^5.0.3" - object-assign "^3.0.0" - -globule@^1.0.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d" - dependencies: - glob "~7.1.1" - lodash "~4.17.10" - minimatch "~3.0.2" - -glogg@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.1.tgz#dcf758e44789cc3f3d32c1f3562a3676e6a34810" - dependencies: - sparkles "^1.0.0" - -graceful-fs@4.X, graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -gulp-angular-gettext@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/gulp-angular-gettext/-/gulp-angular-gettext-2.2.0.tgz#90b5f38e89b4be491e8bb5cabaf69b0a71ddf95f" - dependencies: - angular-gettext-tools "^2.2.0" - gulp-util "^3.0.7" - lodash.isstring "^4.0.1" - through2 "^2.0.1" - -gulp-angular-templatecache@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/gulp-angular-templatecache/-/gulp-angular-templatecache-2.2.1.tgz#f08088fd4b1a80733d627c4ba24728d4e9cca8a9" - dependencies: - event-stream "3.3.4" - gulp-concat "2.6.1" - gulp-footer "2.0.1" - gulp-header "2.0.5" - jsesc "2.5.1" - lodash.template "^4.4.0" - through2 "^2.0.3" - -gulp-cli@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.0.1.tgz#7847e220cb3662f2be8a6d572bf14e17be5a994b" - dependencies: - ansi-colors "^1.0.1" - archy "^1.0.0" - array-sort "^1.0.0" - color-support "^1.1.3" - concat-stream "^1.6.0" - copy-props "^2.0.1" - fancy-log "^1.3.2" - gulplog "^1.0.0" - interpret "^1.1.0" - isobject "^3.0.1" - liftoff "^2.5.0" - matchdep "^2.0.0" - mute-stdout "^1.0.0" - pretty-hrtime "^1.0.0" - replace-homedir "^1.0.0" - semver-greatest-satisfied-range "^1.1.0" - v8flags "^3.0.1" - yargs "^7.1.0" - -gulp-concat@2.6.1, gulp-concat@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/gulp-concat/-/gulp-concat-2.6.1.tgz#633d16c95d88504628ad02665663cee5a4793353" - dependencies: - concat-with-sourcemaps "^1.0.0" - through2 "^2.0.0" - vinyl "^2.0.0" - -gulp-cssnano@^2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/gulp-cssnano/-/gulp-cssnano-2.1.3.tgz#02007e2817af09b3688482b430ad7db807aebf72" - dependencies: - buffer-from "^1.0.0" - cssnano "^3.0.0" - object-assign "^4.0.1" - plugin-error "^1.0.1" - vinyl-sourcemaps-apply "^0.2.1" - -gulp-footer@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/gulp-footer/-/gulp-footer-2.0.1.tgz#76578359feb72aa280a97cb980bcfbe0e6216f3e" - dependencies: - event-stream "*" - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.6.2" - -gulp-header@2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/gulp-header/-/gulp-header-2.0.5.tgz#16e229c73593ade301168024fea68dab75d9d38c" - dependencies: - concat-with-sourcemaps "*" - lodash.template "^4.4.0" - through2 "^2.0.0" - -gulp-if@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/gulp-if/-/gulp-if-2.0.2.tgz#a497b7e7573005041caa2bc8b7dda3c80444d629" - dependencies: - gulp-match "^1.0.3" - ternary-stream "^2.0.1" - through2 "^2.0.1" - -gulp-inject-string@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/gulp-inject-string/-/gulp-inject-string-1.1.1.tgz#94e7a1b5d4102720edbc7491652a2c3d6e8c5bb8" - dependencies: - event-stream "^3.1.7" - plugin-error "^0.1.2" - -gulp-jshint@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/gulp-jshint/-/gulp-jshint-2.1.0.tgz#bfaf927f78eee263c5bbac5f63e314d44a7bd41e" - dependencies: - lodash "^4.12.0" - minimatch "^3.0.3" - plugin-error "^0.1.2" - rcloader "^0.2.2" - through2 "^2.0.0" - -gulp-match@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/gulp-match/-/gulp-match-1.0.3.tgz#91c7c0d7f29becd6606d57d80a7f8776a87aba8e" - dependencies: - minimatch "^3.0.3" - -gulp-rename@^1.2.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/gulp-rename/-/gulp-rename-1.4.0.tgz#de1c718e7c4095ae861f7296ef4f3248648240bd" - -gulp-sass@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/gulp-sass/-/gulp-sass-3.2.1.tgz#2e3688a96fd8be1c0c01340750c191b2e79fab94" - dependencies: - gulp-util "^3.0" - lodash.clonedeep "^4.3.2" - node-sass "^4.8.3" - through2 "^2.0.0" - vinyl-sourcemaps-apply "^0.2.0" - -gulp-sourcemaps@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-1.6.0.tgz#b86ff349d801ceb56e1d9e7dc7bbcb4b7dee600c" - dependencies: - convert-source-map "^1.1.1" - graceful-fs "^4.1.2" - strip-bom "^2.0.0" - through2 "^2.0.0" - vinyl "^1.0.0" - -gulp-sourcemaps@^2.6.0: - version "2.6.4" - resolved "https://registry.yarnpkg.com/gulp-sourcemaps/-/gulp-sourcemaps-2.6.4.tgz#cbb2008450b1bcce6cd23bf98337be751bf6e30a" - dependencies: - "@gulp-sourcemaps/identity-map" "1.X" - "@gulp-sourcemaps/map-sources" "1.X" - acorn "5.X" - convert-source-map "1.X" - css "2.X" - debug-fabulous "1.X" - detect-newline "2.X" - graceful-fs "4.X" - source-map "~0.6.0" - strip-bom-string "1.X" - through2 "2.X" - -gulp-uglify@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/gulp-uglify/-/gulp-uglify-2.1.2.tgz#6db85b1d0ee63d18058592b658649d65c2ec4541" - dependencies: - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash "^4.13.1" - make-error-cause "^1.1.1" - through2 "^2.0.0" - uglify-js "~2.8.10" - uglify-save-license "^0.4.1" - vinyl-sourcemaps-apply "^0.2.0" - -gulp-util@^3.0, gulp-util@^3.0.7: - version "3.0.8" - resolved "https://registry.yarnpkg.com/gulp-util/-/gulp-util-3.0.8.tgz#0054e1e744502e27c04c187c3ecc505dd54bbb4f" - dependencies: - array-differ "^1.0.0" - array-uniq "^1.0.2" - beeper "^1.0.0" - chalk "^1.0.0" - dateformat "^2.0.0" - fancy-log "^1.1.0" - gulplog "^1.0.0" - has-gulplog "^0.1.0" - lodash._reescape "^3.0.0" - lodash._reevaluate "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.template "^3.0.0" - minimist "^1.1.0" - multipipe "^0.1.2" - object-assign "^3.0.0" - replace-ext "0.0.1" - through2 "^2.0.0" - vinyl "^0.5.0" - -gulp@~4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.0.tgz#95766c601dade4a77ed3e7b2b6dc03881b596366" - dependencies: - glob-watcher "^5.0.0" - gulp-cli "^2.0.0" - undertaker "^1.0.0" - vinyl-fs "^3.0.0" - -gulplog@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" - dependencies: - glogg "^1.0.0" - -har-schema@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - -har-validator@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" - dependencies: - ajv "^4.9.1" - har-schema "^1.0.5" - -har-validator@~5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" - dependencies: - ajv "^5.1.0" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - dependencies: - ansi-regex "^2.0.0" - -has-binary@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/has-binary/-/has-binary-0.1.7.tgz#68e61eb16210c9545a0a5cce06a873912fe1e68c" - dependencies: - isarray "0.0.1" - -has-cors@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" - -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" - -has-gulplog@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/has-gulplog/-/has-gulplog-0.1.0.tgz#6414c82913697da51590397dafb12f22967811ce" - dependencies: - sparkles "^1.0.0" - -has-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44" - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - dependencies: - function-bind "^1.1.1" - -hasha@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/hasha/-/hasha-2.2.0.tgz#78d7cbfc1e6d66303fe79837365984517b2f6ee1" - dependencies: - is-stream "^1.0.1" - pinkie-promise "^2.0.0" - -hawk@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" - dependencies: - boom "2.x.x" - cryptiles "2.x.x" - hoek "2.x.x" - sntp "1.x.x" - -hoek@2.x.x: - version "2.16.3" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" - -homedir-polyfill@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" - dependencies: - parse-passwd "^1.0.0" - -hosted-git-info@^2.1.4: - version "2.7.1" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" - -html-comment-regex@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" - -htmlparser2@3.8.x, htmlparser2@~3.8.1: - version "3.8.3" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068" - dependencies: - domelementtype "1" - domhandler "2.3" - domutils "1.5" - entities "1.0" - readable-stream "1.1" - -http-errors@1.6.3, http-errors@~1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-proxy@^1.13.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" - dependencies: - eventemitter3 "^3.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -http-signature@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" - dependencies: - assert-plus "^0.2.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -iconv-lite@0.4.23, iconv-lite@^0.4.4: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" - dependencies: - safer-buffer ">= 2.1.2 < 3" - -ignore-walk@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" - dependencies: - minimatch "^3.0.4" - -in-publish@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - dependencies: - repeating "^2.0.0" - -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -ini@^1.3.4, ini@~1.3.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - -interpret@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - -is-absolute@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" - dependencies: - is-relative "^1.0.0" - is-windows "^1.0.1" - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - dependencies: - kind-of "^6.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - dependencies: - binary-extensions "^1.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - dependencies: - builtin-modules "^1.0.0" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - dependencies: - kind-of "^6.0.0" - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - dependencies: - is-primitive "^2.0.0" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - -is-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - dependencies: - is-extglob "^1.0.0" - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" - dependencies: - is-extglob "^2.1.1" - -is-negated-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" - -is-number@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806" - -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - dependencies: - kind-of "^3.0.2" - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - dependencies: - kind-of "^3.0.2" - -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - -is-plain-obj@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - dependencies: - isobject "^3.0.1" - -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - -is-promise@^2.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - -is-relative@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" - dependencies: - is-unc-path "^1.0.0" - -is-stream@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - -is-svg@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" - dependencies: - html-comment-regex "^1.1.0" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - -is-unc-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" - dependencies: - unc-path-regex "^0.1.2" - -is-utf8@^0.2.0, is-utf8@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - -is-valid-glob@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-0.3.0.tgz#d4b55c69f51886f9b65c70d6c2622d37e29f48fe" - -is-valid-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" - -is-windows@^1.0.1, is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isbinaryfile@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - -jasmine-core@^2.6.1: - version "2.99.1" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.99.1.tgz#e6400df1e6b56e130b61c4bcd093daa7f6e8ca15" - -js-base64@^2.1.8, js-base64@^2.1.9: - version "2.4.8" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.8.tgz#57a9b130888f956834aa40c5b165ba59c758f033" - -js-yaml@~3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" - dependencies: - argparse "^1.0.7" - esprima "^2.6.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - -jsesc@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe" - -jshint@^2.9.4: - version "2.9.5" - resolved "https://registry.yarnpkg.com/jshint/-/jshint-2.9.5.tgz#1e7252915ce681b40827ee14248c46d34e9aa62c" - dependencies: - cli "~1.0.0" - console-browserify "1.1.x" - exit "0.1.x" - htmlparser2 "3.8.x" - lodash "3.7.x" - minimatch "~3.0.2" - shelljs "0.3.x" - strip-json-comments "1.0.x" - -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - -json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - dependencies: - jsonify "~0.0.0" - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - -json3@3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" - -jsonfile@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" - optionalDependencies: - graceful-fs "^4.1.6" - -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -just-debounce@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea" - -karma-chrome-launcher@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz#cf1b9d07136cc18fe239327d24654c3dbc368acf" - dependencies: - fs-access "^1.0.0" - which "^1.2.1" - -karma-jasmine@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.2.tgz#394f2b25ffb4a644b9ada6f22d443e2fd08886c3" - -karma-phantomjs-launcher@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/karma-phantomjs-launcher/-/karma-phantomjs-launcher-1.0.4.tgz#d23ca34801bda9863ad318e3bb4bd4062b13acd2" - dependencies: - lodash "^4.0.1" - phantomjs-prebuilt "^2.1.7" - -karma@^1.5.0: - version "1.7.1" - resolved "https://registry.yarnpkg.com/karma/-/karma-1.7.1.tgz#85cc08e9e0a22d7ce9cca37c4a1be824f6a2b1ae" - dependencies: - bluebird "^3.3.0" - body-parser "^1.16.1" - chokidar "^1.4.1" - colors "^1.1.0" - combine-lists "^1.0.0" - connect "^3.6.0" - core-js "^2.2.0" - di "^0.0.1" - dom-serialize "^2.2.0" - expand-braces "^0.1.1" - glob "^7.1.1" - graceful-fs "^4.1.2" - http-proxy "^1.13.0" - isbinaryfile "^3.0.0" - lodash "^3.8.0" - log4js "^0.6.31" - mime "^1.3.4" - minimatch "^3.0.2" - optimist "^0.6.1" - qjobs "^1.1.4" - range-parser "^1.2.0" - rimraf "^2.6.0" - safe-buffer "^5.0.1" - socket.io "1.7.3" - source-map "^0.5.3" - tmp "0.0.31" - useragent "^2.1.12" - -kew@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/kew/-/kew-0.7.0.tgz#79d93d2d33363d6fdd2970b335d9141ad591d79b" - -kind-of@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0, kind-of@^5.0.2: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - -klaw@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" - optionalDependencies: - graceful-fs "^4.1.9" - -last-run@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b" - dependencies: - default-resolution "^2.0.0" - es6-weak-map "^2.0.1" - -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - -lazystream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" - dependencies: - readable-stream "^2.0.5" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - dependencies: - invert-kv "^1.0.0" - -lead@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" - dependencies: - flush-write-stream "^1.0.2" - -liftoff@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-2.5.0.tgz#2009291bb31cea861bbf10a7c15a28caf75c31ec" - dependencies: - extend "^3.0.0" - findup-sync "^2.0.0" - fined "^1.0.1" - flagged-respawn "^1.0.0" - is-plain-object "^2.0.4" - object.map "^1.0.0" - rechoir "^0.6.2" - resolve "^1.1.7" - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -lodash._basecopy@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" - -lodash._basetostring@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz#d1861d877f824a52f669832dcaf3ee15566a07d5" - -lodash._basevalues@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz#5b775762802bde3d3297503e26300820fdf661b7" - -lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - -lodash._isiterateecall@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c" - -lodash._reescape@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reescape/-/lodash._reescape-3.0.0.tgz#2b1d6f5dfe07c8a355753e5f27fac7f1cde1616a" - -lodash._reevaluate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz#58bc74c40664953ae0b124d806996daca431e2ed" - -lodash._reinterpolate@^3.0.0, lodash._reinterpolate@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - -lodash._root@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" - -lodash.assign@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" - -lodash.clonedeep@^4.3.2: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - -lodash.escape@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-3.2.0.tgz#995ee0dc18c1b48cc92effae71a10aab5b487698" - dependencies: - lodash._root "^3.0.0" - -lodash.isarguments@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" - -lodash.isarray@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" - -lodash.isequal@^4.0.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - -lodash.isobject@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - -lodash.keys@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" - dependencies: - lodash._getnative "^3.0.0" - lodash.isarguments "^3.0.0" - lodash.isarray "^3.0.0" - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - -lodash.merge@^4.6.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54" - -lodash.mergewith@^4.6.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" - -lodash.restparam@^3.0.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - -lodash.template@^3.0.0, lodash.template@^3.6.2: - version "3.6.2" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-3.6.2.tgz#f8cdecc6169a255be9098ae8b0c53d378931d14f" - dependencies: - lodash._basecopy "^3.0.0" - lodash._basetostring "^3.0.0" - lodash._basevalues "^3.0.0" - lodash._isiterateecall "^3.0.0" - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - lodash.keys "^3.0.0" - lodash.restparam "^3.0.0" - lodash.templatesettings "^3.0.0" - -lodash.template@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" - dependencies: - lodash._reinterpolate "~3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz#fb307844753b66b9f1afa54e262c745307dba8e5" - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.escape "^3.0.0" - -lodash.templatesettings@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz#2b4d4e95ba440d915ff08bc899e4553666713316" - dependencies: - lodash._reinterpolate "~3.0.0" - -lodash.unescape@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - -lodash@3.7.x: - version "3.7.0" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.7.0.tgz#3678bd8ab995057c07ade836ed2ef087da811d45" - -lodash@^3.2.0, lodash@^3.8.0: - version "3.10.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" - -lodash@^4.0.0, lodash@^4.0.1, lodash@^4.12.0, lodash@^4.13.1, lodash@^4.5.0, lodash@~4.17.10: - version "4.17.10" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" - -log4js@^0.6.31: - version "0.6.38" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd" - dependencies: - readable-stream "~1.0.2" - semver "~4.3.3" - -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lru-cache@4.1.x, lru-cache@^4.0.1: - version "4.1.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-queue@0.1: - version "0.1.0" - resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" - dependencies: - es5-ext "~0.10.2" - -main-bower-files@^2.13.1: - version "2.13.1" - resolved "https://registry.yarnpkg.com/main-bower-files/-/main-bower-files-2.13.1.tgz#7e1bc5c498352ccecd5df087f13d5f31bc057d3e" - dependencies: - chalk "^1.0.0" - extend "^2.0.1" - globby "^2.0.0" - multimatch "^2.0.0" - path-exists "^1.0.0" - strip-json-comments "^1.0.2" - vinyl-fs "^2.4.3" - -make-error-cause@^1.1.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/make-error-cause/-/make-error-cause-1.2.2.tgz#df0388fcd0b37816dff0a5fb8108939777dcbc9d" - dependencies: - make-error "^1.2.0" - -make-error@^1.2.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.4.tgz#19978ed575f9e9545d2ff8c13e33b5d18a67d535" - -make-iterator@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" - dependencies: - kind-of "^6.0.2" - -map-cache@^0.2.0, map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - -map-stream@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - dependencies: - object-visit "^1.0.0" - -matchdep@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" - dependencies: - findup-sync "^2.0.0" - micromatch "^3.0.4" - resolve "^1.4.0" - stack-trace "0.0.10" - -math-expression-evaluator@^1.2.14: - version "1.2.17" - resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" - -math-random@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - -memoizee@0.4.X: - version "0.4.12" - resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.12.tgz#780e99a219c50c549be6d0fc61765080975c58fb" - dependencies: - d "1" - es5-ext "^0.10.30" - es6-weak-map "^2.0.2" - event-emitter "^0.3.5" - is-promise "^2.1" - lru-queue "0.1" - next-tick "1" - timers-ext "^0.1.2" - -meow@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - -merge-stream@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" - dependencies: - readable-stream "^2.0.1" - -micromatch@^2.1.5, micromatch@^2.3.7: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -micromatch@^3.0.4, micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -mime-db@~1.35.0: - version "1.35.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47" - -mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: - version "2.1.19" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.19.tgz#71e464537a7ef81c15f2db9d97e913fc0ff606f0" - dependencies: - mime-db "~1.35.0" - -mime@^1.3.4: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - -"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - -minipass@^2.2.1, minipass@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233" - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minizlib@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" - dependencies: - minipass "^2.2.1" - -mixin-deep@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -ms@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" - -ms@0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - -multimatch@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b" - dependencies: - array-differ "^1.0.0" - array-union "^1.0.1" - arrify "^1.0.0" - minimatch "^3.0.0" - -multipipe@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/multipipe/-/multipipe-0.1.2.tgz#2a8f2ddf70eed564dff2d57f1e1a137d9f05078b" - dependencies: - duplexer2 "0.0.2" - -mute-stdout@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.0.tgz#5b32ea07eb43c9ded6130434cf926f46b2a7fd4d" - -nan@^2.10.0, nan@^2.9.2: - version "2.10.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -needle@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" - dependencies: - debug "^2.1.2" - iconv-lite "^0.4.4" - sax "^1.2.4" - -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - -next-tick@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" - -node-gyp@^3.3.1: - version "3.7.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.7.0.tgz#789478e8f6c45e277aa014f3e28f958f286f9203" - dependencies: - fstream "^1.0.0" - glob "^7.0.3" - graceful-fs "^4.1.2" - mkdirp "^0.5.0" - nopt "2 || 3" - npmlog "0 || 1 || 2 || 3 || 4" - osenv "0" - request ">=2.9.0 <2.82.0" - rimraf "2" - semver "~5.3.0" - tar "^2.0.0" - which "1" - -node-pre-gyp@^0.10.0: - version "0.10.3" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4" - -node-sass@^4.8.3: - version "4.9.2" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.9.2.tgz#5e63fe6bd0f2ae3ac9d6c14ede8620e2b8bdb437" - dependencies: - async-foreach "^0.1.3" - chalk "^1.1.1" - cross-spawn "^3.0.0" - gaze "^1.0.0" - get-stdin "^4.0.1" - glob "^7.0.3" - in-publish "^2.0.0" - lodash.assign "^4.2.0" - lodash.clonedeep "^4.3.2" - lodash.mergewith "^4.6.0" - meow "^3.7.0" - mkdirp "^0.5.1" - nan "^2.10.0" - node-gyp "^3.3.1" - npmlog "^4.0.0" - request "2.87.0" - sass-graph "^2.2.4" - stdout-stream "^1.4.0" - "true-case-path" "^1.0.2" - -"nopt@2 || 3": - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - dependencies: - abbrev "1" - -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - dependencies: - abbrev "1" - osenv "^0.1.4" - -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" - dependencies: - hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - -normalize-url@^1.4.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" - dependencies: - object-assign "^4.0.1" - prepend-http "^1.0.0" - query-string "^4.1.0" - sort-keys "^1.0.0" - -now-and-later@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.0.tgz#bc61cbb456d79cb32207ce47ca05136ff2e7d6ee" - dependencies: - once "^1.3.2" - -npm-bundled@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" - -npm-packlist@^1.1.6: - version "1.1.11" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.11.tgz#84e8c683cbe7867d34b1d357d893ce29e28a02de" - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -nth-check@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" - dependencies: - boolbase "~1.0.0" - -null-check@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/null-check/-/null-check-1.0.0.tgz#977dffd7176012b9ec30d2a39db5cf72a0439edd" - -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -oauth-sign@~0.8.1, oauth-sign@~0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - -object-assign@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" - -object-assign@4.X, object-assign@^4.0.0, object-assign@^4.0.1, object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - -object-assign@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2" - -object-component@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-keys@^1.0.11, object-keys@^1.0.8: - version "1.0.12" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.12.tgz#09c53855377575310cca62f55bb334abff7b3ed2" - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - dependencies: - isobject "^3.0.0" - -object.assign@^4.0.4: - version "4.1.0" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - -object.defaults@^1.0.0, object.defaults@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" - dependencies: - array-each "^1.0.1" - array-slice "^1.0.0" - for-own "^1.0.0" - isobject "^3.0.0" - -object.map@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -object.omit@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" - dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" - -object.pick@^1.2.0, object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - dependencies: - isobject "^3.0.1" - -object.reduce@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.reduce/-/object.reduce-1.0.1.tgz#6fe348f2ac7fa0f95ca621226599096825bb03ad" - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - dependencies: - ee-first "1.1.1" - -once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - dependencies: - wrappy "1" - -optimist@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" - dependencies: - minimist "~0.0.1" - wordwrap "~0.0.2" - -options@>=0.0.5: - version "0.0.6" - resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" - -ordered-read-streams@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-0.3.0.tgz#7137e69b3298bb342247a1bbee3881c80e2fd78b" - dependencies: - is-stream "^1.0.1" - readable-stream "^2.0.1" - -ordered-read-streams@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" - dependencies: - readable-stream "^2.0.1" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - dependencies: - lcid "^1.0.0" - -os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - -osenv@0, osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -parse-filepath@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" - dependencies: - is-absolute "^1.0.0" - map-cache "^0.2.0" - path-root "^0.1.1" - -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - dependencies: - error-ex "^1.2.0" - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - -parsejson@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/parsejson/-/parsejson-0.0.3.tgz#ab7e3759f209ece99437973f7d0f1f64ae0e64ab" - dependencies: - better-assert "~1.0.0" - -parseqs@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" - dependencies: - better-assert "~1.0.0" - -parseuri@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" - dependencies: - better-assert "~1.0.0" - -parseurl@~1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - -path-exists@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-1.0.0.tgz#d5a8998eb71ef37a74c34eb0d9eba6e878eea081" - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - dependencies: - pinkie-promise "^2.0.0" - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -path-parse@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" - -path-root-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - -path-root@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - dependencies: - path-root-regex "^0.1.0" - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -pause-stream@0.0.11: - version "0.0.11" - resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" - dependencies: - through "~2.3" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - -phantomjs-prebuilt@^2.1.7: - version "2.1.16" - resolved "https://registry.yarnpkg.com/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz#efd212a4a3966d3647684ea8ba788549be2aefef" - dependencies: - es6-promise "^4.0.3" - extract-zip "^1.6.5" - fs-extra "^1.0.0" - hasha "^2.2.0" - kew "^0.7.0" - progress "^1.1.8" - request "^2.81.0" - request-progress "^2.0.1" - which "^1.2.10" - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - -plugin-error@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace" - dependencies: - ansi-cyan "^0.1.1" - ansi-red "^0.1.1" - arr-diff "^1.0.1" - arr-union "^2.0.1" - extend-shallow "^1.1.2" - -plugin-error@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" - dependencies: - ansi-colors "^1.0.1" - arr-diff "^4.0.0" - arr-union "^3.1.0" - extend-shallow "^3.0.2" - -pofile@~1.0.0: - version "1.0.11" - resolved "https://registry.yarnpkg.com/pofile/-/pofile-1.0.11.tgz#35aff58c17491d127a07336d5522ebc9df57c954" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - -postcss-calc@^5.2.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" - dependencies: - postcss "^5.0.2" - postcss-message-helpers "^2.0.0" - reduce-css-calc "^1.2.6" - -postcss-colormin@^2.1.8: - version "2.2.2" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b" - dependencies: - colormin "^1.0.5" - postcss "^5.0.13" - postcss-value-parser "^3.2.3" - -postcss-convert-values@^2.3.4: - version "2.6.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d" - dependencies: - postcss "^5.0.11" - postcss-value-parser "^3.1.2" - -postcss-discard-comments@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" - dependencies: - postcss "^5.0.14" - -postcss-discard-duplicates@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932" - dependencies: - postcss "^5.0.4" - -postcss-discard-empty@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" - dependencies: - postcss "^5.0.14" - -postcss-discard-overridden@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" - dependencies: - postcss "^5.0.16" - -postcss-discard-unused@^2.2.1: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" - dependencies: - postcss "^5.0.14" - uniqs "^2.0.0" - -postcss-filter-plugins@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz#82245fdf82337041645e477114d8e593aa18b8ec" - dependencies: - postcss "^5.0.4" - -postcss-merge-idents@^2.1.5: - version "2.1.7" - resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270" - dependencies: - has "^1.0.1" - postcss "^5.0.10" - postcss-value-parser "^3.1.1" - -postcss-merge-longhand@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658" - dependencies: - postcss "^5.0.4" - -postcss-merge-rules@^2.0.3: - version "2.1.2" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721" - dependencies: - browserslist "^1.5.2" - caniuse-api "^1.5.2" - postcss "^5.0.4" - postcss-selector-parser "^2.2.2" - vendors "^1.0.0" - -postcss-message-helpers@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" - -postcss-minify-font-values@^1.0.2: - version "1.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69" - dependencies: - object-assign "^4.0.1" - postcss "^5.0.4" - postcss-value-parser "^3.0.2" - -postcss-minify-gradients@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" - dependencies: - postcss "^5.0.12" - postcss-value-parser "^3.3.0" - -postcss-minify-params@^1.0.4: - version "1.2.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" - dependencies: - alphanum-sort "^1.0.1" - postcss "^5.0.2" - postcss-value-parser "^3.0.2" - uniqs "^2.0.0" - -postcss-minify-selectors@^2.0.4: - version "2.1.1" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" - dependencies: - alphanum-sort "^1.0.2" - has "^1.0.1" - postcss "^5.0.14" - postcss-selector-parser "^2.0.0" - -postcss-normalize-charset@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" - dependencies: - postcss "^5.0.5" - -postcss-normalize-url@^3.0.7: - version "3.0.8" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^1.4.0" - postcss "^5.0.14" - postcss-value-parser "^3.2.3" - -postcss-ordered-values@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" - dependencies: - postcss "^5.0.4" - postcss-value-parser "^3.0.1" - -postcss-reduce-idents@^2.2.2: - version "2.4.0" - resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3" - dependencies: - postcss "^5.0.4" - postcss-value-parser "^3.0.2" - -postcss-reduce-initial@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea" - dependencies: - postcss "^5.0.4" - -postcss-reduce-transforms@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" - dependencies: - has "^1.0.1" - postcss "^5.0.8" - postcss-value-parser "^3.0.1" - -postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90" - dependencies: - flatten "^1.0.2" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-svgo@^2.1.1: - version "2.1.6" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" - dependencies: - is-svg "^2.0.0" - postcss "^5.0.14" - postcss-value-parser "^3.2.3" - svgo "^0.7.0" - -postcss-unique-selectors@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" - dependencies: - alphanum-sort "^1.0.1" - postcss "^5.0.4" - uniqs "^2.0.0" - -postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" - -postcss-zindex@^2.0.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" - dependencies: - has "^1.0.1" - postcss "^5.0.4" - uniqs "^2.0.0" - -postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.8, postcss@^5.2.16: - version "5.2.18" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" - dependencies: - chalk "^1.1.3" - js-base64 "^2.1.9" - source-map "^0.5.6" - supports-color "^3.2.3" - -prepend-http@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - -pretty-hrtime@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" - -process-nextick-args@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - -process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - -progress@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^1.3.5: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - -qjobs@^1.1.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" - -qs@6.5.2, qs@~6.5.1: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - -qs@~6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" - -query-string@^4.1.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" - dependencies: - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - -randomatic@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.0.0.tgz#d35490030eb4f7578de292ce6dfb04a91a128923" - dependencies: - is-number "^4.0.0" - kind-of "^6.0.0" - math-random "^1.0.1" - -range-parser@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - -raw-body@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" - dependencies: - bytes "3.0.0" - http-errors "1.6.3" - iconv-lite "0.4.23" - unpipe "1.0.0" - -rc@^1.2.7: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -rcfinder@^0.1.6: - version "0.1.9" - resolved "https://registry.yarnpkg.com/rcfinder/-/rcfinder-0.1.9.tgz#f3e80f387ddf9ae80ae30a4100329642eae81115" - dependencies: - lodash.clonedeep "^4.3.2" - -rcloader@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/rcloader/-/rcloader-0.2.2.tgz#58d2298b462d0b9bfd2133d2a1ec74fbd705c717" - dependencies: - lodash.assign "^4.2.0" - lodash.isobject "^3.0.2" - lodash.merge "^4.6.0" - rcfinder "^0.1.6" - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -readable-stream@1.1: - version "1.1.13" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.2: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5: - version "2.3.6" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@~1.1.9: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readdirp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" - dependencies: - graceful-fs "^4.1.2" - minimatch "^3.0.2" - readable-stream "^2.0.2" - set-immediate-shim "^1.0.1" - -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - dependencies: - resolve "^1.1.6" - -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - -reduce-css-calc@^1.2.6: - version "1.3.0" - resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" - dependencies: - balanced-match "^0.4.2" - math-expression-evaluator "^1.2.14" - reduce-function-call "^1.0.1" - -reduce-function-call@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" - dependencies: - balanced-match "^0.4.2" - -regex-cache@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" - dependencies: - is-equal-shallow "^0.1.3" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -remove-bom-buffer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" - dependencies: - is-buffer "^1.1.5" - is-utf8 "^0.2.1" - -remove-bom-stream@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523" - dependencies: - remove-bom-buffer "^3.0.0" - safe-buffer "^5.1.0" - through2 "^2.0.3" - -remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - -repeat-element@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" - -repeat-string@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae" - -repeat-string@^1.5.2, repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - dependencies: - is-finite "^1.0.0" - -replace-ext@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" - -replace-ext@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - -replace-homedir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-1.0.0.tgz#e87f6d513b928dde808260c12be7fec6ff6e798c" - dependencies: - homedir-polyfill "^1.0.1" - is-absolute "^1.0.0" - remove-trailing-separator "^1.1.0" - -request-progress@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-2.0.1.tgz#5d36bb57961c673aa5b788dbc8141fdf23b44e08" - dependencies: - throttleit "^1.0.0" - -request@2.87.0, request@^2.81.0: - version "2.87.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.6.0" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" - forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.17" - oauth-sign "~0.8.2" - performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - tough-cookie "~2.3.3" - tunnel-agent "^0.6.0" - uuid "^3.1.0" - -"request@>=2.9.0 <2.82.0": - version "2.81.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.0" - forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~4.2.1" - hawk "~3.1.3" - http-signature "~1.1.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - performance-now "^0.2.0" - qs "~6.4.0" - safe-buffer "^5.0.1" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "^0.6.0" - uuid "^3.0.0" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - -resolve-options@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131" - dependencies: - value-or-function "^3.0.0" - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.4.0: - version "1.8.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" - dependencies: - path-parse "^1.0.5" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - dependencies: - align-text "^0.1.1" - -rimraf@2, rimraf@^2.6.0, rimraf@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" - dependencies: - glob "^7.0.5" - -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - dependencies: - ret "~0.1.10" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - -sass-graph@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" - dependencies: - glob "^7.0.0" - lodash "^4.0.0" - scss-tokenizer "^0.2.3" - yargs "^7.0.0" - -sax@^1.2.4, sax@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - -scss-tokenizer@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" - dependencies: - js-base64 "^2.1.8" - source-map "^0.4.2" - -semver-greatest-satisfied-range@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" - dependencies: - sver-compat "^1.5.0" - -"semver@2 || 3 || 4 || 5", semver@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" - -semver@5.3.0, semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - -semver@~4.3.3: - version "4.3.6" - resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" - -set-blocking@^2.0.0, set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - -set-value@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.1" - to-object-path "^0.3.0" - -set-value@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - -shelljs@0.3.x: - version "0.3.0" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.3.0.tgz#3596e6307a781544f591f37da618360f31db57b1" - -signal-exit@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -sntp@1.x.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" - dependencies: - hoek "2.x.x" - -socket.io-adapter@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-0.5.0.tgz#cb6d4bb8bec81e1078b99677f9ced0046066bb8b" - dependencies: - debug "2.3.3" - socket.io-parser "2.3.1" - -socket.io-client@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-1.7.3.tgz#b30e86aa10d5ef3546601c09cde4765e381da377" - dependencies: - backo2 "1.0.2" - component-bind "1.0.0" - component-emitter "1.2.1" - debug "2.3.3" - engine.io-client "1.8.3" - has-binary "0.1.7" - indexof "0.0.1" - object-component "0.0.3" - parseuri "0.0.5" - socket.io-parser "2.3.1" - to-array "0.1.4" - -socket.io-parser@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-2.3.1.tgz#dd532025103ce429697326befd64005fcfe5b4a0" - dependencies: - component-emitter "1.1.2" - debug "2.2.0" - isarray "0.0.1" - json3 "3.3.2" - -socket.io@1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-1.7.3.tgz#b8af9caba00949e568e369f1327ea9be9ea2461b" - dependencies: - debug "2.3.3" - engine.io "1.8.3" - has-binary "0.1.7" - object-assign "4.1.0" - socket.io-adapter "0.5.0" - socket.io-client "1.7.3" - socket.io-parser "2.3.1" - -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" - dependencies: - is-plain-obj "^1.0.0" - -source-map-resolve@^0.5.0, source-map-resolve@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" - dependencies: - atob "^2.1.1" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" - -source-map@^0.1.38: - version "0.1.43" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" - dependencies: - amdefine ">=0.0.4" - -source-map@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" - dependencies: - amdefine ">=0.0.4" - -source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - -sparkles@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" - -spdx-correct@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" - -spdx-expression-parse@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - dependencies: - extend-shallow "^3.0.0" - -split@0.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" - dependencies: - through "2" - -sprintf-js@^1.0.3: - version "1.1.1" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - -sshpk@^1.7.0: - version "1.14.2" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98" - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - safer-buffer "^2.0.2" - optionalDependencies: - bcrypt-pbkdf "^1.0.0" - ecc-jsbn "~0.1.1" - jsbn "~0.1.0" - tweetnacl "~0.14.0" - -stack-trace@0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - -statuses@~1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" - -stdout-stream@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" - dependencies: - readable-stream "^2.0.1" - -stream-combiner@~0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" - dependencies: - duplexer "~0.1.1" - -stream-exhaust@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/stream-exhaust/-/stream-exhaust-1.0.2.tgz#acdac8da59ef2bc1e17a2c0ccf6c320d120e555d" - -stream-shift@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" - -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - -string-width@^1.0.1, string-width@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2": - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - dependencies: - safe-buffer "~5.1.0" - -stringstream@~0.0.4: - version "0.0.6" - resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - dependencies: - ansi-regex "^3.0.0" - -strip-bom-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz#e7144398577d51a6bed0fa1994fa05f43fd988ee" - dependencies: - first-chunk-stream "^1.0.0" - strip-bom "^2.0.0" - -strip-bom-string@1.X: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - dependencies: - is-utf8 "^0.2.0" - -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - dependencies: - get-stdin "^4.0.1" - -strip-json-comments@1.0.x, strip-json-comments@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - -supports-color@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - dependencies: - has-flag "^1.0.0" - -sver-compat@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" - dependencies: - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - -svgo@^0.7.0: - version "0.7.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" - dependencies: - coa "~1.0.1" - colors "~1.1.2" - csso "~2.3.1" - js-yaml "~3.7.0" - mkdirp "~0.5.1" - sax "~1.2.1" - whet.extend "~0.9.9" - -tar@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" - -tar@^4: - version "4.4.4" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd" - dependencies: - chownr "^1.0.1" - fs-minipass "^1.2.5" - minipass "^2.3.3" - minizlib "^1.1.0" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.2" - -ternary-stream@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ternary-stream/-/ternary-stream-2.0.1.tgz#064e489b4b5bf60ba6a6b7bc7f2f5c274ecf8269" - dependencies: - duplexify "^3.5.0" - fork-stream "^0.0.4" - merge-stream "^1.0.0" - through2 "^2.0.1" - -throttleit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" - -through2-filter@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-2.0.0.tgz#60bc55a0dacb76085db1f9dae99ab43f83d622ec" - dependencies: - through2 "~2.0.0" - xtend "~4.0.0" - -through2@2.X, through2@^2.0.0, through2@^2.0.1, through2@^2.0.3, through2@~2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" - dependencies: - readable-stream "^2.1.5" - xtend "~4.0.1" - -through2@^0.6.0: - version "0.6.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" - dependencies: - readable-stream ">=1.0.33-1 <1.1.0-0" - xtend ">=4.0.0 <4.1.0-0" - -through@2, through@~2.3, through@~2.3.1: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - -timers-ext@^0.1.2: - version "0.1.5" - resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.5.tgz#77147dd4e76b660c2abb8785db96574cbbd12922" - dependencies: - es5-ext "~0.10.14" - next-tick "1" - -tmp@0.0.31: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" - dependencies: - os-tmpdir "~1.0.1" - -tmp@0.0.x: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - dependencies: - os-tmpdir "~1.0.2" - -to-absolute-glob@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-0.1.1.tgz#1cdfa472a9ef50c239ee66999b662ca0eb39937f" - dependencies: - extend-shallow "^2.0.1" - -to-absolute-glob@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" - dependencies: - is-absolute "^1.0.0" - is-negated-glob "^1.0.0" - -to-array@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -to-through@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6" - dependencies: - through2 "^2.0.3" - -tough-cookie@~2.3.0, tough-cookie@~2.3.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" - dependencies: - punycode "^1.4.1" - -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - -"true-case-path@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" - dependencies: - glob "^6.0.4" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - -type-is@~1.6.16: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" - dependencies: - media-typer "0.3.0" - mime-types "~2.1.18" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - -typescript-eslint-parser@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/typescript-eslint-parser/-/typescript-eslint-parser-3.0.0.tgz#dd0435b303abc841464c02d00184d7b39bd488b5" - dependencies: - lodash.unescape "4.0.1" - semver "5.3.0" - -typescript@~2.3.2: - version "2.3.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.4.tgz#3d38321828231e434f287514959c37a82b629f42" - -uglify-js@~2.8.10: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" - dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" - -uglify-save-license@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/uglify-save-license/-/uglify-save-license-0.4.1.tgz#95726c17cc6fd171c3617e3bf4d8d82aa8c4cce1" - -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - -ultron@1.0.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" - -unc-path-regex@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - -undertaker-registry@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" - -undertaker@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-1.2.0.tgz#339da4646252d082dc378e708067299750e11b49" - dependencies: - arr-flatten "^1.0.1" - arr-map "^2.0.0" - bach "^1.0.0" - collection-map "^1.0.0" - es6-weak-map "^2.0.1" - last-run "^1.1.0" - object.defaults "^1.0.0" - object.reduce "^1.0.0" - undertaker-registry "^1.0.0" - -union-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^0.4.3" - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - -unique-stream@^2.0.2: - version "2.2.1" - resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.2.1.tgz#5aa003cfbe94c5ff866c4e7d668bb1c4dbadb369" - dependencies: - json-stable-stringify "^1.0.0" - through2-filter "^2.0.0" - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -upath@^1.0.5: - version "1.1.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - -useragent@^2.1.12: - version "2.3.0" - resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.3.0.tgz#217f943ad540cb2128658ab23fc960f6a88c9972" - dependencies: - lru-cache "4.1.x" - tmp "0.0.x" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - -uuid@^3.0.0, uuid@^3.1.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" - -v8flags@^3.0.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.1.1.tgz#42259a1461c08397e37fe1d4f1cfb59cad85a053" - dependencies: - homedir-polyfill "^1.0.1" - -vali-date@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/vali-date/-/vali-date-1.0.0.tgz#1b904a59609fb328ef078138420934f6b86709a6" - -validate-npm-package-license@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -value-or-function@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" - -vendors@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801" - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vinyl-fs@^2.4.3: - version "2.4.4" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-2.4.4.tgz#be6ff3270cb55dfd7d3063640de81f25d7532239" - dependencies: - duplexify "^3.2.0" - glob-stream "^5.3.2" - graceful-fs "^4.0.0" - gulp-sourcemaps "1.6.0" - is-valid-glob "^0.3.0" - lazystream "^1.0.0" - lodash.isequal "^4.0.0" - merge-stream "^1.0.0" - mkdirp "^0.5.0" - object-assign "^4.0.0" - readable-stream "^2.0.4" - strip-bom "^2.0.0" - strip-bom-stream "^1.0.0" - through2 "^2.0.0" - through2-filter "^2.0.0" - vali-date "^1.0.0" - vinyl "^1.0.0" - -vinyl-fs@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" - dependencies: - fs-mkdirp-stream "^1.0.0" - glob-stream "^6.1.0" - graceful-fs "^4.0.0" - is-valid-glob "^1.0.0" - lazystream "^1.0.0" - lead "^1.0.0" - object.assign "^4.0.4" - pumpify "^1.3.5" - readable-stream "^2.3.3" - remove-bom-buffer "^3.0.0" - remove-bom-stream "^1.2.0" - resolve-options "^1.1.0" - through2 "^2.0.0" - to-through "^2.0.0" - value-or-function "^3.0.0" - vinyl "^2.0.0" - vinyl-sourcemap "^1.1.0" - -vinyl-sourcemap@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" - dependencies: - append-buffer "^1.0.2" - convert-source-map "^1.5.0" - graceful-fs "^4.1.6" - normalize-path "^2.1.1" - now-and-later "^2.0.0" - remove-bom-buffer "^3.0.0" - vinyl "^2.0.0" - -vinyl-sourcemaps-apply@^0.2.0, vinyl-sourcemaps-apply@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" - dependencies: - source-map "^0.5.1" - -vinyl@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884" - dependencies: - clone "^1.0.0" - clone-stats "^0.0.1" - replace-ext "0.0.1" - -vinyl@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.0.tgz#d85b07da96e458d25b2ffe19fece9f2caa13ed86" - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - -void-elements@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" - -whet.extend@~0.9.9: - version "0.9.9" - resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" - -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - -which@1, which@^1.2.1, which@^1.2.10, which@^1.2.14, which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - dependencies: - string-width "^1.0.2 || 2" - -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - -wordwrap@~0.0.2: - version "0.0.3" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -ws@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f" - dependencies: - options ">=0.0.5" - ultron "1.0.x" - -wtf-8@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a" - -xmlhttprequest-ssl@1.5.3: - version "1.5.3" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d" - -"xtend@>=4.0.0 <4.1.0-0", xtend@~4.0.0, xtend@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - -y18n@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - -yallist@^3.0.0, yallist@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" - -yargs-parser@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" - dependencies: - camelcase "^3.0.0" - -yargs@^7.0.0, yargs@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" - dependencies: - camelcase "^3.0.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" - y18n "^3.2.1" - yargs-parser "^5.0.0" - -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" - -yauzl@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" - dependencies: - fd-slicer "~1.0.1" - -yeast@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"