From b14833fbb56bcd5bff0750c16fd9214009b955be Mon Sep 17 00:00:00 2001 From: Zara Gebru Date: Fri, 2 Dec 2016 15:25:23 +0100 Subject: [refactor] move app dir into public dir --- web-ui/.bowerrc | 2 +- web-ui/.jshintignore | 8 +- web-ui/.tx/config | 4 +- web-ui/app/404.html | 157 -- web-ui/app/favicon.ico | 0 web-ui/app/fonts/OpenSans-Bold.woff | Bin 14504 -> 0 bytes web-ui/app/fonts/OpenSans-BoldItalic.woff | Bin 15488 -> 0 bytes web-ui/app/fonts/OpenSans-Extrabold.woff | Bin 15312 -> 0 bytes web-ui/app/fonts/OpenSans-ExtraboldItalic.woff | Bin 15932 -> 0 bytes web-ui/app/fonts/OpenSans-Italic.woff | Bin 15768 -> 0 bytes web-ui/app/fonts/OpenSans-Light.woff | Bin 15048 -> 0 bytes web-ui/app/fonts/OpenSans-Semibold.woff | Bin 15236 -> 0 bytes web-ui/app/fonts/OpenSans-SemiboldItalic.woff | Bin 15736 -> 0 bytes web-ui/app/fonts/OpenSans.woff | Bin 14604 -> 0 bytes web-ui/app/fonts/OpenSansLight-Italic.woff | Bin 15956 -> 0 bytes web-ui/app/fonts/icomoon.ttf | Bin 1272 -> 0 bytes web-ui/app/fonts/icomoon.woff | Bin 1348 -> 0 bytes web-ui/app/images/LOADING-transparent.gif | Bin 16170 -> 0 bytes web-ui/app/images/fa-sent.svg | 15 - web-ui/app/images/favicon.png | Bin 592 -> 0 bytes web-ui/app/images/logo.svg | 30 - .../pixelated-symbol-blue-transparent-01.png | Bin 9075 -> 0 bytes web-ui/app/index.html | 113 -- web-ui/app/js/dispatchers/left_pane_dispatcher.js | 62 - .../app/js/dispatchers/middle_pane_dispatcher.js | 74 - web-ui/app/js/dispatchers/right_pane_dispatcher.js | 117 -- web-ui/app/js/features/features.js | 61 - web-ui/app/js/feedback/feedback_cache.js | 35 - web-ui/app/js/feedback/feedback_trigger.js | 39 - web-ui/app/js/foundation/initialize_foundation.js | 5 - web-ui/app/js/foundation/off_canvas.js | 46 - web-ui/app/js/helpers/browser.js | 36 - web-ui/app/js/helpers/contenttype.js | 184 -- web-ui/app/js/helpers/iterator.js | 60 - web-ui/app/js/helpers/monitored_ajax.js | 67 - web-ui/app/js/helpers/sanitizer.js | 126 -- web-ui/app/js/helpers/triggering.js | 29 - web-ui/app/js/helpers/view_helper.js | 163 -- web-ui/app/js/lib/highlightRegex.js | 127 -- web-ui/app/js/lib/html4-defs.js | 640 ------ web-ui/app/js/mail_list/domain/refresher.js | 43 - web-ui/app/js/mail_list/ui/mail_item_factory.js | 59 - .../app/js/mail_list/ui/mail_items/draft_item.js | 55 - .../mail_list/ui/mail_items/generic_mail_item.js | 97 - web-ui/app/js/mail_list/ui/mail_items/mail_item.js | 88 - web-ui/app/js/mail_list/ui/mail_items/sent_item.js | 61 - web-ui/app/js/mail_list/ui/mail_list.js | 182 -- .../mail_list_actions/ui/archive_many_trigger.js | 29 - .../app/js/mail_list_actions/ui/compose_trigger.js | 57 - .../js/mail_list_actions/ui/delete_many_trigger.js | 47 - .../js/mail_list_actions/ui/mail_list_actions.js | 93 - .../mail_list_actions/ui/mark_as_unread_trigger.js | 47 - .../ui/mark_many_as_read_trigger.js | 47 - .../js/mail_list_actions/ui/pagination_trigger.js | 66 - .../mail_list_actions/ui/recover_many_trigger.js | 47 - .../app/js/mail_list_actions/ui/refresh_trigger.js | 44 - .../ui/toggle_check_all_trigger.js | 49 - web-ui/app/js/mail_view/data/feedback_sender.js | 49 - web-ui/app/js/mail_view/data/mail_builder.js | 102 - web-ui/app/js/mail_view/data/mail_sender.js | 93 - web-ui/app/js/mail_view/ui/attachment_icon.js | 61 - web-ui/app/js/mail_view/ui/attachment_list.js | 210 -- web-ui/app/js/mail_view/ui/compose_box.js | 84 - web-ui/app/js/mail_view/ui/draft_box.js | 109 -- web-ui/app/js/mail_view/ui/draft_save_status.js | 42 - web-ui/app/js/mail_view/ui/feedback_box.js | 69 - web-ui/app/js/mail_view/ui/forward_box.js | 97 - web-ui/app/js/mail_view/ui/mail_actions.js | 84 - web-ui/app/js/mail_view/ui/mail_view.js | 255 --- .../app/js/mail_view/ui/no_mails_available_pane.js | 50 - .../js/mail_view/ui/no_message_selected_pane.js | 41 - web-ui/app/js/mail_view/ui/recipients/recipient.js | 112 -- .../app/js/mail_view/ui/recipients/recipients.js | 193 -- .../js/mail_view/ui/recipients/recipients_input.js | 180 -- .../mail_view/ui/recipients/recipients_iterator.js | 59 - web-ui/app/js/mail_view/ui/reply_box.js | 116 -- web-ui/app/js/mail_view/ui/reply_section.js | 129 -- web-ui/app/js/mail_view/ui/send_button.js | 130 -- web-ui/app/js/main.js | 84 - web-ui/app/js/mixins/with_auto_refresh.js | 47 - web-ui/app/js/mixins/with_compose_inline.js | 84 - .../app/js/mixins/with_enable_disable_on_event.js | 48 - web-ui/app/js/mixins/with_feature_toggle.js | 40 - web-ui/app/js/mixins/with_hide_and_show.js | 31 - web-ui/app/js/mixins/with_mail_edit_base.js | 263 --- web-ui/app/js/mixins/with_mail_sandbox.js | 80 - web-ui/app/js/mixins/with_mail_tagging.js | 69 - web-ui/app/js/monkey_patching/all.js | 17 - web-ui/app/js/monkey_patching/array.js | 27 - web-ui/app/js/monkey_patching/post_message.js | 32 - web-ui/app/js/page/default.js | 146 -- web-ui/app/js/page/events.js | 222 --- web-ui/app/js/page/logout.js | 43 - web-ui/app/js/page/logout_shortcut.js | 33 - web-ui/app/js/page/pane_contract_expand.js | 51 - web-ui/app/js/page/pix_logo.js | 62 - web-ui/app/js/page/router.js | 71 - web-ui/app/js/page/router/url_params.js | 57 - web-ui/app/js/page/unread_count_title.js | 53 - web-ui/app/js/page/version.js | 41 - web-ui/app/js/sandbox.js | 11 - web-ui/app/js/search/results_highlighter.js | 97 - web-ui/app/js/search/search_trigger.js | 81 - web-ui/app/js/services/delete_service.js | 59 - web-ui/app/js/services/mail_service.js | 335 ---- web-ui/app/js/services/model/mail.js | 126 -- web-ui/app/js/services/recover_service.js | 38 - web-ui/app/js/style_guide/main.js | 33 - web-ui/app/js/tags/data/tags.js | 66 - web-ui/app/js/tags/ui/tag.js | 154 -- web-ui/app/js/tags/ui/tag_base.js | 68 - web-ui/app/js/tags/ui/tag_list.js | 105 - web-ui/app/js/user_alerts/ui/user_alerts.js | 57 - web-ui/app/js/user_settings/data/user_settings.js | 52 - .../app/js/user_settings/ui/user_settings_box.js | 77 - .../app/js/user_settings/ui/user_settings_icon.js | 57 - web-ui/app/js/views/i18n.js | 62 - web-ui/app/js/views/recipientListFormatter.js | 33 - web-ui/app/js/views/templates.js | 85 - web-ui/app/locales/en_US/translation.json | 72 - web-ui/app/locales/pt_BR/translation.json | 72 - web-ui/app/locales/sv_SE/translation.json | 42 - web-ui/app/robots.txt | 3 - web-ui/app/sandbox.html | 16 - web-ui/app/scss/_mixins.scss | 71 - web-ui/app/scss/_others.scss | 72 - web-ui/app/scss/base/_colors.scss | 64 - web-ui/app/scss/base/_fonts.scss | 68 - web-ui/app/scss/base/_scaffolding.scss | 10 - web-ui/app/scss/mixins/_position-helpers.scss | 9 - web-ui/app/scss/mixins/_tags.scss | 110 -- web-ui/app/scss/sandbox.scss | 27 - web-ui/app/scss/style.scss | 39 - .../scss/templates/_no-content-placeholder.scss | 5 - web-ui/app/scss/templates/_unread-count.scss | 14 - web-ui/app/scss/vendor/_customfont.scss | 9 - web-ui/app/scss/vendor/_foundation.scss | 2066 -------------------- web-ui/app/scss/vendor/_reset.scss | 421 ---- web-ui/app/scss/vendor/_scut.scss | 1518 -------------- web-ui/app/scss/views/_action-bar.scss | 159 -- web-ui/app/scss/views/_close-button.scss | 22 - web-ui/app/scss/views/_compose-button.scss | 27 - web-ui/app/scss/views/_compose-view.scss | 451 ----- web-ui/app/scss/views/_mail-list.scss | 124 -- web-ui/app/scss/views/_message-panel.scss | 26 - web-ui/app/scss/views/_navigation.scss | 589 ------ web-ui/app/scss/views/_no-mails-available.scss | 3 - web-ui/app/scss/views/_no-message-selected.scss | 14 - web-ui/app/scss/views/_read-view.scss | 165 -- web-ui/app/scss/views/_security-labels.scss | 67 - web-ui/app/templates/compose/attachment_item.hbs | 4 - .../templates/compose/attachment_upload_item.hbs | 5 - web-ui/app/templates/compose/attachments_list.hbs | 14 - web-ui/app/templates/compose/compose_box.hbs | 32 - web-ui/app/templates/compose/feedback_box.hbs | 18 - web-ui/app/templates/compose/fixed_recipient.hbs | 8 - web-ui/app/templates/compose/inline_box.hbs | 20 - .../app/templates/compose/no_mails_available.hbs | 7 - .../app/templates/compose/no_message_selected.hbs | 3 - web-ui/app/templates/compose/recipient_input.hbs | 1 - web-ui/app/templates/compose/recipients.hbs | 33 - web-ui/app/templates/compose/reply_section.hbs | 6 - .../templates/compose/upload_attachment_failed.hbs | 6 - web-ui/app/templates/feedback/feedback_trigger.hbs | 8 - web-ui/app/templates/mail_actions/actions_box.hbs | 7 - .../app/templates/mail_actions/compose_trigger.hbs | 3 - .../templates/mail_actions/pagination_trigger.hbs | 3 - .../app/templates/mail_actions/refresh_trigger.hbs | 3 - .../templates/mail_actions/trash_actions_box.hbs | 5 - web-ui/app/templates/mails/draft.hbs | 41 - web-ui/app/templates/mails/full_view.hbs | 83 - web-ui/app/templates/mails/mail_actions.hbs | 6 - web-ui/app/templates/mails/sent.hbs | 36 - web-ui/app/templates/mails/single.hbs | 28 - web-ui/app/templates/mails/trash.hbs | 32 - web-ui/app/templates/page/logout.hbs | 9 - web-ui/app/templates/page/logout_shortcut.hbs | 6 - web-ui/app/templates/page/user_settings_box.hbs | 10 - web-ui/app/templates/page/user_settings_icon.hbs | 8 - web-ui/app/templates/page/version.hbs | 2 - web-ui/app/templates/search/search_trigger.hbs | 3 - web-ui/app/templates/tags/shortcut.hbs | 9 - web-ui/app/templates/tags/tag.hbs | 3 - web-ui/app/templates/tags/tag_inner.hbs | 4 - web-ui/app/templates/tags/tag_list.hbs | 6 - web-ui/app/templates/user_alerts/message.hbs | 1 - web-ui/config/add_git_version.sh | 2 +- web-ui/config/buildoptions.js | 4 +- web-ui/config/compass.rb | 8 +- web-ui/config/control-tower.yml | 2 +- web-ui/config/imagemin.js | 2 +- web-ui/config/package.sh | 32 +- web-ui/karma.conf.js | 54 +- web-ui/package.json | 12 +- web-ui/public/404.html | 157 ++ web-ui/public/favicon.ico | 0 web-ui/public/fonts/OpenSans-Bold.woff | Bin 0 -> 14504 bytes web-ui/public/fonts/OpenSans-BoldItalic.woff | Bin 0 -> 15488 bytes web-ui/public/fonts/OpenSans-Extrabold.woff | Bin 0 -> 15312 bytes web-ui/public/fonts/OpenSans-ExtraboldItalic.woff | Bin 0 -> 15932 bytes web-ui/public/fonts/OpenSans-Italic.woff | Bin 0 -> 15768 bytes web-ui/public/fonts/OpenSans-Light.woff | Bin 0 -> 15048 bytes web-ui/public/fonts/OpenSans-Semibold.woff | Bin 0 -> 15236 bytes web-ui/public/fonts/OpenSans-SemiboldItalic.woff | Bin 0 -> 15736 bytes web-ui/public/fonts/OpenSans.woff | Bin 0 -> 14604 bytes web-ui/public/fonts/OpenSansLight-Italic.woff | Bin 0 -> 15956 bytes web-ui/public/fonts/icomoon.ttf | Bin 0 -> 1272 bytes web-ui/public/fonts/icomoon.woff | Bin 0 -> 1348 bytes web-ui/public/images/LOADING-transparent.gif | Bin 0 -> 16170 bytes web-ui/public/images/fa-sent.svg | 15 + web-ui/public/images/favicon.png | Bin 0 -> 592 bytes web-ui/public/images/logo.svg | 30 + .../pixelated-symbol-blue-transparent-01.png | Bin 0 -> 9075 bytes web-ui/public/index.html | 113 ++ .../public/js/dispatchers/left_pane_dispatcher.js | 62 + .../js/dispatchers/middle_pane_dispatcher.js | 74 + .../public/js/dispatchers/right_pane_dispatcher.js | 117 ++ web-ui/public/js/features/features.js | 61 + web-ui/public/js/feedback/feedback_cache.js | 35 + web-ui/public/js/feedback/feedback_trigger.js | 39 + .../public/js/foundation/initialize_foundation.js | 5 + web-ui/public/js/foundation/off_canvas.js | 46 + web-ui/public/js/helpers/browser.js | 36 + web-ui/public/js/helpers/contenttype.js | 184 ++ web-ui/public/js/helpers/iterator.js | 60 + web-ui/public/js/helpers/monitored_ajax.js | 67 + web-ui/public/js/helpers/sanitizer.js | 126 ++ web-ui/public/js/helpers/triggering.js | 29 + web-ui/public/js/helpers/view_helper.js | 163 ++ web-ui/public/js/lib/highlightRegex.js | 127 ++ web-ui/public/js/lib/html4-defs.js | 640 ++++++ web-ui/public/js/mail_list/domain/refresher.js | 43 + web-ui/public/js/mail_list/ui/mail_item_factory.js | 59 + .../js/mail_list/ui/mail_items/draft_item.js | 55 + .../mail_list/ui/mail_items/generic_mail_item.js | 97 + .../public/js/mail_list/ui/mail_items/mail_item.js | 88 + .../public/js/mail_list/ui/mail_items/sent_item.js | 61 + web-ui/public/js/mail_list/ui/mail_list.js | 182 ++ .../mail_list_actions/ui/archive_many_trigger.js | 29 + .../js/mail_list_actions/ui/compose_trigger.js | 57 + .../js/mail_list_actions/ui/delete_many_trigger.js | 47 + .../js/mail_list_actions/ui/mail_list_actions.js | 93 + .../mail_list_actions/ui/mark_as_unread_trigger.js | 47 + .../ui/mark_many_as_read_trigger.js | 47 + .../js/mail_list_actions/ui/pagination_trigger.js | 66 + .../mail_list_actions/ui/recover_many_trigger.js | 47 + .../js/mail_list_actions/ui/refresh_trigger.js | 44 + .../ui/toggle_check_all_trigger.js | 49 + web-ui/public/js/mail_view/data/feedback_sender.js | 49 + web-ui/public/js/mail_view/data/mail_builder.js | 102 + web-ui/public/js/mail_view/data/mail_sender.js | 93 + web-ui/public/js/mail_view/ui/attachment_icon.js | 61 + web-ui/public/js/mail_view/ui/attachment_list.js | 210 ++ web-ui/public/js/mail_view/ui/compose_box.js | 84 + web-ui/public/js/mail_view/ui/draft_box.js | 109 ++ web-ui/public/js/mail_view/ui/draft_save_status.js | 42 + web-ui/public/js/mail_view/ui/feedback_box.js | 69 + web-ui/public/js/mail_view/ui/forward_box.js | 97 + web-ui/public/js/mail_view/ui/mail_actions.js | 84 + web-ui/public/js/mail_view/ui/mail_view.js | 255 +++ .../js/mail_view/ui/no_mails_available_pane.js | 50 + .../js/mail_view/ui/no_message_selected_pane.js | 41 + .../public/js/mail_view/ui/recipients/recipient.js | 112 ++ .../js/mail_view/ui/recipients/recipients.js | 193 ++ .../js/mail_view/ui/recipients/recipients_input.js | 180 ++ .../mail_view/ui/recipients/recipients_iterator.js | 59 + web-ui/public/js/mail_view/ui/reply_box.js | 116 ++ web-ui/public/js/mail_view/ui/reply_section.js | 129 ++ web-ui/public/js/mail_view/ui/send_button.js | 130 ++ web-ui/public/js/main.js | 84 + web-ui/public/js/mixins/with_auto_refresh.js | 47 + web-ui/public/js/mixins/with_compose_inline.js | 84 + .../js/mixins/with_enable_disable_on_event.js | 48 + web-ui/public/js/mixins/with_feature_toggle.js | 40 + web-ui/public/js/mixins/with_hide_and_show.js | 31 + web-ui/public/js/mixins/with_mail_edit_base.js | 263 +++ web-ui/public/js/mixins/with_mail_sandbox.js | 80 + web-ui/public/js/mixins/with_mail_tagging.js | 69 + web-ui/public/js/monkey_patching/all.js | 17 + web-ui/public/js/monkey_patching/array.js | 27 + web-ui/public/js/monkey_patching/post_message.js | 32 + web-ui/public/js/page/default.js | 146 ++ web-ui/public/js/page/events.js | 222 +++ web-ui/public/js/page/logout.js | 43 + web-ui/public/js/page/logout_shortcut.js | 33 + web-ui/public/js/page/pane_contract_expand.js | 51 + web-ui/public/js/page/pix_logo.js | 62 + web-ui/public/js/page/router.js | 71 + web-ui/public/js/page/router/url_params.js | 57 + web-ui/public/js/page/unread_count_title.js | 53 + web-ui/public/js/page/version.js | 41 + web-ui/public/js/sandbox.js | 11 + web-ui/public/js/search/results_highlighter.js | 97 + web-ui/public/js/search/search_trigger.js | 81 + web-ui/public/js/services/delete_service.js | 59 + web-ui/public/js/services/mail_service.js | 335 ++++ web-ui/public/js/services/model/mail.js | 126 ++ web-ui/public/js/services/recover_service.js | 38 + web-ui/public/js/style_guide/main.js | 33 + web-ui/public/js/tags/data/tags.js | 66 + web-ui/public/js/tags/ui/tag.js | 154 ++ web-ui/public/js/tags/ui/tag_base.js | 68 + web-ui/public/js/tags/ui/tag_list.js | 105 + web-ui/public/js/user_alerts/ui/user_alerts.js | 57 + .../public/js/user_settings/data/user_settings.js | 52 + .../js/user_settings/ui/user_settings_box.js | 77 + .../js/user_settings/ui/user_settings_icon.js | 57 + web-ui/public/js/views/i18n.js | 62 + web-ui/public/js/views/recipientListFormatter.js | 33 + web-ui/public/js/views/templates.js | 85 + web-ui/public/locales/en_US/translation.json | 72 + web-ui/public/locales/pt_BR/translation.json | 72 + web-ui/public/locales/sv_SE/translation.json | 42 + web-ui/public/robots.txt | 3 + web-ui/public/sandbox.html | 16 + web-ui/public/scss/_mixins.scss | 71 + web-ui/public/scss/_others.scss | 72 + web-ui/public/scss/base/_colors.scss | 64 + web-ui/public/scss/base/_fonts.scss | 68 + web-ui/public/scss/base/_scaffolding.scss | 10 + web-ui/public/scss/mixins/_position-helpers.scss | 9 + web-ui/public/scss/mixins/_tags.scss | 110 ++ web-ui/public/scss/sandbox.scss | 27 + web-ui/public/scss/style.scss | 39 + .../scss/templates/_no-content-placeholder.scss | 5 + web-ui/public/scss/templates/_unread-count.scss | 14 + web-ui/public/scss/vendor/_customfont.scss | 9 + web-ui/public/scss/vendor/_foundation.scss | 2066 ++++++++++++++++++++ web-ui/public/scss/vendor/_reset.scss | 421 ++++ web-ui/public/scss/vendor/_scut.scss | 1518 ++++++++++++++ web-ui/public/scss/views/_action-bar.scss | 159 ++ web-ui/public/scss/views/_close-button.scss | 22 + web-ui/public/scss/views/_compose-button.scss | 27 + web-ui/public/scss/views/_compose-view.scss | 451 +++++ web-ui/public/scss/views/_mail-list.scss | 124 ++ web-ui/public/scss/views/_message-panel.scss | 26 + web-ui/public/scss/views/_navigation.scss | 589 ++++++ web-ui/public/scss/views/_no-mails-available.scss | 3 + web-ui/public/scss/views/_no-message-selected.scss | 14 + web-ui/public/scss/views/_read-view.scss | 165 ++ web-ui/public/scss/views/_security-labels.scss | 67 + .../public/templates/compose/attachment_item.hbs | 4 + .../templates/compose/attachment_upload_item.hbs | 5 + .../public/templates/compose/attachments_list.hbs | 14 + web-ui/public/templates/compose/compose_box.hbs | 32 + web-ui/public/templates/compose/feedback_box.hbs | 18 + .../public/templates/compose/fixed_recipient.hbs | 8 + web-ui/public/templates/compose/inline_box.hbs | 20 + .../templates/compose/no_mails_available.hbs | 7 + .../templates/compose/no_message_selected.hbs | 3 + .../public/templates/compose/recipient_input.hbs | 1 + web-ui/public/templates/compose/recipients.hbs | 33 + web-ui/public/templates/compose/reply_section.hbs | 6 + .../templates/compose/upload_attachment_failed.hbs | 6 + .../public/templates/feedback/feedback_trigger.hbs | 8 + .../public/templates/mail_actions/actions_box.hbs | 7 + .../templates/mail_actions/compose_trigger.hbs | 3 + .../templates/mail_actions/pagination_trigger.hbs | 3 + .../templates/mail_actions/refresh_trigger.hbs | 3 + .../templates/mail_actions/trash_actions_box.hbs | 5 + web-ui/public/templates/mails/draft.hbs | 41 + web-ui/public/templates/mails/full_view.hbs | 83 + web-ui/public/templates/mails/mail_actions.hbs | 6 + web-ui/public/templates/mails/sent.hbs | 36 + web-ui/public/templates/mails/single.hbs | 28 + web-ui/public/templates/mails/trash.hbs | 32 + web-ui/public/templates/page/logout.hbs | 9 + web-ui/public/templates/page/logout_shortcut.hbs | 6 + web-ui/public/templates/page/user_settings_box.hbs | 10 + .../public/templates/page/user_settings_icon.hbs | 8 + web-ui/public/templates/page/version.hbs | 2 + web-ui/public/templates/search/search_trigger.hbs | 3 + web-ui/public/templates/tags/shortcut.hbs | 9 + web-ui/public/templates/tags/tag.hbs | 3 + web-ui/public/templates/tags/tag_inner.hbs | 4 + web-ui/public/templates/tags/tag_list.hbs | 6 + web-ui/public/templates/user_alerts/message.hbs | 1 + web-ui/test/test-main.js | 54 +- 378 files changed, 15679 insertions(+), 15679 deletions(-) delete mode 100644 web-ui/app/404.html delete mode 100644 web-ui/app/favicon.ico delete mode 100644 web-ui/app/fonts/OpenSans-Bold.woff delete mode 100644 web-ui/app/fonts/OpenSans-BoldItalic.woff delete mode 100644 web-ui/app/fonts/OpenSans-Extrabold.woff delete mode 100644 web-ui/app/fonts/OpenSans-ExtraboldItalic.woff delete mode 100644 web-ui/app/fonts/OpenSans-Italic.woff delete mode 100644 web-ui/app/fonts/OpenSans-Light.woff delete mode 100644 web-ui/app/fonts/OpenSans-Semibold.woff delete mode 100644 web-ui/app/fonts/OpenSans-SemiboldItalic.woff delete mode 100644 web-ui/app/fonts/OpenSans.woff delete mode 100644 web-ui/app/fonts/OpenSansLight-Italic.woff delete mode 100644 web-ui/app/fonts/icomoon.ttf delete mode 100644 web-ui/app/fonts/icomoon.woff delete mode 100644 web-ui/app/images/LOADING-transparent.gif delete mode 100644 web-ui/app/images/fa-sent.svg delete mode 100644 web-ui/app/images/favicon.png delete mode 100644 web-ui/app/images/logo.svg delete mode 100644 web-ui/app/images/pixelated-symbol-blue-transparent-01.png delete mode 100644 web-ui/app/index.html delete mode 100644 web-ui/app/js/dispatchers/left_pane_dispatcher.js delete mode 100644 web-ui/app/js/dispatchers/middle_pane_dispatcher.js delete mode 100644 web-ui/app/js/dispatchers/right_pane_dispatcher.js delete mode 100644 web-ui/app/js/features/features.js delete mode 100644 web-ui/app/js/feedback/feedback_cache.js delete mode 100644 web-ui/app/js/feedback/feedback_trigger.js delete mode 100644 web-ui/app/js/foundation/initialize_foundation.js delete mode 100644 web-ui/app/js/foundation/off_canvas.js delete mode 100644 web-ui/app/js/helpers/browser.js delete mode 100644 web-ui/app/js/helpers/contenttype.js delete mode 100644 web-ui/app/js/helpers/iterator.js delete mode 100644 web-ui/app/js/helpers/monitored_ajax.js delete mode 100644 web-ui/app/js/helpers/sanitizer.js delete mode 100644 web-ui/app/js/helpers/triggering.js delete mode 100644 web-ui/app/js/helpers/view_helper.js delete mode 100644 web-ui/app/js/lib/highlightRegex.js delete mode 100644 web-ui/app/js/lib/html4-defs.js delete mode 100644 web-ui/app/js/mail_list/domain/refresher.js delete mode 100644 web-ui/app/js/mail_list/ui/mail_item_factory.js delete mode 100644 web-ui/app/js/mail_list/ui/mail_items/draft_item.js delete mode 100644 web-ui/app/js/mail_list/ui/mail_items/generic_mail_item.js delete mode 100644 web-ui/app/js/mail_list/ui/mail_items/mail_item.js delete mode 100644 web-ui/app/js/mail_list/ui/mail_items/sent_item.js delete mode 100644 web-ui/app/js/mail_list/ui/mail_list.js delete mode 100644 web-ui/app/js/mail_list_actions/ui/archive_many_trigger.js delete mode 100644 web-ui/app/js/mail_list_actions/ui/compose_trigger.js delete mode 100644 web-ui/app/js/mail_list_actions/ui/delete_many_trigger.js delete mode 100644 web-ui/app/js/mail_list_actions/ui/mail_list_actions.js delete mode 100644 web-ui/app/js/mail_list_actions/ui/mark_as_unread_trigger.js delete mode 100644 web-ui/app/js/mail_list_actions/ui/mark_many_as_read_trigger.js delete mode 100644 web-ui/app/js/mail_list_actions/ui/pagination_trigger.js delete mode 100644 web-ui/app/js/mail_list_actions/ui/recover_many_trigger.js delete mode 100644 web-ui/app/js/mail_list_actions/ui/refresh_trigger.js delete mode 100644 web-ui/app/js/mail_list_actions/ui/toggle_check_all_trigger.js delete mode 100644 web-ui/app/js/mail_view/data/feedback_sender.js delete mode 100644 web-ui/app/js/mail_view/data/mail_builder.js delete mode 100644 web-ui/app/js/mail_view/data/mail_sender.js delete mode 100644 web-ui/app/js/mail_view/ui/attachment_icon.js delete mode 100644 web-ui/app/js/mail_view/ui/attachment_list.js delete mode 100644 web-ui/app/js/mail_view/ui/compose_box.js delete mode 100644 web-ui/app/js/mail_view/ui/draft_box.js delete mode 100644 web-ui/app/js/mail_view/ui/draft_save_status.js delete mode 100644 web-ui/app/js/mail_view/ui/feedback_box.js delete mode 100644 web-ui/app/js/mail_view/ui/forward_box.js delete mode 100644 web-ui/app/js/mail_view/ui/mail_actions.js delete mode 100644 web-ui/app/js/mail_view/ui/mail_view.js delete mode 100644 web-ui/app/js/mail_view/ui/no_mails_available_pane.js delete mode 100644 web-ui/app/js/mail_view/ui/no_message_selected_pane.js delete mode 100644 web-ui/app/js/mail_view/ui/recipients/recipient.js delete mode 100644 web-ui/app/js/mail_view/ui/recipients/recipients.js delete mode 100644 web-ui/app/js/mail_view/ui/recipients/recipients_input.js delete mode 100644 web-ui/app/js/mail_view/ui/recipients/recipients_iterator.js delete mode 100644 web-ui/app/js/mail_view/ui/reply_box.js delete mode 100644 web-ui/app/js/mail_view/ui/reply_section.js delete mode 100644 web-ui/app/js/mail_view/ui/send_button.js delete mode 100644 web-ui/app/js/main.js delete mode 100644 web-ui/app/js/mixins/with_auto_refresh.js delete mode 100644 web-ui/app/js/mixins/with_compose_inline.js delete mode 100644 web-ui/app/js/mixins/with_enable_disable_on_event.js delete mode 100644 web-ui/app/js/mixins/with_feature_toggle.js delete mode 100644 web-ui/app/js/mixins/with_hide_and_show.js delete mode 100644 web-ui/app/js/mixins/with_mail_edit_base.js delete mode 100644 web-ui/app/js/mixins/with_mail_sandbox.js delete mode 100644 web-ui/app/js/mixins/with_mail_tagging.js delete mode 100644 web-ui/app/js/monkey_patching/all.js delete mode 100644 web-ui/app/js/monkey_patching/array.js delete mode 100644 web-ui/app/js/monkey_patching/post_message.js delete mode 100644 web-ui/app/js/page/default.js delete mode 100644 web-ui/app/js/page/events.js delete mode 100644 web-ui/app/js/page/logout.js delete mode 100644 web-ui/app/js/page/logout_shortcut.js delete mode 100644 web-ui/app/js/page/pane_contract_expand.js delete mode 100644 web-ui/app/js/page/pix_logo.js delete mode 100644 web-ui/app/js/page/router.js delete mode 100644 web-ui/app/js/page/router/url_params.js delete mode 100644 web-ui/app/js/page/unread_count_title.js delete mode 100644 web-ui/app/js/page/version.js delete mode 100644 web-ui/app/js/sandbox.js delete mode 100644 web-ui/app/js/search/results_highlighter.js delete mode 100644 web-ui/app/js/search/search_trigger.js delete mode 100644 web-ui/app/js/services/delete_service.js delete mode 100644 web-ui/app/js/services/mail_service.js delete mode 100644 web-ui/app/js/services/model/mail.js delete mode 100644 web-ui/app/js/services/recover_service.js delete mode 100644 web-ui/app/js/style_guide/main.js delete mode 100644 web-ui/app/js/tags/data/tags.js delete mode 100644 web-ui/app/js/tags/ui/tag.js delete mode 100644 web-ui/app/js/tags/ui/tag_base.js delete mode 100644 web-ui/app/js/tags/ui/tag_list.js delete mode 100644 web-ui/app/js/user_alerts/ui/user_alerts.js delete mode 100644 web-ui/app/js/user_settings/data/user_settings.js delete mode 100644 web-ui/app/js/user_settings/ui/user_settings_box.js delete mode 100644 web-ui/app/js/user_settings/ui/user_settings_icon.js delete mode 100644 web-ui/app/js/views/i18n.js delete mode 100644 web-ui/app/js/views/recipientListFormatter.js delete mode 100644 web-ui/app/js/views/templates.js delete mode 100644 web-ui/app/locales/en_US/translation.json delete mode 100644 web-ui/app/locales/pt_BR/translation.json delete mode 100644 web-ui/app/locales/sv_SE/translation.json delete mode 100644 web-ui/app/robots.txt delete mode 100644 web-ui/app/sandbox.html delete mode 100644 web-ui/app/scss/_mixins.scss delete mode 100644 web-ui/app/scss/_others.scss delete mode 100644 web-ui/app/scss/base/_colors.scss delete mode 100644 web-ui/app/scss/base/_fonts.scss delete mode 100644 web-ui/app/scss/base/_scaffolding.scss delete mode 100644 web-ui/app/scss/mixins/_position-helpers.scss delete mode 100644 web-ui/app/scss/mixins/_tags.scss delete mode 100644 web-ui/app/scss/sandbox.scss delete mode 100644 web-ui/app/scss/style.scss delete mode 100644 web-ui/app/scss/templates/_no-content-placeholder.scss delete mode 100644 web-ui/app/scss/templates/_unread-count.scss delete mode 100644 web-ui/app/scss/vendor/_customfont.scss delete mode 100644 web-ui/app/scss/vendor/_foundation.scss delete mode 100644 web-ui/app/scss/vendor/_reset.scss delete mode 100644 web-ui/app/scss/vendor/_scut.scss delete mode 100644 web-ui/app/scss/views/_action-bar.scss delete mode 100644 web-ui/app/scss/views/_close-button.scss delete mode 100644 web-ui/app/scss/views/_compose-button.scss delete mode 100644 web-ui/app/scss/views/_compose-view.scss delete mode 100644 web-ui/app/scss/views/_mail-list.scss delete mode 100644 web-ui/app/scss/views/_message-panel.scss delete mode 100644 web-ui/app/scss/views/_navigation.scss delete mode 100644 web-ui/app/scss/views/_no-mails-available.scss delete mode 100644 web-ui/app/scss/views/_no-message-selected.scss delete mode 100644 web-ui/app/scss/views/_read-view.scss delete mode 100644 web-ui/app/scss/views/_security-labels.scss delete mode 100644 web-ui/app/templates/compose/attachment_item.hbs delete mode 100644 web-ui/app/templates/compose/attachment_upload_item.hbs delete mode 100644 web-ui/app/templates/compose/attachments_list.hbs delete mode 100644 web-ui/app/templates/compose/compose_box.hbs delete mode 100644 web-ui/app/templates/compose/feedback_box.hbs delete mode 100644 web-ui/app/templates/compose/fixed_recipient.hbs delete mode 100644 web-ui/app/templates/compose/inline_box.hbs delete mode 100644 web-ui/app/templates/compose/no_mails_available.hbs delete mode 100644 web-ui/app/templates/compose/no_message_selected.hbs delete mode 100644 web-ui/app/templates/compose/recipient_input.hbs delete mode 100644 web-ui/app/templates/compose/recipients.hbs delete mode 100644 web-ui/app/templates/compose/reply_section.hbs delete mode 100644 web-ui/app/templates/compose/upload_attachment_failed.hbs delete mode 100644 web-ui/app/templates/feedback/feedback_trigger.hbs delete mode 100644 web-ui/app/templates/mail_actions/actions_box.hbs delete mode 100644 web-ui/app/templates/mail_actions/compose_trigger.hbs delete mode 100644 web-ui/app/templates/mail_actions/pagination_trigger.hbs delete mode 100644 web-ui/app/templates/mail_actions/refresh_trigger.hbs delete mode 100644 web-ui/app/templates/mail_actions/trash_actions_box.hbs delete mode 100644 web-ui/app/templates/mails/draft.hbs delete mode 100644 web-ui/app/templates/mails/full_view.hbs delete mode 100644 web-ui/app/templates/mails/mail_actions.hbs delete mode 100644 web-ui/app/templates/mails/sent.hbs delete mode 100644 web-ui/app/templates/mails/single.hbs delete mode 100644 web-ui/app/templates/mails/trash.hbs delete mode 100644 web-ui/app/templates/page/logout.hbs delete mode 100644 web-ui/app/templates/page/logout_shortcut.hbs delete mode 100644 web-ui/app/templates/page/user_settings_box.hbs delete mode 100644 web-ui/app/templates/page/user_settings_icon.hbs delete mode 100644 web-ui/app/templates/page/version.hbs delete mode 100644 web-ui/app/templates/search/search_trigger.hbs delete mode 100644 web-ui/app/templates/tags/shortcut.hbs delete mode 100644 web-ui/app/templates/tags/tag.hbs delete mode 100644 web-ui/app/templates/tags/tag_inner.hbs delete mode 100644 web-ui/app/templates/tags/tag_list.hbs delete mode 100644 web-ui/app/templates/user_alerts/message.hbs create mode 100644 web-ui/public/404.html create mode 100644 web-ui/public/favicon.ico create mode 100644 web-ui/public/fonts/OpenSans-Bold.woff create mode 100644 web-ui/public/fonts/OpenSans-BoldItalic.woff create mode 100644 web-ui/public/fonts/OpenSans-Extrabold.woff create mode 100644 web-ui/public/fonts/OpenSans-ExtraboldItalic.woff create mode 100644 web-ui/public/fonts/OpenSans-Italic.woff create mode 100644 web-ui/public/fonts/OpenSans-Light.woff create mode 100644 web-ui/public/fonts/OpenSans-Semibold.woff create mode 100644 web-ui/public/fonts/OpenSans-SemiboldItalic.woff create mode 100644 web-ui/public/fonts/OpenSans.woff create mode 100644 web-ui/public/fonts/OpenSansLight-Italic.woff create mode 100644 web-ui/public/fonts/icomoon.ttf create mode 100644 web-ui/public/fonts/icomoon.woff create mode 100644 web-ui/public/images/LOADING-transparent.gif create mode 100644 web-ui/public/images/fa-sent.svg create mode 100644 web-ui/public/images/favicon.png create mode 100644 web-ui/public/images/logo.svg create mode 100644 web-ui/public/images/pixelated-symbol-blue-transparent-01.png create mode 100644 web-ui/public/index.html create mode 100644 web-ui/public/js/dispatchers/left_pane_dispatcher.js create mode 100644 web-ui/public/js/dispatchers/middle_pane_dispatcher.js create mode 100644 web-ui/public/js/dispatchers/right_pane_dispatcher.js create mode 100644 web-ui/public/js/features/features.js create mode 100644 web-ui/public/js/feedback/feedback_cache.js create mode 100644 web-ui/public/js/feedback/feedback_trigger.js create mode 100644 web-ui/public/js/foundation/initialize_foundation.js create mode 100644 web-ui/public/js/foundation/off_canvas.js create mode 100644 web-ui/public/js/helpers/browser.js create mode 100644 web-ui/public/js/helpers/contenttype.js create mode 100644 web-ui/public/js/helpers/iterator.js create mode 100644 web-ui/public/js/helpers/monitored_ajax.js create mode 100644 web-ui/public/js/helpers/sanitizer.js create mode 100644 web-ui/public/js/helpers/triggering.js create mode 100644 web-ui/public/js/helpers/view_helper.js create mode 100644 web-ui/public/js/lib/highlightRegex.js create mode 100644 web-ui/public/js/lib/html4-defs.js create mode 100644 web-ui/public/js/mail_list/domain/refresher.js create mode 100644 web-ui/public/js/mail_list/ui/mail_item_factory.js create mode 100644 web-ui/public/js/mail_list/ui/mail_items/draft_item.js create mode 100644 web-ui/public/js/mail_list/ui/mail_items/generic_mail_item.js create mode 100644 web-ui/public/js/mail_list/ui/mail_items/mail_item.js create mode 100644 web-ui/public/js/mail_list/ui/mail_items/sent_item.js create mode 100644 web-ui/public/js/mail_list/ui/mail_list.js create mode 100644 web-ui/public/js/mail_list_actions/ui/archive_many_trigger.js create mode 100644 web-ui/public/js/mail_list_actions/ui/compose_trigger.js create mode 100644 web-ui/public/js/mail_list_actions/ui/delete_many_trigger.js create mode 100644 web-ui/public/js/mail_list_actions/ui/mail_list_actions.js create mode 100644 web-ui/public/js/mail_list_actions/ui/mark_as_unread_trigger.js create mode 100644 web-ui/public/js/mail_list_actions/ui/mark_many_as_read_trigger.js create mode 100644 web-ui/public/js/mail_list_actions/ui/pagination_trigger.js create mode 100644 web-ui/public/js/mail_list_actions/ui/recover_many_trigger.js create mode 100644 web-ui/public/js/mail_list_actions/ui/refresh_trigger.js create mode 100644 web-ui/public/js/mail_list_actions/ui/toggle_check_all_trigger.js create mode 100644 web-ui/public/js/mail_view/data/feedback_sender.js create mode 100644 web-ui/public/js/mail_view/data/mail_builder.js create mode 100644 web-ui/public/js/mail_view/data/mail_sender.js create mode 100644 web-ui/public/js/mail_view/ui/attachment_icon.js create mode 100644 web-ui/public/js/mail_view/ui/attachment_list.js create mode 100644 web-ui/public/js/mail_view/ui/compose_box.js create mode 100644 web-ui/public/js/mail_view/ui/draft_box.js create mode 100644 web-ui/public/js/mail_view/ui/draft_save_status.js create mode 100644 web-ui/public/js/mail_view/ui/feedback_box.js create mode 100644 web-ui/public/js/mail_view/ui/forward_box.js create mode 100644 web-ui/public/js/mail_view/ui/mail_actions.js create mode 100644 web-ui/public/js/mail_view/ui/mail_view.js create mode 100644 web-ui/public/js/mail_view/ui/no_mails_available_pane.js create mode 100644 web-ui/public/js/mail_view/ui/no_message_selected_pane.js create mode 100644 web-ui/public/js/mail_view/ui/recipients/recipient.js create mode 100644 web-ui/public/js/mail_view/ui/recipients/recipients.js create mode 100644 web-ui/public/js/mail_view/ui/recipients/recipients_input.js create mode 100644 web-ui/public/js/mail_view/ui/recipients/recipients_iterator.js create mode 100644 web-ui/public/js/mail_view/ui/reply_box.js create mode 100644 web-ui/public/js/mail_view/ui/reply_section.js create mode 100644 web-ui/public/js/mail_view/ui/send_button.js create mode 100644 web-ui/public/js/main.js create mode 100644 web-ui/public/js/mixins/with_auto_refresh.js create mode 100644 web-ui/public/js/mixins/with_compose_inline.js create mode 100644 web-ui/public/js/mixins/with_enable_disable_on_event.js create mode 100644 web-ui/public/js/mixins/with_feature_toggle.js create mode 100644 web-ui/public/js/mixins/with_hide_and_show.js create mode 100644 web-ui/public/js/mixins/with_mail_edit_base.js create mode 100644 web-ui/public/js/mixins/with_mail_sandbox.js create mode 100644 web-ui/public/js/mixins/with_mail_tagging.js create mode 100644 web-ui/public/js/monkey_patching/all.js create mode 100644 web-ui/public/js/monkey_patching/array.js create mode 100644 web-ui/public/js/monkey_patching/post_message.js create mode 100644 web-ui/public/js/page/default.js create mode 100644 web-ui/public/js/page/events.js create mode 100644 web-ui/public/js/page/logout.js create mode 100644 web-ui/public/js/page/logout_shortcut.js create mode 100644 web-ui/public/js/page/pane_contract_expand.js create mode 100644 web-ui/public/js/page/pix_logo.js create mode 100644 web-ui/public/js/page/router.js create mode 100644 web-ui/public/js/page/router/url_params.js create mode 100644 web-ui/public/js/page/unread_count_title.js create mode 100644 web-ui/public/js/page/version.js create mode 100644 web-ui/public/js/sandbox.js create mode 100644 web-ui/public/js/search/results_highlighter.js create mode 100644 web-ui/public/js/search/search_trigger.js create mode 100644 web-ui/public/js/services/delete_service.js create mode 100644 web-ui/public/js/services/mail_service.js create mode 100644 web-ui/public/js/services/model/mail.js create mode 100644 web-ui/public/js/services/recover_service.js create mode 100644 web-ui/public/js/style_guide/main.js create mode 100644 web-ui/public/js/tags/data/tags.js create mode 100644 web-ui/public/js/tags/ui/tag.js create mode 100644 web-ui/public/js/tags/ui/tag_base.js create mode 100644 web-ui/public/js/tags/ui/tag_list.js create mode 100644 web-ui/public/js/user_alerts/ui/user_alerts.js create mode 100644 web-ui/public/js/user_settings/data/user_settings.js create mode 100644 web-ui/public/js/user_settings/ui/user_settings_box.js create mode 100644 web-ui/public/js/user_settings/ui/user_settings_icon.js create mode 100644 web-ui/public/js/views/i18n.js create mode 100644 web-ui/public/js/views/recipientListFormatter.js create mode 100644 web-ui/public/js/views/templates.js create mode 100644 web-ui/public/locales/en_US/translation.json create mode 100644 web-ui/public/locales/pt_BR/translation.json create mode 100644 web-ui/public/locales/sv_SE/translation.json create mode 100644 web-ui/public/robots.txt create mode 100644 web-ui/public/sandbox.html create mode 100644 web-ui/public/scss/_mixins.scss create mode 100644 web-ui/public/scss/_others.scss create mode 100644 web-ui/public/scss/base/_colors.scss create mode 100644 web-ui/public/scss/base/_fonts.scss create mode 100644 web-ui/public/scss/base/_scaffolding.scss create mode 100644 web-ui/public/scss/mixins/_position-helpers.scss create mode 100644 web-ui/public/scss/mixins/_tags.scss create mode 100644 web-ui/public/scss/sandbox.scss create mode 100644 web-ui/public/scss/style.scss create mode 100644 web-ui/public/scss/templates/_no-content-placeholder.scss create mode 100644 web-ui/public/scss/templates/_unread-count.scss create mode 100644 web-ui/public/scss/vendor/_customfont.scss create mode 100644 web-ui/public/scss/vendor/_foundation.scss create mode 100644 web-ui/public/scss/vendor/_reset.scss create mode 100644 web-ui/public/scss/vendor/_scut.scss create mode 100644 web-ui/public/scss/views/_action-bar.scss create mode 100644 web-ui/public/scss/views/_close-button.scss create mode 100644 web-ui/public/scss/views/_compose-button.scss create mode 100644 web-ui/public/scss/views/_compose-view.scss create mode 100644 web-ui/public/scss/views/_mail-list.scss create mode 100644 web-ui/public/scss/views/_message-panel.scss create mode 100644 web-ui/public/scss/views/_navigation.scss create mode 100644 web-ui/public/scss/views/_no-mails-available.scss create mode 100644 web-ui/public/scss/views/_no-message-selected.scss create mode 100644 web-ui/public/scss/views/_read-view.scss create mode 100644 web-ui/public/scss/views/_security-labels.scss create mode 100644 web-ui/public/templates/compose/attachment_item.hbs create mode 100644 web-ui/public/templates/compose/attachment_upload_item.hbs create mode 100644 web-ui/public/templates/compose/attachments_list.hbs create mode 100644 web-ui/public/templates/compose/compose_box.hbs create mode 100644 web-ui/public/templates/compose/feedback_box.hbs create mode 100644 web-ui/public/templates/compose/fixed_recipient.hbs create mode 100644 web-ui/public/templates/compose/inline_box.hbs create mode 100644 web-ui/public/templates/compose/no_mails_available.hbs create mode 100644 web-ui/public/templates/compose/no_message_selected.hbs create mode 100644 web-ui/public/templates/compose/recipient_input.hbs create mode 100644 web-ui/public/templates/compose/recipients.hbs create mode 100644 web-ui/public/templates/compose/reply_section.hbs create mode 100644 web-ui/public/templates/compose/upload_attachment_failed.hbs create mode 100644 web-ui/public/templates/feedback/feedback_trigger.hbs create mode 100644 web-ui/public/templates/mail_actions/actions_box.hbs create mode 100644 web-ui/public/templates/mail_actions/compose_trigger.hbs create mode 100644 web-ui/public/templates/mail_actions/pagination_trigger.hbs create mode 100644 web-ui/public/templates/mail_actions/refresh_trigger.hbs create mode 100644 web-ui/public/templates/mail_actions/trash_actions_box.hbs create mode 100644 web-ui/public/templates/mails/draft.hbs create mode 100644 web-ui/public/templates/mails/full_view.hbs create mode 100644 web-ui/public/templates/mails/mail_actions.hbs create mode 100644 web-ui/public/templates/mails/sent.hbs create mode 100644 web-ui/public/templates/mails/single.hbs create mode 100644 web-ui/public/templates/mails/trash.hbs create mode 100644 web-ui/public/templates/page/logout.hbs create mode 100644 web-ui/public/templates/page/logout_shortcut.hbs create mode 100644 web-ui/public/templates/page/user_settings_box.hbs create mode 100644 web-ui/public/templates/page/user_settings_icon.hbs create mode 100644 web-ui/public/templates/page/version.hbs create mode 100644 web-ui/public/templates/search/search_trigger.hbs create mode 100644 web-ui/public/templates/tags/shortcut.hbs create mode 100644 web-ui/public/templates/tags/tag.hbs create mode 100644 web-ui/public/templates/tags/tag_inner.hbs create mode 100644 web-ui/public/templates/tags/tag_list.hbs create mode 100644 web-ui/public/templates/user_alerts/message.hbs (limited to 'web-ui') diff --git a/web-ui/.bowerrc b/web-ui/.bowerrc index 5773025b..d1c592ed 100644 --- a/web-ui/.bowerrc +++ b/web-ui/.bowerrc @@ -1,3 +1,3 @@ { - "directory": "app/bower_components" + "directory": "public/bower_components" } diff --git a/web-ui/.jshintignore b/web-ui/.jshintignore index 6a32b1a4..8dfe4354 100644 --- a/web-ui/.jshintignore +++ b/web-ui/.jshintignore @@ -1,4 +1,4 @@ -app/node_modules -app/bower_components -app/js/lib -app/js/generated +public/node_modules +public/bower_components +public/js/lib +public/js/generated diff --git a/web-ui/.tx/config b/web-ui/.tx/config index 25299ced..04fc6bc2 100644 --- a/web-ui/.tx/config +++ b/web-ui/.tx/config @@ -2,7 +2,7 @@ host = https://www.transifex.com [pixelated-user-agent.web-ui] -file_filter = app/locales//translation.json -source_file = app/locales/en_US/translation.json +file_filter = public/locales//translation.json +source_file = public/locales/en_US/translation.json source_lang = en_US type = KEYVALUEJSON diff --git a/web-ui/app/404.html b/web-ui/app/404.html deleted file mode 100644 index fdace4ab..00000000 --- a/web-ui/app/404.html +++ /dev/null @@ -1,157 +0,0 @@ - - - - - Page Not Found :( - - - -
-

Not found :(

-

Sorry, but the page you were trying to view does not exist.

-

It looks like this was the result of either:

-
    -
  • a mistyped address
  • -
  • an out-of-date link
  • -
- - -
- - diff --git a/web-ui/app/favicon.ico b/web-ui/app/favicon.ico deleted file mode 100644 index e69de29b..00000000 diff --git a/web-ui/app/fonts/OpenSans-Bold.woff b/web-ui/app/fonts/OpenSans-Bold.woff deleted file mode 100644 index dacf3c9c..00000000 Binary files a/web-ui/app/fonts/OpenSans-Bold.woff and /dev/null differ diff --git a/web-ui/app/fonts/OpenSans-BoldItalic.woff b/web-ui/app/fonts/OpenSans-BoldItalic.woff deleted file mode 100644 index a4e29c0f..00000000 Binary files a/web-ui/app/fonts/OpenSans-BoldItalic.woff and /dev/null differ diff --git a/web-ui/app/fonts/OpenSans-Extrabold.woff b/web-ui/app/fonts/OpenSans-Extrabold.woff deleted file mode 100644 index 7a2e352b..00000000 Binary files a/web-ui/app/fonts/OpenSans-Extrabold.woff and /dev/null differ diff --git a/web-ui/app/fonts/OpenSans-ExtraboldItalic.woff b/web-ui/app/fonts/OpenSans-ExtraboldItalic.woff deleted file mode 100644 index ce3ab2e7..00000000 Binary files a/web-ui/app/fonts/OpenSans-ExtraboldItalic.woff and /dev/null differ diff --git a/web-ui/app/fonts/OpenSans-Italic.woff b/web-ui/app/fonts/OpenSans-Italic.woff deleted file mode 100644 index c5f6bac1..00000000 Binary files a/web-ui/app/fonts/OpenSans-Italic.woff and /dev/null differ diff --git a/web-ui/app/fonts/OpenSans-Light.woff b/web-ui/app/fonts/OpenSans-Light.woff deleted file mode 100644 index eb601d70..00000000 Binary files a/web-ui/app/fonts/OpenSans-Light.woff and /dev/null differ diff --git a/web-ui/app/fonts/OpenSans-Semibold.woff b/web-ui/app/fonts/OpenSans-Semibold.woff deleted file mode 100644 index 56c44944..00000000 Binary files a/web-ui/app/fonts/OpenSans-Semibold.woff and /dev/null differ diff --git a/web-ui/app/fonts/OpenSans-SemiboldItalic.woff b/web-ui/app/fonts/OpenSans-SemiboldItalic.woff deleted file mode 100644 index 3a439fc3..00000000 Binary files a/web-ui/app/fonts/OpenSans-SemiboldItalic.woff and /dev/null differ diff --git a/web-ui/app/fonts/OpenSans.woff b/web-ui/app/fonts/OpenSans.woff deleted file mode 100644 index 77706fa6..00000000 Binary files a/web-ui/app/fonts/OpenSans.woff and /dev/null differ diff --git a/web-ui/app/fonts/OpenSansLight-Italic.woff b/web-ui/app/fonts/OpenSansLight-Italic.woff deleted file mode 100644 index 3f9f088f..00000000 Binary files a/web-ui/app/fonts/OpenSansLight-Italic.woff and /dev/null differ diff --git a/web-ui/app/fonts/icomoon.ttf b/web-ui/app/fonts/icomoon.ttf deleted file mode 100644 index 61315d04..00000000 Binary files a/web-ui/app/fonts/icomoon.ttf and /dev/null differ diff --git a/web-ui/app/fonts/icomoon.woff b/web-ui/app/fonts/icomoon.woff deleted file mode 100644 index 82f11748..00000000 Binary files a/web-ui/app/fonts/icomoon.woff and /dev/null differ diff --git a/web-ui/app/images/LOADING-transparent.gif b/web-ui/app/images/LOADING-transparent.gif deleted file mode 100644 index ac9abcde..00000000 Binary files a/web-ui/app/images/LOADING-transparent.gif and /dev/null differ diff --git a/web-ui/app/images/fa-sent.svg b/web-ui/app/images/fa-sent.svg deleted file mode 100644 index a4b4bea4..00000000 --- a/web-ui/app/images/fa-sent.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - fa-sent - Created with Sketch. - - - - - - - - - - \ No newline at end of file diff --git a/web-ui/app/images/favicon.png b/web-ui/app/images/favicon.png deleted file mode 100644 index e14841c7..00000000 Binary files a/web-ui/app/images/favicon.png and /dev/null differ diff --git a/web-ui/app/images/logo.svg b/web-ui/app/images/logo.svg deleted file mode 100644 index 6c2d8989..00000000 --- a/web-ui/app/images/logo.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/web-ui/app/images/pixelated-symbol-blue-transparent-01.png b/web-ui/app/images/pixelated-symbol-blue-transparent-01.png deleted file mode 100644 index 96b92155..00000000 Binary files a/web-ui/app/images/pixelated-symbol-blue-transparent-01.png and /dev/null differ diff --git a/web-ui/app/index.html b/web-ui/app/index.html deleted file mode 100644 index 4b6a81a0..00000000 --- a/web-ui/app/index.html +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - -$account_email - Pixelated Mail - - - - - - - - - - -
-
-
-
- -
- - -
-
-
-
-
-
-
-
-
-
    -
    - -
    -
      -
    -
    -
    -
    - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - diff --git a/web-ui/app/js/dispatchers/left_pane_dispatcher.js b/web-ui/app/js/dispatchers/left_pane_dispatcher.js deleted file mode 100644 index 0037a88f..00000000 --- a/web-ui/app/js/dispatchers/left_pane_dispatcher.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'page/router/url_params', - 'page/events' - ], - - function(defineComponent, urlParams, events) { - 'use strict'; - - return defineComponent(leftPaneDispatcher); - - function leftPaneDispatcher() { - var initialized = false; - - this.refreshTagList = function (ev, data) { - this.trigger(document, events.tags.want, { caller: this.$node, skipMailListRefresh: data.skipMailListRefresh }); - }; - - this.loadTags = function (ev, data) { - this.trigger(document, events.ui.tagList.load, data); - }; - - this.selectTag = function (ev, data) { - var tag = (data && data.tag) || urlParams.getTag(); - this.trigger(document, events.ui.tag.select, { tag: tag, skipMailListRefresh: data.skipMailListRefresh }); - }; - - this.pushUrlState = function (ev, data) { - if (initialized) { - this.trigger(document, events.router.pushState, data); - } - initialized = true; - }; - - this.after('initialize', function () { - //this.on(this.$node, events.tags.received, this.loadTags); - this.on(document, events.dispatchers.tags.refreshTagList, this.refreshTagList); - this.on(document, events.ui.tags.loaded, this.selectTag); - this.on(document, events.ui.tag.selected, this.pushUrlState); - this.on(document, events.ui.tag.select, this.pushUrlState); - this.trigger(document, events.tags.want, { caller: this.$node }); - }); - } - } -); diff --git a/web-ui/app/js/dispatchers/middle_pane_dispatcher.js b/web-ui/app/js/dispatchers/middle_pane_dispatcher.js deleted file mode 100644 index 12222aec..00000000 --- a/web-ui/app/js/dispatchers/middle_pane_dispatcher.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(['flight/lib/component', 'page/events', 'helpers/triggering', 'mail_view/ui/no_mails_available_pane'], function(defineComponent, events, triggering, NoMailsAvailablePane) { - 'use strict'; - - return defineComponent(function() { - this.defaultAttrs({ - middlePane: '#middle-pane', - noMailsAvailablePane: 'no-mails-available-pane' - }); - - this.createChildDiv = function (component_id) { - var child_div = $('
    ', {id: component_id}); - this.select('middlePane').append(child_div); - return child_div; - }; - - this.resetChildDiv = function(component_id) { - $('#' + component_id).remove(); - }; - - this.refreshMailList = function (ev, data) { - this.trigger(document, events.ui.mails.fetchByTag, data); - }; - - this.cleanSelected = function(ev, data) { - this.trigger(document, events.ui.mails.cleanSelected); - }; - - this.resetScroll = function() { - this.select('middlePane').scrollTop(0); - }; - - this.updateMiddlePaneHeight = function() { - var vh = $(window).height(); - var top = $('#main').outerHeight() + $('#top-pane').outerHeight(); - this.select('middlePane').css({height: (vh - top) + 'px'}); - }; - - this.onMailsChange = function (ev, data) { - this.resetChildDiv(this.attr.noMailsAvailablePane); - if (data.mails.length > 0) { - NoMailsAvailablePane.teardownAll(); - } else { - var child_div = this.createChildDiv(this.attr.noMailsAvailablePane); - NoMailsAvailablePane.attachTo(child_div, {tag: data.tag, forSearch: data.forSearch}); - } - }; - - this.after('initialize', function () { - this.on(document, events.dispatchers.middlePane.refreshMailList, this.refreshMailList); - this.on(document, events.dispatchers.middlePane.cleanSelected, this.cleanSelected); - this.on(document, events.dispatchers.middlePane.resetScroll, this.resetScroll); - this.on(document, events.mails.available, this.onMailsChange); - - this.updateMiddlePaneHeight(); - $(window).on('resize', this.updateMiddlePaneHeight.bind(this)); - }); - }); -}); diff --git a/web-ui/app/js/dispatchers/right_pane_dispatcher.js b/web-ui/app/js/dispatchers/right_pane_dispatcher.js deleted file mode 100644 index 870bcd92..00000000 --- a/web-ui/app/js/dispatchers/right_pane_dispatcher.js +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'mail_view/ui/compose_box', - 'mail_view/ui/mail_view', - 'mail_view/ui/reply_section', - 'mail_view/ui/draft_box', - 'mail_view/ui/no_message_selected_pane', - 'mail_view/ui/feedback_box', - 'page/events' - ], - - function(defineComponent, ComposeBox, MailView, ReplySection, DraftBox, NoMessageSelectedPane, FeedbackBox, events) { - 'use strict'; - - return defineComponent(rightPaneDispatcher); - - function rightPaneDispatcher() { - this.defaultAttrs({ - rightPane: '#right-pane', - composeBox: 'compose-box', - feedbackBox: 'feedback-box', - mailView: 'mail-view', - noMessageSelectedPane: 'no-message-selected-pane', - replySection: 'reply-section', - draftBox: 'draft-box', - currentTag: '' - }); - - this.createAndAttach = function(newContainer) { - var stage = $('
    ', { id: newContainer }); - this.select('rightPane').append(stage); - return stage; - }; - - this.reset = function (newContainer) { - this.trigger(document, events.dispatchers.rightPane.clear); - this.select('rightPane').empty(); - var stage = this.createAndAttach(newContainer); - return stage; - }; - - this.openComposeBox = function() { - var stage = this.reset(this.attr.composeBox); - ComposeBox.attachTo(stage, {currentTag: this.attr.currentTag}); - }; - - this.openFeedbackBox = function() { - var stage = this.reset(this.attr.feedbackBox); - FeedbackBox.attachTo(stage); - }; - - this.openMail = function(ev, data) { - var stage = this.reset(this.attr.mailView); - MailView.attachTo(stage, data); - - var replySectionContainer = this.createAndAttach(this.attr.replySection); - ReplySection.attachTo(replySectionContainer, { ident: data.ident }); - }; - - this.initializeNoMessageSelectedPane = function () { - var stage = this.reset(this.attr.noMessageSelectedPane); - NoMessageSelectedPane.attachTo(stage); - this.trigger(document, events.dispatchers.middlePane.cleanSelected); - }; - - this.openNoMessageSelectedPane = function(ev, data) { - this.initializeNoMessageSelectedPane(); - - this.trigger(document, events.router.pushState, { tag: this.attr.currentTag, isDisplayNoMessageSelected: true }); - }; - - this.openDraft = function (ev, data) { - var stage = this.reset(this.attr.draftBox); - DraftBox.attachTo(stage, { mailIdent: data.ident, currentTag: this.attr.currentTag }); - }; - - this.selectTag = function(ev, data) { - this.trigger(document, events.ui.tags.loaded, {tag: data.tag}); - }; - - this.saveTag = function(ev, data) { - this.attr.currentTag = data.tag; - }; - - this.after('initialize', function () { - this.on(document, events.dispatchers.rightPane.openComposeBox, this.openComposeBox); - this.on(document, events.dispatchers.rightPane.openDraft, this.openDraft); - this.on(document, events.ui.mail.open, this.openMail); - this.on(document, events.dispatchers.rightPane.openFeedbackBox, this.openFeedbackBox); - this.on(document, events.dispatchers.rightPane.openNoMessageSelected, this.openNoMessageSelectedPane); - this.on(document, events.dispatchers.rightPane.selectTag, this.selectTag); - this.on(document, events.ui.tag.selected, this.saveTag); - this.on(document, events.ui.tag.select, this.saveTag); - this.on(document, events.dispatchers.rightPane.openNoMessageSelectedWithoutPushState, this.initializeNoMessageSelectedPane); - this.initializeNoMessageSelectedPane(); - }); - } - } -); diff --git a/web-ui/app/js/features/features.js b/web-ui/app/js/features/features.js deleted file mode 100644 index f71d56ea..00000000 --- a/web-ui/app/js/features/features.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['helpers/monitored_ajax'], function(monitoredAjax) { - 'use strict'; - var cachedDisabledFeatures; - var cachedMultiUserFeatures; - - function getDisabledFeatures() { - cachedDisabledFeatures = cachedDisabledFeatures || fetchFeatures().disabled_features; - return cachedDisabledFeatures; - } - - function getMultiUserFeatures() { - cachedMultiUserFeatures = cachedMultiUserFeatures || fetchFeatures().multi_user; - return cachedMultiUserFeatures; - } - - function fetchFeatures() { - var features; - monitoredAjax(this, '/features', { - async: false, - success: function (results) { - features = results; - }, - error: function () { - console.error('Could not load feature toggles'); - } - }); - return features; - } - - return { - isEnabled: function (featureName) { - return ! _.contains(getDisabledFeatures(), featureName); - }, - isAutoRefreshEnabled: function () { - return this.isEnabled('autoRefresh'); - }, - isLogoutEnabled: function () { - return _.has(getMultiUserFeatures(), 'logout'); - }, - getLogoutUrl: function () { - return getMultiUserFeatures().logout; - } - }; -}); diff --git a/web-ui/app/js/feedback/feedback_cache.js b/web-ui/app/js/feedback/feedback_cache.js deleted file mode 100644 index a5d92266..00000000 --- a/web-ui/app/js/feedback/feedback_cache.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define([], function() { - 'use strict'; - - return (function() { - var feedbackCache = ''; - return { - resetCache: function () { - feedbackCache = ''; - }, - setCache: function(feedback) { - feedbackCache = feedback; - }, - getCache: function() { - return feedbackCache; - } - }; - })(); -}); diff --git a/web-ui/app/js/feedback/feedback_trigger.js b/web-ui/app/js/feedback/feedback_trigger.js deleted file mode 100644 index 598f9060..00000000 --- a/web-ui/app/js/feedback/feedback_trigger.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2015 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['flight/lib/component', 'views/templates', 'page/events', 'features'], - function (defineComponent, templates, events, features) { - 'use strict'; - - return defineComponent(function () { - this.render = function () { - this.$node.html(templates.feedback.feedback()); - }; - - this.onClick = function() { - this.trigger(document, events.dispatchers.rightPane.openFeedbackBox); - }; - - this.after('initialize', function () { - if (features.isEnabled('feedback')) { - this.render(); - this.on('click', this.onClick); - } - }); - - }); -}); diff --git a/web-ui/app/js/foundation/initialize_foundation.js b/web-ui/app/js/foundation/initialize_foundation.js deleted file mode 100644 index 42405dfe..00000000 --- a/web-ui/app/js/foundation/initialize_foundation.js +++ /dev/null @@ -1,5 +0,0 @@ - -(function() { - 'use strict'; - $(document).foundation(); -})(); diff --git a/web-ui/app/js/foundation/off_canvas.js b/web-ui/app/js/foundation/off_canvas.js deleted file mode 100644 index 66334470..00000000 --- a/web-ui/app/js/foundation/off_canvas.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(['flight/lib/component', 'page/events'], function (defineComponent, events) { - 'use strict'; - return defineComponent(function() { - - this.closeSlider = function (ev){ - $('.off-canvas-wrap.content').removeClass('move-right'); - this.toggleTagsVisibility(); - }; - - this.toggleSlideContent = function (ev) { - ev.preventDefault(); - $('.left-off-canvas-toggle').click(); - this.toggleTagsVisibility(); - }; - - this.toggleTagsVisibility = function () { - if ($('.off-canvas-wrap.content').hasClass('move-right')) { - $('#custom-tag-list').addClass('expanded'); - } else { - $('#custom-tag-list').removeClass('expanded'); - } - }; - - this.after('initialize', function () { - this.on($('#middle-pane-container'), 'click', this.closeSlider); - this.on($('#right-pane'), 'click', this.closeSlider); - this.on($('.side-nav-toggle'), 'click', this.toggleSlideContent); - }); - }); -}); diff --git a/web-ui/app/js/helpers/browser.js b/web-ui/app/js/helpers/browser.js deleted file mode 100644 index dacf2263..00000000 --- a/web-ui/app/js/helpers/browser.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define([], function () { - - 'use strict'; - - function redirect(url) { - window.location.replace(url); - } - - function getCookie(name) { - var value = '; ' + document.cookie; - var parts = value.split('; ' + name + '='); - if (parts.length === 2) { return parts.pop().split(';').shift(); } - } - - return { - redirect: redirect, - getCookie: getCookie - }; -}); diff --git a/web-ui/app/js/helpers/contenttype.js b/web-ui/app/js/helpers/contenttype.js deleted file mode 100644 index a1e5361a..00000000 --- a/web-ui/app/js/helpers/contenttype.js +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -/* jshint curly: false */ -define([], function () { - 'use strict'; - var exports = {}; - - // Licence: PUBLIC DOMAIN - // Author: Austin Wright - - function MediaType(s, p){ - this.type = ''; - this.params = {}; - var c, i, n; - if(typeof s==='string'){ - c = splitQuotedString(s); - this.type = c.shift(); - for(i=0; i=0){ - offset = [delim,quote].reduce(findNextChar, 1/0); - if(offset===1/0) break; - switch(str[offset]){ - case quote: - // Skip to end of quoted string - while(1){ - offset=str.indexOf(quote, offset+1); - if(offset<0) break; - if(str[offset-1]==='\\') continue; - break; - } - continue; - case delim: - res.push(str.substr(start, offset-start).trim()); - start = ++offset; - break; - } - } - res.push(str.substr(start).trim()); - return res; - } - exports.splitQuotedString = splitQuotedString; - - // Split a list of content types found in an Accept header - // Maybe use it like: splitContentTypes(request.headers.accept).map(parseMedia) - function splitContentTypes(str){ - return splitQuotedString(str, ','); - } - exports.splitContentTypes = splitContentTypes; - - function parseMedia(str){ - var o = new MediaType(str); - if(o.q===undefined) o.q=1; - return o; - } - exports.parseMedia = parseMedia; - - // Pick an ideal representation to send given a list of representations to choose from and the client-preferred list - function select(reps, accept){ - var cr = {q:0}; - var ca = {q:0}; - var cq = 0; - for(var i=0; i=0){ - if(aq*rq>cq){ - ca = a; - cr = r; - cq = ca.q*cr.q; - if(cq===1 && cr.type) return cr; - } - } - } - } - return cr.type&&cr; - } - exports.select = select; - - // Determine if one media type is a subset of another - // If a is a superset of b (b is smaller than a), return 1 - // If b is a superset of a, return -1 - // If they are the exact same, return 0 - // If they are disjoint, return null - function mediaCmp(a, b){ - if(a.type==='*/*' && b.type!=='*/*') return 1; - else if(a.type!=='*/*' && b.type==='*/*') return -1; - var ac = (a.type||'').split('/'); - var bc = (b.type||'').split('/'); - if(ac[0]==='*' && bc[0]!=='*') return 1; - if(ac[0]!=='*' && bc[0]==='*') return -1; - if(a.type!==b.type) return null; - var ap = a.params || {}; - var bp = b.params || {}; - var ak = Object.keys(ap); - var bk = Object.keys(bp); - if(ak.length < bk.length) return 1; - if(ak.length > bk.length) return -1; - var k = ak.concat(bk).sort(); - var dir = 0; - for(var n in ap){ - if(ap[n] && !bp[n]){ if(dir<0) return null; else dir=1; } - if(!ap[n] && bp[n]){ if(dir>0) return null; else dir=-1; } - } - return dir; - } - exports.mediaCmp = mediaCmp; - - return exports; -}); diff --git a/web-ui/app/js/helpers/iterator.js b/web-ui/app/js/helpers/iterator.js deleted file mode 100644 index 236c7a40..00000000 --- a/web-ui/app/js/helpers/iterator.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(function () { - 'use strict'; - - return Iterator; - - function Iterator(elems, startingIndex) { - - this.index = startingIndex || 0; - this.elems = elems; - - this.hasPrevious = function () { - return this.index !== 0; - }; - - this.hasNext = function () { - return this.index < this.elems.length - 1; - }; - - this.previous = function () { - return this.elems[--this.index]; - }; - - this.next = function () { - return this.elems[++this.index]; - }; - - this.current = function () { - return this.elems[this.index]; - }; - - this.hasElements = function () { - return this.elems.length > 0; - }; - - this.removeCurrent = function () { - var removed = this.current(), - toRemove = this.index; - - if(!this.hasNext()) { this.index--; } - this.elems.remove(toRemove); - return removed; - }; - } -}); diff --git a/web-ui/app/js/helpers/monitored_ajax.js b/web-ui/app/js/helpers/monitored_ajax.js deleted file mode 100644 index bbf85c45..00000000 --- a/web-ui/app/js/helpers/monitored_ajax.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['page/events', 'views/i18n', 'helpers/browser'], function (events, i18n, browser) { - - 'use strict'; - - var messages = { - timeout: 'error.timeout', - error: 'error.general', - parseerror: 'error.parse' - }; - - function monitoredAjax(on, url, config) { - config = config || {}; - config.timeout = 60 * 1000; - - var originalBeforeSend = config.beforeSend; - config.beforeSend = function () { - if (originalBeforeSend) { - originalBeforeSend(); - } - }; - - config.headers = {'X-XSRF-TOKEN': browser.getCookie('XSRF-TOKEN')}; - - var originalComplete = config.complete; - config.complete = function () { - if (originalComplete) { - originalComplete(); - } - }; - - return $.ajax(url, config).fail(function (xmlhttprequest, textstatus, message) { - if (!config.skipErrorMessage) { - var msg = (xmlhttprequest.responseJSON && xmlhttprequest.responseJSON.message) || - messages[textstatus] || messages.error; - on.trigger(document, events.ui.userAlerts.displayMessage, {message: i18n.t(msg), class: 'error'}); - } - - if (xmlhttprequest.status === 302) { - var redirectUrl = xmlhttprequest.getResponseHeader('Location'); - browser.redirect(redirectUrl); - } else if (xmlhttprequest.status === 401) { - browser.redirect('/'); - } - - }.bind(this)); - } - - return monitoredAjax; - -}); diff --git a/web-ui/app/js/helpers/sanitizer.js b/web-ui/app/js/helpers/sanitizer.js deleted file mode 100644 index 443e8602..00000000 --- a/web-ui/app/js/helpers/sanitizer.js +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2016 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['DOMPurify', 'he'], function (DOMPurify, he) { - 'use strict'; - - /** - * Sanitizes a mail body to safe-to-display HTML - */ - var sanitizer = {}; - - sanitizer.whitelist = [{ - // highlight tag open - pre: '<em class="search-highlight">', - post: '' - }, { - // highlight tag close - pre: '</em>', - post: '' - }]; - - /** - * Adds html line breaks to a plaintext with line breaks (incl carriage return) - * - * @param {string} textPlainBody Plaintext input - * @returns {string} Plaintext with HTML line breals (
    ) - */ - sanitizer.addLineBreaks = function (textPlainBody) { - return textPlainBody.replace(/(\r)?\n/g, '
    ').replace(/( )? /g, '
    '); - }; - - /** - * Runs a given dirty body through DOMPurify, thereby removing - * potentially hazardous XSS attacks. Please be advised that this - * will not act as a privacy leak prevention. Contained contents - * will still point to remote sources. - * - * For future reference: Running DOMPurify with these parameters - * can help mitigate some of the most widely used privacy leaks. - * FORBID_TAGS: ['style', 'svg', 'audio', 'video', 'math'], - * FORBID_ATTR: ['src'] - * - * @param {string} dirtyBody The unsanitized string - * @return {string} Safe-to-display HTML string - */ - sanitizer.purifyHtml = function (dirtyBody) { - return DOMPurify.sanitize(dirtyBody, { - SAFE_FOR_JQUERY: true, - SAFE_FOR_TEMPLATES: true - }); - }; - - /** - * Runs a given dirty body through he, thereby encoding everything - * as HTML entities. - * - * @param {string} dirtyBody The unsanitized string - * @return {string} Safe-to-display HTML string - */ - sanitizer.purifyText = function (dirtyBody) { - var escapedBody = he.encode(dirtyBody, { - encodeEverything: true - }); - - this.whitelist.forEach(function(entry) { - while (escapedBody.indexOf(entry.pre) > -1) { - escapedBody = escapedBody.replace(entry.pre, entry.post); - } - }); - - return escapedBody; - }; - - /** - * Calls #purify and #addLineBreaks to turn untrusted mail body content - * into safe-to-display HTML. - * - * NB: HTML content is preferred to plaintext content. - * - * @param {object} mail Pixelated Mail Object - * @return {string} Safe-to-display HTML string - */ - sanitizer.sanitize = function (mail) { - var body; - - if (mail.htmlBody) { - body = this.purifyHtml(mail.htmlBody); - } else { - body = this.purifyText(mail.textPlainBody); - body = this.addLineBreaks(body); - } - - return body; - }; - - /** - * Add hooks to DOMPurify for opening links in new windows - */ - DOMPurify.addHook('afterSanitizeAttributes', function (node) { - // set all elements owning target to target=_blank - if ('target' in node) { - node.setAttribute('target', '_blank'); - } - - // set non-HTML/MathML links to xlink:show=new - if (!node.hasAttribute('target') && (node.hasAttribute('xlink:href') || node.hasAttribute('href'))) { - node.setAttribute('xlink:show', 'new'); - } - }); - - return sanitizer; -}); diff --git a/web-ui/app/js/helpers/triggering.js b/web-ui/app/js/helpers/triggering.js deleted file mode 100644 index d26d9fc6..00000000 --- a/web-ui/app/js/helpers/triggering.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define([], function() { - 'use strict'; - - return function(that, event, data, on) { - return function() { - if(on) { - that.trigger(on, event, data || {}); - } else { - that.trigger(event, data || {}); - } - }; - }; -}); diff --git a/web-ui/app/js/helpers/view_helper.js b/web-ui/app/js/helpers/view_helper.js deleted file mode 100644 index ed9e0559..00000000 --- a/web-ui/app/js/helpers/view_helper.js +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'helpers/contenttype', - 'views/i18n', - 'quoted-printable/quoted-printable', - 'utf8/utf8', - 'helpers/sanitizer' - ], - function(contentType, i18n, quotedPrintable, utf8, sanitizer) { - 'use strict'; - - function formatStatusClasses(ss) { - return _.map(ss, function(s) { - return 'status-' + s; - }).join(' '); - } - - function formatMailBody(mail) { - return sanitizer.sanitize(mail); - } - - function moveCaretToEnd(el) { - if (typeof el.selectionStart === 'number') { - el.selectionStart = el.selectionEnd = el.value.length; - } else if (typeof el.createTextRange !== 'undefined') { - el.focus(); - var range = el.createTextRange(); - range.collapse(false); - range.select(); - } - } - - function fixedSizeNumber(num, size) { - var res = num.toString(); - while(res.length < size) { - res = '0' + res; - } - return res; - } - - function createTodayDate() { - var today = new Date(); - today.setHours(0); - today.setMinutes(0); - today.setSeconds(0); - return today; - } - - function moveCaretToEndOfText() { - var self = this; - - moveCaretToEnd(self); - window.setTimeout(function() { - moveCaretToEnd(self); - }, 1); - } - - function prependFrom(mail) { - return i18n.t( - 'reply-author-line', {'date': new Date(mail.header.date).toString(), 'from': mail.header.from} - ); - } - - function quoteMail(mail) { - return '\n\n' + prependFrom(mail) + mail.textPlainBody.replace(/^/mg, '> '); - } - - function formatDate(dateString) { - var date = new Date(dateString); - var today = createTodayDate(); - if (date.getTime() > today.getTime()) { - return fixedSizeNumber(date.getHours(), 2) + ':' + fixedSizeNumber(date.getMinutes(), 2); - } else { - return '' + date.getFullYear() + '-' + fixedSizeNumber(date.getMonth() + 1, 2) + '-' + fixedSizeNumber(date.getDate(), 2); - } - } - - function formatSize(bytes) { - var e = Math.floor(Math.log(bytes) / Math.log(1024)); - return (bytes / Math.pow(1024, e)).toFixed(2) + ' ' + ' KMGTP'.charAt(e) + 'b'; - } - - - function formatFingerPrint(fingerprint) { - fingerprint = fingerprint || ''; - return fingerprint.replace(/(.{4})/g, '$1 ').trim(); - } - - function getSinceDate(sinceDate){ - var commitDate = new Date(sinceDate); - var number = Date.now(); - var millisecondsSince = number - commitDate; - - var SECONDS = 1000, - MIN = 60 * SECONDS, - HOUR = MIN * 60, - DAY = HOUR * 24, - WEEK = DAY * 7, - MONTH = WEEK * 4, - YEAR = DAY * 365; - - var years = Math.floor(millisecondsSince / YEAR); - if (years >= 1){ - return years + ' year(s)'; - } - - var months = Math.floor(millisecondsSince / MONTH); - if (months >= 1) { - return months + ' month(s)'; - } - - var weeks = Math.floor(millisecondsSince / WEEK); - if (weeks >= 1) { - return weeks + ' week(s)'; - } - - var days = Math.floor(millisecondsSince / DAY); - if (days >= 1) { - return days + ' day(s)'; - } - - var hours = Math.floor(millisecondsSince / HOUR); - if (hours >= 1) { - return hours + ' hour(s)'; - } - - var minutes = Math.floor(millisecondsSince / MIN); - return minutes + ' minute(s)'; - } - - Handlebars.registerHelper('formatDate', formatDate); - Handlebars.registerHelper('formatSize', formatSize); - Handlebars.registerHelper('formatStatusClasses', formatStatusClasses); - Handlebars.registerHelper('formatFingerPrint', formatFingerPrint); - Handlebars.registerHelper('sinceDate', getSinceDate); - - return { - formatStatusClasses: formatStatusClasses, - formatSize: formatSize, - formatMailBody: formatMailBody, - formatFingerPrint: formatFingerPrint, - moveCaretToEndOfText: moveCaretToEndOfText, - quoteMail: quoteMail, - sinceDate: getSinceDate, - i18n: i18n - }; -}); diff --git a/web-ui/app/js/lib/highlightRegex.js b/web-ui/app/js/lib/highlightRegex.js deleted file mode 100644 index 17caaa23..00000000 --- a/web-ui/app/js/lib/highlightRegex.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - * jQuery Highlight Regex Plugin v0.1.2 - * - * Based on highlight v3 by Johann Burkard - * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html - * - * (c) 2009-13 Jacob Rothstein - * MIT license - */ - -;(function( $ ) { - - - - var normalize = function( node ) { - if ( ! ( node && node.childNodes )) return - - var children = $.makeArray( node.childNodes ) - , prevTextNode = null - - $.each( children, function( i, child ) { - if ( child.nodeType === 3 ) { - if ( child.nodeValue === "" ) { - - node.removeChild( child ) - - } else if ( prevTextNode !== null ) { - - prevTextNode.nodeValue += child.nodeValue; - node.removeChild( child ) - - } else { - - prevTextNode = child - - } - } else { - prevTextNode = null - - if ( child.childNodes ) { - normalize( child ) - } - } - }) - } - - - - - $.fn.highlightRegex = function( regex, options ) { - - if ( typeof regex === 'object' && !(regex.constructor.name == 'RegExp' || regex instanceof RegExp ) ) { - options = regex - regex = undefined - } - - if ( typeof options === 'undefined' ) options = {} - - options.className = options.className || 'highlight' - options.tagType = options.tagType || 'span' - options.attrs = options.attrs || {} - - if ( typeof regex === 'undefined' || regex.source === '' ) { - - $( this ).find( options.tagType + '.' + options.className ).each( function() { - - $( this ).replaceWith( $( this ).text() ) - - normalize( $( this ).parent().get( 0 )) - - }) - - } else { - - $( this ).each( function() { - - var elt = $( this ).get( 0 ) - - normalize( elt ) - - $.each( $.makeArray( elt.childNodes ), function( i, searchnode ) { - - var spannode, middlebit, middleclone, pos, match, parent - - normalize( searchnode ) - - if ( searchnode.nodeType == 3 ) { - - // don't re-highlight the same node over and over - if ( $(searchnode).parent(options.tagType + '.' + options.className).length ) { - return; - } - - while ( searchnode.data && - ( pos = searchnode.data.search( regex )) >= 0 ) { - - match = searchnode.data.slice( pos ).match( regex )[ 0 ] - - if ( match.length > 0 ) { - - spannode = document.createElement( options.tagType ) - spannode.className = options.className - $(spannode).attr(options.attrs) - - parent = searchnode.parentNode - middlebit = searchnode.splitText( pos ) - searchnode = middlebit.splitText( match.length ) - middleclone = middlebit.cloneNode( true ) - - spannode.appendChild( middleclone ) - parent.replaceChild( spannode, middlebit ) - - } else break - } - - } else { - - $( searchnode ).highlightRegex( regex, options ) - - } - }) - }) - } - - return $( this ) - } -})( jQuery ); diff --git a/web-ui/app/js/lib/html4-defs.js b/web-ui/app/js/lib/html4-defs.js deleted file mode 100644 index 1ec575da..00000000 --- a/web-ui/app/js/lib/html4-defs.js +++ /dev/null @@ -1,640 +0,0 @@ -// Copyright Google Inc. -// Licensed under the Apache Licence Version 2.0 -// Autogenerated at Mon Jul 14 18:51:33 BRT 2014 -// @overrides window -// @provides html4 -define([], function() { -var html4 = {}; -html4.atype = { - 'NONE': 0, - 'URI': 1, - 'URI_FRAGMENT': 11, - 'SCRIPT': 2, - 'STYLE': 3, - 'HTML': 12, - 'ID': 4, - 'IDREF': 5, - 'IDREFS': 6, - 'GLOBAL_NAME': 7, - 'LOCAL_NAME': 8, - 'CLASSES': 9, - 'FRAME_TARGET': 10, - 'MEDIA_QUERY': 13 -}; -html4[ 'atype' ] = html4.atype; -html4.ATTRIBS = { - '*::class': 9, - '*::dir': 0, - '*::draggable': 0, - '*::hidden': 0, - '*::id': 4, - '*::inert': 0, - '*::itemprop': 0, - '*::itemref': 6, - '*::itemscope': 0, - '*::lang': 0, - '*::onblur': 2, - '*::onchange': 2, - '*::onclick': 2, - '*::ondblclick': 2, - '*::onerror': 2, - '*::onfocus': 2, - '*::onkeydown': 2, - '*::onkeypress': 2, - '*::onkeyup': 2, - '*::onload': 2, - '*::onmousedown': 2, - '*::onmousemove': 2, - '*::onmouseout': 2, - '*::onmouseover': 2, - '*::onmouseup': 2, - '*::onreset': 2, - '*::onscroll': 2, - '*::onselect': 2, - '*::onsubmit': 2, - '*::ontouchcancel': 2, - '*::ontouchend': 2, - '*::ontouchenter': 2, - '*::ontouchleave': 2, - '*::ontouchmove': 2, - '*::ontouchstart': 2, - '*::onunload': 2, - '*::spellcheck': 0, - '*::style': 3, - '*::tabindex': 0, - '*::title': 0, - '*::translate': 0, - 'a::accesskey': 0, - 'a::coords': 0, - 'a::href': 1, - 'a::hreflang': 0, - 'a::name': 7, - 'a::onblur': 2, - 'a::onfocus': 2, - 'a::shape': 0, - 'a::target': 10, - 'a::type': 0, - 'area::accesskey': 0, - 'area::alt': 0, - 'area::coords': 0, - 'area::href': 1, - 'area::nohref': 0, - 'area::onblur': 2, - 'area::onfocus': 2, - 'area::shape': 0, - 'area::target': 10, - 'audio::controls': 0, - 'audio::loop': 0, - 'audio::mediagroup': 5, - 'audio::muted': 0, - 'audio::preload': 0, - 'audio::src': 1, - 'bdo::dir': 0, - 'blockquote::cite': 1, - 'br::clear': 0, - 'button::accesskey': 0, - 'button::disabled': 0, - 'button::name': 8, - 'button::onblur': 2, - 'button::onfocus': 2, - 'button::type': 0, - 'button::value': 0, - 'canvas::height': 0, - 'canvas::width': 0, - 'caption::align': 0, - 'col::align': 0, - 'col::char': 0, - 'col::charoff': 0, - 'col::span': 0, - 'col::valign': 0, - 'col::width': 0, - 'colgroup::align': 0, - 'colgroup::char': 0, - 'colgroup::charoff': 0, - 'colgroup::span': 0, - 'colgroup::valign': 0, - 'colgroup::width': 0, - 'command::checked': 0, - 'command::command': 5, - 'command::disabled': 0, - 'command::icon': 1, - 'command::label': 0, - 'command::radiogroup': 0, - 'command::type': 0, - 'data::value': 0, - 'del::cite': 1, - 'del::datetime': 0, - 'details::open': 0, - 'dir::compact': 0, - 'div::align': 0, - 'dl::compact': 0, - 'fieldset::disabled': 0, - 'font::color': 0, - 'font::face': 0, - 'font::size': 0, - 'form::accept': 0, - 'form::action': 1, - 'form::autocomplete': 0, - 'form::enctype': 0, - 'form::method': 0, - 'form::name': 7, - 'form::novalidate': 0, - 'form::onreset': 2, - 'form::onsubmit': 2, - 'form::target': 10, - 'h1::align': 0, - 'h2::align': 0, - 'h3::align': 0, - 'h4::align': 0, - 'h5::align': 0, - 'h6::align': 0, - 'hr::align': 0, - 'hr::noshade': 0, - 'hr::size': 0, - 'hr::width': 0, - 'iframe::align': 0, - 'iframe::frameborder': 0, - 'iframe::height': 0, - 'iframe::marginheight': 0, - 'iframe::marginwidth': 0, - 'iframe::width': 0, - 'img::align': 0, - 'img::alt': 0, - 'img::border': 0, - 'img::height': 0, - 'img::hspace': 0, - 'img::ismap': 0, - 'img::name': 7, - 'img::src': 1, - 'img::usemap': 11, - 'img::vspace': 0, - 'img::width': 0, - 'input::accept': 0, - 'input::accesskey': 0, - 'input::align': 0, - 'input::alt': 0, - 'input::autocomplete': 0, - 'input::checked': 0, - 'input::disabled': 0, - 'input::inputmode': 0, - 'input::ismap': 0, - 'input::list': 5, - 'input::max': 0, - 'input::maxlength': 0, - 'input::min': 0, - 'input::multiple': 0, - 'input::name': 8, - 'input::onblur': 2, - 'input::onchange': 2, - 'input::onfocus': 2, - 'input::onselect': 2, - 'input::pattern': 0, - 'input::placeholder': 0, - 'input::readonly': 0, - 'input::required': 0, - 'input::size': 0, - 'input::src': 1, - 'input::step': 0, - 'input::type': 0, - 'input::usemap': 11, - 'input::value': 0, - 'ins::cite': 1, - 'ins::datetime': 0, - 'label::accesskey': 0, - 'label::for': 5, - 'label::onblur': 2, - 'label::onfocus': 2, - 'legend::accesskey': 0, - 'legend::align': 0, - 'li::type': 0, - 'li::value': 0, - 'map::name': 7, - 'menu::compact': 0, - 'menu::label': 0, - 'menu::type': 0, - 'meter::high': 0, - 'meter::low': 0, - 'meter::max': 0, - 'meter::min': 0, - 'meter::value': 0, - 'ol::compact': 0, - 'ol::reversed': 0, - 'ol::start': 0, - 'ol::type': 0, - 'optgroup::disabled': 0, - 'optgroup::label': 0, - 'option::disabled': 0, - 'option::label': 0, - 'option::selected': 0, - 'option::value': 0, - 'output::for': 6, - 'output::name': 8, - 'p::align': 0, - 'pre::width': 0, - 'progress::max': 0, - 'progress::min': 0, - 'progress::value': 0, - 'q::cite': 1, - 'select::autocomplete': 0, - 'select::disabled': 0, - 'select::multiple': 0, - 'select::name': 8, - 'select::onblur': 2, - 'select::onchange': 2, - 'select::onfocus': 2, - 'select::required': 0, - 'select::size': 0, - 'source::type': 0, - 'table::align': 0, - 'table::bgcolor': 0, - 'table::border': 0, - 'table::cellpadding': 0, - 'table::cellspacing': 0, - 'table::frame': 0, - 'table::rules': 0, - 'table::summary': 0, - 'table::width': 0, - 'tbody::align': 0, - 'tbody::char': 0, - 'tbody::charoff': 0, - 'tbody::valign': 0, - 'td::abbr': 0, - 'td::align': 0, - 'td::axis': 0, - 'td::bgcolor': 0, - 'td::char': 0, - 'td::charoff': 0, - 'td::colspan': 0, - 'td::headers': 6, - 'td::height': 0, - 'td::nowrap': 0, - 'td::rowspan': 0, - 'td::scope': 0, - 'td::valign': 0, - 'td::width': 0, - 'textarea::accesskey': 0, - 'textarea::autocomplete': 0, - 'textarea::cols': 0, - 'textarea::disabled': 0, - 'textarea::inputmode': 0, - 'textarea::name': 8, - 'textarea::onblur': 2, - 'textarea::onchange': 2, - 'textarea::onfocus': 2, - 'textarea::onselect': 2, - 'textarea::placeholder': 0, - 'textarea::readonly': 0, - 'textarea::required': 0, - 'textarea::rows': 0, - 'textarea::wrap': 0, - 'tfoot::align': 0, - 'tfoot::char': 0, - 'tfoot::charoff': 0, - 'tfoot::valign': 0, - 'th::abbr': 0, - 'th::align': 0, - 'th::axis': 0, - 'th::bgcolor': 0, - 'th::char': 0, - 'th::charoff': 0, - 'th::colspan': 0, - 'th::headers': 6, - 'th::height': 0, - 'th::nowrap': 0, - 'th::rowspan': 0, - 'th::scope': 0, - 'th::valign': 0, - 'th::width': 0, - 'thead::align': 0, - 'thead::char': 0, - 'thead::charoff': 0, - 'thead::valign': 0, - 'tr::align': 0, - 'tr::bgcolor': 0, - 'tr::char': 0, - 'tr::charoff': 0, - 'tr::valign': 0, - 'track::default': 0, - 'track::kind': 0, - 'track::label': 0, - 'track::srclang': 0, - 'ul::compact': 0, - 'ul::type': 0, - 'video::controls': 0, - 'video::height': 0, - 'video::loop': 0, - 'video::mediagroup': 5, - 'video::muted': 0, - 'video::poster': 1, - 'video::preload': 0, - 'video::src': 1, - 'video::width': 0 -}; -html4[ 'ATTRIBS' ] = html4.ATTRIBS; -html4.eflags = { - 'OPTIONAL_ENDTAG': 1, - 'EMPTY': 2, - 'CDATA': 4, - 'RCDATA': 8, - 'UNSAFE': 16, - 'FOLDABLE': 32, - 'SCRIPT': 64, - 'STYLE': 128, - 'VIRTUALIZED': 256 -}; -html4[ 'eflags' ] = html4.eflags; -html4.ELEMENTS = { - 'a': 0, - 'abbr': 0, - 'acronym': 0, - 'address': 0, - 'applet': 272, - 'area': 2, - 'article': 0, - 'aside': 0, - 'audio': 0, - 'b': 0, - 'base': 274, - 'basefont': 274, - 'bdi': 0, - 'bdo': 0, - 'big': 0, - 'blockquote': 0, - 'body': 305, - 'br': 2, - 'button': 0, - 'canvas': 0, - 'caption': 0, - 'center': 0, - 'cite': 0, - 'code': 0, - 'col': 2, - 'colgroup': 1, - 'command': 2, - 'data': 0, - 'datalist': 0, - 'dd': 1, - 'del': 0, - 'details': 0, - 'dfn': 0, - 'dialog': 272, - 'dir': 0, - 'div': 0, - 'dl': 0, - 'dt': 1, - 'em': 0, - 'fieldset': 0, - 'figcaption': 0, - 'figure': 0, - 'font': 0, - 'footer': 0, - 'form': 0, - 'frame': 274, - 'frameset': 272, - 'h1': 0, - 'h2': 0, - 'h3': 0, - 'h4': 0, - 'h5': 0, - 'h6': 0, - 'head': 305, - 'header': 0, - 'hgroup': 0, - 'hr': 2, - 'html': 305, - 'i': 0, - 'iframe': 4, - 'img': 2, - 'input': 2, - 'ins': 0, - 'isindex': 274, - 'kbd': 0, - 'keygen': 274, - 'label': 0, - 'legend': 0, - 'li': 1, - 'link': 274, - 'map': 0, - 'mark': 0, - 'menu': 0, - 'meta': 274, - 'meter': 0, - 'nav': 0, - 'nobr': 0, - 'noembed': 276, - 'noframes': 276, - 'noscript': 276, - 'object': 272, - 'ol': 0, - 'optgroup': 0, - 'option': 1, - 'output': 0, - 'p': 1, - 'param': 274, - 'pre': 0, - 'progress': 0, - 'q': 0, - 's': 0, - 'samp': 0, - 'script': 84, - 'section': 0, - 'select': 0, - 'small': 0, - 'source': 2, - 'span': 0, - 'strike': 0, - 'strong': 0, - 'style': 148, - 'sub': 0, - 'summary': 0, - 'sup': 0, - 'table': 0, - 'tbody': 1, - 'td': 1, - 'textarea': 8, - 'tfoot': 1, - 'th': 1, - 'thead': 1, - 'time': 0, - 'title': 280, - 'tr': 1, - 'track': 2, - 'tt': 0, - 'u': 0, - 'ul': 0, - 'var': 0, - 'video': 0, - 'wbr': 2 -}; -html4[ 'ELEMENTS' ] = html4.ELEMENTS; -html4.ELEMENT_DOM_INTERFACES = { - 'a': 'HTMLAnchorElement', - 'abbr': 'HTMLElement', - 'acronym': 'HTMLElement', - 'address': 'HTMLElement', - 'applet': 'HTMLAppletElement', - 'area': 'HTMLAreaElement', - 'article': 'HTMLElement', - 'aside': 'HTMLElement', - 'audio': 'HTMLAudioElement', - 'b': 'HTMLElement', - 'base': 'HTMLBaseElement', - 'basefont': 'HTMLBaseFontElement', - 'bdi': 'HTMLElement', - 'bdo': 'HTMLElement', - 'big': 'HTMLElement', - 'blockquote': 'HTMLQuoteElement', - 'body': 'HTMLBodyElement', - 'br': 'HTMLBRElement', - 'button': 'HTMLButtonElement', - 'canvas': 'HTMLCanvasElement', - 'caption': 'HTMLTableCaptionElement', - 'center': 'HTMLElement', - 'cite': 'HTMLElement', - 'code': 'HTMLElement', - 'col': 'HTMLTableColElement', - 'colgroup': 'HTMLTableColElement', - 'command': 'HTMLCommandElement', - 'data': 'HTMLElement', - 'datalist': 'HTMLDataListElement', - 'dd': 'HTMLElement', - 'del': 'HTMLModElement', - 'details': 'HTMLDetailsElement', - 'dfn': 'HTMLElement', - 'dialog': 'HTMLDialogElement', - 'dir': 'HTMLDirectoryElement', - 'div': 'HTMLDivElement', - 'dl': 'HTMLDListElement', - 'dt': 'HTMLElement', - 'em': 'HTMLElement', - 'fieldset': 'HTMLFieldSetElement', - 'figcaption': 'HTMLElement', - 'figure': 'HTMLElement', - 'font': 'HTMLFontElement', - 'footer': 'HTMLElement', - 'form': 'HTMLFormElement', - 'frame': 'HTMLFrameElement', - 'frameset': 'HTMLFrameSetElement', - 'h1': 'HTMLHeadingElement', - 'h2': 'HTMLHeadingElement', - 'h3': 'HTMLHeadingElement', - 'h4': 'HTMLHeadingElement', - 'h5': 'HTMLHeadingElement', - 'h6': 'HTMLHeadingElement', - 'head': 'HTMLHeadElement', - 'header': 'HTMLElement', - 'hgroup': 'HTMLElement', - 'hr': 'HTMLHRElement', - 'html': 'HTMLHtmlElement', - 'i': 'HTMLElement', - 'iframe': 'HTMLIFrameElement', - 'img': 'HTMLImageElement', - 'input': 'HTMLInputElement', - 'ins': 'HTMLModElement', - 'isindex': 'HTMLUnknownElement', - 'kbd': 'HTMLElement', - 'keygen': 'HTMLKeygenElement', - 'label': 'HTMLLabelElement', - 'legend': 'HTMLLegendElement', - 'li': 'HTMLLIElement', - 'link': 'HTMLLinkElement', - 'map': 'HTMLMapElement', - 'mark': 'HTMLElement', - 'menu': 'HTMLMenuElement', - 'meta': 'HTMLMetaElement', - 'meter': 'HTMLMeterElement', - 'nav': 'HTMLElement', - 'nobr': 'HTMLElement', - 'noembed': 'HTMLElement', - 'noframes': 'HTMLElement', - 'noscript': 'HTMLElement', - 'object': 'HTMLObjectElement', - 'ol': 'HTMLOListElement', - 'optgroup': 'HTMLOptGroupElement', - 'option': 'HTMLOptionElement', - 'output': 'HTMLOutputElement', - 'p': 'HTMLParagraphElement', - 'param': 'HTMLParamElement', - 'pre': 'HTMLPreElement', - 'progress': 'HTMLProgressElement', - 'q': 'HTMLQuoteElement', - 's': 'HTMLElement', - 'samp': 'HTMLElement', - 'script': 'HTMLScriptElement', - 'section': 'HTMLElement', - 'select': 'HTMLSelectElement', - 'small': 'HTMLElement', - 'source': 'HTMLSourceElement', - 'span': 'HTMLSpanElement', - 'strike': 'HTMLElement', - 'strong': 'HTMLElement', - 'style': 'HTMLStyleElement', - 'sub': 'HTMLElement', - 'summary': 'HTMLElement', - 'sup': 'HTMLElement', - 'table': 'HTMLTableElement', - 'tbody': 'HTMLTableSectionElement', - 'td': 'HTMLTableDataCellElement', - 'textarea': 'HTMLTextAreaElement', - 'tfoot': 'HTMLTableSectionElement', - 'th': 'HTMLTableHeaderCellElement', - 'thead': 'HTMLTableSectionElement', - 'time': 'HTMLTimeElement', - 'title': 'HTMLTitleElement', - 'tr': 'HTMLTableRowElement', - 'track': 'HTMLTrackElement', - 'tt': 'HTMLElement', - 'u': 'HTMLElement', - 'ul': 'HTMLUListElement', - 'var': 'HTMLElement', - 'video': 'HTMLVideoElement', - 'wbr': 'HTMLElement' -}; -html4[ 'ELEMENT_DOM_INTERFACES' ] = html4.ELEMENT_DOM_INTERFACES; -html4.ueffects = { - 'NOT_LOADED': 0, - 'SAME_DOCUMENT': 1, - 'NEW_DOCUMENT': 2 -}; -html4[ 'ueffects' ] = html4.ueffects; -html4.URIEFFECTS = { - 'a::href': 2, - 'area::href': 2, - 'audio::src': 1, - 'blockquote::cite': 0, - 'command::icon': 1, - 'del::cite': 0, - 'form::action': 2, - 'img::src': 1, - 'input::src': 1, - 'ins::cite': 0, - 'q::cite': 0, - 'video::poster': 1, - 'video::src': 1 -}; -html4[ 'URIEFFECTS' ] = html4.URIEFFECTS; -html4.ltypes = { - 'UNSANDBOXED': 2, - 'SANDBOXED': 1, - 'DATA': 0 -}; -html4[ 'ltypes' ] = html4.ltypes; -html4.LOADERTYPES = { - 'a::href': 2, - 'area::href': 2, - 'audio::src': 2, - 'blockquote::cite': 2, - 'command::icon': 1, - 'del::cite': 2, - 'form::action': 2, - 'img::src': 1, - 'input::src': 1, - 'ins::cite': 2, - 'q::cite': 2, - 'video::poster': 1, - 'video::src': 2 -}; -html4[ 'LOADERTYPES' ] = html4.LOADERTYPES; - -return html4 -}); diff --git a/web-ui/app/js/mail_list/domain/refresher.js b/web-ui/app/js/mail_list/domain/refresher.js deleted file mode 100644 index 38c9cde5..00000000 --- a/web-ui/app/js/mail_list/domain/refresher.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(['flight/lib/component', 'page/events', 'features'], function(defineComponent, events, features) { - 'use strict'; - - return defineComponent(refresher); - - function refresher() { - this.defaultAttrs({ - interval: 20000 - }); - - this.setupRefresher = function() { - setTimeout(this.doRefresh.bind(this), this.attr.interval); - }; - - this.doRefresh = function() { - this.trigger(document, events.ui.mails.refresh); - this.setupRefresher(); - }; - - this.after('initialize', function () { - if (features.isAutoRefreshEnabled()) { - this.setupRefresher(); - } - }); - } - } -); diff --git a/web-ui/app/js/mail_list/ui/mail_item_factory.js b/web-ui/app/js/mail_list/ui/mail_item_factory.js deleted file mode 100644 index 7205d35c..00000000 --- a/web-ui/app/js/mail_list/ui/mail_item_factory.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'mail_list/ui/mail_items/generic_mail_item', - 'mail_list/ui/mail_items/draft_item', - 'mail_list/ui/mail_items/sent_item' - ], - function (GenericMailItem, DraftItem, SentItem) { - 'use strict'; - - var MAIL_ITEM_TYPE = { - 'drafts': DraftItem, - 'sent': SentItem, - 'trash': GenericMailItem - }; - - var TEMPLATE_TYPE = { - 'drafts': 'draft', - 'sent': 'sent', - 'trash': 'trash' - }; - - var createAndAttach = function (nodeToAttachTo, mail, currentMailIdent, currentTag, isChecked) { - var mailItemContainer = $('
  • ', { id: 'mail-' + mail.ident}); - nodeToAttachTo.append(mailItemContainer); - - mail.currentTag = currentTag; - var mailToCreate = MAIL_ITEM_TYPE[mail.mailbox] || GenericMailItem; - mailToCreate.attachTo(mailItemContainer, { - mail: mail, - selected: mail.ident === currentMailIdent, - tag: currentTag, - isChecked: isChecked, - templateType: TEMPLATE_TYPE[mail.mailbox] || 'single' - }); - - }; - - return { - createAndAttach: createAndAttach - }; - } -); diff --git a/web-ui/app/js/mail_list/ui/mail_items/draft_item.js b/web-ui/app/js/mail_list/ui/mail_items/draft_item.js deleted file mode 100644 index 57fbafd5..00000000 --- a/web-ui/app/js/mail_list/ui/mail_items/draft_item.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'helpers/view_helper', - 'mail_list/ui/mail_items/mail_item', - 'page/events' - ], - - function (defineComponent, viewHelpers, mailItem, events) { - 'use strict'; - - return defineComponent(draftItem, mailItem); - - function draftItem() { - this.triggerOpenMail = function (ev) { - if (this.isOpeningOnANewTab(ev)) { - return; - } - this.trigger(document, events.dispatchers.rightPane.openDraft, { ident: this.attr.mail.ident }); - this.trigger(document, events.ui.mail.updateSelected, { ident: this.attr.mail.ident }); - this.trigger(document, events.router.pushState, { mailIdent: this.attr.mail.ident }); - ev.preventDefault(); // don't let the hashchange trigger a popstate - }; - - this.after('initialize', function () { - this.render(); - - if (this.attr.isChecked) { - this.checkCheckbox(); - } - - this.on(document, events.ui.composeBox.newMessage, this.doUnselect); - this.on(document, events.ui.mail.updateSelected, this.updateSelected); - this.on(document, events.mails.teardown, this.teardown); - }); - } - } -); diff --git a/web-ui/app/js/mail_list/ui/mail_items/generic_mail_item.js b/web-ui/app/js/mail_list/ui/mail_items/generic_mail_item.js deleted file mode 100644 index 939f7e1b..00000000 --- a/web-ui/app/js/mail_list/ui/mail_items/generic_mail_item.js +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'helpers/view_helper', - 'mail_list/ui/mail_items/mail_item', - 'page/events' - ], - - function (defineComponent, viewHelpers, mailItem, events) { - 'use strict'; - - return defineComponent(genericMailItem, mailItem); - - function genericMailItem() { - this.status = { - READ: 'read' - }; - - this.triggerOpenMail = function (ev) { - if (this.isOpeningOnANewTab(ev)) { - updateMailStatusToRead.call(this); - return; - } - this.trigger(document, events.ui.mail.open, { ident: this.attr.mail.ident }); - this.trigger(document, events.router.pushState, { mailIdent: this.attr.mail.ident }); - ev.preventDefault(); // don't let the hashchange trigger a popstate - }; - - function updateMailStatusToRead() { - if (!_.contains(this.attr.mail.status, this.status.READ)) { - var mail_read_data = { ident: this.attr.mail.ident, tags: this.attr.mail.tags, mailbox: this.attr.mail.mailbox }; - this.trigger(document, events.mail.read, mail_read_data); - this.attr.mail.status.push(this.status.READ); - this.$node.addClass(viewHelpers.formatStatusClasses(this.attr.mail.status)); - } - } - - this.openMail = function (ev, data) { - if (data.ident !== this.attr.mail.ident) { - return; - } - updateMailStatusToRead.call(this); - - this.trigger(document, events.ui.mail.updateSelected, { ident: this.attr.mail.ident }); - }; - - this.updateTags = function(ev, data) { - if(data.ident === this.attr.mail.ident){ - this.attr.tags = data.tags; - if(!_.contains(this.attr.tags, this.attr.tag)) { - this.teardown(); - } else { - this.render(); - } - } - }; - - this.deleteMail = function(ev, data) { - if(data.mail.ident === this.attr.mail.ident){ - this.teardown(); - } - }; - - this.after('initialize', function () { - this.render(); - - if (this.attr.isChecked) { - this.checkCheckbox(); - } - - this.on(document, events.ui.composeBox.newMessage, this.doUnselect); - this.on(document, events.ui.mail.open, this.openMail); - this.on(document, events.ui.mail.updateSelected, this.updateSelected); - this.on(document, events.mails.teardown, this.teardown); - this.on(document, events.mail.tags.update, this.updateTags); - this.on(document, events.mail.delete, this.deleteMail); - }); - } - } -); diff --git a/web-ui/app/js/mail_list/ui/mail_items/mail_item.js b/web-ui/app/js/mail_list/ui/mail_items/mail_item.js deleted file mode 100644 index be664289..00000000 --- a/web-ui/app/js/mail_list/ui/mail_items/mail_item.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'helpers/view_helper', - 'views/templates', - 'page/events' - ], - function (viewHelper, templates, events) { - - 'use strict'; - function mailItem() { - this.updateSelected = function (ev, data) { - if (data.ident === this.attr.mail.ident) { this.doSelect(); } - else { this.doUnselect(); } - }; - - this.isOpeningOnANewTab = function (ev) { - return ev.metaKey || ev.ctrlKey || ev.which === 2; - }; - - this.doSelect = function () { - this.$node.addClass('selected'); - }; - - this.doUnselect = function () { - this.$node.removeClass('selected'); - }; - - this.doMailChecked = function (ev) { - if (ev.target.checked) { - this.checkCheckbox(); - } else { - this.uncheckCheckbox(); - } - }; - - this.checkboxElement = function () { - return this.$node.find('input[type=checkbox]'); - }; - - this.checkCheckbox = function () { - this.checkboxElement().prop('checked', true); - this.trigger(document, events.ui.mail.checked, { mail: this.attr.mail}); - }; - - this.uncheckCheckbox = function () { - this.checkboxElement().prop('checked', false); - this.trigger(document, events.ui.mail.unchecked, { mail: this.attr.mail}); - }; - - this.render = function () { - this.attr.mail.tagsForListView = _.without(this.attr.mail.tags, this.attr.tag); - var mailItemHtml = templates.mails[this.attr.templateType](this.attr.mail); - this.$node.html(mailItemHtml); - this.$node.addClass("mail-list-entry"); - this.$node.addClass(viewHelper.formatStatusClasses(this.attr.mail.status)); - if (this.attr.selected) { this.doSelect(); } - this.on(this.$node.find('a'), 'click', this.triggerOpenMail); - }; - - this.after('initialize', function () { - this.on(this.$node.find('input[type=checkbox]'), 'change', this.doMailChecked); - this.on(document, events.ui.mails.cleanSelected, this.doUnselect); - this.on(document, events.ui.tag.select, this.doUnselect); - this.on(document, events.ui.tag.select, this.uncheckCheckbox); - this.on(document, events.ui.mails.uncheckAll, this.uncheckCheckbox); - this.on(document, events.ui.mails.checkAll, this.checkCheckbox); - }); - } - - return mailItem; -}); diff --git a/web-ui/app/js/mail_list/ui/mail_items/sent_item.js b/web-ui/app/js/mail_list/ui/mail_items/sent_item.js deleted file mode 100644 index 9e511068..00000000 --- a/web-ui/app/js/mail_list/ui/mail_items/sent_item.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'mail_list/ui/mail_items/mail_item', - 'page/events' - ], - - function (defineComponent, mailItem, events) { - 'use strict'; - - return defineComponent(sentItem, mailItem); - - function sentItem() { - this.triggerOpenMail = function (ev) { - if (this.isOpeningOnANewTab(ev)) { - return; - } - this.trigger(document, events.ui.mail.open, { ident: this.attr.mail.ident }); - this.trigger(document, events.router.pushState, { mailIdent: this.attr.mail.ident }); - ev.preventDefault(); // don't let the hashchange trigger a popstate - }; - - this.openMail = function (ev, data) { - if (data.ident !== this.attr.mail.ident) { - return; - } - this.trigger(document, events.ui.mail.updateSelected, { ident: this.attr.mail.ident }); - }; - - this.after('initialize', function () { - this.render(); - - if (this.attr.isChecked) { - this.checkCheckbox(); - } - - this.on(document, events.ui.composeBox.newMessage, this.doUnselect); - this.on(document, events.ui.mail.open, this.openMail); - this.on(document, events.ui.mail.updateSelected, this.updateSelected); - this.on(document, events.mails.teardown, this.teardown); - }); - } - } -); diff --git a/web-ui/app/js/mail_list/ui/mail_list.js b/web-ui/app/js/mail_list/ui/mail_list.js deleted file mode 100644 index af4821a8..00000000 --- a/web-ui/app/js/mail_list/ui/mail_list.js +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'flight/lib/utils', - 'mail_list/ui/mail_item_factory', - 'page/router/url_params', - 'page/events' - ], - - function (defineComponent, utils, MailItemFactory, urlParams, events) { - 'use strict'; - - return defineComponent(mailList); - - function mailList () { - var openMailEventFor = function (tag) { - return tag === 'drafts' ? events.dispatchers.rightPane.openDraft : events.ui.mail.open; - }; - - this.defaultAttrs({ - mail: '.mail', - currentMailIdent: '', - urlParams: urlParams, - initialized: false, - checkedMails: {} - }); - - this.appendMail = function (mail) { - var isChecked = mail.ident in this.attr.checkedMails; - MailItemFactory.createAndAttach(this.$node, mail, this.attr.currentMailIdent, this.attr.currentTag, isChecked); - }; - - this.resetMailList = function () { - this.trigger(document, events.mails.teardown); - this.$node.empty(); - }; - - this.triggerMailOpenForPopState = function (data) { - if (data.mailIdent) { - this.trigger(document, openMailEventFor(data.tag), { ident: data.mailIdent }); - } - }; - - this.shouldSelectEmailFromUrlMailIdent = function () { - return this.attr.urlParams.hasMailIdent(); - }; - - this.selectMailBasedOnUrlMailIdent = function () { - var mailIdent = this.attr.urlParams.getMailIdent(); - this.trigger(document, openMailEventFor(this.attr.currentTag), { ident: mailIdent }); - this.trigger(document, events.router.pushState, { tag: this.attr.currentTag, mailIdent: mailIdent }); - }; - - this.updateCurrentTagAndMail = function (data) { - if (data.ident) { - this.attr.currentMailIdent = data.ident; - } - - this.attr.currentTag = data.tag || this.attr.currentTag; - - this.updateCheckAllCheckbox(); - }; - - this.renderMails = function (mails) { - _.each(mails, this.appendMail, this); - this.trigger(document, events.search.highlightResults, {where: '#mail-list'}); - this.trigger(document, events.search.highlightResults, {where: '.mail-read-view__header'}); - }; - - this.triggerScrollReset = function () { - this.trigger(document, events.dispatchers.middlePane.resetScroll); - }; - - this.showMails = function (event, data) { - this.updateCurrentTagAndMail(data); - this.refreshMailList(null, data); - this.triggerMailOpenForPopState(data); - this.openMailFromUrl(); - }; - - this.refreshMailList = function (ev, data) { - if (ev) { // triggered by the event, so we need to refresh the tag list - this.trigger(document, events.dispatchers.tags.refreshTagList, { skipMailListRefresh: true }); - } - this.resetMailList(); - this.renderMails(data.mails); - }; - - this.updateSelected = function (ev, data) { - if (data.ident !== this.attr.currentMailIdent) { - this.attr.currentMailIdent = data.ident; - } - }; - - this.cleanSelected = function () { - this.attr.currentMailIdent = ''; - this.triggerScrollReset(); - }; - - this.respondWithCheckedMails = function (ev, caller) { - this.trigger(caller, events.ui.mail.hereChecked, {checkedMails: this.attr.checkedMails}); - }; - - this.updateCheckAllCheckbox = function () { - this.trigger(document, events.ui.mails.hasMailsChecked, _.keys(this.attr.checkedMails).length > 0); - }; - - this.addToCheckedMails = function (ev, data) { - this.attr.checkedMails[data.mail.ident] = data.mail; - this.updateCheckAllCheckbox(); - }; - - this.removeFromCheckedMails = function (ev, data) { - if (data.mails) { - _.each(data.mails, function (mail) { - delete this.attr.checkedMails[mail.ident]; - }, this); - } else { - delete this.attr.checkedMails[data.mail.ident]; - } - this.updateCheckAllCheckbox(); - }; - - this.refreshWithScroll = function () { - this.trigger(document, events.ui.mails.refresh); - this.triggerScrollReset(); - }; - - this.refreshAfterSaveDraft = function () { - if (this.attr.currentTag === 'drafts') { - this.refreshWithScroll(); - } - }; - - this.refreshAfterMailSent = function () { - if (this.attr.currentTag === 'drafts' || this.attr.currentTag === 'sent') { - this.refreshWithScroll(); - } - }; - - this.after('initialize', function () { - this.on(document, events.ui.mails.cleanSelected, this.cleanSelected); - this.on(document, events.ui.tag.select, this.cleanSelected); - - this.on(document, events.mails.available, this.showMails); - this.on(document, events.mails.availableForRefresh, this.refreshMailList); - - this.on(document, events.mail.draftSaved, this.refreshAfterSaveDraft); - this.on(document, events.mail.sent, this.refreshAfterMailSent); - - this.on(document, events.ui.mail.updateSelected, this.updateSelected); - this.on(document, events.ui.mail.wantChecked, this.respondWithCheckedMails); - this.on(document, events.ui.mail.checked, this.addToCheckedMails); - this.on(document, events.ui.mail.unchecked, this.removeFromCheckedMails); - - this.openMailFromUrl = utils.once(function () { - if (this.shouldSelectEmailFromUrlMailIdent()) { - this.selectMailBasedOnUrlMailIdent(); - } - }); - - }); - } - } -); diff --git a/web-ui/app/js/mail_list_actions/ui/archive_many_trigger.js b/web-ui/app/js/mail_list_actions/ui/archive_many_trigger.js deleted file mode 100644 index b148cdce..00000000 --- a/web-ui/app/js/mail_list_actions/ui/archive_many_trigger.js +++ /dev/null @@ -1,29 +0,0 @@ -define( - [ - 'flight/lib/component', - 'views/templates', - 'mixins/with_enable_disable_on_event', - 'page/events' - ], - - function(definecomponent, templates, withEnableDisableOnEvent, events) { - 'use strict'; - - return definecomponent(archiveManyTrigger, withEnableDisableOnEvent(events.ui.mails.hasMailsChecked)); - function archiveManyTrigger() { - - this.getMailsToArchive = function() { - this.trigger(document, events.ui.mail.wantChecked, this.$node); - }; - - this.archiveManyEmails = function(event, data) { - this.trigger(document, events.mail.archiveMany, data); - }; - - this.after('initialize', function () { - this.on('click', this.getMailsToArchive); - this.on(events.ui.mail.hereChecked, this.archiveManyEmails); - }); - } - } -); diff --git a/web-ui/app/js/mail_list_actions/ui/compose_trigger.js b/web-ui/app/js/mail_list_actions/ui/compose_trigger.js deleted file mode 100644 index ec79cb26..00000000 --- a/web-ui/app/js/mail_list_actions/ui/compose_trigger.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'page/events' - ], - - function(defineComponent, templates, events) { - 'use strict'; - - return defineComponent(composeTrigger); - - function composeTrigger() { - - this.defaultAttrs({}); - - this.render = function() { - this.$node.html(templates.mailActions.composeTrigger); - }; - - this.enableComposing = function(event, data) { - this.trigger(document, events.dispatchers.rightPane.openComposeBox); - }; - - this.showEmailSuccess = function () { - this.trigger(document, events.ui.userAlerts.displayMessage, {message: 'Your message was sent!', class: 'success'}); - }; - - this.showEmailError = function (ev, data) { - this.trigger(document, events.ui.userAlerts.displayMessage, {message: 'Error, message not sent: ' + data.responseJSON.message, class: 'error'}); - }; - - this.after('initialize', function () { - this.render(); - this.on('click', this.enableComposing); - this.on(document, events.mail.sent, this.showEmailSuccess); - this.on(document, events.mail.send_failed, this.showEmailError); - }); - } - } -); diff --git a/web-ui/app/js/mail_list_actions/ui/delete_many_trigger.js b/web-ui/app/js/mail_list_actions/ui/delete_many_trigger.js deleted file mode 100644 index dd2f67a5..00000000 --- a/web-ui/app/js/mail_list_actions/ui/delete_many_trigger.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'mixins/with_enable_disable_on_event', - 'page/events' - ], - - function(defineComponent, templates, withEnableDisableOnEvent, events) { - 'use strict'; - - return defineComponent(deleteManyTrigger, withEnableDisableOnEvent(events.ui.mails.hasMailsChecked)); - - function deleteManyTrigger() { - this.defaultAttrs({}); - - this.getMailsToDelete = function(event) { - this.trigger(document, events.ui.mail.wantChecked, this.$node); - }; - - this.deleteManyEmails = function (event, data) { - this.trigger(document, events.ui.mail.deleteMany, data); - }; - - this.after('initialize', function () { - this.on('click', this.getMailsToDelete); - this.on(events.ui.mail.hereChecked, this.deleteManyEmails); - }); - } - } -); diff --git a/web-ui/app/js/mail_list_actions/ui/mail_list_actions.js b/web-ui/app/js/mail_list_actions/ui/mail_list_actions.js deleted file mode 100644 index 69e5fde4..00000000 --- a/web-ui/app/js/mail_list_actions/ui/mail_list_actions.js +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'views/templates', - 'page/events', - 'page/router/url_params', - 'mail_list_actions/ui/compose_trigger', - 'mail_list_actions/ui/refresh_trigger', - 'mail_list/domain/refresher', - 'mail_list_actions/ui/toggle_check_all_trigger', - 'mail_list_actions/ui/pagination_trigger', - 'mail_list_actions/ui/delete_many_trigger', - 'mail_list_actions/ui/recover_many_trigger', - 'mail_list_actions/ui/archive_many_trigger', - 'mail_list_actions/ui/mark_many_as_read_trigger', - 'mail_list_actions/ui/mark_as_unread_trigger' - ], - - function ( - defineComponent, - templates, - events, - urlParams, - composeTrigger, - refreshTrigger, - refresher, - toggleCheckAllMailTrigger, - paginationTrigger, - deleteManyTrigger, - recoverManyTrigger, - archiveManyTrigger, - markManyAsReadTrigger, - markAsUnreadTrigger - ) { - 'use strict'; - return defineComponent(mailsActions); - - function mailsActions() { - this.render = function() { - this.$node.html(this.getActionsBoxTemplate()); - refreshTrigger.attachTo('#refresh-trigger'); - composeTrigger.attachTo('#compose-trigger'); - toggleCheckAllMailTrigger.attachTo('#toggle-check-all-emails'); - paginationTrigger.attachTo('#pagination-trigger'); - deleteManyTrigger.attachTo('#delete-selected'); - recoverManyTrigger.attachTo('#recover-selected'); - archiveManyTrigger.attachTo('#archive-selected'); - markManyAsReadTrigger.attachTo('#mark-selected-as-read'); - markAsUnreadTrigger.attachTo('#mark-selected-as-unread'); - refresher.attachTo(document); - }; - - this.getCurrentTag = function () { - return this.attr.currentTag || urlParams.getTag(); - }; - - this.updateCurrentTag = function (ev, data) { - this.attr.currentTag = data.tag; - this.render(); - }; - - this.getActionsBoxTemplate = function () { - if(this.getCurrentTag() === 'trash') { - return templates.mailActions.trashActionsBox(); - } else { - return templates.mailActions.actionsBox(); - } - }; - - this.after('initialize', function () { - this.on(document, events.ui.tag.select, this.updateCurrentTag); - this.render(); - }); - } - } -); diff --git a/web-ui/app/js/mail_list_actions/ui/mark_as_unread_trigger.js b/web-ui/app/js/mail_list_actions/ui/mark_as_unread_trigger.js deleted file mode 100644 index 2584e453..00000000 --- a/web-ui/app/js/mail_list_actions/ui/mark_as_unread_trigger.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'mixins/with_enable_disable_on_event', - 'page/events' - ], - - function(defineComponent, templates, withEnableDisableOnEvent, events) { - 'use strict'; - - return defineComponent(markAsUnreadTrigger, withEnableDisableOnEvent(events.ui.mails.hasMailsChecked)); - - function markAsUnreadTrigger() { - this.defaultAttrs({}); - - this.getMailsToMarkAsUnread = function(event) { - this.trigger(document, events.ui.mail.wantChecked, this.$node); - }; - - this.markManyEmailsAsUnread = function (event, data) { - this.trigger(document, events.mail.unread, data); - }; - - this.after('initialize', function () { - this.on('click', this.getMailsToMarkAsUnread); - this.on(events.ui.mail.hereChecked, this.markManyEmailsAsUnread); - }); - } - } -); diff --git a/web-ui/app/js/mail_list_actions/ui/mark_many_as_read_trigger.js b/web-ui/app/js/mail_list_actions/ui/mark_many_as_read_trigger.js deleted file mode 100644 index c16a2229..00000000 --- a/web-ui/app/js/mail_list_actions/ui/mark_many_as_read_trigger.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'mixins/with_enable_disable_on_event', - 'page/events' - ], - - function(defineComponent, templates, withEnableDisableOnEvent, events) { - 'use strict'; - - return defineComponent(markManyAsReadTrigger, withEnableDisableOnEvent(events.ui.mails.hasMailsChecked)); - - function markManyAsReadTrigger() { - this.defaultAttrs({}); - - this.getMailsToMarkAsRead = function(event) { - this.trigger(document, events.ui.mail.wantChecked, this.$node); - }; - - this.markManyEmailsAsRead = function (event, data) { - this.trigger(document, events.mail.read, data); - }; - - this.after('initialize', function () { - this.on('click', this.getMailsToMarkAsRead); - this.on(events.ui.mail.hereChecked, this.markManyEmailsAsRead); - }); - } - } -); diff --git a/web-ui/app/js/mail_list_actions/ui/pagination_trigger.js b/web-ui/app/js/mail_list_actions/ui/pagination_trigger.js deleted file mode 100644 index 3bc13d40..00000000 --- a/web-ui/app/js/mail_list_actions/ui/pagination_trigger.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'page/events' - ], - - function(defineComponent, templates, events) { - 'use strict'; - - return defineComponent(paginationTrigger); - - function paginationTrigger() { - this.defaultAttrs({ - previous: '#left-arrow', - next: '#right-arrow', - currentPage: '#current-page' - }); - - this.renderWithPageNumber = function(pageNumber) { - this.$node.html(templates.mailActions.paginationTrigger({ - currentPage: pageNumber - })); - this.on(this.attr.previous, 'click', this.previousPage); - this.on(this.attr.next, 'click', this.nextPage); - }; - - this.render = function() { - this.renderWithPageNumber(1); - }; - - this.updatePageDisplay = function(event, data) { - this.renderWithPageNumber(data.currentPage); - }; - - this.previousPage = function(event) { - this.trigger(document, events.ui.page.previous); - }; - - this.nextPage = function(event) { - this.trigger(document, events.ui.page.next); - }; - - this.after('initialize', function () { - this.render(); - this.on(document, events.ui.page.changed, this.updatePageDisplay); - }); - } - } -); diff --git a/web-ui/app/js/mail_list_actions/ui/recover_many_trigger.js b/web-ui/app/js/mail_list_actions/ui/recover_many_trigger.js deleted file mode 100644 index e0a32094..00000000 --- a/web-ui/app/js/mail_list_actions/ui/recover_many_trigger.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'mixins/with_enable_disable_on_event', - 'page/events' - ], - - function(defineComponent, templates, withEnableDisableOnEvent, events) { - 'use strict'; - - return defineComponent(recoverManyTrigger, withEnableDisableOnEvent(events.ui.mails.hasMailsChecked)); - - function recoverManyTrigger() { - this.defaultAttrs({}); - - this.getMailsToRecover = function(event) { - this.trigger(document, events.ui.mail.wantChecked, this.$node); - }; - - this.recoverManyEmails = function (event, data) { - this.trigger(document, events.ui.mail.recoverMany, data); - }; - - this.after('initialize', function () { - this.on('click', this.getMailsToRecover); - this.on(events.ui.mail.hereChecked, this.recoverManyEmails); - }); - } - } -); diff --git a/web-ui/app/js/mail_list_actions/ui/refresh_trigger.js b/web-ui/app/js/mail_list_actions/ui/refresh_trigger.js deleted file mode 100644 index a16270d2..00000000 --- a/web-ui/app/js/mail_list_actions/ui/refresh_trigger.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'page/events' - ], - - function(defineComponent, templates, events) { - 'use strict'; - - return defineComponent(refreshTrigger); - - function refreshTrigger() { - this.render = function() { - this.$node.html(templates.mailActions.refreshTrigger); - }; - - this.refresh = function(event) { - this.trigger(document, events.ui.mails.refresh); - }; - - this.after('initialize', function () { - this.render(); - this.on('click', this.refresh); - }); - } - } -); diff --git a/web-ui/app/js/mail_list_actions/ui/toggle_check_all_trigger.js b/web-ui/app/js/mail_list_actions/ui/toggle_check_all_trigger.js deleted file mode 100644 index 71c65346..00000000 --- a/web-ui/app/js/mail_list_actions/ui/toggle_check_all_trigger.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'page/events' - ], - - function(defineComponent, events) { - 'use strict'; - - return defineComponent(toggleCheckAllEmailsTrigger); - - function toggleCheckAllEmailsTrigger() { - this.defaultAttrs({ }); - - this.toggleCheckAll = function(event) { - if (this.$node.prop('checked')) { - this.trigger(document, events.ui.mails.checkAll); - } else { - this.trigger(document, events.ui.mails.uncheckAll); - } - }; - - this.setCheckbox = function (event, state) { - this.$node.prop('checked', state); - }; - - this.after('initialize', function () { - this.on('click', this.toggleCheckAll); - this.on(document, events.ui.mails.hasMailsChecked, this.setCheckbox); - }); - } - } -); diff --git a/web-ui/app/js/mail_view/data/feedback_sender.js b/web-ui/app/js/mail_view/data/feedback_sender.js deleted file mode 100644 index 2232dbe4..00000000 --- a/web-ui/app/js/mail_view/data/feedback_sender.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2015 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'helpers/monitored_ajax', - 'page/events' - ], - function (defineComponent, monitoredAjax, events) { - 'use strict'; - - return defineComponent(function () { - this.defaultAttrs({ - feedbackResource: '/feedback' - }); - - this.successSubmittingFeedback = function() { - this.trigger(document, events.feedback.submitted); - }; - - this.submitFeedback = function(event, data) { - monitoredAjax.call(_, this, this.attr.feedbackResource, { - type: 'POST', - dataType: 'json', - contentType: 'application/json; charset=utf-8', - data: JSON.stringify(data) - }).done(this.successSubmittingFeedback()); - }; - - this.after('initialize', function () { - this.on(document, events.feedback.submit, this.submitFeedback); - }); - - }); -}); diff --git a/web-ui/app/js/mail_view/data/mail_builder.js b/web-ui/app/js/mail_view/data/mail_builder.js deleted file mode 100644 index 7a478dd8..00000000 --- a/web-ui/app/js/mail_view/data/mail_builder.js +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['services/model/mail'], function (mailModel) { - 'use strict'; - - var mail; - - function recipients(mail, place, v) { - if (v !== '' && !_.isUndefined(v)) { - if (_.isArray(v)) { - mail[place] = v; - } else { - mail[place] = v.split(' '); - } - } else { - mail[place] = []; - } - } - - return { - newMail: function (ident) { - ident = _.isUndefined(ident) ? '' : ident; - - mail = { - header: { - to: [], - cc: [], - bcc: [], - from: undefined, - subject: '' - }, - tags: [], - body: '', - attachments: [], - ident: ident - }; - return this; - }, - - subject: function (subject) { - mail.header.subject = subject; - return this; - }, - - body: function (body) { - mail.body = body; - return this; - }, - - to: function (to) { - recipients(mail.header, 'to', to); - return this; - }, - - cc: function (cc) { - recipients(mail.header, 'cc', cc); - return this; - }, - - bcc: function (bcc) { - recipients(mail.header, 'bcc', bcc); - return this; - }, - - header: function (name, value) { - mail.header[name] = value; - return this; - }, - - tag: function (tag) { - if (_.isUndefined(tag)) { - tag = 'drafts'; - } - mail.tags.push(tag); - return this; - }, - - attachment: function (attachmentList) { - mail.attachments = attachmentList; - return this; - }, - - build: function () { - return mailModel.create(mail); - } - }; -}); diff --git a/web-ui/app/js/mail_view/data/mail_sender.js b/web-ui/app/js/mail_view/data/mail_sender.js deleted file mode 100644 index 8bb01f70..00000000 --- a/web-ui/app/js/mail_view/data/mail_sender.js +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'mail_view/data/mail_builder', - 'page/events', - 'helpers/monitored_ajax', - 'features' - ], - function (defineComponent, mailBuilder, events, monitoredAjax, features) { - 'use strict'; - - return defineComponent(mailSender); - - function mailSender() { - function successSendingMail(on){ - return function(result) { - on.trigger(document, events.mail.sent, result); - }; - } - - function failureSendingMail(on) { - return function(result) { - on.trigger(document, events.mail.send_failed, result); - }; - } - - function successSaveDraft(on){ - return function(result){ - on.trigger(document, events.mail.draftSaved, result); - }; - } - - this.defaultAttrs({ - mailsResource: '/mails' - }); - - this.sendMail = function(event, data) { - this.trigger(events.dispatchers.rightPane.openNoMessageSelected); - monitoredAjax.call(_, this, this.attr.mailsResource, { - type: 'POST', - dataType: 'json', - contentType: 'application/json; charset=utf-8', - data: JSON.stringify(data) - }).done(successSendingMail(this)).fail(failureSendingMail(this)); - - }; - - this.saveMail = function(mail) { - return monitoredAjax.call(_, this, this.attr.mailsResource, { - type: 'PUT', - dataType: 'json', - contentType: 'application/json; charset=utf-8', - data: JSON.stringify(mail), - skipErrorMessage: true - }); - }; - - this.saveDraft = function(event, data) { - this.saveMail(data) - .done(successSaveDraft(this)); - }; - - this.saveMailWithCallback = function(event, data) { - this.saveMail(data.mail) - .done(function(result) { return data.callback(result); }) - .fail(function(result) { return data.callback(result); }); - }; - - this.after('initialize', function () { - this.on(events.mail.send, this.sendMail); - if(features.isEnabled('saveDraft')) { - this.on(events.mail.saveDraft, this.saveDraft); - } - this.on(document, events.mail.save, this.saveMailWithCallback); - }); - } - }); diff --git a/web-ui/app/js/mail_view/ui/attachment_icon.js b/web-ui/app/js/mail_view/ui/attachment_icon.js deleted file mode 100644 index e04fc02a..00000000 --- a/web-ui/app/js/mail_view/ui/attachment_icon.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2015 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'page/events', - 'features' - ], - - function (defineComponent, events, features) { - 'use strict'; - - return defineComponent(function () { - this.render = function () { - this.$node.html(''); - }; - - this.triggerUploadAttachment = function () { - this.trigger(document, events.mail.startUploadAttachment); - }; - - this.uploadInProgress = function (ev, data) { - this.attr.busy = true; - this.$node.addClass('busy'); - }; - - this.uploadFinished = function (ev, data) { - this.attr.busy = false; - this.$node.removeClass('busy'); - }; - - this.after('initialize', function () { - if (features.isEnabled('attachment')) { - this.render(); - this.on(document, events.mail.uploadingAttachment, this.uploadInProgress); - this.on(document, events.mail.uploadedAttachment, this.uploadFinished); - this.on(document, events.mail.failedUploadAttachment, this.uploadFinished); - } - this.on(this.$node, 'click', function() { - if (!this.attr.busy) { - this.triggerUploadAttachment(); - } - }); - }); - }); - }); diff --git a/web-ui/app/js/mail_view/ui/attachment_list.js b/web-ui/app/js/mail_view/ui/attachment_list.js deleted file mode 100644 index 4ef64960..00000000 --- a/web-ui/app/js/mail_view/ui/attachment_list.js +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (c) 2015 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'views/templates', - 'page/events', - 'helpers/view_helper', - 'helpers/monitored_ajax' - ], - - function (templates, events, viewHelper, monitoredAjax) { - 'use strict'; - - function attachmentList() { - this.defaultAttrs({ - inputFileUpload: '#fileupload', - attachmentListItem: '#attachment-list-item', - attachmentUploadItem: '#attachment-upload-item', - attachmentUploadItemProgress: '#attachment-upload-item-progress', - attachmentUploadItemAbort: '#attachment-upload-item-abort', - attachmentBaseUrl: '/attachment', - attachments: [], - closeIcon: '#upload-error-close', - uploadError: '#upload-error', - dismissButton: '#dismiss-button', - uploadFileButton: '#upload-file-button' - }); - - var ONE_MEGABYTE = 1024*1024; - var ATTACHMENT_SIZE_LIMIT = 5*ONE_MEGABYTE; - - this.showAttachment = function (ev, data) { - this.trigger(document, events.mail.appendAttachment, data); - this.renderAttachmentListView(data); - }; - - this.addAttachment = function (event, data) { - this.attr.attachments.push(data); - }; - - this.renderAttachmentListView = function (data) { - var currentHtml = this.select('attachmentListItem').html(); - var item = this.buildAttachmentListItem(data); - this.select('attachmentListItem').append(item); - }; - - this.buildAttachmentListItem = function (attachment) { - var attachmentData = {ident: attachment.ident, - encoding: attachment.encoding, - name: attachment.name, - size: attachment.size, - removable: true}; - - var element = $(templates.compose.attachmentItem(attachmentData)); - var self = this; - element.find('i.remove-icon').bind('click', function(event) { - var element = $(this); - var ident = element.closest('li').attr('data-ident'); - self.trigger(document, events.mail.removeAttachment, {ident: ident, element: element}); - event.preventDefault(); - }); - return element; - }; - - this.performPreUploadCheck = function(e, data) { - if (data.originalFiles[0].size > ATTACHMENT_SIZE_LIMIT) { - return false; - } - - return true; - }; - - this.removeUploadError = function() { - var uploadError = this.select('uploadError'); - if (uploadError) { - uploadError.remove(); - } - }; - - this.showUploadError = function () { - var self = this; - - var html = $(templates.compose.uploadAttachmentFailed()); - html.insertAfter(self.select('attachmentListItem')); - - self.on(self.select('closeIcon'), 'click', dismissUploadFailed); - self.on(self.select('dismissButton'), 'click', dismissUploadFailed); - self.on(self.select('uploadFileButton'), 'click', uploadAnotherFile); - - function dismissUploadFailed(event) { - event.preventDefault(); - self.select('uploadError').remove(); - } - - function uploadAnotherFile(event) { - event.preventDefault(); - self.trigger(document, events.mail.startUploadAttachment); - } - }; - - this.showUploadProgressBar = function(e, data) { - var element = $(templates.compose.attachmentUploadItem({ - name: data.originalFiles[0].name, - size: data.originalFiles[0].size - })); - this.select('attachmentUploadItem').append(element); - this.select('attachmentUploadItem').show(); - }; - - this.hideUploadProgressBar = function() { - this.select('attachmentUploadItem').hide(); - this.select('attachmentUploadItem').empty(); - }; - - this.attachUploadAbort = function(e, data) { - this.on(this.select('attachmentUploadItemAbort'), 'click', function(e) { - data.abort(); - e.preventDefault(); - }); - }; - - this.detachUploadAbort = function() { - this.off(this.select('attachmentUploadItemAbort'), 'click'); - }; - - this.addJqueryFileUploadConfig = function() { - var self = this; - - self.removeUploadError(); - - this.select('inputFileUpload').fileupload({ - add: function(e, data) { - if (self.performPreUploadCheck(e, data)) { - self.showUploadProgressBar(e, data); - self.attachUploadAbort(e, data); - data.submit(); - } else { - self.showUploadError(); - } - }, - url: self.attr.attachmentBaseUrl, - dataType: 'json', - done: function (e, response) { - self.detachUploadAbort(); - self.hideUploadProgressBar(); - self.trigger(document, events.mail.uploadedAttachment, response.result); - }, - fail: function(e, data){ - self.detachUploadAbort(); - self.hideUploadProgressBar(); - self.trigger(document, events.mail.failedUploadAttachment); - }, - progressall: function (e, data) { - var progressRate = parseInt(data.loaded / data.total * 100, 10); - self.select('attachmentUploadItemProgress').css('width', progressRate + '%'); - } - }).bind('fileuploadstart', function (e) { - self.trigger(document, events.mail.uploadingAttachment); - }); - }; - - this.startUpload = function () { - this.addJqueryFileUploadConfig(); - this.select('inputFileUpload').click(); - }; - - this.removeAttachmentFromList = function(ident) { - for (var i = 0; i < this.attr.attachments.length; i++) { - if (this.attr.attachments[i].ident === ident) { - this.attr.attachments.remove(i); - break; - } - } - }; - - this.destroyAttachmentElement = function(element) { - element.closest('li').remove(); - }; - - this.removeAttachments = function(event, data) { - this.removeAttachmentFromList(data.ident); - this.destroyAttachmentElement(data.element); - }; - - this.after('initialize', function () { - this.addJqueryFileUploadConfig(); - this.on(document, events.mail.uploadedAttachment, this.showAttachment); - this.on(document, events.mail.startUploadAttachment, this.startUpload); - this.on(document, events.mail.appendAttachment, this.addAttachment); - this.on(document, events.mail.removeAttachment, this.removeAttachments); - }); - } - - return attachmentList; - }); diff --git a/web-ui/app/js/mail_view/ui/compose_box.js b/web-ui/app/js/mail_view/ui/compose_box.js deleted file mode 100644 index 101dc939..00000000 --- a/web-ui/app/js/mail_view/ui/compose_box.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'mixins/with_mail_edit_base', - 'page/events', - 'mail_view/data/mail_builder' - ], - - function (defineComponent, templates, withMailEditBase, events, mailBuilder) { - 'use strict'; - - return defineComponent(composeBox, withMailEditBase); - - function composeBox() { - - this.defaultAttrs({ - 'closeButton': '.close-mail-button' - }); - - this.showNoMessageSelected = function() { - this.trigger(events.dispatchers.rightPane.openNoMessageSelected); - }; - - this.buildMail = function(tag) { - return this.builtMail(tag).build(); - }; - - this.builtMail = function(tag) { - return mailBuilder.newMail(this.attr.ident) - .subject(this.select('subjectBox').val()) - .to(this.attr.recipientValues.to) - .cc(this.attr.recipientValues.cc) - .bcc(this.attr.recipientValues.bcc) - .body(this.select('bodyBox').val()) - .attachment(this.attr.attachments) - .tag(tag); - }; - - this.renderComposeBox = function() { - this.render(templates.compose.box, {}); - this.enableFloatlabel('input.floatlabel'); - this.enableFloatlabel('textarea.floatlabel'); - this.select('recipientsFields').show(); - this.on(this.select('closeButton'), 'click', this.showNoMessageSelected); - this.enableAutoSave(); - }; - - this.mailDeleted = function(event, data) { - if (_.contains(_.pluck(data.mails, 'ident'), this.attr.ident)) { - this.trigger(events.dispatchers.rightPane.openNoMessageSelected); - } - }; - - this.discardDraft = function () { - this.trigger(events.dispatchers.rightPane.openNoMessageSelected); - }; - - this.after('initialize', function () { - this.renderComposeBox(); - - this.select('toBox').focus(); - this.on(document, events.mail.deleted, this.mailDeleted); - this.on(document, events.mail.sent, this.showNoMessageSelected); - }); - } - } -); diff --git a/web-ui/app/js/mail_view/ui/draft_box.js b/web-ui/app/js/mail_view/ui/draft_box.js deleted file mode 100644 index afe31914..00000000 --- a/web-ui/app/js/mail_view/ui/draft_box.js +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'mixins/with_mail_edit_base', - 'page/events', - 'mail_view/data/mail_builder' - ], - - function (defineComponent, templates, withMailEditBase, events, mailBuilder) { - 'use strict'; - - return defineComponent(draftBox, withMailEditBase); - - function draftBox() { - this.defaultAttrs({ - closeMailButton: '.close-mail-button' - }); - - this.showNoMessageSelected = function() { - this.trigger(events.dispatchers.rightPane.openNoMessageSelected); - }; - - this.buildMail = function(tag) { - return this.builtMail(tag).build(); - }; - - this.builtMail = function(tag) { - return mailBuilder.newMail(this.attr.ident) - .subject(this.select('subjectBox').val()) - .to(this.attr.recipientValues.to) - .cc(this.attr.recipientValues.cc) - .bcc(this.attr.recipientValues.bcc) - .body(this.select('bodyBox').val()) - .attachment(this.attr.attachments) - .tag(tag); - }; - - this.renderDraftBox = function(ev, data) { - var mail = data.mail; - var body = mail.textPlainBody; - this.attr.ident = mail.ident; - this.render(templates.compose.box, { - recipients: { - to: mail.header.to, - cc: mail.header.cc, - bcc: mail.header.bcc - }, - subject: mail.header.subject, - body: body, - attachments: this.convertToRemovableAttachments(mail.attachments) - }); - - var self = this; - this.$node.find('i.remove-icon').bind('click', function(event) { - var element = $(this); - var ident = element.closest('li').attr('data-ident'); - self.trigger(document, events.mail.removeAttachment, {ident: ident, element: element}); - event.preventDefault(); - }); - - this.enableFloatlabel('input.floatlabel'); - this.enableFloatlabel('textarea.floatlabel'); - this.select('recipientsFields').show(); - this.select('bodyBox').focus(); - this.select('tipMsg').hide(); - this.enableAutoSave(); - this.bindCollapse(); - this.on(this.select('closeMailButton'), 'click', this.showNoMessageSelected); - }; - - this.convertToRemovableAttachments = function(attachments) { - return attachments.map(function(attachment) { - attachment.removable = true; - return attachment; - }); - }; - - this.mailDeleted = function(event, data) { - if (_.contains(_.pluck(data.mails, 'ident'), this.attr.ident)) { - this.trigger(events.dispatchers.rightPane.openNoMessageSelected); - } - }; - - this.after('initialize', function () { - this.on(this, events.mail.here, this.renderDraftBox); - this.on(document, events.mail.sent, this.showNoMessageSelected); - this.on(document, events.mail.deleted, this.mailDeleted); - this.trigger(document, events.mail.want, { mail: this.attr.mailIdent, caller: this }); - }); - } - } -); diff --git a/web-ui/app/js/mail_view/ui/draft_save_status.js b/web-ui/app/js/mail_view/ui/draft_save_status.js deleted file mode 100644 index 47751d91..00000000 --- a/web-ui/app/js/mail_view/ui/draft_save_status.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'page/events', - 'views/i18n' - ], - - function (defineComponent, events, i18n) { - 'use strict'; - - return defineComponent(draftSaveStatus); - - function draftSaveStatus() { - this.setMessage = function(msg) { - var node = this.$node; - return function () { node.text(msg); }; - }; - - this.after('initialize', function () { - this.on(document, events.mail.saveDraft, this.setMessage(i18n.t('draft-saving'))); - this.on(document, events.mail.draftSaved, this.setMessage(i18n.t('draft-saved'))); - this.on(document, events.ui.mail.changedSinceLastSave, this.setMessage('')); - }); - } - } -); diff --git a/web-ui/app/js/mail_view/ui/feedback_box.js b/web-ui/app/js/mail_view/ui/feedback_box.js deleted file mode 100644 index 4e00ece8..00000000 --- a/web-ui/app/js/mail_view/ui/feedback_box.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2015 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['flight/lib/component', 'views/templates', 'page/events', 'features', 'feedback/feedback_cache'], - function (defineComponent, templates, events, features, feedbackCache) { - 'use strict'; - - return defineComponent(function () { - this.defaultAttrs({ - 'closeButton': '.close-mail-button', - 'submitButton': '#send-button', - 'textBox': '#text-box', - }); - - this.render = function () { - this.$node.html(templates.compose.feedback()); - }; - - this.startCachingData = function () { - this.select('textBox').val(feedbackCache.getCache()); - this.select('textBox').on('change', this.cacheFeedbackData.bind(this)); - }; - - - this.cacheFeedbackData = function () { - feedbackCache.setCache(this.select('textBox').val()); - }; - - this.showNoMessageSelected = function () { - this.trigger(document, events.dispatchers.rightPane.openNoMessageSelected); - }; - - this.submitFeedback = function () { - var feedback = this.select('textBox').val(); - this.trigger(document, events.feedback.submit, {feedback: feedback}); - feedbackCache.resetCache(); - }; - - this.showSuccessMessage = function () { - this.trigger(document, events.ui.userAlerts.displayMessage, {message: 'Thanks for your feedback!'}); - }; - - this.after('initialize', function () { - if (features.isEnabled('feedback')) { - this.render(); - this.startCachingData(); - this.on(document, events.feedback.submitted, this.showNoMessageSelected); - this.on(document, events.feedback.submitted, this.showSuccessMessage); - this.on(this.select('closeButton'), 'click', this.showNoMessageSelected); - this.on(this.select('submitButton'), 'click', this.submitFeedback); - } - }); - - }); - }); diff --git a/web-ui/app/js/mail_view/ui/forward_box.js b/web-ui/app/js/mail_view/ui/forward_box.js deleted file mode 100644 index a34bd55d..00000000 --- a/web-ui/app/js/mail_view/ui/forward_box.js +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'helpers/view_helper', - 'mixins/with_hide_and_show', - 'mixins/with_compose_inline', - 'page/events', - 'views/i18n' - ], - - function (defineComponent, viewHelper, withHideAndShow, withComposeInline, events, i18n) { - 'use strict'; - - return defineComponent(forwardBox, withHideAndShow, withComposeInline); - - function forwardBox() { - var fwd = function(v) { return i18n.t('fwd') + ': ' + v; }; - - this.fetchTargetMail = function (ev) { - this.trigger(document, events.mail.want, { mail: this.attr.ident, caller: this }); - }; - - this.setupForwardBox = function() { - var mail = this.attr.mail; - this.attr.subject = fwd(mail.header.subject); - this.attr.attachments = mail.attachments; - - this.renderInlineCompose('forward-box', { - subject: this.attr.subject, - recipients: { to: [], cc: []}, - body: viewHelper.quoteMail(mail), - attachments: this.convertToRemovableAttachments(mail.attachments) - }); - - var self = this; - this.$node.find('i.remove-icon').bind('click', function(event) { - var element = $(this); - var ident = element.closest('li').attr('data-ident'); - self.trigger(document, events.mail.removeAttachment, {ident: ident}); - event.preventDefault(); - }); - - this.on(this.select('subjectDisplay'), 'click', this.showSubjectInput); - this.select('recipientsDisplay').hide(); - this.select('recipientsFields').show(); - }; - - this.convertToRemovableAttachments = function(attachments) { - return attachments.map(function(attachment) { - attachment.removable = true; - return attachment; - }); - }; - - this.showSubjectInput = function() { - this.select('subjectDisplay').hide(); - this.select('subjectInput').show(); - this.select('subjectInput').focus(); - }; - - this.buildMail = function(tag) { - var builder = this.builtMail(tag).subject(this.select('subjectInput').val()); - - var headersToFwd = ['bcc', 'cc', 'date', 'from', 'message_id', 'reply_to', 'sender', 'to']; - var header = this.attr.mail.header; - _.each(headersToFwd, function (h) { - if (!_.isUndefined(header[h])) { - builder.header('resent_' + h, header[h]); - } - }); - - return builder.build(); - }; - - this.after('initialize', function () { - this.setupForwardBox(); - }); - } - } -); diff --git a/web-ui/app/js/mail_view/ui/mail_actions.js b/web-ui/app/js/mail_view/ui/mail_actions.js deleted file mode 100644 index 65cd0aaa..00000000 --- a/web-ui/app/js/mail_view/ui/mail_actions.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'views/templates', - 'page/events' - ], - - function (defineComponent, templates, events) { - 'use strict'; - - return defineComponent(mailActions); - - function mailActions() { - - this.defaultAttrs({ - replyButtonTop: '#reply-button-top', - viewMoreActions: '#view-more-actions', - replyAllButtonTop: '#reply-all-button-top', - deleteButtonTop: '#delete-button-top', - moreActions: '#more-actions' - }); - - - this.displayMailActions = function () { - - this.$node.html(templates.mails.mailActions()); - - this.select('moreActions').hide(); - - this.on(this.select('replyButtonTop'), 'click', function () { - this.trigger(document, events.ui.replyBox.showReply); - }.bind(this)); - - this.on(this.select('replyAllButtonTop'), 'click', function () { - this.trigger(document, events.ui.replyBox.showReplyAll); - this.select('moreActions').hide(); - }.bind(this)); - - this.on(this.select('deleteButtonTop'), 'click', function () { - this.trigger(document, events.ui.mail.delete, {mail: this.attr.mail}); - this.select('moreActions').hide(); - }.bind(this)); - - this.on(this.select('viewMoreActions'), 'click', function () { - this.select('moreActions').toggle(); - }.bind(this)); - - this.on(this.select('viewMoreActions'), 'blur', function (event) { - var replyButtonTopHover = this.select('replyAllButtonTop').is(':hover'); - var deleteButtonTopHover = this.select('deleteButtonTop').is(':hover'); - - if (replyButtonTopHover || deleteButtonTopHover) { - event.preventDefault(); - } else { - this.select('moreActions').hide(); - } - }.bind(this)); - - }; - - this.after('initialize', function () { - this.on(document, events.dispatchers.rightPane.clear, this.teardown); - this.displayMailActions(); - }); - } - } -); diff --git a/web-ui/app/js/mail_view/ui/mail_view.js b/web-ui/app/js/mail_view/ui/mail_view.js deleted file mode 100644 index 3408c8af..00000000 --- a/web-ui/app/js/mail_view/ui/mail_view.js +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'views/templates', - 'mail_view/ui/mail_actions', - 'helpers/view_helper', - 'mixins/with_hide_and_show', - 'mixins/with_mail_tagging', - 'mixins/with_mail_sandbox', - 'page/events', - 'views/i18n' - ], - - function (defineComponent, templates, mailActions, viewHelpers, withHideAndShow, withMailTagging, withMailSandbox, events, i18n) { - 'use strict'; - - return defineComponent(mailView, mailActions, withHideAndShow, withMailTagging, withMailSandbox); - - function mailView() { - this.defaultAttrs({ - tags: '.mail-read-view__header-tags-tag', - newTagInput: '#new-tag-input', - newTagButton: '#new-tag-button', - addNew: '.mail-read-view__header-tags-new-button', - trashButton: '#trash-button', - archiveButton: '#archive-button', - closeMailButton: '.close-mail-button' - }); - - this.displayMail = function (event, data) { - this.attr.mail = data.mail; - - var signed, encrypted, attachments; - - data.mail.security_casing = data.mail.security_casing || {}; - signed = this.checkSigned(data.mail); - encrypted = this.checkEncrypted(data.mail); - attachments = data.mail.attachments.map(function (attachment) { - attachment.received = true; - return attachment; - }); - - if(data.mail.mailbox === 'sent') { - encrypted = undefined; - signed = undefined; - } - - this.$node.html(templates.mails.fullView({ - header: data.mail.header, - body: [], - statuses: viewHelpers.formatStatusClasses(data.mail.status), - ident: data.mail.ident, - tags: data.mail.tags, - encryptionStatus: encrypted, - signatureStatus: signed, - attachments: attachments - })); - - this.showMailOnSandbox(this.attr.mail); - - this.attachTagCompletion(this.attr.mail); - - this.select('tags').on('click', function (event) { - this.removeTag($(event.target).text()); - }.bind(this)); - - this.addTagLoseFocus(); - this.on(this.select('newTagButton'), 'click', this.showNewTagInput); - this.on(this.select('newTagInput'), 'keydown', this.handleKeyDown); - this.on(this.select('newTagInput'), 'blur', this.addTagLoseFocus); - this.on(this.select('trashButton'), 'click', this.moveToTrash); - this.on(this.select('closeMailButton'), 'click', this.openNoMessageSelectedPane); - - mailActions.attachTo('#mail-actions', data); - this.resetScroll(); - }; - - this.resetScroll = function(){ - $('#right-pane').scrollTop(0); - }; - - this.checkEncrypted = function(mail) { - if(_.isEmpty(mail.security_casing.locks)) { - return { - cssClass: 'security-status__label--not-encrypted', - label: 'not-encrypted' - }; - } - - var statusClass = ['security-status__label--encrypted']; - var statusLabel; - - var hasAnyEncryptionInfo = _.any(mail.security_casing.locks, function (lock) { - return lock.state === 'valid'; - }); - - if(hasAnyEncryptionInfo) { - statusLabel = 'encrypted'; - } else { - statusClass.push('--with-error'); - statusLabel = 'encryption-error'; - } - - return { - cssClass: statusClass.join(''), - label: statusLabel - }; - }; - - this.checkSigned = function(mail) { - var statusNotSigned = { - cssClass: 'security-status__label--not-signed', - label: 'not-signed' - }; - - if(_.isEmpty(mail.security_casing.imprints)) { - return statusNotSigned; - } - - var hasNoSignatureInformation = _.any(mail.security_casing.imprints, function (imprint) { - return imprint.state === 'no_signature_information'; - }); - - if(hasNoSignatureInformation) { - return statusNotSigned; - } - - var statusClass = ['security-status__label--signed']; - var statusLabel = ['signed']; - - if(_.any(mail.security_casing.imprints, function(imprint) { return imprint.state === 'from_revoked'; })) { - statusClass.push('--revoked'); - statusLabel.push('signature-revoked'); - } - - if(_.any(mail.security_casing.imprints, function(imprint) { return imprint.state === 'from_expired'; })) { - statusClass.push('--expired'); - statusLabel.push('signature-expired'); - } - - if(this.isNotTrusted(mail)) { - statusClass.push('--not-trusted'); - statusLabel.push('signature-not-trusted'); - } - - return { - cssClass: statusClass.join(''), - label: statusLabel.join(' ') - }; - }; - - this.isNotTrusted = function(mail){ - return _.any(mail.security_casing.imprints, function(imprint) { - if(_.isNull(imprint.seal)){ - return true; - } - var currentTrust = _.isUndefined(imprint.seal.trust) ? imprint.seal.validity : imprint.seal.trust; - return currentTrust === 'no_trust'; - }); - }; - - this.openNoMessageSelectedPane = function(ev, data) { - this.trigger(document, events.dispatchers.rightPane.openNoMessageSelected); - }; - - this.handleKeyDown = function(event) { - var ENTER_KEY = 13; - var ESC_KEY = 27; - - if (event.which === ENTER_KEY){ - event.preventDefault(); - if (this.select('newTagInput').val().trim() !== '') { - this.createNewTag(); - } - } else if (event.which === ESC_KEY) { - event.preventDefault(); - this.addTagLoseFocus(); - } - }; - - this.addTagLoseFocus = function () { - this.select('newTagInput').hide(); - this.select('newTagInput').typeahead('val', ''); - this.select('addNew').show(); - }; - - this.showNewTagInput = function () { - this.select('newTagInput').show(); - this.select('newTagInput').focus(); - this.select('addNew').hide(); - }; - - this.removeTag = function (tag) { - tag = tag.toString(); - var filteredTags = _.without(this.attr.mail.tags, tag); - this.updateTags(this.attr.mail, filteredTags); - this.trigger(document, events.dispatchers.tags.refreshTagList); - }; - - this.moveToTrash = function(){ - this.trigger(document, events.ui.mail.delete, { mail: this.attr.mail }); - }; - - this.tagsUpdated = function(ev, data) { - data = data || {}; - this.attr.mail.tags = data.tags; - this.displayMail({}, { mail: this.attr.mail }); - }; - - this.mailDeleted = function(ev, data) { - if (_.contains(_.pluck(data.mails, 'ident'), this.attr.mail.ident)) { - this.openNoMessageSelectedPane(); - } - }; - - this.fetchMailToShow = function () { - this.trigger(events.mail.want, {mail: this.attr.ident, caller: this}); - }; - - this.highlightMailContent = function (event, data) { - // we can't directly manipulate the iFrame to highlight the content - // so we need to take an indirection where we directly manipulate - // the mail content to accomodate the highlighting - this.trigger(document, events.mail.highlightMailContent, data); - }; - - this.after('initialize', function () { - this.on(this, events.mail.notFound, this.openNoMessageSelectedPane); - this.on(this, events.mail.here, this.highlightMailContent); - this.on(document, events.mail.display, this.displayMail); - this.on(document, events.dispatchers.rightPane.clear, this.teardown); - this.on(document, events.mail.tags.updated, this.tagsUpdated); - this.on(document, events.mail.deleted, this.mailDeleted); - this.fetchMailToShow(); - }); - } - } -); diff --git a/web-ui/app/js/mail_view/ui/no_mails_available_pane.js b/web-ui/app/js/mail_view/ui/no_mails_available_pane.js deleted file mode 100644 index c62c6b30..00000000 --- a/web-ui/app/js/mail_view/ui/no_mails_available_pane.js +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'mixins/with_hide_and_show', - 'page/events' - ], - - function(defineComponent, templates, withHideAndShow, events) { - 'use strict'; - - //return defineComponent(noMailsAvailablePane, withHideAndShow); - return defineComponent(noMailsAvailablePane); - - function noMailsAvailablePane() { - this.defaultAttrs({ - tag: null, - forSearch: '' - }); - - var mailsQueryMatch = /-?in:"?[\w]+"?|tag:"[\w]+"/g; - - this.render = function() { - this.attr.tag = 'tags.' + this.attr.tag; - this.attr.forSearch = this.attr.forSearch.replace(mailsQueryMatch, '').trim(); - this.$node.html(templates.noMailsAvailable(this.attr)); - }; - - this.after('initialize', function () { - this.render(); - }); - } - } -); diff --git a/web-ui/app/js/mail_view/ui/no_message_selected_pane.js b/web-ui/app/js/mail_view/ui/no_message_selected_pane.js deleted file mode 100644 index a5fc2393..00000000 --- a/web-ui/app/js/mail_view/ui/no_message_selected_pane.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'mixins/with_hide_and_show', - 'page/events' - ], - - function(defineComponent, templates, withHideAndShow, events) { - 'use strict'; - - return defineComponent(noMessageSelectedPane, withHideAndShow); - - function noMessageSelectedPane() { - this.render = function() { - this.$node.html(templates.noMessageSelected()); - }; - - this.after('initialize', function () { - this.render(); - this.on(document, events.dispatchers.rightPane.clear, this.teardown); - }); - } - } -); diff --git a/web-ui/app/js/mail_view/ui/recipients/recipient.js b/web-ui/app/js/mail_view/ui/recipients/recipient.js deleted file mode 100644 index c13a52b1..00000000 --- a/web-ui/app/js/mail_view/ui/recipients/recipient.js +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'views/templates', - 'page/events' - ], - - function (defineComponent, templates, events) { - 'use strict'; - - return defineComponent(recipient); - - function recipient() { - this.renderAndPrepend = function (nodeToPrependTo, recipient) { - var html = $(templates.compose.fixedRecipient(recipient)); - html.insertBefore(nodeToPrependTo.children().last()); - var component = new this.constructor(); - component.initialize(html, recipient); - component.attr.recipient = recipient; - return component; - }; - - this.recipientDelActions = function () { - this.on(this.$node.find('.recipient-del'), 'click', function (event) { - this.doSelect(); - this.trigger(events.ui.recipients.deleteRecipient, this); - event.preventDefault(); - }); - - this.on(this.$node.find('.recipient-del'), 'mouseover', function () { - this.$node.find('.recipient-value').addClass('deleting'); - this.$node.find('.recipient-del').addClass('deleteTooltip'); - }); - - this.on(this.$node.find('.recipient-del'), 'mouseout', function () { - this.$node.find('.recipient-value').removeClass('deleting'); - this.$node.find('.recipient-del').removeClass('deleteTooltip'); - }); - }; - - this.destroy = function () { - this.$node.remove(); - this.teardown(); - }; - - this.doSelect = function () { - this.$node.find('.recipient-value').addClass('selected'); - }; - - this.doUnselect = function () { - this.$node.find('.recipient-value').removeClass('selected'); - }; - - this.isSelected = function () { - return this.$node.find('.recipient-value').hasClass('selected'); - }; - - this.sinalizeInvalid = function () { - this.$node.find('.recipient-value>span').addClass('invalid-format'); - }; - - this.discoverEncryption = function () { - this.$node.addClass('discover-encryption'); - var p = $.getJSON('/keys?search=' + this.attr.address).promise(); - p.done(function () { - this.$node.find('.recipient-value').addClass('encrypted'); - this.$node.removeClass('discover-encryption'); - }.bind(this)); - p.fail(function () { - this.$node.find('.recipient-value').addClass('not-encrypted'); - this.$node.removeClass('discover-encryption'); - }.bind(this)); - }; - - this.getMailAddress = function() { - return this.$node.find('input[type=hidden]').val(); - }; - - this.triggerEditRecipient = function(event, element) { - this.trigger(this.$node.closest('.recipients-area'), events.ui.recipients.clickToEdit, this); - }; - - this.after('initialize', function () { - this.recipientDelActions(); - this.on('click', this.triggerEditRecipient); - - if (this.attr.invalidAddress){ - this.sinalizeInvalid(); - } else { - this.discoverEncryption(); - } - }); - } - } -); diff --git a/web-ui/app/js/mail_view/ui/recipients/recipients.js b/web-ui/app/js/mail_view/ui/recipients/recipients.js deleted file mode 100644 index 2caa8d14..00000000 --- a/web-ui/app/js/mail_view/ui/recipients/recipients.js +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'views/templates', - 'page/events', - 'helpers/iterator', - 'mail_view/ui/recipients/recipients_input', - 'mail_view/ui/recipients/recipient', - 'mail_view/ui/recipients/recipients_iterator' - ], - function (defineComponent, templates, events, Iterator, RecipientsInput, Recipient, RecipientsIterator) { - 'use strict'; - - return defineComponent(recipients); - - function recipients() { - this.defaultAttrs({ - navigationHandler: '.recipients-navigation-handler', - recipientsList: '.recipients-list' - }); - - function getAddresses(recipients) { - return _.flatten(_.map(recipients, function (e) { return e.attr.address;})); - } - - function moveLeft() { this.attr.iterator.moveLeft(); } - function moveRight() { this.attr.iterator.moveRight(); } - function deleteCurrentRecipient() { - this.attr.iterator.deleteCurrent(); - this.addressesUpdated(); - } - - function editCurrentRecipient(event, recipient) { - var mailAddr = this.attr.iterator.current().getMailAddress(); - this.attr.iterator.deleteCurrent(); - this.attr.input.$node.val(mailAddr).focus(); - this.unselectAllRecipients(); - this.addressesUpdated(); - } - - this.clickToEditRecipient = function(event, recipient) { - this.attr.iterator = null; - var mailAddr = recipient.getMailAddress(); - - var position = this.getRecipientPosition(recipient); - this.attr.recipients.splice(position, 1); - recipient.destroy(); - - this.addressesUpdated(); - this.unselectAllRecipients(); - this.attr.input.$node.val(mailAddr).focus(); - }; - - this.getRecipientPosition = function(recipient) { - return recipient.$node.closest('.recipients-area').find('.fixed-recipient').index(recipient.$node); - }; - - this.unselectAllRecipients = function() { - this.$node.find('.recipient-value.selected').removeClass('selected'); - }; - - var SPECIAL_KEYS_ACTIONS = { - 8: deleteCurrentRecipient, - 46: deleteCurrentRecipient, - 32: editCurrentRecipient, - 13: editCurrentRecipient, - 37: moveLeft, - 39: moveRight - }; - - this.addRecipient = function(recipient) { - var newRecipient = Recipient.prototype.renderAndPrepend(this.$node.find(this.attr.recipientsList), recipient); - this.attr.recipients.push(newRecipient); - }; - - this.recipientEntered = function (event, recipient) { - this.addRecipient(recipient); - this.addressesUpdated(); - }; - - this.invalidRecipientEntered = function(event, recipient) { - recipient.invalidAddress = true; - this.addRecipient(recipient); - }; - - this.deleteRecipient = function (event, recipient) { - this.attr.iterator = null; - var position = this.getRecipientPosition(recipient); - - this.attr.recipients.splice(position, 1); - recipient.destroy(); - - this.addressesUpdated(); - }; - - this.deleteLastRecipient = function () { - this.attr.recipients.pop().destroy(); - this.addressesUpdated(); - }; - - this.enterNavigationMode = function () { - this.attr.iterator = new RecipientsIterator({ - elements: this.attr.recipients, - exitInput: this.attr.input.$node - }); - - this.attr.iterator.current().doSelect(); - this.attr.input.$node.blur(); - this.select('navigationHandler').focus(); - }; - - this.leaveNavigationMode = function () { - if(this.attr.iterator) { this.attr.iterator.current().unselect(); } - this.attr.iterator = null; - }; - - this.selectLastRecipient = function () { - if (this.attr.recipients.length === 0) { return; } - this.enterNavigationMode(); - }; - - this.attachInput = function () { - this.attr.input = RecipientsInput.prototype.attachAndReturn(this.$node.find('input[type=text]'), this.attr.name); - }; - - this.processSpecialKey = function (event) { - if(SPECIAL_KEYS_ACTIONS.hasOwnProperty(event.which)) { SPECIAL_KEYS_ACTIONS[event.which].apply(this); } - }; - - this.initializeAddresses = function () { - _.each(_.flatten(this.attr.addresses), function (address) { - this.addRecipient({ address: address, name: this.attr.name }); - }.bind(this)); - }; - - this.addressesUpdated = function() { - this.trigger(document, events.ui.recipients.updated, {recipientsName: this.attr.name, newRecipients: getAddresses(this.attr.recipients)}); - }; - - this.doCompleteRecipients = function () { - var address = this.attr.input.$node.val(); - if (!_.isEmpty(address)) { - var recipient = Recipient.prototype.renderAndPrepend(this.$node, { name: this.attr.name, address: address }); - this.attr.recipients.push(recipient); - this.attr.input.$node.val(''); - } - - this.trigger(document, events.ui.recipients.updated, { - recipientsName: this.attr.name, - newRecipients: getAddresses(this.attr.recipients), - skipSaveDraft: true - }); - - }; - - this.after('initialize', function () { - this.attr.recipients = []; - this.attachInput(); - this.initializeAddresses(); - - this.on(events.ui.recipients.deleteRecipient, this.deleteRecipient); - this.on(events.ui.recipients.deleteLast, this.deleteLastRecipient); - this.on(events.ui.recipients.selectLast, this.selectLastRecipient); - this.on(events.ui.recipients.entered, this.recipientEntered); - this.on(events.ui.recipients.enteredInvalid, this.invalidRecipientEntered); - this.on(events.ui.recipients.clickToEdit, this.clickToEditRecipient); - - this.on(document, events.ui.recipients.doCompleteInput, this.doCompleteRecipients); - - this.on(this.attr.input.$node, 'focus', this.leaveNavigationMode); - this.on(this.select('navigationHandler'), 'keydown', this.processSpecialKey); - - this.on(document, events.dispatchers.rightPane.clear, this.teardown); - }); - } - }); diff --git a/web-ui/app/js/mail_view/ui/recipients/recipients_input.js b/web-ui/app/js/mail_view/ui/recipients/recipients_input.js deleted file mode 100644 index 8a9c4eaf..00000000 --- a/web-ui/app/js/mail_view/ui/recipients/recipients_input.js +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define([ - 'flight/lib/component', - 'page/events', - 'features' - ], - function (defineComponent, events, features) { - 'use strict'; - - function recipientsInput() { - var EXIT_KEY_CODE_MAP = { - 8: 'backspace', - 37: 'left' - }, - ENTER_ADDRESS_KEY_CODE_MAP = { - 9: 'tab', - 186: 'semicolon', - 188: 'comma', - 13: 'enter', - 27: 'esc' - }, - EVENT_FOR = { - 8: events.ui.recipients.deleteLast, - 37: events.ui.recipients.selectLast - }, - self; - - var simpleAddressMatch = /[^<\w,;]?([^\s<;,]+@[\w-]+\.[^\s>;,]+)/; - var canonicalAddressMatch = /([^,;\s][^,;@]+<[^\s;,]+@[\w-]+\.[^\s;,]+>)/; - var emailAddressMatch = new RegExp([simpleAddressMatch.source, '|', canonicalAddressMatch.source].join(''), 'g'); - - var extractContactNames = function (response) { - return _.map(response, function(a) { return { value: a }; }); - }; - - function createEmailCompleter() { - var emailCompleter = new Bloodhound({ - datumTokenizer: function (d) { - return [d.value]; - }, - queryTokenizer: function (q) { - return [q.trim()]; - }, - remote: { - url: '/contacts?q=%QUERY', - filter: extractContactNames - } - }); - emailCompleter.initialize(); - return emailCompleter; - } - - function reset(node) { - node.typeahead('val', ''); - } - - function caretIsInTheBeginningOfInput(input) { - return input.selectionStart === 0; - } - - function isExitKey(keyPressed) { - return EXIT_KEY_CODE_MAP.hasOwnProperty(keyPressed); - } - - function isEnterAddressKey(keyPressed) { - return ENTER_ADDRESS_KEY_CODE_MAP.hasOwnProperty(keyPressed); - } - - this.processSpecialKey = function (event) { - var keyPressed = event.which; - - if (isExitKey(keyPressed) && caretIsInTheBeginningOfInput(this.$node[0])) { - this.trigger(EVENT_FOR[keyPressed]); - return; - } - - if (!event.shiftKey && isEnterAddressKey(keyPressed)) { - this.tokenizeRecipient(event); - - if ((keyPressed !== 9 /* tab */)) { - event.preventDefault(); - } - } - - }; - - this.tokenizeRecipient = function (event) { - if (_.isEmpty(this.$node.val().trim())) { - return; - } - - this.recipientSelected(null, {value: this.$node.val() }); - event.preventDefault(); - }; - - this.recipientSelected = function (event, data) { - var value = (data && data.value) || this.$node.val(); - - var validAddresses = this.extractValidAddresses(value); - var invalidAddresses = this.extractInvalidAddresses(value); - - this.triggerEventForEach(validAddresses, events.ui.recipients.entered); - this.triggerEventForEach(invalidAddresses, events.ui.recipients.enteredInvalid); - - reset(this.$node); - }; - - this.triggerEventForEach = function (addresses, event) { - var that = this; - _.each(addresses, function(address) { - if (!_.isEmpty(address.trim())) { - that.trigger(that.$node, event, { name: that.attr.name, address: address.trim() }); - } - }); - }; - - this.extractValidAddresses = function(rawAddresses) { - return rawAddresses.match(emailAddressMatch); - }; - - this.extractInvalidAddresses = function(rawAddresses) { - return rawAddresses.replace(emailAddressMatch, '').split(/[,;]/); - }; - - this.init = function () { - this.$node.typeahead({ - hint: true, - highlight: true, - minLength: 1 - }, { - source: createEmailCompleter().ttAdapter(), - templates: { - suggestion: function (o) { return _.escape(o.value); } - } - }); - }; - - this.attachAndReturn = function (node, name) { - var input = new this.constructor(); - input.initialize(node, { name: name}); - return input; - }; - - this.warnSendButtonOfInputState = function () { - var toTrigger = _.isEmpty(this.$node.val()) ? events.ui.recipients.inputFieldIsEmpty : events.ui.recipients.inputFieldHasCharacters; - this.trigger(document, toTrigger, { name: this.attr.name }); - }; - - this.after('initialize', function () { - self = this; - this.init(); - this.on('typeahead:selected typeahead:autocompleted', this.recipientSelected); - this.on(this.$node, 'focusout', this.tokenizeRecipient); - this.on(this.$node, 'keydown', this.processSpecialKey); - this.on(this.$node, 'keyup', this.warnSendButtonOfInputState); - - this.on(document, events.dispatchers.rightPane.clear, this.teardown); - }); - } - - return defineComponent(recipientsInput); - - } -); diff --git a/web-ui/app/js/mail_view/ui/recipients/recipients_iterator.js b/web-ui/app/js/mail_view/ui/recipients/recipients_iterator.js deleted file mode 100644 index 624ac4f5..00000000 --- a/web-ui/app/js/mail_view/ui/recipients/recipients_iterator.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['helpers/iterator'], function (Iterator) { - 'use strict'; - - return RecipientsIterator; - - function RecipientsIterator(options) { - - this.iterator = new Iterator(options.elements, options.elements.length - 1); - this.input = options.exitInput; - - this.current = function () { - return this.iterator.current(); - }; - - this.moveLeft = function () { - if (this.iterator.hasPrevious()) { - this.iterator.current().doUnselect(); - this.iterator.previous().doSelect(); - } - }; - - this.moveRight = function () { - this.iterator.current().doUnselect(); - if (this.iterator.hasNext()) { - this.iterator.next().doSelect(); - } else { - this.input.focus(); - } - }; - - this.deleteCurrent = function () { - this.iterator.removeCurrent().destroy(); - - if (this.iterator.hasElements()) { - this.iterator.current().doSelect(); - } else { - this.input.focus(); - } - }; - } - -}); diff --git a/web-ui/app/js/mail_view/ui/reply_box.js b/web-ui/app/js/mail_view/ui/reply_box.js deleted file mode 100644 index a174d185..00000000 --- a/web-ui/app/js/mail_view/ui/reply_box.js +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'helpers/view_helper', - 'mixins/with_hide_and_show', - 'mixins/with_compose_inline', - 'page/events', - 'views/i18n' - ], - - function (defineComponent, viewHelper, withHideAndShow, withComposeInline, events, i18n) { - 'use strict'; - - return defineComponent(replyBox, withHideAndShow, withComposeInline); - - function replyBox() { - this.defaultAttrs({ - replyType: 'reply', - draftReply: false, - mail: null, - mailBeingRepliedIdent: undefined - }); - - this.getRecipients = function() { - if (this.attr.replyType === 'replyall') { - return this.attr.mail.replyToAllAddress(); - } else { - return this.attr.mail.replyToAddress(); - } - }; - - var re = function(v) { return i18n.t('re') + ': ' + v; }; - - this.setupReplyBox = function() { - var recipients, body; - - if (this.attr.draftReply){ - this.attr.ident = this.attr.mail.ident; - this.attr.mailBeingRepliedIdent = this.attr.mail.draft_reply_for; - - recipients = this.attr.mail.recipients(); - body = this.attr.mail.body; - this.attr.subject = this.attr.mail.header.subject; - } else { - this.attr.mailBeingRepliedIdent = this.attr.mail.ident; - recipients = this.getRecipients(); - body = viewHelper.quoteMail(this.attr.mail); - this.attr.subject = re(this.attr.mail.header.subject); - } - - this.attr.recipientValues.to = recipients.to; - this.attr.recipientValues.cc = recipients.cc; - - this.renderInlineCompose('reply-box', { - recipients: recipients, - subject: this.attr.subject, - body: body - }); - - this.on(this.select('recipientsDisplay'), 'click keydown', this.showRecipientFields); - this.on(this.select('subjectDisplay'), 'click', this.showSubjectInput); - }; - - this.showRecipientFields = function(ev, data) { - if(!ev.keyCode || ev.keyCode === 13){ - this.select('recipientsDisplay').hide(); - this.select('recipientsFields').show(); - $('#recipients-to-area .tt-input').focus(); - } - }; - - this.showSubjectInput = function() { - this.select('subjectDisplay').hide(); - this.select('subjectInput').show(); - this.select('subjectInput').focus(); - }; - - this.buildMail = function(tag) { - var builder = this.builtMail(tag).subject(this.select('subjectInput').val()); - if(!_.isUndefined(this.attr.mail.header.message_id)) { - builder.header('in_reply_to', this.attr.mail.header.message_id); - } - - if(!_.isUndefined(this.attr.mail.header.list_id)) { - builder.header('list_id', this.attr.mail.header.list_id); - } - - var mail = builder.build(); - mail.setDraftReplyFor(this.attr.mailBeingRepliedIdent); - - return mail; - }; - - this.after('initialize', function () { - this.setupReplyBox(); - }); - } - } -); diff --git a/web-ui/app/js/mail_view/ui/reply_section.js b/web-ui/app/js/mail_view/ui/reply_section.js deleted file mode 100644 index cbe64205..00000000 --- a/web-ui/app/js/mail_view/ui/reply_section.js +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'mail_view/ui/reply_box', - 'mail_view/ui/forward_box', - 'mixins/with_hide_and_show', - 'mixins/with_feature_toggle', - 'page/events' - ], - - function (defineComponent, templates, ReplyBox, ForwardBox, withHideAndShow, withFeatureToggle, events) { - 'use strict'; - - return defineComponent(replySection, withHideAndShow, withFeatureToggle('replySection')); - - function replySection() { - this.defaultAttrs({ - replyButton: '#reply-button', - replyAllButton: '#reply-all-button', - forwardButton: '#forward-button', - replyBox: '#reply-box', - replyType: 'reply', - replyContainer: '.reply-container' - }); - - this.showReply = function() { - this.attr.replyType = 'reply'; - this.fetchEmailToReplyTo(); - }; - - this.showReplyAll = function() { - this.attr.replyType = 'replyall'; - this.fetchEmailToReplyTo(); - }; - - this.showForward = function() { - this.attr.replyType = 'forward'; - this.fetchEmailToReplyTo(); - }; - - this.render = function () { - this.$node.html(templates.compose.replySection); - - this.on(this.select('replyButton'), 'click', this.showReply); - this.on(this.select('replyAllButton'), 'click', this.showReplyAll); - this.on(this.select('forwardButton'), 'click', this.showForward); - }; - - this.checkForDraftReply = function() { - this.render(); - this.hideContainer(); - - this.trigger(document, events.mail.draftReply.want, {ident: this.attr.ident}); - }; - - this.fetchEmailToReplyTo = function (ev) { - this.trigger(document, events.mail.want, { mail: this.attr.ident, caller: this }); - }; - - this.showDraftReply = function(ev, data) { - this.showContainer(); - this.hideButtons(); - ReplyBox.attachTo(this.select('replyBox'), { mail: data.mail, draftReply: true }); - }; - - this.showReplyComposeBox = function (ev, data) { - this.showContainer(); - this.hideButtons(); - if(this.attr.replyType === 'forward') { - ForwardBox.attachTo(this.select('replyBox'), { mail: data.mail }); - } else { - ReplyBox.attachTo(this.select('replyBox'), { mail: data.mail, replyType: this.attr.replyType }); - } - }; - - this.hideContainer = function() { - this.select('replyContainer').hide(); - }; - - this.showContainer = function() { - this.select('replyContainer').show(); - }; - - this.hideButtons = function() { - this.select('replyButton').hide(); - this.select('replyAllButton').hide(); - this.select('forwardButton').hide(); - }; - - this.showButtons = function () { - this.showContainer(); - this.select('replyBox').empty(); - this.select('replyButton').show(); - this.select('replyAllButton').show(); - this.select('forwardButton').show(); - }; - - this.after('initialize', function () { - this.on(document, events.ui.replyBox.showReply, this.showReply); - this.on(document, events.ui.replyBox.showReplyAll, this.showReplyAll); - this.on(document, events.ui.composeBox.trashReply, this.showButtons); - this.on(this, events.mail.here, this.showReplyComposeBox); - this.on(document, events.dispatchers.rightPane.clear, this.teardown); - - this.on(document, events.ui.replyBox.showReplyContainer, this.showContainer); - this.on(document, events.mail.draftReply.here, this.showDraftReply); - - this.checkForDraftReply(); - }); - } - } -); diff --git a/web-ui/app/js/mail_view/ui/send_button.js b/web-ui/app/js/mail_view/ui/send_button.js deleted file mode 100644 index 66fe1233..00000000 --- a/web-ui/app/js/mail_view/ui/send_button.js +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define([ - 'flight/lib/component', - 'flight/lib/utils', - 'page/events', - 'helpers/view_helper' - ], - function (defineComponent, utils, events, viewHelper) { - 'use strict'; - - return defineComponent(sendButton); - - function sendButton() { - var RECIPIENTS_BOXES_COUNT = 3; - - this.enableButton = function () { - this.$node.prop('disabled', false); - }; - - this.disableButton = function () { - this.$node.prop('disabled', true); - }; - - this.atLeastOneInputFieldHasRecipients = function () { - return _.any(_.values(this.attr.recipients), function (e) { return !_.isEmpty(e); }); - }; - - this.atLeastOneInputFieldHasCharacters = function () { - return _.any(_.values(this.attr.inputFieldHasCharacters), function (e) { return e === true; }); - }; - - this.updateButton = function () { - if (this.attr.sendingInProgress === false) { - if (this.attr.uploading === false && (this.atLeastOneInputFieldHasCharacters() || this.atLeastOneInputFieldHasRecipients())) { - this.enableButton(); - } else { - this.disableButton(); - } - } - }; - - this.inputFieldIsEmpty = function (ev, data) { - this.attr.inputFieldHasCharacters[data.name] = false; - this.updateButton(); - }; - - this.inputFieldHasCharacters = function (ev, data) { - this.attr.inputFieldHasCharacters[data.name] = true; - this.updateButton(); - }; - - this.uploadInProgress = function (ev, data) { - this.attr.uploading = true; - this.updateButton(); - }; - - this.uploadFinished = function (ev, data) { - this.attr.uploading = false; - this.updateButton(); - }; - - this.updateRecipientsForField = function (ev, data) { - this.attr.recipients[data.recipientsName] = data.newRecipients; - this.attr.inputFieldHasCharacters[data.recipientsName] = false; - - this.updateButton(); - }; - - this.updateRecipientsAndSendMail = function () { - - this.on(document, events.ui.mail.recipientsUpdated, utils.countThen(RECIPIENTS_BOXES_COUNT, function () { - this.trigger(document, events.ui.mail.send); - this.off(document, events.ui.mail.recipientsUpdated); - }.bind(this))); - - this.disableButton(); - this.$node.text(viewHelper.i18n.t('sending-mail')); - - this.attr.sendingInProgress = true; - - this.trigger(document, events.ui.recipients.doCompleteInput); - }; - - this.resetButton = function () { - this.attr.sendingInProgress = false; - this.attr.uploading = false; - this.$node.html(viewHelper.i18n.t('send')); - this.enableButton(); - }; - - this.after('initialize', function () { - this.attr.recipients = {}; - this.attr.inputFieldHasCharacters = {}; - this.resetButton(); - - this.on(document, events.ui.recipients.inputFieldHasCharacters, this.inputFieldHasCharacters); - this.on(document, events.ui.recipients.inputFieldIsEmpty, this.inputFieldIsEmpty); - this.on(document, events.ui.recipients.updated, this.updateRecipientsForField); - - this.on(this.$node, 'click', this.updateRecipientsAndSendMail); - - this.on(document, events.mail.uploadingAttachment, this.uploadInProgress); - this.on(document, events.mail.uploadedAttachment, this.uploadFinished); - this.on(document, events.mail.failedUploadAttachment, this.uploadFinished); - - this.on(document, events.dispatchers.rightPane.clear, this.teardown); - this.on(document, events.ui.sendbutton.enable, this.resetButton); - this.on(document, events.mail.send_failed, this.resetButton); - - this.disableButton(); - }); - } - - } -); diff --git a/web-ui/app/js/main.js b/web-ui/app/js/main.js deleted file mode 100644 index b8836a6b..00000000 --- a/web-ui/app/js/main.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -requirejs.config({ - baseUrl: '../assets/', - paths: { - 'mail_list': 'js/mail_list', - 'page': 'js/page', - 'feedback': 'js/feedback', - 'flight': 'bower_components/flight', - 'DOMPurify': 'bower_components/DOMPurify/dist/purify.min', - 'he': 'bower_components/he/he', - 'hbs': 'js/generated/hbs', - 'helpers': 'js/helpers', - 'lib': 'js/lib', - 'views': 'js/views', - 'tags': 'js/tags', - 'mail_list_actions': 'js/mail_list_actions', - 'user_alerts': 'js/user_alerts', - 'mail_view': 'js/mail_view', - 'dispatchers': 'js/dispatchers', - 'services': 'js/services', - 'mixins': 'js/mixins', - 'search': 'js/search', - 'foundation': 'js/foundation', - 'features': 'js/features/features', - 'i18next': 'bower_components/i18next/i18next', - 'i18nextXHRBackend': 'bower_components/i18next-xhr-backend/i18nextXHRBackend', - 'i18nextBrowserLanguageDetector': 'bower_components/i18next-browser-languagedetector/i18nextBrowserLanguageDetector', - 'quoted-printable': 'bower_components/quoted-printable', - 'utf8': 'bower_components/utf8', - 'user_settings': 'js/user_settings' - } -}); - -require([ - 'flight/lib/compose', - 'flight/lib/debug' -], function(compose, debug){ - 'use strict'; - debug.enable(true); - debug.events.logAll(); -}); - -require( - [ - 'flight/lib/compose', - 'flight/lib/registry', - 'flight/lib/advice', - 'flight/lib/logger', - 'flight/lib/debug', - 'page/events', - 'page/default', - 'js/monkey_patching/all' - ], - - function(compose, registry, advice, withLogging, debug, events, initializeDefault, _monkeyPatched) { - 'use strict'; - - window.Pixelated = window.Pixelated || {}; - window.Pixelated.events = events; - - compose.mixin(registry, [advice.withAdvice, withLogging]); - - debug.enable(true); - debug.events.logAll(); - - initializeDefault(''); - } -); diff --git a/web-ui/app/js/mixins/with_auto_refresh.js b/web-ui/app/js/mixins/with_auto_refresh.js deleted file mode 100644 index c75fda45..00000000 --- a/web-ui/app/js/mixins/with_auto_refresh.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['features'], - function (features) { - 'use strict'; - - function withAutoRefresh(refreshMethod) { - return function () { - this.defaultAttrs({ - refreshInterval: 15000 - }); - - this.setupRefresher = function () { - clearTimeout(this.attr.refreshTimer); - this.attr.refreshTimer = setTimeout(function () { - this[refreshMethod](); - this.setupRefresher(); - }.bind(this), this.attr.refreshInterval); - }; - - this.after('initialize', function () { - if (features.isAutoRefreshEnabled()) { - this.setupRefresher(); - } - }); - }; - } - - return withAutoRefresh; - } -); - diff --git a/web-ui/app/js/mixins/with_compose_inline.js b/web-ui/app/js/mixins/with_compose_inline.js deleted file mode 100644 index b8266f28..00000000 --- a/web-ui/app/js/mixins/with_compose_inline.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'page/events', - 'views/templates', - 'mail_view/data/mail_builder', - 'mixins/with_mail_edit_base' - ], - function(events, templates, mailBuilder, withMailEditBase) { - 'use strict'; - - function withComposeInline() { - this.defaultAttrs({ - subjectDisplay: '#reply-subject', - subjectInput: '#subject-container input', - forwardBox: '#forward-box', - recipientsDisplay: '#all-recipients' - }); - - this.openMail = function(ev, data) { - this.trigger(document, events.ui.mail.open, {ident: this.attr.mail.ident}); - }; - - this.trashReply = function() { - this.trigger(document, events.ui.composeBox.trashReply); - this.teardown(); - }; - - this.builtMail = function(tag) { - return mailBuilder.newMail(this.attr.ident) - .subject(this.select('subjectBox').val()) - .to(this.attr.recipientValues.to) - .cc(this.attr.recipientValues.cc) - .bcc(this.attr.recipientValues.bcc) - .body(this.select('bodyBox').val()) - .attachment(this.attr.attachments) - .tag(tag); - }; - - this.renderInlineCompose = function(className, viewData) { - this.show(); - this.render(templates.compose.inlineBox, viewData); - - this.$node.addClass(className); - this.select('bodyBox').focus(); - - this.enableAutoSave(); - }; - - this.updateIdent = function(ev, data) { - this.attr.mail.ident = data.ident; - }; - - this.discardDraft = function() { - this.trashReply(); - }; - - this.after('initialize', function () { - this.on(document, events.mail.sent, this.openMail); - this.on(document, events.mail.deleted, this.trashReply); - this.on(document, events.mail.draftSaved, this.updateIdent); - }); - - withMailEditBase.call(this); - } - - return withComposeInline; - }); diff --git a/web-ui/app/js/mixins/with_enable_disable_on_event.js b/web-ui/app/js/mixins/with_enable_disable_on_event.js deleted file mode 100644 index 5b28a67b..00000000 --- a/web-ui/app/js/mixins/with_enable_disable_on_event.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define([], - function () { - 'use strict'; - - function withEnableDisableOnEvent(ev) { - return function () { - this.disableElement = function () { - this.$node.attr('disabled', 'disabled'); - }; - - this.enableElement = function () { - this.$node.removeAttr('disabled'); - }; - - this.toggleEnabled = function (ev, enable) { - if (enable) { - this.enableElement(); - } else { - this.disableElement(); - } - }; - - this.after('initialize', function () { - this.on(document, ev, this.toggleEnabled); - }); - }; - } - - return withEnableDisableOnEvent; - } -); diff --git a/web-ui/app/js/mixins/with_feature_toggle.js b/web-ui/app/js/mixins/with_feature_toggle.js deleted file mode 100644 index 195b08bc..00000000 --- a/web-ui/app/js/mixins/with_feature_toggle.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['features'], - function(features) { - 'use strict'; - - function withFeatureToggle(componentName, behaviorForFeatureOff) { - return function() { - - this.around('initialize', _.bind(function(basicInitialize, node, attrs) { - if(features.isEnabled(componentName)) { - return basicInitialize(node, attrs); - } - else if (behaviorForFeatureOff){ - behaviorForFeatureOff.call(this); - - return this; - } - }, this)); - }; - } - - return withFeatureToggle; - -}); diff --git a/web-ui/app/js/mixins/with_hide_and_show.js b/web-ui/app/js/mixins/with_hide_and_show.js deleted file mode 100644 index c8902f61..00000000 --- a/web-ui/app/js/mixins/with_hide_and_show.js +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(function(require) { - 'use strict'; - - function withHideAndShow() { - this.hide = function () { - this.$node.hide(); - }; - this.show = function () { - this.$node.show(); - }; - } - - return withHideAndShow; - -}); diff --git a/web-ui/app/js/mixins/with_mail_edit_base.js b/web-ui/app/js/mixins/with_mail_edit_base.js deleted file mode 100644 index a088080e..00000000 --- a/web-ui/app/js/mixins/with_mail_edit_base.js +++ /dev/null @@ -1,263 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/compose', - 'helpers/view_helper', - 'mail_view/ui/recipients/recipients', - 'mail_view/ui/draft_save_status', - 'page/events', - 'views/i18n', - 'mail_view/ui/send_button', - 'mail_view/ui/attachment_icon', - 'mail_view/ui/attachment_list', - 'flight/lib/utils' - ], - function (compose, viewHelper, Recipients, DraftSaveStatus, events, i18n, SendButton, AttachmentIcon, attachmentList, utils) { - 'use strict'; - - function withMailEditBase() { - - this.defaultAttrs({ - bodyBox: '#text-box', - sendButton: '#send-button', - attachmentButton: '#attachment-button', - attachmentList: '#attachment-list', - cancelButton: '#cancel-button', - trashButton: '#trash-button', - toArea: '#recipients-to-area', - toBox: '#recipients-to-box', - ccArea: '#recipients-cc-area', - bccArea: '#recipients-bcc-area', - ccsTrigger: '#ccs-trigger', - bccsTrigger: '#bccs-trigger', - toTrigger: '#to-trigger', - subjectBox: '#subject', - tipMsg: '.tip-msg', - draftSaveStatus: '#draft-save-status', - recipientsFields: '#recipients-fields', - currentTag: '', - recipientValues: {to: [], cc: [], bcc: []}, - saveDraftInterval: 3000 - }); - - this.attachRecipients = function (context) { - Recipients.attachTo(this.select('toArea'), {name: 'to', addresses: context.recipients.to}); - Recipients.attachTo(this.select('ccArea'), {name: 'cc', addresses: context.recipients.cc || []}); - Recipients.attachTo(this.select('bccArea'), {name: 'bcc', addresses: context.recipients.bcc || []}); - }; - - function thereAreRecipientsToDisplay() { - - var allRecipients = _.chain(this.attr.recipientValues). - values(). - flatten(). - remove(undefined). - value(); - - return !_.isEmpty(allRecipients); - } - - this.warnSendButtonOfRecipients = function () { - if (thereAreRecipientsToDisplay.call(this)) { - _.forOwn(this.attr.recipientValues, function (recipients, recipientsType) { - if (!_.isUndefined(recipients) && !_.isEmpty(recipients)) { - var recipientsUpdatedData = { - newRecipients: recipients, - recipientsName: recipientsType - }; - this.trigger(document, events.ui.recipients.updated, recipientsUpdatedData); - } - }.bind(this)); - } - }; - - this.render = function (template, context) { - this.$node.html(template(context)); - - if (!context || _.isEmpty(context)) { - context.recipients = {to: [], cc: [], bcc: []}; - } - this.attr.recipientValues = context.recipients; - this.attr.attachments = context.attachments || []; - this.attachRecipients(context); - - this.on(this.select('trashButton'), 'click', this.discardMail); - SendButton.attachTo(this.select('sendButton')); - AttachmentIcon.attachTo(this.select('attachmentButton')); - - this.warnSendButtonOfRecipients(); - }; - - this.enableAutoSave = function () { - this.select('bodyBox').on('input', this.monitorInput.bind(this)); - this.select('subjectBox').on('input', this.monitorInput.bind(this)); - this.on(document, events.mail.appendAttachment, this.monitorInput.bind(this)); - this.on(document, events.mail.removeAttachment, this.monitorInput.bind(this)); - DraftSaveStatus.attachTo(this.select('draftSaveStatus')); - }; - - this.monitorInput = function () { - this.trigger(events.ui.mail.changedSinceLastSave); - this.cancelPostponedSaveDraft(); - var mail = this.buildMail(); - this.postponeSaveDraft(mail); - }; - - this.discardMail = function () { - this.cancelPostponedSaveDraft(); - if (this.attr.ident) { - var mail = this.buildMail(); - this.trigger(document, events.ui.mail.delete, {mail: mail}); - } else { - this.trigger(document, events.ui.mail.discard); - } - }; - - this.trim_recipient = function (recipients) { - return recipients.map(function (recipient) { - return recipient.trim(); - }); - }; - - this.sendMail = function () { - this.cancelPostponedSaveDraft(); - var mail = this.buildMail('sent'); - - if (allRecipientsAreEmails(mail)) { - mail.header.to = this.trim_recipient(mail.header.to); - mail.header.cc = this.trim_recipient(mail.header.cc); - mail.header.bcc = this.trim_recipient(mail.header.bcc); - this.trigger(events.mail.send, mail); - } else { - this.trigger( - events.ui.userAlerts.displayMessage, - {message: i18n.t('recipients-not-valid')} - ); - this.trigger(events.mail.send_failed); - } - }; - - this.buildAndSaveDraft = function () { - var mail = this.buildMail(); - this.saveDraft(mail); - }; - - this.recipientsUpdated = function (ev, data) { - this.attr.recipientValues[data.recipientsName] = data.newRecipients; - this.trigger(document, events.ui.mail.recipientsUpdated); - if (data.skipSaveDraft) { - return; - } - - var mail = this.buildMail(); - this.postponeSaveDraft(mail); - }; - - this.saveDraft = function (mail) { - this.cancelPostponedSaveDraft(); - this.trigger(document, events.mail.saveDraft, mail); - }; - - this.cancelPostponedSaveDraft = function () { - clearTimeout(this.attr.timeout); - }; - - this.postponeSaveDraft = function (mail) { - this.cancelPostponedSaveDraft(); - - this.attr.timeout = window.setTimeout(_.bind(function () { - this.saveDraft(mail); - }, this), this.attr.saveDraftInterval); - }; - - this.draftSaved = function (event, data) { - this.attr.ident = data.ident; - }; - - this.validateAnyRecipient = function () { - return !_.isEmpty(_.flatten(_.values(this.attr.recipientValues))); - }; - - function allRecipientsAreEmails(mail) { - var allRecipients = mail.header.to.concat(mail.header.cc).concat(mail.header.bcc); - return _.isEmpty(allRecipients) ? false : _.all(allRecipients, emailFormatChecker); - } - - function emailFormatChecker(email) { - var emailFormat = /[^\s@]+@[^\s@]+\.[^\s@]+$/; - return emailFormat.test(email); - } - - this.saveTag = function (ev, data) { - this.attr.currentTag = data.tag; - }; - - this.mailSent = function () { - this.trigger(document, events.ui.userAlerts.displayMessage, {message: 'Your message was sent!'}); - }; - - this.enableFloatlabel = function (element) { - var showClass = 'showfloatlabel'; - $(element).bind('keyup', function () { - var label = $(this).prev('label'); - if (this.value !== '') { - label.addClass(showClass); - $(this).addClass(showClass); - } else { - label.removeClass(showClass); - $(this).removeClass(showClass); - } - }); - }; - - this.toggleRecipientsArrows = function () { - $('#cc-bcc-collapse').toggleClass('fa-angle-down'); - $('#cc-bcc-collapse').toggleClass('fa-angle-up'); - }; - - this.before('initialize', function () { - if (!this.discardDraft) { - this.discardDraft = function () { - }; - } - }); - - this.bindCollapse = function () { - this.on($('#cc-bcc-collapse'), 'click', this.toggleRecipientsArrows); - }; - - this.after('initialize', function () { - this.on(document, events.dispatchers.rightPane.clear, this.teardown); - this.on(document, events.ui.recipients.updated, this.recipientsUpdated); - this.on(document, events.mail.draftSaved, this.draftSaved); - this.on(document, events.mail.sent, this.mailSent); - - this.on(document, events.ui.mail.send, this.sendMail); - - this.on(document, events.ui.mail.discard, this.discardDraft); - this.on(document, events.ui.tag.selected, this.saveTag); - this.on(document, events.ui.tag.select, this.saveTag); - this.bindCollapse(); - }); - - compose.mixin(this, [attachmentList]); - } - - return withMailEditBase; - }); diff --git a/web-ui/app/js/mixins/with_mail_sandbox.js b/web-ui/app/js/mixins/with_mail_sandbox.js deleted file mode 100644 index 1a51840d..00000000 --- a/web-ui/app/js/mixins/with_mail_sandbox.js +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - ['helpers/view_helper', 'page/events'], - function(viewHelpers, events) { - 'use strict'; - - function withMailSandbox() { - this.showMailOnSandbox = function(mail) { - var that = this; - var $iframe = $("#read-sandbox"); - var iframe = $iframe[0]; - var content = viewHelpers.formatMailBody(mail); - - window.addEventListener('message', function(e) { - if (e.origin === 'null' && e.source === iframe.contentWindow) { - that.trigger(document, events.ui.replyBox.showReplyContainer); - that.trigger(document, events.search.highlightResults, {where: '.mail-read-view__header'}); - } - }); - - iframe.onload = function() { - if ($iframe.iFrameResize) { - // use iframe-resizer to dynamically adapt iframe size to its content - var config = { - resizedCallback: scaleToFit, - checkOrigin: false - }; - $iframe.iFrameResize(config); - } - - iframe.contentWindow.postMessage({ - html: content - }, '*'); - - // transform scale iframe to fit container width - // necessary if iframe is wider than container - function scaleToFit() { - var parentWidth = $iframe.parent().width(); - var w = $iframe.width(); - var scale = 'none'; - - // only scale html mails - if (mail && mail.htmlBody && (w > parentWidth)) { - scale = parentWidth / w; - scale = 'scale(' + scale + ',' + scale + ')'; - } - - $iframe.css({ - '-webkit-transform-origin': '0 0', - '-moz-transform-origin': '0 0', - '-ms-transform-origin': '0 0', - 'transform-origin': '0 0', - '-webkit-transform': scale, - '-moz-transform': scale, - '-ms-transform': scale, - 'transform': scale - }); - } - }; - }; - } - - return withMailSandbox; - } -); diff --git a/web-ui/app/js/mixins/with_mail_tagging.js b/web-ui/app/js/mixins/with_mail_tagging.js deleted file mode 100644 index 1fc1c3bd..00000000 --- a/web-ui/app/js/mixins/with_mail_tagging.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - ['page/events', 'features'], - function (events, features) { - 'use strict'; - function withMailTagging () { - this.updateTags = function(mail, tags) { - this.trigger(document, events.mail.tags.update, {ident: mail.ident, tags: tags}); - }; - - this.attachTagCompletion = function(mail) { - this.tagFilter = function (parsedResult) { - var filtered = _.filter(parsedResult, function (tag) {return ! _.contains(mail.tags, tag.name); }); - return _.map(filtered, function(tag) { return {value: Handlebars.Utils.escapeExpression(tag.name)}; }); - }; - - this.tagCompleter = new Bloodhound({ - datumTokenizer: function(d) { return [d.value]; }, - queryTokenizer: function(q) { return [q.trim()]; }, - remote: { - url: '/tags?skipDefaultTags=true&q=%QUERY', - filter: this.tagFilter - } - }); - - this.tagCompleter.initialize(); - - this.select('newTagInput').typeahead({ - hint: true, - highlight: true, - minLength: 1 - }, { - source: this.tagCompleter.ttAdapter() - }); - }; - - this.createNewTag = function () { - var tagsCopy = this.attr.mail.tags.slice(); - tagsCopy.push(this.select('newTagInput').val()); - this.tagCompleter.clear(); - this.tagCompleter.clearPrefetchCache(); - this.tagCompleter.clearRemoteCache(); - this.updateTags(this.attr.mail, _.uniq(tagsCopy)); - }; - - this.after('displayMail', function () { - this.on(this.select('newTagInput'), 'typeahead:selected typeahead:autocompleted', this.createNewTag); - }); - } - - return withMailTagging; - } -); diff --git a/web-ui/app/js/monkey_patching/all.js b/web-ui/app/js/monkey_patching/all.js deleted file mode 100644 index 2c29c9a1..00000000 --- a/web-ui/app/js/monkey_patching/all.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -require(['js/monkey_patching/array', 'js/monkey_patching/post_message'], function () {}); diff --git a/web-ui/app/js/monkey_patching/array.js b/web-ui/app/js/monkey_patching/array.js deleted file mode 100644 index d0ccc4b8..00000000 --- a/web-ui/app/js/monkey_patching/array.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -(function () { - 'use strict'; - - // Array Remove - By John Resig (MIT Licensed) - Array.prototype.remove = function (from, to) { - var rest = this.slice((to || from) + 1 || this.length); - this.length = from < 0 ? this.length + from : from; - return this.push.apply(this, rest); - }; - -}()); diff --git a/web-ui/app/js/monkey_patching/post_message.js b/web-ui/app/js/monkey_patching/post_message.js deleted file mode 100644 index 363ce581..00000000 --- a/web-ui/app/js/monkey_patching/post_message.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -/* - * origin window.postMessage fails with non serializable objects, so we fallback to console.log to do the job - */ -(function () { - 'use strict'; - - var originalPostMessage = window.postMessage; - window.postMessage = function(a, b) { - try { - originalPostMessage(a, b); - } catch (e) { - console.log(a, b); - } - }; - -}()); diff --git a/web-ui/app/js/page/default.js b/web-ui/app/js/page/default.js deleted file mode 100644 index ecaedfd8..00000000 --- a/web-ui/app/js/page/default.js +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'mail_view/ui/compose_box', - 'mail_list_actions/ui/mail_list_actions', - 'user_alerts/ui/user_alerts', - 'mail_list/ui/mail_list', - 'mail_view/ui/no_message_selected_pane', - 'mail_view/ui/no_mails_available_pane', - 'mail_view/ui/mail_view', - 'mail_view/ui/mail_actions', - 'mail_view/ui/reply_section', - 'mail_view/data/mail_sender', - 'services/mail_service', - 'services/delete_service', - 'services/recover_service', - 'tags/ui/tag_list', - 'tags/data/tags', - 'page/router', - 'dispatchers/right_pane_dispatcher', - 'dispatchers/middle_pane_dispatcher', - 'dispatchers/left_pane_dispatcher', - 'search/search_trigger', - 'search/results_highlighter', - 'foundation/off_canvas', - 'page/pane_contract_expand', - 'views/i18n', - 'views/recipientListFormatter', - 'flight/lib/logger', - 'user_settings/data/user_settings', - 'user_settings/ui/user_settings_icon', - 'page/logout', - 'page/logout_shortcut', - 'feedback/feedback_trigger', - 'mail_view/ui/feedback_box', - 'mail_view/data/feedback_sender', - 'page/version', - 'page/unread_count_title', - 'page/pix_logo', - 'helpers/browser' - ], - - function ( - composeBox, - mailListActions, - userAlerts, - mailList, - noMessageSelectedPane, - noMailsAvailablePane, - mailView, - mailViewActions, - replyButton, - mailSender, - mailService, - deleteService, - recoverService, - tagList, - tags, - router, - rightPaneDispatcher, - middlePaneDispatcher, - leftPaneDispatcher, - searchTrigger, - resultsHighlighter, - offCanvas, - paneContractExpand, - viewI18n, - recipientListFormatter, - withLogging, - userSettings, - userSettingsIcon, - logout, - logoutShortcut, - feedback, - feedbackBox, - feedbackSender, - version, - unreadCountTitle, - pixLogo, - browser) { - - 'use strict'; - function initialize(path) { - viewI18n.init(path + '/assets/'); - viewI18n.loaded(function() { - paneContractExpand.attachTo(document); - - userAlerts.attachTo('#user-alerts'); - - mailList.attachTo('#mail-list'); - mailListActions.attachTo('#list-actions'); - - searchTrigger.attachTo('#search-trigger'); - resultsHighlighter.attachTo(document); - - mailSender.attachTo(document); - - mailService.attachTo(document); - deleteService.attachTo(document); - recoverService.attachTo(document); - - tags.attachTo(document); - tagList.attachTo('#tag-list'); - - router.attachTo(document); - - rightPaneDispatcher.attachTo(document); - middlePaneDispatcher.attachTo(document); - leftPaneDispatcher.attachTo(document); - - offCanvas.attachTo(document); - userSettings.attachTo(document); - userSettingsIcon.attachTo('#user-settings-icon'); - logout.attachTo('#logout'); - logoutShortcut.attachTo('#logout-shortcut'); - version.attachTo('.version'); - - feedback.attachTo('#feedback'); - feedbackSender.attachTo(document); - - unreadCountTitle.attachTo(document); - - pixLogo.attachTo(document); - - $.ajaxSetup({headers: {'X-XSRF-TOKEN': browser.getCookie('XSRF-TOKEN')}}); - }); - } - - return initialize; - } -); diff --git a/web-ui/app/js/page/events.js b/web-ui/app/js/page/events.js deleted file mode 100644 index 68a6aad1..00000000 --- a/web-ui/app/js/page/events.js +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(function () { - 'use strict'; - - var events = { - router: { - pushState: 'router:pushState' - }, - ui: { - sendbutton: { - enable: 'ui:sendbutton:enable' - }, - middlePane: { - expand: 'ui:middlePane:expand', - contract: 'ui:middlePane:contract' - }, - userAlerts: { - displayMessage: 'ui:userAlerts:displayMessage' - }, - tag: { - selected: 'ui:tagSelected', - select: 'ui:tagSelect', - }, - tags: { - loaded: 'ui:tagsLoaded' - }, - tagList: { - load: 'ui:tagList:load' - }, - mails: { - refresh: 'ui:mails:refresh', - fetchByTag: 'ui:mails:fetchByTag', - cleanSelected: 'ui:mails:cleanSelected', - checkAll: 'ui:mails:checkAll', - uncheckAll: 'ui:mails:uncheckAll', - hasMailsChecked: 'ui:mails:hasMailsChecked' - }, - mail: { - open: 'ui:mail:open', - updateSelected: 'ui:mail:updateSelected', - delete: 'ui:mail:delete', - deleteMany: 'ui:mail:deleteMany', - recoverMany: 'ui:mail:recoverMany', - archiveMany: 'ui:mail:archiveMany', - wantChecked: 'ui:mail:wantChecked', - hereChecked: 'ui:mail:hereChecked', - checked: 'ui:mail:checked', - discard: 'ui:mail:discard', - unchecked: 'ui:mail:unchecked', - changedSinceLastSave: 'ui:mail:changedSinceLastSave', - send: 'ui:mail:send', - recipientsUpdated: 'ui:mail:recipientsUpdated' - }, - page: { - previous: 'ui:page:previous', - next: 'ui:page:next', - changed: 'ui:page:changed', - spinLogo: 'ui:page:spinLogo', - stopSpinningLogo: 'ui:page:stopSpinningLogo' - }, - composeBox: { - newMessage: 'ui:composeBox:newMessage', - newReply: 'ui:composeBox:newReply', - trashReply: 'ui:composeBox:trashReply', - requestCancelReply: 'ui:composeBox:requestCancelReply' - }, - replyBox: { - showReply: 'ui:replyBox:showReply', - showReplyAll: 'ui:replyBox:showReplyAll', - showReplyContainer: 'ui:replyBox:showReplyContainer', - }, - recipients: { - entered: 'ui:recipients:entered', - enteredInvalid: 'ui:recipients:enteredInvalid', - updated: 'ui:recipients:updated', - editRecipient: 'ui:recipients:editRecipient', - deleteRecipient: 'ui:recipients:deleteRecipient', - deleteLast: 'ui:recipients:deleteLast', - selectLast: 'ui:recipients:selectLast', - unselectAll: 'ui:recipients:unselectAll', - addressesExist: 'ui:recipients:addressesExist', - inputFieldHasCharacters: 'ui:recipients:inputFieldHasCharacters', - inputFieldIsEmpty: 'ui:recipients:inputFieldIsEmpty', - doCompleteInput: 'ui:recipients:doCompleteInput', - doCompleteRecipients: 'ui:recipients:doCompleteRecipients', - clickToEdit: 'ui:recipients:clickToEdit' - }, - userSettingsBox: { - toggle: 'ui:userSettingsBox:toggle' - } - }, - search: { - perform: 'search:perform', - results: 'search:results', - empty: 'search:empty', - highlightResults: 'search:highlightResults', - resetHighlight: 'search:resetHighlight' - }, - feedback: { - submit: 'feedback:submit', - submitted: 'feedback:submitted' - }, - userSettings: { - here: 'userSettings:here', - getInfo: 'userSettings:getInfo', - destroyPopup: 'userSettings:destroyPopup' - }, - mail: { - here: 'mail:here', - want: 'mail:want', - display: 'mail:display', - highlightMailContent: 'mail:highlightMailContent', - send: 'mail:send', - send_failed: 'mail:send_failed', - sent: 'mail:sent', - read: 'mail:read', - unread: 'mail:unread', - delete: 'mail:delete', - deleteMany: 'mail:deleteMany', - archiveMany: 'mail:archiveMany', - recoverMany: 'mail:recoverMany', - deleted: 'mail:deleted', - saveDraft: 'draft:save', - draftSaved: 'draft:saved', - draftReply: { - want: 'mail:draftReply:want', - here: 'mail:draftReply:here', - notFound: 'mail:draftReply:notFound' - }, - notFound: 'mail:notFound', - save: 'mail:saved', - tags: { - update: 'mail:tags:update', - updated: 'mail:tags:updated' - }, - uploadedAttachment: 'mail:uploaded:attachment', - uploadingAttachment: 'mail:uploading:attachment', - startUploadAttachment: 'mail:start:upload:attachment', - failedUploadAttachment: 'mail:failed:upload:attachment', - appendAttachment: 'mail:append:attachment', - resetAttachments: 'mail:reset:attachments', - removeAttachment: 'mail:remove:attachment' - }, - mails: { - available: 'mails:available', - availableForRefresh: 'mails:available:refresh', - teardown: 'mails:teardown' - }, - tags: { - want: 'tags:want', - received: 'tags:received', - teardown: 'tags:teardown', - shortcuts: { - teardown: 'tags:shortcuts:teardown' - } - }, - route: { - toUrl: 'route:toUrl' - }, - - components: { - composeBox: { - open: 'components:composeBox:open', - close: 'components:composeBox:close' - }, - mailPane: { - open: 'components:mailPane:open', - close: 'components:mailPane:close' - }, - mailView: { - show: 'components:mailView:show', - close: 'components:mailView:close' - }, - replySection: { - initialize: 'components:replySection:initialize', - close: 'components:replySection:close' - }, - noMessageSelectedPane: { - open: 'components:noMessageSelectedPane:open', - close: 'components:noMessageSelectedPane:close' - } - }, - - dispatchers: { - rightPane: { - openComposeBox: 'dispatchers:rightPane:openComposeBox', - openFeedbackBox: 'dispatchers:rightPane:openFeedbackBox', - openNoMessageSelected: 'dispatchers:rightPane:openNoMessageSelected', - openNoMessageSelectedWithoutPushState: 'dispatchers:rightPane:openNoMessageSelectedWithoutPushState', - refreshMailList: 'dispatchers:rightPane:refreshMailList', - openDraft: 'dispatchers:rightPane:openDraft', - selectTag: 'dispatchers:rightPane:selectTag', - clear: 'dispatchers:rightPane:clear' - }, - middlePane: { - refreshMailList: 'dispatchers:middlePane:refreshMailList', - cleanSelected: 'dispatchers:middlePane:unselect', - resetScroll: 'dispatchers:middlePane:resetScroll' - }, - tags: { - refreshTagList: 'dispatchers:tag:refresh' - } - } - }; - - return events; -}); diff --git a/web-ui/app/js/page/logout.js b/web-ui/app/js/page/logout.js deleted file mode 100644 index 81b57db2..00000000 --- a/web-ui/app/js/page/logout.js +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(['flight/lib/component', 'features', 'views/templates', 'helpers/browser'], - function (defineComponent, features, templates, browser) { - 'use strict'; - - return defineComponent(function () { - - this.defaultAttrs({form: '#logout-form'}); - - this.render = function () { - var logoutHTML = templates.page.logout({ logout_url: features.getLogoutUrl(), - csrf_token: browser.getCookie('XSRF-TOKEN')}); - this.$node.html(logoutHTML); - }; - - this.logout = function(){ - this.select('form').submit(); - }; - - this.after('initialize', function () { - if (features.isLogoutEnabled()) { - this.render(); - this.on(this.$node, 'click', this.logout); - } - }); - - }); -}); diff --git a/web-ui/app/js/page/logout_shortcut.js b/web-ui/app/js/page/logout_shortcut.js deleted file mode 100644 index 10a69c7d..00000000 --- a/web-ui/app/js/page/logout_shortcut.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(['flight/lib/component', 'features', 'views/templates'], function (defineComponent, features, templates) { - 'use strict'; - - return defineComponent(function () { - - this.render = function () { - if (features.isLogoutEnabled()) { - var logoutShortcutHTML = templates.page.logoutShortcut(); - this.$node.html(logoutShortcutHTML); - } - }; - - this.after('initialize', function () { - this.render(); - }); - }); -}); diff --git a/web-ui/app/js/page/pane_contract_expand.js b/web-ui/app/js/page/pane_contract_expand.js deleted file mode 100644 index 9bb435c4..00000000 --- a/web-ui/app/js/page/pane_contract_expand.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['flight/lib/component', 'page/events'], function (describeComponent, events) { - 'use strict'; - - return describeComponent(paneContractExpand); - - function paneContractExpand() { - this.defaultAttrs({ - RIGHT_PANE_EXPAND_CLASSES: 'small-7 medium-7 large-7 columns', - RIGHT_PANE_CONTRACT_CLASSES: 'small-7 medium-4 large-4 columns', - MIDDLE_PANE_EXPAND_CLASSES: 'small-5 medium-8 large-8 columns no-padding', - MIDDLE_PANE_CONTRACT_CLASSES: 'small-5 medium-5 large-5 columns no-padding' - }); - - this.expandMiddlePaneContractRightPane = function () { - $('#middle-pane-container').attr('class', this.attr.MIDDLE_PANE_EXPAND_CLASSES); - $('#right-pane').attr('class', this.attr.RIGHT_PANE_CONTRACT_CLASSES); - }; - - this.contractMiddlePaneExpandRightPane = function () { - $('#middle-pane-container').attr('class', this.attr.MIDDLE_PANE_CONTRACT_CLASSES); - $('#right-pane').attr('class', this.attr.RIGHT_PANE_EXPAND_CLASSES); - }; - - this.after('initialize', function () { - this.on(document, events.ui.mail.open, this.contractMiddlePaneExpandRightPane); - this.on(document, events.dispatchers.rightPane.openComposeBox, this.contractMiddlePaneExpandRightPane); - this.on(document, events.dispatchers.rightPane.openDraft, this.contractMiddlePaneExpandRightPane); - this.on(document, events.dispatchers.rightPane.openFeedbackBox, this.contractMiddlePaneExpandRightPane); - this.on(document, events.dispatchers.rightPane.openNoMessageSelected, this.expandMiddlePaneContractRightPane); - this.expandMiddlePaneContractRightPane(); - }); - - } -}); diff --git a/web-ui/app/js/page/pix_logo.js b/web-ui/app/js/page/pix_logo.js deleted file mode 100644 index ad17f3be..00000000 --- a/web-ui/app/js/page/pix_logo.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'page/events' - ], - - function(defineComponent, events) { - 'use strict'; - - return defineComponent(pixLogo); - - function pixLogo() { - this.turnAnimationOn = function () { - $('.logo-part-animation-off').attr('class', 'logo-part-animation-on'); - }; - - this.turnAnimationOff = function () { - setTimeout(function(){ - $('.logo-part-animation-on').attr('class', 'logo-part-animation-off'); - }, 600); - }; - - this.triggerSpinLogo = function (ev, data) { - this.trigger(document, events.ui.page.spinLogo); - }; - - this.triggerStopSpinningLogo = function(ev, data) { - this.trigger(document, events.ui.page.stopSpinningLogo); - }; - - this.after('initialize', function () { - this.on(document, events.ui.page.spinLogo, this.turnAnimationOn); - this.on(document, events.ui.page.stopSpinningLogo, this.turnAnimationOff); - - this.on(document, events.ui.tag.select, this.triggerSpinLogo); - this.on(document, events.mails.available, this.triggerStopSpinningLogo); - this.on(document, events.mail.saveDraft, this.triggerSpinLogo); - this.on(document, events.mail.draftSaved, this.triggerStopSpinningLogo); - this.on(document, events.ui.mail.open, this.triggerSpinLogo); - this.on(document, events.dispatchers.rightPane.openDraft, this.triggerSpinLogo); - this.on(document, events.search.perform, this.triggerSpinLogo); - this.on(document, events.mail.want, this.triggerStopSpinningLogo); - }); - } - } -); diff --git a/web-ui/app/js/page/router.js b/web-ui/app/js/page/router.js deleted file mode 100644 index ce0d7d04..00000000 --- a/web-ui/app/js/page/router.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(['flight/lib/component', 'page/events', 'page/router/url_params'], function (defineComponent, events, urlParams) { - 'use strict'; - - return defineComponent(function () { - this.defaultAttrs({ - history: window.history - }); - - function createHash(data) { - var hash = '/#/' + data.tag; - if (!_.isUndefined(data.mailIdent)) { - hash += '/mail/' + data.mailIdent; - } - return hash; - } - - function createState(data, previousState) { - return { - tag: data.tag || (previousState && previousState.tag) || urlParams.defaultTag(), - mailIdent: data.mailIdent, - query: data.query, - isDisplayNoMessageSelected: !!data.isDisplayNoMessageSelected - }; - } - - this.pushState = function (ev, data) { - if (!data.fromPopState) { - var nextState = createState(data, this.attr.history.state); - this.attr.history.pushState(nextState, '', createHash(nextState)); - } - }; - - this.popState = function (ev) { - var state = ev.state || {}; - - this.trigger(document, events.ui.tag.select, { - tag: state.tag || urlParams.getTag(), - mailIdent: state.mailIdent, - fromPopState: true - }); - - if (ev.state.isDisplayNoMessageSelected) { - this.trigger(document, events.dispatchers.rightPane.openNoMessageSelectedWithoutPushState); - } - }; - - this.after('initialize', function () { - this.on(document, events.router.pushState, this.pushState); - this.on(document, events.ui.tag.select, this.pushState); - this.on(document, events.search.perform, this.pushState); - this.on(document, events.search.empty, this.pushState); - window.onpopstate = this.popState.bind(this); - }); - }); -}); diff --git a/web-ui/app/js/page/router/url_params.js b/web-ui/app/js/page/router/url_params.js deleted file mode 100644 index 4fa11c6d..00000000 --- a/web-ui/app/js/page/router/url_params.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define([], function () { - 'use strict'; - - function defaultTag() { - return 'inbox'; - } - - function getDocumentHash() { - return document.location.hash.replace(/\/$/, ''); - } - - function hashTag(hash) { - if (hasMailIdent(hash)) { - return /\/(.+)\/mail\/[-\w]+$/.exec(getDocumentHash())[1]; - } - return hash.substring(2); - } - - - function getTag() { - if (document.location.hash !== '') { - return hashTag(getDocumentHash()); - } - return defaultTag(); - } - - function hasMailIdent() { - return getDocumentHash().match(/mail\/[-\w]+$/); - } - - function getMailIdent() { - return /mail\/([-\w]+)$/.exec(getDocumentHash())[1]; - } - - return { - getTag: getTag, - hasMailIdent: hasMailIdent, - getMailIdent: getMailIdent, - defaultTag: defaultTag - }; -}); diff --git a/web-ui/app/js/page/unread_count_title.js b/web-ui/app/js/page/unread_count_title.js deleted file mode 100644 index 89dcd47d..00000000 --- a/web-ui/app/js/page/unread_count_title.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2015 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - - -define( - [ - 'flight/lib/component', - 'page/events', - ], - - function (defineComponent, events) { - 'use strict'; - - return defineComponent(function () { - this.getTitleText = function () { - return document.title; - }; - - this.updateCount = function (ev, data) { - var unread = data.mails.filter(function (mail) { - return mail.status.indexOf('read') === -1; - }).length; - - var tag = this.toTitleCase(data.tag); - var counter = unread > 0 ? ' (' + unread + ') - ' : ' - '; - document.title = tag + counter + this.rawTitle; - }; - - this.toTitleCase = function (str) { - return str.replace(/\b\w/g, function (txt) { return txt.toUpperCase(); }); - }; - - this.after('initialize', function () { - this.rawTitle = document.title; - this.on(document, events.mails.available, this.updateCount); - }); - - }); -}); diff --git a/web-ui/app/js/page/version.js b/web-ui/app/js/page/version.js deleted file mode 100644 index 9fd5e629..00000000 --- a/web-ui/app/js/page/version.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2015 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(['flight/lib/component', 'views/templates', 'helpers/view_helper'], function (defineComponent, templates, viewHelper) { - 'use strict'; - - return defineComponent(function () { - this.defaultAttrs({ - 'sinceDate': '#version-date' - }); - - this.render = function () { - this.$node.html(templates.page.version()); - this.renderCommitDate(); - }; - - this.renderCommitDate = function(){ - var since = this.select('sinceDate').attr('data-since'), - commitDate = viewHelper.sinceDate(since); - this.select('sinceDate').html(commitDate + ' ago'); - }; - - this.after('initialize', function () { - this.render(); - }); - - }); -}); diff --git a/web-ui/app/js/sandbox.js b/web-ui/app/js/sandbox.js deleted file mode 100644 index 33b16ea4..00000000 --- a/web-ui/app/js/sandbox.js +++ /dev/null @@ -1,11 +0,0 @@ -(function () { - 'use strict'; - - window.onmessage = function (e) { - if (e.data.html) { - document.body.innerHTML = e.data.html; - var mainWindow = e.source; - mainWindow.postMessage('data ok', e.origin); - } - }; -})(); diff --git a/web-ui/app/js/search/results_highlighter.js b/web-ui/app/js/search/results_highlighter.js deleted file mode 100644 index 831be0cd..00000000 --- a/web-ui/app/js/search/results_highlighter.js +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'page/events' - ], function (defineComponent, events) { - - 'use strict'; - - return defineComponent(resultsHighlighter); - - function resultsHighlighter(){ - this.defaultAttrs({ - keywords: [] - }); - - this.getKeywordsSearch = function (event, data) { - this.attr.keywords = data.query.split(' ').map(function(keyword) { - return keyword.toLowerCase(); - }); - }; - - this.highlightResults = function (event, data) { - var domIdent = data.where; - if(this.attr.keywords) { - _.each(this.attr.keywords, function (keyword) { - keyword = escapeRegExp(keyword); - $(domIdent).highlightRegex(new RegExp(keyword, 'i'), { - tagType: 'em', - className: 'search-highlight' - }); - }); - } - }; - - this.clearHighlights = function (event, data) { - this.attr.keywords = []; - _.each($('em.search-highlight'), function(highlighted) { - var jqueryHighlighted = $(highlighted); - var text = jqueryHighlighted.text(); - jqueryHighlighted.replaceWith(text); - }); - }; - - this.highlightString = function (string) { - _.each(this.attr.keywords, function (keyword) { - keyword = escapeRegExp(keyword); - var regex = new RegExp('(' + keyword + ')', 'ig'); - string = string.replace(regex, '$1'); - }); - return string; - }; - - /* - * Alter data.mail.textPlainBody to highlight each of this.attr.keywords - * and pass it back to the mail_view when done - */ - this.highlightMailContent = function(ev, data){ - var mail = data.mail; - mail.textPlainBody = this.highlightString(mail.textPlainBody); - this.trigger(document, events.mail.display, data); - }; - - /* - * Escapes the special charaters used regular expressions that - * would cause problems with strings in the RegExp constructor - */ - function escapeRegExp(string){ - return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - } - - this.after('initialize', function () { - this.on(document, events.search.perform, this.getKeywordsSearch); - this.on(document, events.ui.tag.select, this.clearHighlights); - this.on(document, events.search.resetHighlight, this.clearHighlights); - - this.on(document, events.search.highlightResults, this.highlightResults); - this.on(document, events.mail.highlightMailContent, this.highlightMailContent); - }); - } -}); diff --git a/web-ui/app/js/search/search_trigger.js b/web-ui/app/js/search/search_trigger.js deleted file mode 100644 index 2aff027c..00000000 --- a/web-ui/app/js/search/search_trigger.js +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'views/templates', - 'page/events', - 'views/i18n' - ], function (defineComponent, templates, events, i18n) { - - 'use strict'; - - return defineComponent(searchTrigger); - - function searchTrigger() { - this.defaultAttrs({ - input: 'input[type=search]', - form: 'form', - searchResultsPrefix: 'search-results-for' - }); - - this.render = function() { - this.$node.html(templates.search.trigger()); - }; - - this.search = function(ev, data) { - this.trigger(document, events.search.resetHighlight); - ev.preventDefault(); - var input = this.select('input'); - var value = input.val(); - input.blur(); - if(!_.isEmpty(value)){ - this.trigger(document, events.search.perform, { query: value }); - } else { - this.trigger(document, events.search.empty); - } - }; - - this.clearInput = function() { - this.select('input').val(''); - }; - - this.showOnlySearchTerms = function(event){ - var value = this.select('input').val(); - var searchTerms = value.slice((i18n.t(this.attr.searchResultsPrefix) + ': ').length); - this.select('input').val(searchTerms); - }; - - this.showSearchTermsAndPlaceHolder = function(event){ - var value = this.select('input').val(); - if (value.length > 0){ - this.select('input').val(i18n.t(this.attr.searchResultsPrefix) + ': ' + value); - } - }; - - this.after('initialize', function () { - this.render(); - this.on(this.select('form'), 'submit', this.search); - this.on(this.select('input'), 'focus', this.showOnlySearchTerms); - this.on(this.select('input'), 'blur', this.showSearchTermsAndPlaceHolder); - this.on(document, events.ui.tag.selected, this.clearInput); - this.on(document, events.ui.tag.select, this.clearInput); - }); - } - } -); diff --git a/web-ui/app/js/services/delete_service.js b/web-ui/app/js/services/delete_service.js deleted file mode 100644 index 0dfc1bdb..00000000 --- a/web-ui/app/js/services/delete_service.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['flight/lib/component', 'page/events', 'views/i18n'], function (defineComponent, events, i18n) { - 'use strict'; - - return defineComponent(function() { - - this.successDeleteMessageFor = function(mail) { - return mail.isInTrash() ? - i18n.t('delete-single') : - i18n.t('trash-single'); - }; - - this.successDeleteManyMessageFor = function(mail) { - return mail.isInTrash() ? - i18n.t('delete-bulk') : - i18n.t('trash-bulk'); - }; - - this.deleteEmail = function (event, data) { - this.trigger(document, events.mail.delete, { - mail: data.mail, - successMessage: this.successDeleteMessageFor(data.mail) - }); - }; - - this.deleteManyEmails = function (event, data) { - var emails = _.values(data.checkedMails), - firstEmail = emails[_.first(_.keys(emails))]; - - this.trigger(document, events.mail.deleteMany, { - mails: emails, - successMessage: this.successDeleteManyMessageFor(firstEmail) - }); - - }; - - this.after('initialize', function () { - this.on(document, events.ui.mail.delete, this.deleteEmail); - this.on(document, events.ui.mail.deleteMany, this.deleteManyEmails); - }); - - }); -}); diff --git a/web-ui/app/js/services/mail_service.js b/web-ui/app/js/services/mail_service.js deleted file mode 100644 index 5e4bd4f3..00000000 --- a/web-ui/app/js/services/mail_service.js +++ /dev/null @@ -1,335 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'views/i18n', - 'services/model/mail', - 'helpers/monitored_ajax', - 'page/events', - 'features', - 'mixins/with_auto_refresh', - 'page/router/url_params' - ], function (defineComponent, i18n, Mail, monitoredAjax, events, features, withAutoRefresh, urlParams) { - - 'use strict'; - - return defineComponent(mailService, withAutoRefresh('refreshMails')); - - function mailService() { - var that; - - this.defaultAttrs({ - mailsResource: '/mails', - singleMailResource: '/mail', - currentTag: '', - lastQuery: '', - currentPage: 1, - numPages: 1, - pageSize: 25 - }); - - this.errorMessage = function (msg) { - return function () { - that.trigger(document, events.ui.userAlerts.displayMessage, { message: msg }); - }; - }; - - this.updateTags = function (ev, data) { - var ident = data.ident; - - var success = function (data) { - this.refreshMails(); - $(document).trigger(events.mail.tags.updated, { ident: ident, tags: data.tags }); - $(document).trigger(events.dispatchers.tags.refreshTagList, { skipMailListRefresh: true }); - }; - - var failure = function (resp) { - var msg = i18n.t('failed-change-tags'); - if (resp.status === 403) { - msg = i18n.t('invalid-tag-name'); - } - this.trigger(document, events.ui.userAlerts.displayMessage, { message: msg }); - }; - - monitoredAjax(this, '/mail/' + ident + '/tags', { - type: 'POST', - contentType: 'application/json; charset=utf-8', - data: JSON.stringify({newtags: data.tags}) - }).done(success.bind(this)).fail(failure.bind(this)); - - }; - - this.readMail = function (ev, data) { - var mailIdents; - if (data.checkedMails) { - mailIdents = _.map(data.checkedMails, function (mail) { - return mail.ident; - }); - } else { - mailIdents = [data.ident]; - } - monitoredAjax(this, '/mails/read', { - type: 'POST', - data: JSON.stringify({idents: mailIdents}) - }).done(this.triggerMailsRead(data.checkedMails)); - }; - - this.unreadMail = function (ev, data) { - var mailIdents; - if (data.checkedMails) { - mailIdents = _.map(data.checkedMails, function (mail) { - return mail.ident; - }); - } else { - mailIdents = [data.ident]; - } - monitoredAjax(this, '/mails/unread', { - type: 'POST', - data: JSON.stringify({idents: mailIdents}) - }).done(this.triggerMailsRead(data.checkedMails)); - }; - - this.triggerMailsRead = function (mails) { - return _.bind(function () { - this.refreshMails(); - this.trigger(document, events.ui.mails.uncheckAll); - }, this); - }; - - this.triggerDeleted = function (dataToDelete) { - return _.bind(function () { - var mails = dataToDelete.mails || [dataToDelete.mail]; - - this.refreshMails(); - this.trigger(document, events.ui.userAlerts.displayMessage, { message: dataToDelete.successMessage}); - this.trigger(document, events.ui.mails.uncheckAll); - this.trigger(document, events.mail.deleted, { mails: mails }); - }, this); - }; - - this.triggerRecovered = function (dataToRecover) { - return _.bind(function () { - var mails = dataToRecover.mails || [dataToRecover.mail]; - - this.refreshMails(); - this.trigger(document, events.ui.userAlerts.displayMessage, { message: i18n.t(dataToRecover.successMessage)}); - this.trigger(document, events.ui.mails.uncheckAll); - }, this); - }; - - this.triggerArchived = function (dataToArchive) { - return _.bind(function (response) { - this.refreshMails(); - this.trigger(document, events.ui.userAlerts.displayMessage, { message: i18n.t(response.successMessage)}); - this.trigger(document, events.ui.mails.uncheckAll); - }, this); - }; - - this.archiveManyMails = function(event, dataToArchive) { - var mailIdents = _.map(dataToArchive.checkedMails, function (mail) { - return mail.ident; - }); - monitoredAjax(this, '/mails/archive', { - type: 'POST', - dataType: 'json', - contentType: 'application/json; charset=utf-8', - data: JSON.stringify({idents: mailIdents}) - }).done(this.triggerArchived(dataToArchive)) - .fail(this.errorMessage(i18n.t('failed-archive'))); - }; - - this.deleteMail = function (ev, data) { - monitoredAjax(this, '/mail/' + data.mail.ident, - {type: 'DELETE'}) - .done(this.triggerDeleted(data)) - .fail(this.errorMessage(i18n.t('failed-delete-single'))); - }; - - this.deleteManyMails = function (ev, data) { - var dataToDelete = data; - var mailIdents = _.map(data.mails, function (mail) { - return mail.ident; - }); - - monitoredAjax(this, '/mails/delete', { - type: 'POST', - dataType: 'json', - contentType: 'application/json; charset=utf-8', - data: JSON.stringify({idents: mailIdents}) - }).done(this.triggerDeleted(dataToDelete)) - .fail(this.errorMessage(i18n.t('failed-delete-bulk'))); - }; - - this.recoverManyMails = function (ev, data) { - var dataToRecover = data; - var mailIdents = _.map(data.mails, function (mail) { - return mail.ident; - }); - - monitoredAjax(this, '/mails/recover', { - type: 'POST', - dataType: 'json', - contentType: 'application/json; charset=utf-8', - data: JSON.stringify({idents: mailIdents}) - }).done(this.triggerRecovered(dataToRecover)) - .fail(this.errorMessage(i18n.t('Could not move emails to inbox'))); - }; - - function compileQuery(data) { - var query = 'tag:"' + that.attr.currentTag + '"'; - - if (data.tag === 'all') { - query = 'in:all'; - } - return query; - } - - this.fetchByTag = function (ev, data) { - this.attr.currentTag = data.tag; - this.attr.lastQuery = compileQuery(data); - this.updateCurrentPageNumber(1); - - this.refreshMails(); - }; - - this.newSearch = function (ev, data) { - this.attr.lastQuery = data.query; - this.attr.currentTag = 'all'; - this.refreshMails(); - }; - - this.mailFromJSON = function (mail) { - return Mail.create(mail); - }; - - this.parseMails = function (data) { - data.mails = _.map(data.mails, this.mailFromJSON, this); - - return data; - }; - - function escaped(s) { - return encodeURIComponent(s); - } - - this.excludeTrashedEmailsForDraftsAndSent = function (query) { - if (query === 'tag:"drafts"' || query === 'tag:"sent"') { - return query + ' -in:"trash"'; - } - return query; - }; - - this.refreshMails = function () { - var url = this.attr.mailsResource + '?q=' + escaped(this.attr.lastQuery) + '&p=' + this.attr.currentPage + '&w=' + this.attr.pageSize; - - this.attr.lastQuery = this.excludeTrashedEmailsForDraftsAndSent(this.attr.lastQuery); - - monitoredAjax(this, url, { dataType: 'json' }) - .done(function (data) { - this.attr.numPages = Math.ceil(data.stats.total / this.attr.pageSize); - this.trigger(document, events.mails.available, _.merge({tag: this.attr.currentTag, forSearch: this.attr.lastQuery }, this.parseMails(data))); - }.bind(this)) - .fail(function () { - this.trigger(document, events.ui.userAlerts.displayMessage, { message: i18n.t('failed-fetch-messages'), class: 'error' }); - }.bind(this)); - }; - - function createSingleMailUrl(mailsResource, ident) { - return mailsResource + '/' + ident; - } - - this.fetchSingle = function (event, data) { - var fetchUrl = createSingleMailUrl(this.attr.singleMailResource, data.mail); - - monitoredAjax(this, fetchUrl, { dataType: 'json' }) - .done(function (mail) { - if (_.isNull(mail)) { - this.trigger(data.caller, events.mail.notFound); - return; - } - - this.trigger(data.caller, events.mail.here, { mail: this.mailFromJSON(mail) }); - }.bind(this)); - }; - - this.previousPage = function () { - if (this.attr.currentPage > 1) { - this.updateCurrentPageNumber(this.attr.currentPage - 1); - this.refreshMails(); - } - }; - - this.nextPage = function () { - if (this.attr.currentPage < (this.attr.numPages)) { - this.updateCurrentPageNumber(this.attr.currentPage + 1); - this.refreshMails(); - } - }; - - this.updateCurrentPageNumber = function (newCurrentPage) { - this.attr.currentPage = newCurrentPage; - this.trigger(document, events.ui.page.changed, { - currentPage: this.attr.currentPage, - numPages: this.attr.numPages - }); - }; - - this.wantDraftReplyForMail = function (ev, data) { - if (!features.isEnabled('draftReply')) { - this.trigger(document, events.mail.draftReply.notFound); - return; - } - - monitoredAjax(this, '/draft_reply_for/' + data.ident, { dataType: 'json' }) - .done(function (mail) { - if (_.isNull(mail)) { - this.trigger(document, events.mail.draftReply.notFound); - return; - } - this.trigger(document, events.mail.draftReply.here, { mail: this.mailFromJSON(mail) }); - }.bind(this)); - }; - - this.after('initialize', function () { - that = this; - - if (features.isEnabled('tags')) { - this.on(events.mail.tags.update, this.updateTags); - } - - this.on(document, events.mail.draftReply.want, this.wantDraftReplyForMail); - this.on(document, events.mail.want, this.fetchSingle); - this.on(document, events.mail.read, this.readMail); - this.on(document, events.mail.unread, this.unreadMail); - this.on(document, events.mail.delete, this.deleteMail); - this.on(document, events.mail.deleteMany, this.deleteManyMails); - this.on(document, events.mail.recoverMany, this.recoverManyMails); - this.on(document, events.mail.archiveMany, this.archiveManyMails); - this.on(document, events.search.perform, this.newSearch); - this.on(document, events.ui.tag.selected, this.fetchByTag); - this.on(document, events.ui.tag.select, this.fetchByTag); - this.on(document, events.ui.mails.refresh, this.refreshMails); - this.on(document, events.ui.page.previous, this.previousPage); - this.on(document, events.ui.page.next, this.nextPage); - - this.fetchByTag(null, {tag: urlParams.getTag()}); - }); - } - } -); diff --git a/web-ui/app/js/services/model/mail.js b/web-ui/app/js/services/model/mail.js deleted file mode 100644 index 64a10c1c..00000000 --- a/web-ui/app/js/services/model/mail.js +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(['helpers/contenttype'], function (contentType) { - 'use strict'; - function isSentMail() { - return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'SENT'; - } - - function isDraftMail() { - return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'DRAFTS'; - } - - function isInTrash() { - return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'TRASH'; - } - - function setDraftReplyFor(ident) { - this.draft_reply_for = ident; - } - - function replyToAddress() { - return { - to: [this.replying.single], - cc: [] - }; - } - - function replyToAllAddress() { - return { - to: this.replying.all['to-field'], - cc: this.replying.all['cc-field'] - }; - } - - function getHeadersFromMailPart (rawBody) { - var lines, headerLines, endOfHeaders, headers; - - lines = rawBody.split('\n'); - endOfHeaders = _.indexOf(lines, ''); - headerLines = lines.slice(0, endOfHeaders); - - headers = _.map(headerLines, function (headerLine) { - return _.map(headerLine.split(':'), function(elem){return elem.trim();}); - }); - - return _.object(headers); - } - - function getBodyFromMailPart (rawBody) { - var lines, endOfHeaders; - - lines = rawBody.split('\n'); - endOfHeaders = _.indexOf(lines, ''); - - return lines.slice(endOfHeaders + 1).join('\n'); - } - - function parseWithHeaders(rawBody) { - return {headers: getHeadersFromMailPart(rawBody), body: getBodyFromMailPart(rawBody)}; - } - - function getMailMultiParts () { - var mediaType = this.getMailMediaType(); - var boundary = '--' + mediaType.params.boundary + '\n'; - var finalBoundary = '--' + mediaType.params.boundary + '--'; - - var bodyParts = this.body.split(finalBoundary)[0].split(boundary); - - bodyParts = _.reject(bodyParts, function(bodyPart) { return _.isEmpty(bodyPart.trim()); }); - - return _.map(bodyParts, parseWithHeaders); - } - - function getMailMediaType () { - return new contentType.MediaType(this.header.content_type); - } - - function isMailMultipartAlternative () { - return this.getMailMediaType().type === 'multipart/alternative'; - } - - function availableBodyPartsContentType () { - var bodyParts = this.getMailMultiParts(); - - return _.pluck(_.pluck(bodyParts, 'headers'), 'Content-Type'); - } - - function getMailPartByContentType (contentType) { - var bodyParts = this.getMailMultiParts(); - - return _.findWhere(bodyParts, {headers: {'Content-Type': contentType}}); - } - - return { - create: function (mail) { - if (!mail) { return; } - - mail.isSentMail = isSentMail; - mail.isDraftMail = isDraftMail; - mail.isInTrash = isInTrash; - mail.setDraftReplyFor = setDraftReplyFor; - mail.replyToAddress = replyToAddress; - mail.replyToAllAddress = replyToAllAddress; - mail.getMailMediaType = getMailMediaType; - mail.isMailMultipartAlternative = isMailMultipartAlternative; - mail.getMailMultiParts = getMailMultiParts; - mail.availableBodyPartsContentType = availableBodyPartsContentType; - mail.getMailPartByContentType = getMailPartByContentType; - return mail; - } - }; -}); diff --git a/web-ui/app/js/services/recover_service.js b/web-ui/app/js/services/recover_service.js deleted file mode 100644 index d7d9cdc9..00000000 --- a/web-ui/app/js/services/recover_service.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['flight/lib/component', 'page/events', 'views/i18n'], function (defineComponent, events, i18n) { - 'use strict'; - - return defineComponent(function() { - - this.recoverManyEmails = function (event, data) { - var emails = _.values(data.checkedMails); - - this.trigger(document, events.mail.recoverMany, { - mails: emails, - successMessage: i18n.t('Your messages were moved to inbox!') - }); - - }; - - this.after('initialize', function () { - this.on(document, events.ui.mail.recoverMany, this.recoverManyEmails); - }); - - }); -}); diff --git a/web-ui/app/js/style_guide/main.js b/web-ui/app/js/style_guide/main.js deleted file mode 100644 index 32c213cf..00000000 --- a/web-ui/app/js/style_guide/main.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -$(document).ready(function(){ - 'use strict'; - $('a[href*=#]').click(function() { - if (location.pathname.replace(/^\//,'') === this.pathname.replace(/^\//,'') && - location.hostname === this.hostname) { - var $target = $(this.hash); - $target = $target.length && $target || - $('[name=' + this.hash.slice(1) +']'); - if ($target.length) { - var targetOffset = $target.offset().top; - $('html,body') - .animate({scrollTop: targetOffset}, 500); - return false; - } - } - }); -}); diff --git a/web-ui/app/js/tags/data/tags.js b/web-ui/app/js/tags/data/tags.js deleted file mode 100644 index 31703b2a..00000000 --- a/web-ui/app/js/tags/data/tags.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(['flight/lib/component', 'page/events', 'helpers/monitored_ajax', 'mixins/with_feature_toggle', 'mixins/with_auto_refresh'], function (defineComponent, events, monitoredAjax, withFeatureToggle, withAutoRefresh) { - 'use strict'; - - var DataTags = defineComponent(dataTags, withFeatureToggle('tags', function() { - $(document).trigger(events.ui.mails.refresh); - }), withAutoRefresh('refreshTags')); - - DataTags.all = { - name: 'all', - ident: '8752888923742657436', - query: 'in:all', - default: true, - counts:{ - total:0, - read:0, - starred:0, - replied:0 - } - }; - - function dataTags() { - function sendTagsBackTo(on) { - return function(data) { - data.push(DataTags.all); - on.trigger(document, events.tags.received, {tags: data}); - }; - } - - this.defaultAttrs({ - tagsResource: '/tags' - }); - - this.fetchTags = function(event, params) { - monitoredAjax(this, this.attr.tagsResource) - .done(sendTagsBackTo(this)); - }; - - this.refreshTags = function() { - var notTriggeredByEvent = null; - this.fetchTags(notTriggeredByEvent); - }; - - this.after('initialize', function () { - this.on(document, events.tags.want, this.fetchTags); - this.on(document, events.mail.sent, this.fetchTags); - }); - } - - return DataTags; -}); diff --git a/web-ui/app/js/tags/ui/tag.js b/web-ui/app/js/tags/ui/tag.js deleted file mode 100644 index 37814cfc..00000000 --- a/web-ui/app/js/tags/ui/tag.js +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define( - [ - 'flight/lib/component', - 'views/templates', - 'page/events', - 'views/i18n' - ], - - function (defineComponent, templates, events, i18n) { - 'use strict'; - - var Tag = defineComponent(tag); - - Tag.appendedTo = function (parent, data) { - var res = new this(); - res.renderAndAttach(parent, data); - return res; - }; - - return Tag; - - function tag() { - - var ALWAYS_HIDE_BADGE_FOR = ['sent', 'trash', 'all']; - var TOTAL_BADGE = ['drafts']; - - this.displayBadge = function(tag) { - if(_.include(ALWAYS_HIDE_BADGE_FOR, tag.name)) { return false; } - if(this.badgeType(tag) === 'total') { - return tag.counts.total > 0; - } else { - return (tag.counts.total - tag.counts.read) > 0; - } - }; - - this.badgeType = function(tag) { - return _.include(TOTAL_BADGE, tag.name) ? 'total' : 'unread'; - }; - - this.doUnselect = function () { - this.$node.removeClass('selected'); - }; - - this.doSelect = function () { - this.$node.addClass('selected'); - }; - - this.selectTag = function (ev, data) { - this.attr.currentTag = data.tag; - if (data.tag === this.attr.tag.name) { - this.doSelect(); - } - else { - this.doUnselect(); - } - }; - - this.selectTagAll = function () { - this.selectTag(null, {tag: 'all'}); - }; - - this.viewFor = function (tag, template, currentTag) { - return template({ - tagName: tag.default ? i18n.t('tags.' + tag.name) : tag.name, - ident: this.hashIdent(tag.ident), - count: this.badgeType(tag) === 'total' ? tag.counts.total : (tag.counts.total - tag.counts.read), - displayBadge: this.displayBadge(tag), - badgeType: this.badgeType(tag), - icon: tag.icon, - selected: tag.name === currentTag ? 'selected' : '' - }); - }; - - this.decreaseReadCountIfMatchingTag = function (ev, data) { - var mailbox_and_tags = _.flatten([data.tags, data.mailbox]); - if (_.contains(mailbox_and_tags, this.attr.tag.name)) { - this.attr.tag.counts.read++; - this.$node.html(this.viewFor(this.attr.tag, templates.tags.tagInner, this.attr.currentTag)); - if (!_.isUndefined(this.attr.shortcut)) { - this.attr.shortcut.reRender(); - } - } - }; - - this.triggerSelect = function () { - this.trigger(document, events.ui.tag.select, { tag: this.attr.tag.name }); - - this.removeSearchingClass(); - }; - - this.addSearchingClass = function() { - if (this.attr.tag.name === 'all'){ - this.$node.addClass('searching'); - } - }; - - this.hashIdent = function(ident) { - if (typeof ident === 'undefined') { - return ''; - } - if (typeof ident === 'number') { - return ident; - } - if (ident.match(/^[a-zA-Z0-9]+$/)) { - return ident; - } - - /*jslint bitwise: true */ - return Math.abs(String(ident).split('').reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a;},0)); - }; - - this.removeSearchingClass = function() { - if (this.attr.tag.name === 'all'){ - this.$node.removeClass('searching'); - } - }; - - this.after('initialize', function () { - this.on('click', this.triggerSelect); - this.on(document, events.mail.read, this.decreaseReadCountIfMatchingTag); - this.on(document, events.search.perform, this.addSearchingClass); - this.on(document, events.search.empty, this.removeSearchingClass); - - this.on(document, events.ui.tag.select, this.selectTag); - this.on(document, events.search.perform, this.selectTagAll); - this.on(document, events.search.empty, this.selectTagAll); - }); - - this.renderAndAttach = function (parent, data) { - var rendered = this.viewFor(data.tag, templates.tags.tag, data.currentTag); - parent.append(rendered); - this.initialize('#tag-' + this.hashIdent(data.tag.ident), data); - this.on(parent, events.tags.teardown, this.teardown); - }; - } - } -); diff --git a/web-ui/app/js/tags/ui/tag_base.js b/web-ui/app/js/tags/ui/tag_base.js deleted file mode 100644 index 9dc1ccbb..00000000 --- a/web-ui/app/js/tags/ui/tag_base.js +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(['views/i18n', 'page/events'], function(i18n, events) { - 'use strict'; - - function tagBase() { - var ALWAYS_HIDE_BADGE_FOR = ['sent', 'trash', 'all']; - var TOTAL_BADGE = ['drafts']; - - this.displayBadge = function(tag) { - if(_.include(ALWAYS_HIDE_BADGE_FOR, tag.name)) { return false; } - if(this.badgeType(tag) === 'total') { - return tag.counts.total > 0; - } else { - return (tag.counts.total - tag.counts.read) > 0; - } - }; - - this.badgeType = function(tag) { - return _.include(TOTAL_BADGE, tag.name) ? 'total' : 'unread'; - }; - - this.doUnselect = function () { - this.$node.removeClass('selected'); - }; - - this.doSelect = function () { - this.$node.addClass('selected'); - }; - - this.selectTag = function (ev, data) { - this.attr.currentTag = data.tag; - if (data.tag === this.attr.tag.name) { - this.doSelect(); - } - else { - this.doUnselect(); - } - }; - - this.selectTagAll = function () { - this.selectTag(null, {tag: 'all'}); - }; - - this.after('initialize', function () { - this.on(document, events.ui.tag.select, this.selectTag); - this.on(document, events.search.perform, this.selectTagAll); - this.on(document, events.search.empty, this.selectTagAll); - }); - } - - return tagBase; - -}); diff --git a/web-ui/app/js/tags/ui/tag_list.js b/web-ui/app/js/tags/ui/tag_list.js deleted file mode 100644 index a2172c6d..00000000 --- a/web-ui/app/js/tags/ui/tag_list.js +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'tags/ui/tag', - 'views/templates', - 'page/events', - 'page/router/url_params' - ], - - function(defineComponent, Tag, templates, events, urlParams) { - 'use strict'; - - var ICON_FOR = { - 'inbox': 'inbox', - 'sent': 'send', - 'drafts': 'pencil', - 'trash': 'trash-o', - 'all': 'archive' - }; - - var ORDER = { - 'inbox': '0', - 'sent': '1', - 'drafts': '2', - 'trash': '3', - 'all': '4' - }; - - return defineComponent(tagList); - - function tagOrder(nm) { - return ORDER[nm.name] || '999' + nm.name; - } - - function tagList() { - this.defaultAttrs({ - defaultTagList: '#default-tag-list', - customTagList: '#custom-tag-list' - }); - - function renderTag(tag, defaultList, customList) { - var list = tag.default ? defaultList : customList; - - var tagComponent = Tag.appendedTo(list, {tag: tag, currentTag: this.getCurrentTag()}); - } - - function resetTagList(lists) { - _.each(lists, function (list) { - this.trigger(list, events.tags.teardown); - list.empty(); - }.bind(this)); - - } - - this.renderTagList = function(tags) { - var defaultList = this.select('defaultTagList'); - var customList = this.select('customTagList'); - - resetTagList.call(this, [defaultList, customList]); - - tags.forEach(function (tag) { - renderTag.call(this, tag, defaultList, customList); - }.bind(this)); - }; - - this.displayTags = function(ev, data) { - this.renderTagList(_.sortBy(data.tags, tagOrder)); - }; - - this.getCurrentTag = function () { - return this.attr.currentTag || urlParams.getTag(); - }; - - this.updateCurrentTag = function(ev, data) { - this.attr.currentTag = data.tag; - }; - - this.renderTagListTemplate = function () { - this.$node.html(templates.tags.tagList()); - }; - - this.after('initialize', function() { - this.on(document, events.tags.received, this.displayTags); - this.on(document, events.ui.tag.select, this.updateCurrentTag); - this.renderTagListTemplate(); - }); - } - } -); diff --git a/web-ui/app/js/user_alerts/ui/user_alerts.js b/web-ui/app/js/user_alerts/ui/user_alerts.js deleted file mode 100644 index e944a7a5..00000000 --- a/web-ui/app/js/user_alerts/ui/user_alerts.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'mixins/with_hide_and_show', - 'page/events' - ], - - function(defineComponent, templates, withHideAndShow, events) { - 'use strict'; - - return defineComponent(userAlerts, withHideAndShow); - - function userAlerts() { - this.defaultAttrs({ - dismissTimeout: 3000 - }); - - this.render = function(message) { - this.$node.html(templates.userAlerts.message(message)); - this.show(); - setTimeout(this.hide.bind(this), this.attr.dismissTimeout); - }; - - - this.displayMessage = function(ev, data) { - this.render({ - message: { - content: data.message, - class: 'message-panel__growl--' + (data.class || 'success') - } - }); - }; - - this.after('initialize', function() { - this.on(document, events.ui.userAlerts.displayMessage, this.displayMessage); - }); - } - } -); - diff --git a/web-ui/app/js/user_settings/data/user_settings.js b/web-ui/app/js/user_settings/data/user_settings.js deleted file mode 100644 index dac29cec..00000000 --- a/web-ui/app/js/user_settings/data/user_settings.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2015 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'helpers/monitored_ajax', - 'page/events' - ], - function (defineComponent, monitoredAjax, events) { - 'use strict'; - - return defineComponent(function() { - this.defaultAttrs({ - userSettingsResource: '/user-settings', - userSettings: {} - }); - - this.sendInfo = function() { - this.trigger(document, events.userSettings.here, this.attr.userSettings); - }; - - this.getUserSettings = function() { - var getUserSettingsSuccess = function (userSettings) { - this.attr.userSettings = userSettings; - }; - - monitoredAjax(this, this.attr.userSettingsResource, { - type: 'GET', - contentType: 'application/json; charset=utf-8' - }).done(getUserSettingsSuccess.bind(this)); - }; - - this.after('initialize', function() { - this.getUserSettings(); - this.on(document, events.userSettings.getInfo, this.sendInfo); - }); - }); -}); diff --git a/web-ui/app/js/user_settings/ui/user_settings_box.js b/web-ui/app/js/user_settings/ui/user_settings_box.js deleted file mode 100644 index d3de23ed..00000000 --- a/web-ui/app/js/user_settings/ui/user_settings_box.js +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'features', - 'views/templates', - 'page/events', - 'helpers/monitored_ajax' - ], function (defineComponent, features, templates, events, monitoredAjax) { - - 'use strict'; - - return defineComponent(function () { - this.defaultAttrs({ - close: '#user-settings-close', - userSettingsBoxContainer: '#user-settings-box' - }); - - this.render = function (event, userSettings) { - if (features.isLogoutEnabled()) { - this.$node.addClass('extra-bottom-space'); - } - - this.$node.addClass('arrow-box'); - this.$node.html(templates.page.userSettingsBox(userSettings)); - - this.on(this.attr.close, 'click', function() { - this.trigger(document, events.userSettings.destroyPopup); - }); - - this.on(document, 'click', function(e) { - var userSettingsBoxContainer = $(this.attr.userSettingsBoxContainer).get(0); - var target = e.target || e.srcElement; - - if (target !== userSettingsBoxContainer && !isChildOf(target, userSettingsBoxContainer)) { - this.destroy(); - } - }); - - function isChildOf(child, parent) { - if (child.parentNode === parent) { - return true; - } else if (child.parentNode === null) { - return false; - } else { - return isChildOf(child.parentNode, parent); - } - } - }; - - this.destroy = function () { - this.$node.remove(); - this.teardown(); - }; - - this.after('initialize', function () { - this.on(document, events.userSettings.here, this.render); - this.on(document, events.userSettings.destroyPopup, this.destroy); - this.trigger(document, events.userSettings.getInfo); - }); - }); -}); diff --git a/web-ui/app/js/user_settings/ui/user_settings_icon.js b/web-ui/app/js/user_settings/ui/user_settings_icon.js deleted file mode 100644 index a6385dc1..00000000 --- a/web-ui/app/js/user_settings/ui/user_settings_icon.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define( - [ - 'flight/lib/component', - 'views/templates', - 'page/events', - 'user_settings/ui/user_settings_box' - ], function (defineComponent, templates, events, userSettingsBox) { - 'use strict'; - - return defineComponent(function () { - this.defaultAttrs({ - userSettingsBox: $('#user-settings-box') - }); - - this.render = function () { - this.$node.html(templates.page.userSettingsIcon()); - }; - - this.toggleUserSettingsBox = function() { - if(this.attr.userSettingsBox.children().length === 0) { - var div = $('
    '); - $(this.attr.userSettingsBox).append(div); - userSettingsBox.attachTo(div); - this.attr.userSettingsInfo = userSettingsBox; - } else { - this.trigger(document, events.userSettings.destroyPopup); - } - }; - - this.triggerToggleUserSettingsBox = function(e) { - this.trigger(document, events.ui.userSettingsBox.toggle); - e.stopPropagation(); - }; - - this.after('initialize', function () { - this.render(); - this.on('click', this.triggerToggleUserSettingsBox); - this.on(document, events.ui.userSettingsBox.toggle, this.toggleUserSettingsBox); - }); - }); -}); diff --git a/web-ui/app/js/views/i18n.js b/web-ui/app/js/views/i18n.js deleted file mode 100644 index 29a1beca..00000000 --- a/web-ui/app/js/views/i18n.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ -define(['i18next', - 'i18nextXHRBackend', - 'i18nextBrowserLanguageDetector'], -function(i18n, i18n_backend, I18n_detector) { - 'use strict'; - - var detector = new I18n_detector(); - var detect = detector.detect.bind(detector); - - detector.detect = function(detectionOrder) { - var result = detect(detectionOrder); - return result.replace('-', '_'); - }; - - function t(i18n_key, options) { - var result = i18n.t(i18n_key, options); - var safe_string = new Handlebars.SafeString(result); - return safe_string.string; - } - - function loaded(callback) { - i18n.on('loaded', function(loaded) { - callback(); - }); - } - - function init(path) { - i18n - .use(i18n_backend) - .use(detector) - .init({ - fallbackLng: 'en_US', - backend: { - loadPath: path + 'locales/{{lng}}/{{ns}}.json' - } - }); - // Handlebars.registerHelper('t', self.bind(self)); - Handlebars.registerHelper('t', t); - } - - return { - t: t, - init: init, - loaded: loaded - }; -}); diff --git a/web-ui/app/js/views/recipientListFormatter.js b/web-ui/app/js/views/recipientListFormatter.js deleted file mode 100644 index 0b887142..00000000 --- a/web-ui/app/js/views/recipientListFormatter.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(function() { - 'use strict'; - Handlebars.registerHelper('formatRecipients', function (header) { - function wrapWith(begin, end) { - return function (x) { - return begin + Handlebars.Utils.escapeExpression(x) + end; - }; - } - - var to = _.map(header.to, wrapWith('', '')); - var cc = _.map(header.cc, wrapWith('cc: ', '')); - var bcc = _.map(header.bcc, wrapWith('bcc: ', '')); - - return new Handlebars.SafeString(to.concat(cc, bcc).join(', ')); - }); -}); diff --git a/web-ui/app/js/views/templates.js b/web-ui/app/js/views/templates.js deleted file mode 100644 index d4185471..00000000 --- a/web-ui/app/js/views/templates.js +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2014 ThoughtWorks, Inc. - * - * Pixelated is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Pixelated is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Pixelated. If not, see . - */ - -define(['hbs/templates'], function (templates) { - 'use strict'; - - var Templates = { - compose: { - box: window.Pixelated['app/templates/compose/compose_box.hbs'], - inlineBox: window.Pixelated['app/templates/compose/inline_box.hbs'], - replySection: window.Pixelated['app/templates/compose/reply_section.hbs'], - recipientInput: window.Pixelated['app/templates/compose/recipient_input.hbs'], - fixedRecipient: window.Pixelated['app/templates/compose/fixed_recipient.hbs'], - recipients: window.Pixelated['app/templates/compose/recipients.hbs'], - feedback: window.Pixelated['app/templates/compose/feedback_box.hbs'], - attachmentsList: window.Pixelated['app/templates/compose/attachments_list.hbs'], - attachmentItem: window.Pixelated['app/templates/compose/attachment_item.hbs'], - attachmentUploadItem: window.Pixelated['app/templates/compose/attachment_upload_item.hbs'], - uploadAttachmentFailed: window.Pixelated['app/templates/compose/upload_attachment_failed.hbs'] - }, - tags: { - tagList: window.Pixelated['app/templates/tags/tag_list.hbs'], - tag: window.Pixelated['app/templates/tags/tag.hbs'], - tagInner: window.Pixelated['app/templates/tags/tag_inner.hbs'], - shortcut: window.Pixelated['app/templates/tags/shortcut.hbs'] - }, - userAlerts: { - message: window.Pixelated['app/templates/user_alerts/message.hbs'] - }, - mails: { - single: window.Pixelated['app/templates/mails/single.hbs'], - fullView: window.Pixelated['app/templates/mails/full_view.hbs'], - mailActions: window.Pixelated['app/templates/mails/mail_actions.hbs'], - draft: window.Pixelated['app/templates/mails/draft.hbs'], - sent: window.Pixelated['app/templates/mails/sent.hbs'], - trash: window.Pixelated['app/templates/mails/trash.hbs'] - }, - mailActions: { - actionsBox: window.Pixelated['app/templates/mail_actions/actions_box.hbs'], - trashActionsBox: window.Pixelated['app/templates/mail_actions/trash_actions_box.hbs'], - composeTrigger: window.Pixelated['app/templates/mail_actions/compose_trigger.hbs'], - refreshTrigger: window.Pixelated['app/templates/mail_actions/refresh_trigger.hbs'], - paginationTrigger: window.Pixelated['app/templates/mail_actions/pagination_trigger.hbs'] - }, - noMessageSelected: window.Pixelated['app/templates/compose/no_message_selected.hbs'], - noMailsAvailable: window.Pixelated['app/templates/compose/no_mails_available.hbs'], - search: { - trigger: window.Pixelated['app/templates/search/search_trigger.hbs'] - }, - page: { - userSettingsIcon: window.Pixelated['app/templates/page/user_settings_icon.hbs'], - userSettingsBox: window.Pixelated['app/templates/page/user_settings_box.hbs'], - logout: window.Pixelated['app/templates/page/logout.hbs'], - logoutShortcut: window.Pixelated['app/templates/page/logout_shortcut.hbs'], - version: window.Pixelated['app/templates/page/version.hbs'] - }, - feedback: { - feedback: window.Pixelated['app/templates/feedback/feedback_trigger.hbs'] - } - }; - - Handlebars.registerPartial('tag_inner', Templates.tags.tagInner); - Handlebars.registerPartial('recipients', Templates.compose.recipients); - Handlebars.registerPartial('attachments_list', Templates.compose.attachmentsList); - Handlebars.registerPartial('attachments_upload', Templates.compose.attachmentsList); - Handlebars.registerPartial('attachment_item', Templates.compose.attachmentItem); - Handlebars.registerPartial('attachment_upload_item', Templates.compose.attachmentUploadItem); - Handlebars.registerPartial('uploadAttachmentFailed', Templates.compose.uploadAttachmentFailed); - - return Templates; -}); diff --git a/web-ui/app/locales/en_US/translation.json b/web-ui/app/locales/en_US/translation.json deleted file mode 100644 index 3e006156..00000000 --- a/web-ui/app/locales/en_US/translation.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "compose": "Compose", - "re": "Re", - "fwd": "Fwd", - "trash-single": "Your message was moved to trash!", - "trash-bulk": "Your messages were moved to trash!", - "your-message-was-archived": "Your message was archived", - "delete-single": "Your message was permanently deleted!", - "delete-bulk": "Your messages were permanently deleted!", - "draft-saving": "Saving to Drafts...", - "draft-saved": "Draft saved", - "recipients-not-valid": "One or more of the recipients are not valid emails", - "failed-change-tags": "Could not change mail tags", - "invalid-tag-name": "Invalid tag name", - "failed-delete-single": "Could not delete email", - "failed-delete-bulk": "Could not delete emails", - "failed-fetch-messages": "Could not fetch messages", - "failed-archive": "Could not archive emails", - "to": "to", - "cc": "CC", - "bcc": "BCC", - "body": "Body", - "subject": "Subject", - "send": "Send", - "reply": "Reply", - "reply-to-all": "Reply to all", - "delete-this-message": "Delete this message", - "mark-as-read": "Mark as read", - "mark-as-unread": "Mark as unread", - "delete": "Delete", - "archive": "Archive", - "nothing-selected": "Nothing selected", - "add-tag-placeholder": "Press Enter to add tag", - "no-subject": "", - "no-recipient": "", - "you": "you", - "encrypted": "Encrypted", - "not-encrypted": "Not encrypted", - "signed": "Verified sender", - "not-signed": "Not signed", - "sending-mail": "Sending...", - "trash-button": "Delete it", - "search-placeholder" : "Search...", - "search-results-for": "Search results for", - "forward": "Forward", - "feedback-placeholder": "Tell us what you liked, didn't like, what is missing and generally what you think about Pixelated.", - "user-account": "My Account", - "email-address": "Email address", - "public-key-fingerprint": "Public key fingerprint", - "version": "version", - "logout": "Logout", - "delete-permanently": "Delete Permanently", - "move-to-inbox": "Move to Inbox", - "reply-author-line": "On {{date}}, <{{from}}> wrote:\n", - "refresh": "refresh", - "click-to-remove": "Click to remove", - "no-results-for": "No results for", - "no-emails-in": "No emails in", - "error": { - "timeout": "A timeout occurred", - "general": "Problems talking to server", - "parse": "Got invalid response from server" - }, - "tags": { - "inbox": "Inbox", - "sent": "Sent", - "drafts": "Drafts", - "trash": "Trash", - "all": "All", - "tags": "Tags" - } -} diff --git a/web-ui/app/locales/pt_BR/translation.json b/web-ui/app/locales/pt_BR/translation.json deleted file mode 100644 index ff766a2b..00000000 --- a/web-ui/app/locales/pt_BR/translation.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "compose": "Escrever", - "re": "Res", - "fwd": "Enc", - "trash-single": "Sua mensagem foi movida para a lixeira!", - "trash-bulk": "Suas mensagens foram movidas para a lixeira!", - "your-message-was-archived": "Sua mensagem foi arquivada", - "delete-single": "Sua mensagem foi permanentemente deletada!", - "delete-bulk": "Suas mensagens foram permanentemente deletadas!", - "draft-saving": "Salvando rascunho...", - "draft-saved": "Rascunho salvo", - "recipients-not-valid": "Um ou mais destinatários não são emails válidos", - "failed-change-tags": "Não pode atualizar as tags do email", - "invalid-tag-name": "Nome inválido para tag", - "failed-delete-single": "Não pode deletar o email", - "failed-delete-bulk": "Não foi possível remover os emails", - "failed-fetch-messages": "Não pode receber as mensagens", - "failed-archive": "Não foi possível arquivar os emails", - "to": "para", - "cc": "CC", - "bcc": "CCO", - "body": "Mensagem", - "subject": "Assunto", - "send": "Enviar", - "reply": "Responder", - "reply-to-all": "Responder para todos", - "delete-this-message": "Deletar essa mensagem", - "mark-as-read": "Marcar como lida", - "mark-as-unread": "Marcar como não lida", - "delete": "Deletar", - "archive": "Arquivar", - "nothing-selected": "Nada selecionado", - "add-tag-placeholder": "Aperte enter para adicionar a tag", - "no-subject": "", - "no-recipient": "", - "you": "você", - "encrypted": "Criptografado", - "not-encrypted": "Não criptografado", - "signed": "Rementente verificado", - "not-signed": "Não assinado", - "sending-mail": "Enviando...", - "trash-button": "Deletar", - "search-placeholder" : "Pesquisar...", - "search-results-for": "Resultado da pesquisa por", - "forward": "Encaminhar", - "feedback-placeholder": "Nos diga o que gosta, não gosta, o que está faltando e o que pensa sobre o Pixelated.", - "user-account": "Opções de usuário", - "email-address": "Endereço de email", - "public-key-fingerprint": "Identificação da chave pública", - "version": "versão", - "logout": "Sair", - "delete-permanently": "Excluir permanentemente", - "move-to-inbox": "Mover para Caixa de Entrada", - "reply-author-line": "Em {{date}}, <{{from}}> escreveu:\n", - "refresh": "atualizar", - "click-to-remove": "Pressione para remover", - "no-results-for": "Sem resultados para", - "no-emails-in": "Nenhum email em", - "error": { - "timeout": "A operação excedeu o limite de tempo", - "general": "Problemas ao se comunicar com o servidor", - "parse": "Obteve uma resposta inválida do servidor" - }, - "tags": { - "inbox": "Caixa de Entrada", - "sent": "Enviadas", - "drafts": "Rascunhos", - "trash": "Lixeira", - "all": "Todas", - "tags": "Etiquetas" - } -} diff --git a/web-ui/app/locales/sv_SE/translation.json b/web-ui/app/locales/sv_SE/translation.json deleted file mode 100644 index d4da0711..00000000 --- a/web-ui/app/locales/sv_SE/translation.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "compose": "Skriv nytt", - "re": "Sv", - "fwd": "VB", - "trash-single": "Ditt meddelande har flyttats till papperskorgen!", - "trash-bulk": "Ditt meddelande har arkiverats!", - "recipients-not-valid": "En eller flera mottagare är inte giltiga epost-adresser", - "failed-change-tags": "Kan inte ändra taggar", - "invalid-tag-name": "Ogiltigt taggnamn", - "failed-delete-single": "Kan inte ta bort meddelande", - "failed-fetch-messages": "Kan inte hämta meddelanden", - "to": "till", - "cc": "CC", - "bcc": "BCC", - "body": "Innehåll", - "subject": "Titel", - "send": "Skicka", - "reply": "Svara", - "reply-to-all": "Svara Alla", - "mark-as-read": "Markera som läst", - "delete": "Ta bort", - "archive": "Arkivera", - "nothing-selected": "INGET VALT", - "add-tag-placeholder": "Tryck retur för att skapa", - "no-subject": "", - "no-recipient": "", - "you": "du", - "encrypted": "krypterad", - "not-encrypted": "Meddelandet var läsbart medans det var på väg.", - "signed": "Certifierad avsändare.", - "not-signed": "Avsändaren kunde inte säkert identifieras.", - "search-placeholder" : "Sök...", - "search-results-for": "Sökresultat för", - "forward": "Vidarebefodra", - "tags": { - "inbox": "Inlåda", - "sent": "Skickat", - "drafts": "Utkast", - "trash": "Skräp", - "all": "Alla" - } -} diff --git a/web-ui/app/robots.txt b/web-ui/app/robots.txt deleted file mode 100644 index 6b0157e2..00000000 --- a/web-ui/app/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# robotstxt.org - -User-agent: * \ No newline at end of file diff --git a/web-ui/app/sandbox.html b/web-ui/app/sandbox.html deleted file mode 100644 index 8325b0da..00000000 --- a/web-ui/app/sandbox.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/web-ui/app/scss/_mixins.scss b/web-ui/app/scss/_mixins.scss deleted file mode 100644 index d3aa0220..00000000 --- a/web-ui/app/scss/_mixins.scss +++ /dev/null @@ -1,71 +0,0 @@ -// SHARED MIXINS -@mixin btn-transition { - @include transition-property(background-color); - @include transition-duration(300ms); - @include transition-timing-function(ease-out); -} - -@mixin tooltip($top: 8px, $left: 40px) { - background: rgba(0, 0, 0, 0.7); - color: $white; - position: absolute; - z-index: 2; - left: $left; - top: $top; - font-size: 0.8rem; - padding: 2px 10px; - white-space: nowrap; - @include border-radius(2px); -} - -// FORM MIXINS -@mixin check-box { - background-color: $white; - border: 1px solid $light_gray; - padding: 7px; - margin: 3px 0; - cursor: pointer; - display: inline-block; - position: relative; - @include border-radius(2px); - @include appearance(none); - - &:focus { - outline: none; - border-color: $medium_dark_grey; - } - - &:active, &:checked:active { - } - - &:checked { - background-color: $contrast; - border: 1px solid darken($lighter_gray, 10%); - color: $dark_grey; - } - - &:checked:after { - content: '\2714'; - font-size: 1em; - position: absolute; - bottom: -2px; - left: 1px; - color: $navigation_background; - } -} - - -@mixin searching($top, $left, $color, $size){ - &.searching { - &:after { - font-family: FontAwesome; - content: "\f002"; - font-size: $size; - top: $top; - left: $left; - position: absolute; - color: $color; - text-shadow: -1px 0 $contrast, 0 1px $contrast, 1px 0 $contrast, 0 -1px $contrast; - } - } -} diff --git a/web-ui/app/scss/_others.scss b/web-ui/app/scss/_others.scss deleted file mode 100644 index 039d94bd..00000000 --- a/web-ui/app/scss/_others.scss +++ /dev/null @@ -1,72 +0,0 @@ -.hidden { - display: none; -} - -.no-padding { - padding: 0; -} - -.text-right { - text-align: right; -} - -.search-highlight { - background-color: $search-highlight; -} - -button { - border: 1px solid transparent; - - i { - margin-left: 5px; - } - - &#trash-button { - background: $white; - border: 1px solid $medium_light_grey; - color: $medium_light_grey; - float: right; - margin-left: 5px; - - &:hover, &:focus { - background: $contrast; - } - } - - &.no-style { - background: transparent; - color: $medium_light_grey; - padding: 0; - margin: 0; - - i { - margin: 0; - padding: 0; - vertical-align: middle; - } - } -} - -section { - display: inline-block; - vertical-align: top; - height: 100vh; - overflow-y: scroll; - - &#left-pane { - background-color: $navigation_background; - color: white; - } - - &#middle-pane { - background: $white; - } - - &#right-pane { - padding: 0 10px 60px 0px; - background: $white; - box-shadow: -2px -2px 5px rgba(0, 0, 0, 0.12); - z-index: 2; - overflow-y: auto; - } -} diff --git a/web-ui/app/scss/base/_colors.scss b/web-ui/app/scss/base/_colors.scss deleted file mode 100644 index 17333ff9..00000000 --- a/web-ui/app/scss/base/_colors.scss +++ /dev/null @@ -1,64 +0,0 @@ -/* Pixelated Color Palette - don't change these! */ -$dark_slate_gray: #3E3A37; -$light_gray: #C2C2C2; -$lighter_blue: #91C2D1; -$light_blue: #3DABC4; -$dark_blue: #178CA6; -$bullet-blue: #5cacde; -$light_orange: #FF9C00; -$dark_orange: #FF7902; - - -/* Side nav background color */ -$navigation_background: $dark_slate_gray; - -/* Action buttons and links */ -$action_buttons: $light_blue; - -/* Primary Highlight*/ -$primary_highlight: $light_orange; - -/* Logo color*/ -$logo_color: $light_orange; - -/* Unread count dialog bubble background color */ -$secondary_callout: darken($primary_highlight, 5); - -/* Grayscale */ -$contrast: #EEE; -$white: #FFF; -$dark_white: #FAFAFA; -$lighter_gray: #DDD; -$medium_light_grey: #999; -$medium_grey: #777; -$medium_dark_grey: #666; -$dark_grey: #333; -$black: #000; -$top_pane: $contrast; -$total_count_bg: #C0B9B9; -$background_dropdown_grey: #f0f0f0; - -$background_light_grey: #F5F5F5; -$border_light_grey: #D9D9D9; - -/* Feedback to Users */ -$warning: #F7E8AF; -$search-highlight: #FFEF29; - -/* Light gray indicator icons */ -$indicator_icon_color: $light_gray; - -$error: #D93C38; -$attention: #F6A41C; -$success: #50BA5B; - -$will_be_encrypted: $success; -$wont_be_encrypted: $attention; -$recipients_font_color: #828282; - -/* Attachments */ -$attachment_text: #555; -$attachment_icon: lighten($attachment_text, 30); -$attachment_size: lighten($attachment_text, 30); -$attachment_area_background: #F5F5F5; -$attachment_area_border: #D9D9D9; diff --git a/web-ui/app/scss/base/_fonts.scss b/web-ui/app/scss/base/_fonts.scss deleted file mode 100644 index dfc56dd8..00000000 --- a/web-ui/app/scss/base/_fonts.scss +++ /dev/null @@ -1,68 +0,0 @@ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - src: local('Open Sans Light'), local('OpenSans-Light'), url('/assets/fonts/OpenSans-Light.woff') format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - src: local('Open Sans'), local('OpenSans'), url('/assets/fonts/OpenSans.woff') format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - src: local('Open Sans Semibold'), local('OpenSans-Semibold'), url('/assets/fonts/OpenSans-Semibold.woff') format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 700; - src: local('Open Sans Bold'), local('OpenSans-Bold'), url('/assets/fonts/OpenSans-Bold.woff') format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 800; - src: local('Open Sans Extrabold'), local('OpenSans-Extrabold'), url('/assets/fonts/OpenSans-Extrabold.woff') format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - src: local('Open Sans Light Italic'), local('OpenSansLight-Italic'), url('/assets/fonts/OpenSansLight-Italic.woff') format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - src: local('Open Sans Italic'), local('OpenSans-Italic'), url('/assets/fonts/OpenSans-Italic.woff') format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - src: local('Open Sans Semibold Italic'), local('OpenSans-SemiboldItalic'), url('/assets/fonts/OpenSans-SemiboldItalic.woff') format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 700; - src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url('/assets/fonts/OpenSans-BoldItalic.woff') format('woff'); -} -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 800; - src: local('Open Sans Extrabold Italic'), local('OpenSans-ExtraboldItalic'), url('/assets/fonts/OpenSans-ExtraboldItalic.woff') format('woff'); -} - -@font-face { - font-family: 'icomoon'; - font-style: normal; - font-weight: 400; - src: url('/assets/fonts/icomoon.woff') format('woff'), url('/assets/fonts/icomoon.ttf') format('truetype'), ; -} - diff --git a/web-ui/app/scss/base/_scaffolding.scss b/web-ui/app/scss/base/_scaffolding.scss deleted file mode 100644 index b8b5fa3b..00000000 --- a/web-ui/app/scss/base/_scaffolding.scss +++ /dev/null @@ -1,10 +0,0 @@ -html { - height: 100% ; -} - -body { - min-height: 100% ; - overflow: hidden; - background: $white; -} - diff --git a/web-ui/app/scss/mixins/_position-helpers.scss b/web-ui/app/scss/mixins/_position-helpers.scss deleted file mode 100644 index 254bfc6c..00000000 --- a/web-ui/app/scss/mixins/_position-helpers.scss +++ /dev/null @@ -1,9 +0,0 @@ -@mixin absolute-center-unknown-height-width() { - margin: auto; - position: absolute; - left: 50%; - top: 50%; - -ms-transform: translate(-50%, -50%); - -webkit-transform: translate(-50%, -50%); - transform: translate(-50%, -50%); -} diff --git a/web-ui/app/scss/mixins/_tags.scss b/web-ui/app/scss/mixins/_tags.scss deleted file mode 100644 index 9bb287ea..00000000 --- a/web-ui/app/scss/mixins/_tags.scss +++ /dev/null @@ -1,110 +0,0 @@ -$tags-font-size: 0.6rem; - -@mixin tags { - & > * { - display: inline; - } - - &-tag { - font-size: $tags-font-size; - font-weight: 700; - background-color: $dark_blue; - color: white; - padding: 2px 4px; - margin: 0 1px; - border-radius: 2px; - } -} - -@mixin tags-editable { - @include tags; - - &-tag:hover { - text-decoration: line-through; - cursor: pointer; - position: relative; - - &:before { - @include tooltip(130%, 25%); - - content: "click to remove"; - text-transform: lowercase; - } - } - - &-label { - vertical-align: bottom; - color: $light_gray; - } - - &-new-button { - font-size: $tags-font-size; - padding: 0; - background: transparent; - border-radius: 2px; - padding: 2px; - - &:hover { - opacity: 1; - background: $lighter_gray; - } - } - - &-name-input { - opacity: 0.6; - transition: background-color 150ms ease-out; - - &:hover { - opacity: 1; - } - - // twitter typeahead classes. those are set via JS, with relatively high specificity, - // hence box-model-related properties are repeated - // https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md#class-names - - $suggestion-border: 1px solid darken($contrast, 5%); - $input-field-padding: 1px 5px; - $input-field-margin: 2px; - - & * .tt-input { - border-radius: $input-field-margin; - padding: $input-field-padding; - margin-top: 2px; - font-size: $tags-font-size; - } - - & * .tt-hint { - color: $medium_light_grey; - padding: $input-field-padding; - margin-top: $input-field-margin; - font-size: $tags-font-size; - background: transparent; - } - - & * .tt-dropdown-menu { - min-width: 250px; - padding: 0; - font-size: $tags-font-size; - background-color: $contrast; - border: $suggestion-border; - } - - & * .tt-suggestion { - padding: 5px 10px; - font-size: $tags-font-size; - border-bottom: $suggestion-border; - - &:last-child { - border-bottom: none; - } - - p { - margin: 0; - } - } - - & * .tt-cursor { - background-color: $white; - } - } -} diff --git a/web-ui/app/scss/sandbox.scss b/web-ui/app/scss/sandbox.scss deleted file mode 100644 index 3c1be358..00000000 --- a/web-ui/app/scss/sandbox.scss +++ /dev/null @@ -1,27 +0,0 @@ -$search-highlight: #FFEF29; - -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - src: local('Open Sans'), local('OpenSans'), url('/sandbox/fonts/OpenSans.woff') format('woff'); -} - -body { - font-family: "Open Sans", "Microsoft YaHei", "Hiragino Sans GB", "Hiragino Sans GB W3", "微软雅黑", "Helvetica Neue", Arial, sans-serif; - font-size: 13px; - line-height: 1.2em; - background: white; - color: #333; - padding: 0; - margin: 0; - font-weight: normal; - -webkit-font-smoothing: antialiased; - font-style: normal; - box-sizing: border-box; - word-wrap: break-word; -} - -.search-highlight { - background-color: $search-highlight; -} diff --git a/web-ui/app/scss/style.scss b/web-ui/app/scss/style.scss deleted file mode 100644 index e99ab194..00000000 --- a/web-ui/app/scss/style.scss +++ /dev/null @@ -1,39 +0,0 @@ -// vendor stylesheets and resets -@import "vendor/reset"; -@import "vendor/scut"; -@import "compass/css3"; -@import "vendor/foundation"; -@import "vendor/customfont"; - -// basic configuration -@import "base/fonts"; -@import "base/colors"; -@import "base/scaffolding"; - -// mixins -@import "mixins/position-helpers"; -@import "mixins/tags"; - -// TODO -@import "mixins"; - -// templates -@import "templates/no-content-placeholder"; -@import "templates/unread-count"; - -// views -@import "views/message-panel"; -@import "views/close-button"; -@import "views/no-message-selected"; -@import "views/no-mails-available"; -@import "views/read-view"; -@import "views/security-labels"; -@import "views/compose-view"; -@import "views/compose-button"; -@import "views/mail-list"; -@import "views/_action-bar.scss"; -@import "views/_navigation.scss"; - -// misc stuff -@import "others"; - diff --git a/web-ui/app/scss/templates/_no-content-placeholder.scss b/web-ui/app/scss/templates/_no-content-placeholder.scss deleted file mode 100644 index c6807011..00000000 --- a/web-ui/app/scss/templates/_no-content-placeholder.scss +++ /dev/null @@ -1,5 +0,0 @@ -.no-content-placeholder { - @include absolute-center-unknown-height-width; - - color: $medium_dark_grey; -} diff --git a/web-ui/app/scss/templates/_unread-count.scss b/web-ui/app/scss/templates/_unread-count.scss deleted file mode 100644 index f7852227..00000000 --- a/web-ui/app/scss/templates/_unread-count.scss +++ /dev/null @@ -1,14 +0,0 @@ -.mail-count { - background: $white; - border-radius: 50%; - border: 1px solid $white; - color: $white; - font-size: 0.7em; - font-weight: 700; - left: 0; - margin-left: 5px; - opacity: 0.95; - padding: 0px 5px 0; - position: absolute; - top: 1px; -} diff --git a/web-ui/app/scss/vendor/_customfont.scss b/web-ui/app/scss/vendor/_customfont.scss deleted file mode 100644 index d72cca0f..00000000 --- a/web-ui/app/scss/vendor/_customfont.scss +++ /dev/null @@ -1,9 +0,0 @@ -[class^="icon-"], [class*=" icon-"] { - /* use !important to prevent issues with browser extensions that change fonts */ - font-family: 'icomoon' !important; - line-height: 1; -} - -.icon-px-sent:before { - content: "\e900"; -} diff --git a/web-ui/app/scss/vendor/_foundation.scss b/web-ui/app/scss/vendor/_foundation.scss deleted file mode 100644 index 7918cf26..00000000 --- a/web-ui/app/scss/vendor/_foundation.scss +++ /dev/null @@ -1,2066 +0,0 @@ -@import 'compass/css3'; - -meta { - &.foundation-version { - font-family: "/5.2.3/"; - } - &.foundation-mq-small { - font-family: "/only screen/"; - width: 0em; - } - &.foundation-mq-medium { - font-family: "/only screen and (min-width:40.063em)/"; - width: 40.063em; - } - &.foundation-mq-large { - font-family: "/only screen and (min-width:64.063em)/"; - width: 64.063em; - } - &.foundation-mq-xlarge { - font-family: "/only screen and (min-width:90.063em)/"; - width: 90.063em; - } - &.foundation-mq-xxlarge { - font-family: "/only screen and (min-width:120.063em)/"; - width: 120.063em; - } - &.foundation-data-attribute-namespace { - font-family: false; - } -} - -html, body { - height: 100%; -} - -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - &:before, &:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - } -} - -html { - font-size: 100%; -} - -body { - font-family: "Open Sans", "Microsoft YaHei", "Hiragino Sans GB", "Hiragino Sans GB W3", "微软雅黑", "Helvetica Neue", Arial, sans-serif; - font-size: 13px; - line-height: 1.2em; - background: white; - color: #333; - padding: 0; - margin: 0; - font-weight: normal; - -webkit-font-smoothing: antialiased; - font-style: normal; - position: relative; - cursor: default; -} - -a:hover { - cursor: pointer; -} - -img { - max-width: 100%; - height: auto; - -ms-interpolation-mode: bicubic; -} - -#map_canvas { - img, embed, object { - max-width: none !important; - } -} - -.map_canvas { - img, embed, object { - max-width: none !important; - } -} - -.left { - float: left !important; -} - -.right { - float: right !important; -} - -.clearfix { - &:before { - content: " "; - display: table; - } - &:after { - content: " "; - display: table; - clear: both; - } -} - -.hide { - display: none; -} - -.antialiased { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -img { - display: inline-block; - vertical-align: middle; -} - -textarea { - height: auto; - min-height: 50px; - &:focus { - outline: none; - } -} - -select { - width: 100%; -} - -.row { - width: 100%; - margin-left: auto; - margin-right: auto; - margin-top: 0; - margin-bottom: 0; - &:before { - content: " "; - display: table; - } - &:after { - content: " "; - display: table; - clear: both; - } - &.collapse { - > { - .column, .columns { - padding-left: 0; - padding-right: 0; - } - } - .row { - margin-left: 0; - margin-right: 0; - } - } - .row { - width: auto; - margin-left: -0.9375em; - margin-right: -0.9375em; - margin-top: 0; - margin-bottom: 0; - max-width: none; - &:before { - content: " "; - display: table; - } - &:after { - content: " "; - display: table; - clear: both; - } - &.collapse { - width: auto; - margin: 0; - max-width: none; - &:before { - content: " "; - display: table; - } - &:after { - content: " "; - display: table; - clear: both; - } - } - } -} - -.column, .columns { - padding-left: 0.9375em; - padding-right: 0.9375em; - width: 100%; - float: left; -} - -@media only screen { - .small-push-0 { - position: relative; - left: 0%; - right: auto; - } - .small-pull-0 { - position: relative; - right: 0%; - left: auto; - } - .small-push-1 { - position: relative; - left: 8.33333%; - right: auto; - } - .small-pull-1 { - position: relative; - right: 8.33333%; - left: auto; - } - .small-push-2 { - position: relative; - left: 16.66667%; - right: auto; - } - .small-pull-2 { - position: relative; - right: 16.66667%; - left: auto; - } - .small-push-3 { - position: relative; - left: 25%; - right: auto; - } - .small-pull-3 { - position: relative; - right: 25%; - left: auto; - } - .small-push-4 { - position: relative; - left: 33.33333%; - right: auto; - } - .small-pull-4 { - position: relative; - right: 33.33333%; - left: auto; - } - .small-push-5 { - position: relative; - left: 41.66667%; - right: auto; - } - .small-pull-5 { - position: relative; - right: 41.66667%; - left: auto; - } - .small-push-6 { - position: relative; - left: 50%; - right: auto; - } - .small-pull-6 { - position: relative; - right: 50%; - left: auto; - } - .small-push-7 { - position: relative; - left: 58.33333%; - right: auto; - } - .small-pull-7 { - position: relative; - right: 58.33333%; - left: auto; - } - .small-push-8 { - position: relative; - left: 66.66667%; - right: auto; - } - .small-pull-8 { - position: relative; - right: 66.66667%; - left: auto; - } - .small-push-9 { - position: relative; - left: 75%; - right: auto; - } - .small-pull-9 { - position: relative; - right: 75%; - left: auto; - } - .small-push-10 { - position: relative; - left: 83.33333%; - right: auto; - } - .small-pull-10 { - position: relative; - right: 83.33333%; - left: auto; - } - .small-push-11 { - position: relative; - left: 91.66667%; - right: auto; - } - .small-pull-11 { - position: relative; - right: 91.66667%; - left: auto; - } - .column, .columns { - position: relative; - padding-left: 0.9375em; - padding-right: 0.9375em; - float: left; - } - .small-1 { - width: 8.33333%; - } - .small-2 { - width: 16.66667%; - } - .small-3 { - width: 25%; - } - .small-4 { - width: 33.33333%; - } - .small-5 { - width: 41.66667%; - } - .small-6 { - width: 50%; - } - .small-7 { - width: 58.33333%; - } - .small-8 { - width: 66.66667%; - } - .small-9 { - width: 75%; - } - .small-10 { - width: 83.33333%; - } - .small-11 { - width: 91.66667%; - } - .small-12 { - width: 100%; - } - [class*="column"] + [class*="column"] { - &:last-child { - float: right; - } - &.end { - float: left; - } - } - .small-offset-0 { - margin-left: 0% !important; - } - .small-offset-1 { - margin-left: 8.33333% !important; - } - .small-offset-2 { - margin-left: 16.66667% !important; - } - .small-offset-3 { - margin-left: 25% !important; - } - .small-offset-4 { - margin-left: 33.33333% !important; - } - .small-offset-5 { - margin-left: 41.66667% !important; - } - .small-offset-6 { - margin-left: 50% !important; - } - .small-offset-7 { - margin-left: 58.33333% !important; - } - .small-offset-8 { - margin-left: 66.66667% !important; - } - .small-offset-9 { - margin-left: 75% !important; - } - .small-offset-10 { - margin-left: 83.33333% !important; - } - .small-offset-11 { - margin-left: 91.66667% !important; - } - .small-reset-order { - margin-left: 0; - margin-right: 0; - left: auto; - right: auto; - float: left; - } - .column.small-centered, .columns.small-centered { - margin-left: auto; - margin-right: auto; - float: none !important; - } - .column.small-uncentered, .columns.small-uncentered { - margin-left: 0; - margin-right: 0; - float: left !important; - } - .column.small-uncentered.opposite, .columns.small-uncentered.opposite { - float: right; - } -} - -@media only screen and (min-width: 40.063em) { - .medium-push-0 { - position: relative; - left: 0%; - right: auto; - } - .medium-pull-0 { - position: relative; - right: 0%; - left: auto; - } - .medium-push-1 { - position: relative; - left: 8.33333%; - right: auto; - } - .medium-pull-1 { - position: relative; - right: 8.33333%; - left: auto; - } - .medium-push-2 { - position: relative; - left: 16.66667%; - right: auto; - } - .medium-pull-2 { - position: relative; - right: 16.66667%; - left: auto; - } - .medium-push-3 { - position: relative; - left: 25%; - right: auto; - } - .medium-pull-3 { - position: relative; - right: 25%; - left: auto; - } - .medium-push-4 { - position: relative; - left: 33.33333%; - right: auto; - } - .medium-pull-4 { - position: relative; - right: 33.33333%; - left: auto; - } - .medium-push-5 { - position: relative; - left: 41.66667%; - right: auto; - } - .medium-pull-5 { - position: relative; - right: 41.66667%; - left: auto; - } - .medium-push-6 { - position: relative; - left: 50%; - right: auto; - } - .medium-pull-6 { - position: relative; - right: 50%; - left: auto; - } - .medium-push-7 { - position: relative; - left: 58.33333%; - right: auto; - } - .medium-pull-7 { - position: relative; - right: 58.33333%; - left: auto; - } - .medium-push-8 { - position: relative; - left: 66.66667%; - right: auto; - } - .medium-pull-8 { - position: relative; - right: 66.66667%; - left: auto; - } - .medium-push-9 { - position: relative; - left: 75%; - right: auto; - } - .medium-pull-9 { - position: relative; - right: 75%; - left: auto; - } - .medium-push-10 { - position: relative; - left: 83.33333%; - right: auto; - } - .medium-pull-10 { - position: relative; - right: 83.33333%; - left: auto; - } - .medium-push-11 { - position: relative; - left: 91.66667%; - right: auto; - } - .medium-pull-11 { - position: relative; - right: 91.66667%; - left: auto; - } - .column, .columns { - position: relative; - padding-left: 0.9375em; - padding-right: 0.9375em; - float: left; - } - .medium-1 { - width: 8.33333%; - } - .medium-2 { - width: 16.66667%; - } - .medium-3 { - width: 25%; - } - .medium-4 { - width: 33.33333%; - } - .medium-5 { - width: 41.66667%; - } - .medium-6 { - width: 50%; - } - .medium-7 { - width: 58.33333%; - } - .medium-8 { - width: 66.66667%; - } - .medium-9 { - width: 75%; - } - .medium-10 { - width: 83.33333%; - } - .medium-11 { - width: 91.66667%; - } - .medium-12 { - width: 100%; - } - [class*="column"] + [class*="column"] { - &:last-child { - float: right; - } - &.end { - float: left; - } - } - .medium-offset-0 { - margin-left: 0% !important; - } - .medium-offset-1 { - margin-left: 8.33333% !important; - } - .medium-offset-2 { - margin-left: 16.66667% !important; - } - .medium-offset-3 { - margin-left: 25% !important; - } - .medium-offset-4 { - margin-left: 33.33333% !important; - } - .medium-offset-5 { - margin-left: 41.66667% !important; - } - .medium-offset-6 { - margin-left: 50% !important; - } - .medium-offset-7 { - margin-left: 58.33333% !important; - } - .medium-offset-8 { - margin-left: 66.66667% !important; - } - .medium-offset-9 { - margin-left: 75% !important; - } - .medium-offset-10 { - margin-left: 83.33333% !important; - } - .medium-offset-11 { - margin-left: 91.66667% !important; - } - .medium-reset-order { - margin-left: 0; - margin-right: 0; - left: auto; - right: auto; - float: left; - } - .column.medium-centered, .columns.medium-centered { - margin-left: auto; - margin-right: auto; - float: none !important; - } - .column.medium-uncentered, .columns.medium-uncentered { - margin-left: 0; - margin-right: 0; - float: left !important; - } - .column.medium-uncentered.opposite, .columns.medium-uncentered.opposite { - float: right; - } - .push-0 { - position: relative; - left: 0%; - right: auto; - } - .pull-0 { - position: relative; - right: 0%; - left: auto; - } - .push-1 { - position: relative; - left: 8.33333%; - right: auto; - } - .pull-1 { - position: relative; - right: 8.33333%; - left: auto; - } - .push-2 { - position: relative; - left: 16.66667%; - right: auto; - } - .pull-2 { - position: relative; - right: 16.66667%; - left: auto; - } - .push-3 { - position: relative; - left: 25%; - right: auto; - } - .pull-3 { - position: relative; - right: 25%; - left: auto; - } - .push-4 { - position: relative; - left: 33.33333%; - right: auto; - } - .pull-4 { - position: relative; - right: 33.33333%; - left: auto; - } - .push-5 { - position: relative; - left: 41.66667%; - right: auto; - } - .pull-5 { - position: relative; - right: 41.66667%; - left: auto; - } - .push-6 { - position: relative; - left: 50%; - right: auto; - } - .pull-6 { - position: relative; - right: 50%; - left: auto; - } - .push-7 { - position: relative; - left: 58.33333%; - right: auto; - } - .pull-7 { - position: relative; - right: 58.33333%; - left: auto; - } - .push-8 { - position: relative; - left: 66.66667%; - right: auto; - } - .pull-8 { - position: relative; - right: 66.66667%; - left: auto; - } - .push-9 { - position: relative; - left: 75%; - right: auto; - } - .pull-9 { - position: relative; - right: 75%; - left: auto; - } - .push-10 { - position: relative; - left: 83.33333%; - right: auto; - } - .pull-10 { - position: relative; - right: 83.33333%; - left: auto; - } - .push-11 { - position: relative; - left: 91.66667%; - right: auto; - } - .pull-11 { - position: relative; - right: 91.66667%; - left: auto; - } -} - -@media only screen and (min-width: 64.063em) { - .large-push-0 { - position: relative; - left: 0%; - right: auto; - } - .large-pull-0 { - position: relative; - right: 0%; - left: auto; - } - .large-push-1 { - position: relative; - left: 8.33333%; - right: auto; - } - .large-pull-1 { - position: relative; - right: 8.33333%; - left: auto; - } - .large-push-2 { - position: relative; - left: 16.66667%; - right: auto; - } - .large-pull-2 { - position: relative; - right: 16.66667%; - left: auto; - } - .large-push-3 { - position: relative; - left: 25%; - right: auto; - } - .large-pull-3 { - position: relative; - right: 25%; - left: auto; - } - .large-push-4 { - position: relative; - left: 33.33333%; - right: auto; - } - .large-pull-4 { - position: relative; - right: 33.33333%; - left: auto; - } - .large-push-5 { - position: relative; - left: 41.66667%; - right: auto; - } - .large-pull-5 { - position: relative; - right: 41.66667%; - left: auto; - } - .large-push-6 { - position: relative; - left: 50%; - right: auto; - } - .large-pull-6 { - position: relative; - right: 50%; - left: auto; - } - .large-push-7 { - position: relative; - left: 58.33333%; - right: auto; - } - .large-pull-7 { - position: relative; - right: 58.33333%; - left: auto; - } - .large-push-8 { - position: relative; - left: 66.66667%; - right: auto; - } - .large-pull-8 { - position: relative; - right: 66.66667%; - left: auto; - } - .large-push-9 { - position: relative; - left: 75%; - right: auto; - } - .large-pull-9 { - position: relative; - right: 75%; - left: auto; - } - .large-push-10 { - position: relative; - left: 83.33333%; - right: auto; - } - .large-pull-10 { - position: relative; - right: 83.33333%; - left: auto; - } - .large-push-11 { - position: relative; - left: 91.66667%; - right: auto; - } - .large-pull-11 { - position: relative; - right: 91.66667%; - left: auto; - } - .column, .columns { - position: relative; - padding-left: 0.9375em; - padding-right: 0.9375em; - float: left; - } - .large-1 { - width: 8.33333%; - } - .large-2 { - width: 16.66667%; - } - .large-3 { - width: 25%; - } - .large-4 { - width: 33.33333%; - } - .large-5 { - width: 41.66667%; - } - .large-6 { - width: 50%; - } - .large-7 { - width: 58.33333%; - } - .large-8 { - width: 66.66667%; - } - .large-9 { - width: 75%; - } - .large-10 { - width: 83.33333%; - } - .large-11 { - width: 91.66667%; - } - .large-12 { - width: 100%; - } - [class*="column"] + [class*="column"] { - &:last-child { - float: right; - } - &.end { - float: left; - } - } - .large-offset-0 { - margin-left: 0% !important; - } - .large-offset-1 { - margin-left: 8.33333% !important; - } - .large-offset-2 { - margin-left: 16.66667% !important; - } - .large-offset-3 { - margin-left: 25% !important; - } - .large-offset-4 { - margin-left: 33.33333% !important; - } - .large-offset-5 { - margin-left: 41.66667% !important; - } - .large-offset-6 { - margin-left: 50% !important; - } - .large-offset-7 { - margin-left: 58.33333% !important; - } - .large-offset-8 { - margin-left: 66.66667% !important; - } - .large-offset-9 { - margin-left: 75% !important; - } - .large-offset-10 { - margin-left: 83.33333% !important; - } - .large-offset-11 { - margin-left: 91.66667% !important; - } - .large-reset-order { - margin-left: 0; - margin-right: 0; - left: auto; - right: auto; - float: left; - } - .column.large-centered, .columns.large-centered { - margin-left: auto; - margin-right: auto; - float: none !important; - } - .column.large-uncentered, .columns.large-uncentered { - margin-left: 0; - margin-right: 0; - float: left !important; - } - .column.large-uncentered.opposite, .columns.large-uncentered.opposite { - float: right; - } - .push-0 { - position: relative; - left: 0%; - right: auto; - } - .pull-0 { - position: relative; - right: 0%; - left: auto; - } - .push-1 { - position: relative; - left: 8.33333%; - right: auto; - } - .pull-1 { - position: relative; - right: 8.33333%; - left: auto; - } - .push-2 { - position: relative; - left: 16.66667%; - right: auto; - } - .pull-2 { - position: relative; - right: 16.66667%; - left: auto; - } - .push-3 { - position: relative; - left: 25%; - right: auto; - } - .pull-3 { - position: relative; - right: 25%; - left: auto; - } - .push-4 { - position: relative; - left: 33.33333%; - right: auto; - } - .pull-4 { - position: relative; - right: 33.33333%; - left: auto; - } - .push-5 { - position: relative; - left: 41.66667%; - right: auto; - } - .pull-5 { - position: relative; - right: 41.66667%; - left: auto; - } - .push-6 { - position: relative; - left: 50%; - right: auto; - } - .pull-6 { - position: relative; - right: 50%; - left: auto; - } - .push-7 { - position: relative; - left: 58.33333%; - right: auto; - } - .pull-7 { - position: relative; - right: 58.33333%; - left: auto; - } - .push-8 { - position: relative; - left: 66.66667%; - right: auto; - } - .pull-8 { - position: relative; - right: 66.66667%; - left: auto; - } - .push-9 { - position: relative; - left: 75%; - right: auto; - } - .pull-9 { - position: relative; - right: 75%; - left: auto; - } - .push-10 { - position: relative; - left: 83.33333%; - right: auto; - } - .pull-10 { - position: relative; - right: 83.33333%; - left: auto; - } - .push-11 { - position: relative; - left: 91.66667%; - right: auto; - } - .pull-11 { - position: relative; - right: 91.66667%; - left: auto; - } -} - -.inline-list { - margin: 0 auto 1.0625rem auto; - margin-left: -1.375rem; - margin-right: 0; - padding: 0; - list-style: none; - overflow: hidden; - > li { - list-style: none; - float: left; - margin-left: 1.375rem; - display: block; - > * { - display: block; - } - } -} - -.text-left { - text-align: left !important; -} - -.text-right { - text-align: right !important; -} - -.text-center { - text-align: center !important; -} - -.text-justify { - text-align: justify !important; -} - -@media only screen and (max-width: 40em) { - .small-only-text-left { - text-align: left !important; - } - .small-only-text-right { - text-align: right !important; - } - .small-only-text-center { - text-align: center !important; - } - .small-only-text-justify { - text-align: justify !important; - } -} - -@media only screen { - .small-text-left { - text-align: left !important; - } - .small-text-right { - text-align: right !important; - } - .small-text-center { - text-align: center !important; - } - .small-text-justify { - text-align: justify !important; - } -} - -@media only screen and (min-width: 40.063em) and (max-width: 64em) { - .medium-only-text-left { - text-align: left !important; - } - .medium-only-text-right { - text-align: right !important; - } - .medium-only-text-center { - text-align: center !important; - } - .medium-only-text-justify { - text-align: justify !important; - } -} - -@media only screen and (min-width: 40.063em) { - .medium-text-left { - text-align: left !important; - } - .medium-text-right { - text-align: right !important; - } - .medium-text-center { - text-align: center !important; - } - .medium-text-justify { - text-align: justify !important; - } -} - -@media only screen and (min-width: 64.063em) and (max-width: 90em) { - .large-only-text-left { - text-align: left !important; - } - .large-only-text-right { - text-align: right !important; - } - .large-only-text-center { - text-align: center !important; - } - .large-only-text-justify { - text-align: justify !important; - } -} - -@media only screen and (min-width: 64.063em) { - .large-text-left { - text-align: left !important; - } - .large-text-right { - text-align: right !important; - } - .large-text-center { - text-align: center !important; - } - .large-text-justify { - text-align: justify !important; - } -} - -@media only screen and (min-width: 90.063em) and (max-width: 120em) { - .xlarge-only-text-left { - text-align: left !important; - } - .xlarge-only-text-right { - text-align: right !important; - } - .xlarge-only-text-center { - text-align: center !important; - } - .xlarge-only-text-justify { - text-align: justify !important; - } -} - -@media only screen and (min-width: 90.063em) { - .xlarge-text-left { - text-align: left !important; - } - .xlarge-text-right { - text-align: right !important; - } - .xlarge-text-center { - text-align: center !important; - } - .xlarge-text-justify { - text-align: justify !important; - } -} - -@media only screen and (min-width: 120.063em) and (max-width: 99999999em) { - .xxlarge-only-text-left { - text-align: left !important; - } - .xxlarge-only-text-right { - text-align: right !important; - } - .xxlarge-only-text-center { - text-align: center !important; - } - .xxlarge-only-text-justify { - text-align: justify !important; - } -} - -@media only screen and (min-width: 120.063em) { - .xxlarge-text-left { - text-align: left !important; - } - .xxlarge-text-right { - text-align: right !important; - } - .xxlarge-text-center { - text-align: center !important; - } - .xxlarge-text-justify { - text-align: justify !important; - } -} - -/* Typography resets */ - -div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, p, blockquote, th, td { - margin: 0; - padding: 0; -} - -/* Default Link Styles */ - -a { - color: #2ba6cb; - text-decoration: none; - line-height: inherit; - &:hover, &:focus { - color: #258faf; - outline: none; - } - img { - border: none; - } -} - -/* Default paragraph styles */ - -p { - font-family: inherit; - font-weight: normal; - font-size: 0.9rem; - line-height: 1.4; - margin-bottom: 1.25rem; - text-rendering: optimizeLegibility; - &.lead { - font-size: 1.21875rem; - line-height: 1.4; - } - aside { - font-size: 0.875rem; - line-height: 1.35; - font-style: italic; - } -} - -/* Default header styles */ - -h1, h2, h3, h4, h5, h6 { - font-weight: normal; - font-style: normal; - color: #222; - text-rendering: optimizeLegibility; - margin-top: 0.2rem; - margin-bottom: 0.5rem; - line-height: 1.2; -} - -h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { - font-size: 60%; - color: #6f6f6f; - line-height: 0; -} - -h1 { - font-size: 2.125rem; -} - -h2 { - font-size: 1.6875rem; -} - -h3 { - font-size: 1.375rem; -} - -h4, h5 { - font-size: 1.125rem; -} - -h6 { - font-size: 1rem; -} - -.subheader { - line-height: 1.4; - color: #6f6f6f; - font-weight: normal; - margin-top: 0.2rem; - margin-bottom: 0.5rem; -} - -hr { - border: solid #dddddd; - border-width: 1px 0 0; - clear: both; - margin: 1.25rem 0 1.1875rem; - height: 0; -} - -/* Helpful Typography Defaults */ - -em, i { - font-style: italic; - line-height: inherit; -} - -strong, b { - font-weight: bold; - line-height: inherit; -} - -small { - font-size: 60%; - line-height: inherit; -} - -code { - font-family: Consolas, "Liberation Mono", Courier, monospace; - font-weight: bold; - color: #910b0e; -} - -/* Lists */ - -ul, ol, dl { - font-size: 0.9rem; - line-height: 1.6; - margin-bottom: 1.25rem; - list-style-position: outside; - font-family: inherit; -} - -ul { - margin-left: 0; - &.bullets { - margin-left: 1.1rem; - li { - margin-left: 1.25rem; - margin-bottom: 0; - list-style: circle; - } - } - li { - margin-bottom: 0; - list-style: none; - } -} - -/* Abbreviations */ - -abbr, acronym { - text-transform: uppercase; - font-size: 90%; - color: #222222; - border-bottom: 1px dotted #dddddd; - cursor: help; -} - -abbr { - text-transform: none; -} - -/* Blockquotes */ - -blockquote { - margin: 0 0 1.25rem; - padding: 0.5625rem 1.25rem 0 1.1875rem; - border-left: 1px solid #dddddd; - cite { - display: block; - font-size: 0.8125rem; - color: #555555; - &:before { - content: "\2014 \0020"; - } - a { - color: #555555; - &:visited { - color: #555555; - } - } - } - line-height: 1.6; - color: #6f6f6f; - p { - line-height: 1.6; - color: #6f6f6f; - } -} - -/* Microformats */ - -.vcard { - display: inline-block; - margin: 0 0 1.25rem 0; - border: 1px solid #dddddd; - padding: 0.625rem 0.75rem; - li { - margin: 0; - display: block; - } - .fn { - font-weight: bold; - font-size: 0.9375rem; - } -} - -.vevent { - .summary { - font-weight: bold; - } - abbr { - cursor: default; - text-decoration: none; - font-weight: bold; - border: none; - padding: 0 0.0625rem; - } -} - -@media only screen and (min-width: 40.063em) { - h1, h2, h3, h4, h5, h6 { - line-height: 1.2; - } - h1 { - font-size: 2.55rem; - } - h2 { - font-size: 2.3125rem; - } - h3 { - font-size: 1.4875rem; - } - h4 { - font-size: 1.1375rem; - } -} - -/* - * Print styles. - * - * Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ - * Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com) -*/ - -.print-only { - display: none !important; -} - -@media print { - * { - background: transparent !important; - color: black !important; - /* Black prints faster: h5bp.com/s */ - box-shadow: none !important; - text-shadow: none !important; - } - a { - text-decoration: underline; - &:visited { - text-decoration: underline; - } - &[href]:after { - content: " (" attr(href) ")"; - } - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - .ir a:after { - content: ""; - } - a { - &[href^="javascript:"]:after, &[href^="#"]:after { - content: ""; - } - } - pre, blockquote { - border: 1px solid #999999; - page-break-inside: avoid; - } - thead { - display: table-header-group; - /* h5bp.com/t */ - } - tr { - page-break-inside: avoid; - } - img { - page-break-inside: avoid; - max-width: 100% !important; - } - @page { - margin: 0.5cm; - } - - p, h2, h3 { - orphans: 3; - widows: 3; - } - h2, h3 { - page-break-after: avoid; - } - .hide-on-print { - display: none !important; - } - .print-only { - display: block !important; - } - .hide-for-print { - display: none !important; - } - .show-for-print { - display: inherit !important; - } -} - -.reveal-modal-bg { - position: fixed; - height: 100%; - width: 100%; - background: black; - background: rgba(0, 0, 0, 0.45); - z-index: 99; - display: none; - top: 0; - left: 0; -} - -dialog, .reveal-modal { - visibility: hidden; - display: none; - position: absolute; - z-index: 100; - width: 100vw; - top: 0; - left: 0; - background-color: white; - padding: 1.25rem; - border: solid 1px #666666; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); -} - -@media only screen and (max-width: 40em) { - dialog, .reveal-modal { - min-height: 100vh; - } -} - -@media only screen and (min-width: 40.063em) { - dialog, .reveal-modal { - left: 50%; - } -} - -dialog { - .column, .columns { - min-width: 0; - } -} - -.reveal-modal { - .column, .columns { - min-width: 0; - } -} - -dialog > :first-child, .reveal-modal > :first-child { - margin-top: 0; -} - -dialog > :last-child, .reveal-modal > :last-child { - margin-bottom: 0; -} - -@media only screen and (min-width: 40.063em) { - dialog, .reveal-modal { - margin-left: -26%; - width: 50%; - } -} - -@media only screen and (min-width: 40.063em) { - dialog, .reveal-modal { - top: 6.25rem; - } -} - -dialog .close-reveal-modal, .reveal-modal .close-reveal-modal { - font-size: 2.5rem; - line-height: 1; - position: absolute; - top: 0.5rem; - right: 0.6875rem; - color: #aaaaaa; - font-weight: bold; - cursor: pointer; -} - -dialog[open] { - display: block; - visibility: visible; -} - -@media only screen and (min-width: 40.063em) { - dialog, .reveal-modal { - padding: 1.875rem; - } - dialog.radius, .reveal-modal.radius { - border-radius: 3px; - } - dialog.round, .reveal-modal.round { - border-radius: 1000px; - } - dialog.collapse, .reveal-modal.collapse { - padding: 0; - } - dialog.full, .reveal-modal.full { - top: 0; - left: 0; - height: 100vh; - min-height: 100vh; - margin-left: 0 !important; - } -} - -@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.tiny, .reveal-modal.tiny { - margin-left: -15%; - width: 30%; - } -} - -@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.small, .reveal-modal.small { - margin-left: -20%; - width: 40%; - } -} - -@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.medium, .reveal-modal.medium { - margin-left: -30%; - width: 60%; - } -} - -@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.large, .reveal-modal.large { - margin-left: -35%; - width: 70%; - } -} - -@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.xlarge, .reveal-modal.xlarge { - margin-left: -47.5%; - width: 95%; - } -} - -@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { - dialog.full, .reveal-modal.full { - margin-left: -50vw; - width: 100vw; - } -} - -@media print { - dialog, .reveal-modal { - background: white !important; - } -} - -.label { - font-weight: normal; - text-align: center; - text-decoration: none; - line-height: 1; - white-space: nowrap; - display: inline-block; - position: relative; - margin-bottom: inherit; - padding: 0.25rem 0.5rem 0.375rem; - font-size: 0.6875rem; - background-color: #2ba6cb; - color: white; - &.radius { - border-radius: 3px; - } - &.round { - border-radius: 1000px; - } - &.alert { - background-color: #c60f13; - color: white; - } - &.success { - background-color: #5da423; - color: white; - } - &.secondary { - background-color: #e9e9e9; - color: #333333; - } -} - -button, .button, input[type=button] { - cursor: pointer; - margin: 0 0 1.25rem; - border: none; - position: relative; - text-decoration: none; - text-align: center; - -webkit-appearance: none; - display: inline-block; - padding: 0.4rem 1.1rem; - font-size: 0.9rem; - background-color: #2ba6cb; - border-color: #2285a2; - color: white; - transition: background-color 150ms ease-out; - @include border-radius(2px); - &:hover, &:focus { - background-color: #2285a2; - outline: none; - color: white; - } - &.large { - padding-top: 1.125rem; - padding-right: 2.25rem; - padding-bottom: 1.1875rem; - padding-left: 2.25rem; - font-size: 1.25rem; - } - - &.small { - padding-top: 0.875rem; - padding-right: 1.75rem; - padding-bottom: 0.9375rem; - padding-left: 1.75rem; - font-size: 0.8125rem; - } - - &.tiny { - padding-top: 0.625rem; - padding-right: 1.25rem; - padding-bottom: 0.6875rem; - padding-left: 1.25rem; - font-size: 0.6875rem; - } - - &.expand { - padding-right: 0; - padding-left: 0; - width: 100%; - } - - &.left-align { - text-align: left; - text-indent: 0.75rem; - } - - &.right-align { - text-align: right; - padding-right: 0.75rem; - } - - &.round { - border-radius: 1000px; - } - - &.disabled, &[disabled] { - background-color: #2285a2; - border-color: #2285a2; - color: white; - cursor: default; - opacity: 0.5; - box-shadow: none; - &:hover, &:focus { - background-color: #2285a2; - opacity: 0.5; - } - } -} - - -@media only screen and (min-width: 40.063em) { - button, .button { - display: inline-block; - } -} - -.keystroke, kbd { - background-color: #ededed; - border-color: #dddddd; - color: #222222; - border-style: solid; - border-width: 1px; - margin: 0; - font-family: "Consolas", "Menlo", "Courier", monospace; - font-size: inherit; - padding: 0.125rem 0.25rem 0; - border-radius: 3px; -} - - - -/* We use this to get basic styling on all basic form elements */ -input[type="text"], -input[type="password"], -input[type="date"], -input[type="datetime"], -input[type="datetime-local"], -input[type="month"], -input[type="week"], -input[type="email"], -input[type="number"], -input[type="search"], -input[type="tel"], -input[type="time"], -input[type="url"], -textarea { - -webkit-appearance: none; - background-color: white; - font-family: inherit; - border: 1px solid #cccccc; - color: rgba(0, 0, 0, 0.75); - display: block; - font-size: 0.875rem; - margin: 0 0 1rem 0; - padding: 0.4rem; - width: 100%; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} - input[type="text"]:focus, - input[type="password"]:focus, - input[type="date"]:focus, - input[type="datetime"]:focus, - input[type="datetime-local"]:focus, - input[type="month"]:focus, - input[type="week"]:focus, - input[type="email"]:focus, - input[type="number"]:focus, - input[type="search"]:focus, - input[type="tel"]:focus, - input[type="time"]:focus, - input[type="url"]:focus, - textarea:focus {} - input[type="text"]:focus, - input[type="password"]:focus, - input[type="date"]:focus, - input[type="datetime"]:focus, - input[type="datetime-local"]:focus, - input[type="month"]:focus, - input[type="week"]:focus, - input[type="email"]:focus, - input[type="number"]:focus, - input[type="search"]:focus, - input[type="tel"]:focus, - input[type="time"]:focus, - input[type="url"]:focus, - textarea:focus { - background: #fafafa; - border-color: #999999; - outline: none; } - input[type="text"][disabled], fieldset[disabled] input[type="text"], - input[type="password"][disabled], fieldset[disabled] - input[type="password"], - input[type="date"][disabled], fieldset[disabled] - input[type="date"], - input[type="datetime"][disabled], fieldset[disabled] - input[type="datetime"], - input[type="datetime-local"][disabled], fieldset[disabled] - input[type="datetime-local"], - input[type="month"][disabled], fieldset[disabled] - input[type="month"], - input[type="week"][disabled], fieldset[disabled] - input[type="week"], - input[type="email"][disabled], fieldset[disabled] - input[type="email"], - input[type="number"][disabled], fieldset[disabled] - input[type="number"], - input[type="search"][disabled], fieldset[disabled] - input[type="search"], - input[type="tel"][disabled], fieldset[disabled] - input[type="tel"], - input[type="time"][disabled], fieldset[disabled] - input[type="time"], - input[type="url"][disabled], fieldset[disabled] - input[type="url"], - textarea[disabled], fieldset[disabled] - textarea { - background-color: #dddddd; } - input[type="text"].radius, - input[type="password"].radius, - input[type="date"].radius, - input[type="datetime"].radius, - input[type="datetime-local"].radius, - input[type="month"].radius, - input[type="week"].radius, - input[type="email"].radius, - input[type="number"].radius, - input[type="search"].radius, - input[type="tel"].radius, - input[type="time"].radius, - input[type="url"].radius, - textarea.radius { - border-radius: 3px; } - -input[type="submit"] { - -webkit-appearance: none; } - -/* Respect enforced amount of rows for textarea */ -textarea[rows] { - height: auto; } - -/* Add height value for select elements to match text input height */ -select { - -webkit-appearance: none !important; - background-color: #fafafa; - background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iM3B4IiB2aWV3Qm94PSIwIDAgNiAzIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2IDMiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwb2x5Z29uIHBvaW50cz0iNS45OTIsMCAyLjk5MiwzIC0wLjAwOCwwICIvPjwvc3ZnPg=="); - background-repeat: no-repeat; - background-position: 97% center; - border: 1px solid #cccccc; - padding: 0.5rem; - font-size: 0.875rem; - color: rgba(0, 0, 0, 0.75); - line-height: normal; - border-radius: 0; -} - select.radius { - border-radius: 3px; } - select:hover { - background-color: #f3f3f3; - border-color: #999999; } - -/* Adjust margin for form elements below */ -input[type="file"], -input[type="checkbox"], -input[type="radio"], -select { - margin: 0 0 1rem 0; } - -input[type="checkbox"] + label, -input[type="radio"] + label { - display: inline-block; - - margin-left: 0.5rem; - margin-right: 1rem; - margin-bottom: 0; - vertical-align: baseline; } - -/* Normalize file input width */ -input[type="file"] { - width: 100%; } diff --git a/web-ui/app/scss/vendor/_reset.scss b/web-ui/app/scss/vendor/_reset.scss deleted file mode 100644 index 55f8d054..00000000 --- a/web-ui/app/scss/vendor/_reset.scss +++ /dev/null @@ -1,421 +0,0 @@ -/*! normalize.css v3.0.1 | MIT License | git.io/normalize */ - -/** - * 1. Set default font family to sans-serif. - * 2. Prevent iOS text size adjust after orientation change, without disabling - * user zoom. - */ - -html { - font-family: sans-serif; - /* 1 */ - -ms-text-size-adjust: 100%; - /* 2 */ - -webkit-text-size-adjust: 100%; - /* 2 */ -} - -/** - * Remove default margin. - */ - -body { - margin: 0; -} - -/* HTML5 display definitions - ========================================================================== */ - -/** - * Correct `block` display not defined for any HTML5 element in IE 8/9. - * Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox. - * Correct `block` display not defined for `main` in IE 11. - */ - -article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { - display: block; -} - -/** - * 1. Correct `inline-block` display not defined in IE 8/9. - * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. - */ - -audio, canvas, progress, video { - display: inline-block; - /* 1 */ - vertical-align: baseline; - /* 2 */ -} - -/** - * Prevent modern browsers from displaying `audio` without controls. - * Remove excess height in iOS 5 devices. - */ - -audio:not([controls]) { - display: none; - height: 0; -} - -/** - * Address `[hidden]` styling not present in IE 8/9/10. - * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. - */ - -[hidden], template { - display: none; -} - -/* Links - ========================================================================== */ - -/** - * Remove the gray background color from active links in IE 10. - */ - -a { - background: transparent; - &:active, &:hover { - outline: 0; - } -} - -/** - * Improve readability when focused and also mouse hovered in all browsers. - */ - -/* Text-level semantics - ========================================================================== */ - -/** - * Address styling not present in IE 8/9/10/11, Safari, and Chrome. - */ - -abbr[title] { - border-bottom: 1px dotted; -} - -/** - * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. - */ - -b, strong { - font-weight: bold; -} - -/** - * Address styling not present in Safari and Chrome. - */ - -dfn { - font-style: italic; -} - -/** - * Address variable `h1` font-size and margin within `section` and `article` - * contexts in Firefox 4+, Safari, and Chrome. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/** - * Address styling not present in IE 8/9. - */ - -mark { - background: #ff0; - color: #000; -} - -/** - * Address inconsistent and variable font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` affecting `line-height` in all browsers. - */ - -sub { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; - top: -0.5em; -} - -sub { - bottom: -0.25em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Remove border when inside `a` element in IE 8/9/10. - */ - -img { - border: 0; -} - -/** - * Correct overflow not hidden in IE 9/10/11. - */ - -svg:not(:root) { - overflow: hidden; -} - -/* Grouping content - ========================================================================== */ - -/** - * Address margin not present in IE 8/9 and Safari. - */ - -figure { - margin: 1em 40px; -} - -/** - * Address differences between Firefox and other browsers. - */ - -hr { - -moz-box-sizing: content-box; - box-sizing: content-box; - height: 0; -} - -/** - * Contain overflow in all browsers. - */ - -pre { - overflow: auto; -} - -/** - * Address odd `em`-unit font size rendering in all browsers. - */ - -code, kbd, pre, samp { - font-family: monospace, monospace; - font-size: 1em; -} - -/* Forms - ========================================================================== */ - -/** - * Known limitation: by default, Chrome and Safari on OS X allow very limited - * styling of `select`, unless a `border` property is set. - */ - -/** - * 1. Correct color not being inherited. - * Known issue: affects color of disabled elements. - * 2. Correct font properties not being inherited. - * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. - */ - -button, input, optgroup, select, textarea { - color: inherit; - /* 1 */ - font: inherit; - /* 2 */ - margin: 0; - /* 3 */ -} - -/** - * Address `overflow` set to `hidden` in IE 8/9/10/11. - */ - -button { - overflow: visible; - text-transform: none; -} - -/** - * Address inconsistent `text-transform` inheritance for `button` and `select`. - * All other form control elements do not inherit `text-transform` values. - * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. - * Correct `select` style inheritance in Firefox. - */ - -select { - text-transform: none; -} - -/** - * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` - * and `video` controls. - * 2. Correct inability to style clickable `input` types in iOS. - * 3. Improve usability and consistency of cursor style between image-type - * `input` and others. - */ - -button, html input[type="button"] { - -webkit-appearance: button; - /* 2 */ - cursor: pointer; - /* 3 */ -} - -input { - &[type="reset"], &[type="submit"] { - -webkit-appearance: button; - /* 2 */ - cursor: pointer; - /* 3 */ - } -} - -/** - * Re-set default cursor for disabled elements. - */ - -button[disabled], html input[disabled] { - cursor: default; -} - -/** - * Remove inner padding and border in Firefox 4+. - */ - -button::-moz-focus-inner { - border: 0; - padding: 0; -} - -input { - &::-moz-focus-inner { - border: 0; - padding: 0; - } - line-height: normal; - &[type="checkbox"], &[type="radio"] { - box-sizing: border-box; - /* 1 */ - padding: 0; - /* 2 */ - } - &[type="number"] { - &::-webkit-inner-spin-button, &::-webkit-outer-spin-button { - height: auto; - } - } - &[type="search"] { - -webkit-appearance: textfield; - /* 1 */ - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; - /* 2 */ - box-sizing: content-box; - &::-webkit-search-cancel-button, &::-webkit-search-decoration { - -webkit-appearance: none; - } - } -} - -/** - * Address Firefox 4+ setting `line-height` on `input` using `!important` in - * the UA stylesheet. - */ - -/** - * It's recommended that you don't attempt to style these elements. - * Firefox's implementation doesn't respect box-sizing, padding, or width. - * - * 1. Address box sizing set to `content-box` in IE 8/9/10. - * 2. Remove excess padding in IE 8/9/10. - */ - -/** - * Fix the cursor style for Chrome's increment/decrement buttons. For certain - * `font-size` values of the `input`, it causes the cursor style of the - * decrement button to change from `default` to `text`. - */ - -/** - * 1. Address `appearance` set to `searchfield` in Safari and Chrome. - * 2. Address `box-sizing` set to `border-box` in Safari and Chrome - * (include `-moz` to future-proof). - */ - -/** - * Remove inner padding and search cancel button in Safari and Chrome on OS X. - * Safari (but not Chrome) clips the cancel button when the search input has - * padding (and `textfield` appearance). - */ - -/** - * Define consistent border, margin, and padding. - */ - -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} - -/** - * 1. Correct `color` not being inherited in IE 8/9/10/11. - * 2. Remove padding so people aren't caught out if they zero out fieldsets. - */ - -legend { - border: 0; - /* 1 */ - padding: 0; - /* 2 */ -} - -/** - * Remove default vertical scrollbar in IE 8/9/10/11. - */ - -textarea { - overflow: auto; -} - -/** - * Don't inherit the `font-weight` (applied by a rule above). - * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. - */ - -optgroup { - font-weight: bold; -} - -/* Tables - ========================================================================== */ - -/** - * Remove most spacing between table cells. - */ - -table { - border-collapse: collapse; - border-spacing: 0; -} - -td, th { - padding: 0; -} diff --git a/web-ui/app/scss/vendor/_scut.scss b/web-ui/app/scss/vendor/_scut.scss deleted file mode 100644 index 3e16fa65..00000000 --- a/web-ui/app/scss/vendor/_scut.scss +++ /dev/null @@ -1,1518 +0,0 @@ -/* -* Scut, a collection of Sass utilities -* to ease and improve our implementations of common style-code patterns. -* v1.3.0 -* Docs at http://davidtheclark.github.io/scut -*/ - -@mixin scut-clearfix { - - &:after { - content: ""; - display: table; - clear: both; - } - -} - -%scut-clearfix { - @include scut-clearfix; -} -@mixin scut-list-unstyled( - $no-margin: true -) { - - list-style-type: none; - padding-left: 0; - - @if $no-margin { - margin-top: 0; - margin-bottom: 0; - } - -} - -%scut-list-unstyled { - @include scut-list-unstyled(); -} -// Depends on `list-unstyled` and `clearfix`. - -@mixin scut-list-floated ( - $space: false, - $dir: left, - $no-margin: true -) { - - @include scut-list-unstyled($no-margin); - @include scut-clearfix; - - & > li { - float: $dir; - } - - @if $space { - & > li + li { - margin-#{$dir}: $space; - } - } - -} - -%scut-list-floated { - @include scut-list-floated; -} - -@function scut-autoOrValue ($val) { - @if $val == a or $val == auto { - @return auto; - } - @else { - @return $val; - } -} - -@mixin scut-coords ( - $coordinates: n n n n -) { - - $top: nth($coordinates, 1); - $right: nth($coordinates, 2); - $bottom: nth($coordinates, 3); - $left: nth($coordinates, 4); - - @if $top != n { - top: scut-autoOrValue($top); - } - @if $right != n { - right: scut-autoOrValue($right); - } - @if $bottom != n { - bottom: scut-autoOrValue($bottom); - } - @if $left != n { - left: scut-autoOrValue($left); - } - -} -@function scut-strip-unit ( - $num -) { - - @return $num / ($num * 0 + 1); - -} -// Depends on `scut-strip-unit`. - -$scut-em-base: 16 !default; - -@function scut-em ( - $pixels, - $base: $scut-em-base -) { - - // $base could be in em or px (no unit = px). - // Adjust accordingly to create a $divisor that - // serves as context for $pixels. - $multiplier: if(unit($base) == em, 16, 1); - $divisor: scut-strip-unit($base) * $multiplier; - - $em-vals: (); - @each $val in $pixels { - $val-in-ems: (scut-strip-unit($val) / $divisor) * 1em; - $em-vals: append($em-vals, $val-in-ems); - } - - @if length($em-vals) == 1 { - // return a single value instead of a list, - // so it can be used in calculations - @return nth($em-vals, 1); - } - @else { - @return $em-vals; - } - -} -// Depends on `scut-strip-unit`. - -$scut-rem-base: 16 !default; - -@function scut-rem ( - $pixels -) { - - $rem-vals: (); - @each $val in $pixels { - $val-in-rems: scut-strip-unit($val) / $scut-rem-base * 1rem; - $rem-vals: append($rem-vals, $val-in-rems); - } - - @if length($rem-vals) == 1 { - // return a single value instead of a list, - // so it can be used in calculations - @return nth($rem-vals, 1); - } - @else { - @return $rem-vals; - } - -} -@mixin scut-border ( - $style, - $sides: n y -) { - - @if length($sides) == 2 { - @if nth($sides, 1) != n { - border-top: $style; - border-bottom: $style; - } - @if nth($sides, 2) != n { - border-left: $style; - border-right: $style; - } - } - - @else if length($sides) == 4 { - @if nth($sides, 1) != n { - border-top: $style; - } - @if nth($sides, 2) != n { - border-right: $style; - } - @if nth($sides, 3) != n { - border-bottom: $style; - } - @if nth($sides, 4) != n { - border-left: $style; - } - } - - @else { - @warn "Scut-border requires a $sides argument of 2 or 4 values." - } - -} -@mixin scut-circle ( - $size, - $color: inherit -) { - - border-radius: 50%; - display: inline-block; - - @if $color == inherit { - // If user wants to inherit the color, - // take advantage of the fact that border - // color defaults to the text color of the element. - border-width: $size / 2; - border-style: solid; - height: 0; - width: 0; - } - @else { - // Otherwise, just use background-color. - background-color: $color; - height: $size; - width: $size; - } - -} -@mixin scut-color-swap ( - $off, - $on, - $duration: 0, - $bg: false -) { - - $transition-properties: null; - $off-is-list: type-of($off) == list; - $on-is-list: type-of($on) == list; - - // If $off IS a list, - // assign color and background-color. - @if $off-is-list { - color: nth($off, 1); - background-color: nth($off, 2); - $transition-properties: background-color, color; - } - - // If $off IS NOT a list and $bg is TRUE, - // assign background-color. - @else if $bg and not($off-is-list) { - background-color: $off; - $transition-properties: background-color; - } - - // If $off IS NOT a list and $bg is FALSE, - // assign color. - @else { - color: $off; - $transition-properties: color; - } - - // Only set-up transition if $duration != 0. - @if $duration != 0 { - transition-property: $transition-properties; - transition-duration: $duration; - } - - &:hover, - &:focus { - - // $on is treated the same as $off, above. - @if $on-is-list { - color: nth($on, 1); - background-color: nth($on, 2); - } - - @else if $bg and not($on-is-list) { - background-color: $on; - } - - @else { - color: $on; - } - } - -} -@mixin scut-hd-bp ( - $ratio: 1.3 -) { - - @media (-o-min-device-pixel-ratio: ($ratio / 1)), - (-webkit-min-device-pixel-ratio: $ratio), - (min-resolution: (round(96 * $ratio) * 1dpi)) { - @content; - } - -} - -@mixin scut-hide-visually { - - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; - -} - -%scut-hide-visually { - @include scut-hide-visually; -} -@mixin scut-image-replace { - - text-indent: 102%; - white-space: nowrap; - overflow: hidden; - padding: 0; - -} - -%scut-image-replace { - @include scut-image-replace; -} - -// Depends on scut-rem and scut-strip-unit - -@mixin scut-rem-fallback ( - $pixels, - $property: font-size -) { - - $px-vals: null; - @each $val in $pixels { - $val-in-px: scut-strip-unit($val) * 1px; - $px-vals: append($px-vals, $val-in-px); - } - $rem-vals: scut-rem($pixels); - - #{$property}: $px-vals; - #{$property}: $rem-vals; - -} -@mixin scut-reset-border-box { - // Make everything a border-box, because why not? - html { - box-sizing: border-box; - } - *, *:before, *:after { - box-sizing: inherit; - } -} - -@mixin scut-reset-antialias { - // Antialias! - body { - -webkit-font-smoothing: antialiased; - } - *, *:before, *:after { - -webkit-font-smoothing: inherit; - } -} - -@mixin scut-reset-semanticize { - // Make headers and semantic, not presentational. - h1, - h2, - h3, - h4, - h5, - h6 { - font-size: 1em; - font-weight: normal; - margin: 0; - } - b { - font-weight: normal; - } -} - -@mixin scut-reset-pointer { - // Clickable form elements should have a pointer. - label, - select, - option, - button { - cursor: pointer; - } -} - -@mixin scut-reset-form { - fieldset { - border: 0; - margin: 0; - padding: 0; - } - textarea { - resize: vertical; - } -} - -@mixin scut-reset-button { - // Reset default button styles, which are never used. - button, - input[type="button"], - input[type="submit"], - input[type="reset"] { - background: transparent; - border: 0; - color: inherit; - font: inherit; - margin: 0; - padding: 0; - width: auto; - -webkit-appearance: none; - -webkit-font-smoothing: antialiased; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - &::-moz-focus-inner { - padding: 0; - border: 0; - } - } -} - -@mixin scut-reset-paragraph { - // Some paragraph margins just get in the way. - p:first-of-type { - margin-top: 0; - } - p:last-of-type { - margin-bottom: 0; - } -} - -@mixin scut-reset-media { - // You want these elements fluid, probably. - img, - video { - max-width: 100%; - height: auto; - } -} - -@mixin scut-reset-figure { - // Remove default margins. - figure { - margin: 0; - } -} - -// Call them all, minus exclusions! -@mixin scut-reset ($exclude: false) { - @if not(index($exclude, border-box)) { - @include scut-reset-border-box; - } - @if not(index($exclude, antialias)) { - @include scut-reset-antialias; - } - @if not(index($exclude, semanticize)) { - @include scut-reset-semanticize; - } - @if not(index($exclude, pointer)) { - @include scut-reset-pointer; - } - @if not(index($exclude, form)) { - @include scut-reset-form; - } - @if not(index($exclude, button)) { - @include scut-reset-button; - } - @if not(index($exclude, paragraph)) { - @include scut-reset-paragraph; - } - @if not(index($exclude, media)) { - @include scut-reset-media; - } - @if not(index($exclude, figure)) { - @include scut-reset-figure; - } -} - -@mixin scut-selected ( - $active: false -) { - - @if $active { - &:hover, - &:focus, - &:active { - @content; - } - } - @else { - &:hover, - &:focus { - @content; - } - } - -} -@mixin scut-triangle ( - $direction: right, - $size: 0.75em, - $color: inherit -) { - - display: inline-block; - height: 0; - width: 0; - // For improved appearance in some Webkit browsers - -webkit-transform: rotate(360deg); - - // Set up some variables - $width: null; - $height: null; - $border-widths: null; - - @if type-of($size) == list { - $width: nth($size, 1); - $height: nth($size, 2); - } - @else { - $width: $size; - $height: $size; - } - - @if ($direction == up) or ($direction == down) { - // For up and down, width gets two borders but height only one, - // so divide second border-width value by 2 - $border-widths: $height ($width / 2); - } - @else if ($direction == right) or ($direction == left) { - // For right and left, height gets two borders but width only one, - // so divide first border-width value by 2 - $border-widths: ($height / 2) $width; - } - @else { - // For right triangles (the rest), both sides get two borders, - // so divide both by 2 - $border-widths: ($height / 2) ($width / 2); - } - - border-width: $border-widths; - border-style: solid; - - - // STANDARD TRIANGLES - - @if ($direction == up) or ($direction == down) or ($direction == right) or ($direction == left) { - border-color: transparent; - @if $direction == up { - border-bottom-color: $color; - border-top-width: 0; - } - @else if $direction == right { - border-left-color: $color; - border-right-width: 0; - } - @else if $direction == down { - border-top-color: $color; - border-bottom-width: 0; - } - @else if $direction == left { - border-right-color: $color; - border-left-width: 0; - } - } - - - // CORNER TRIANGLES - - @else if ($direction == top-right) or ($direction == top-left) { - border-top-color: $color; - border-bottom-color: transparent; - @if $direction == top-right { - border-left-color: transparent; - border-right-color: $color; - } - @else if $direction == top-left { - border-left-color: $color; - border-right-color: transparent; - } - } - - @else if ($direction == bottom-right) or ($direction == bottom-left) { - border-top-color: transparent; - border-bottom-color: $color; - @if $direction == bottom-right { - border-left-color: transparent; - border-right-color: $color; - } - @else if $direction == bottom-left { - border-left-color: $color; - border-right-color: transparent; - } - } - -} - -%scut-triangle { - @include scut-triangle; -} -@mixin scut-center-absolutely ( - $dimensions -) { - - $width: nth($dimensions, 1); - $height: nth($dimensions, 2); - - position: absolute; - - @if $width != n { - width: $width; - left: 50%; - margin-left: (-$width / 2); - } - - @if $height != n { - height: $height; - top: 50%; - margin-top: (-$height / 2); - } - -} -@mixin scut-center-block ( - $max-width: false -) { - - margin-left: auto; - margin-right: auto; - @if $max-width { - max-width: $max-width; - } - -} - -%scut-center-block { - @include scut-center-block; -} - -@mixin scut-center-transform ( - $axis: false // or x or y -) { - - position: absolute; - - @if $axis != x { - top: 50%; - margin-top: auto; - margin-bottom: auto; - } - - @if $axis != y { - left: 50%; - margin-left: auto; - margin-right: auto; - } - - $translate-val: null; - - @if not($axis) { - $translate-val: translate(-50%, -50%); - } - @else if $axis != x { - $translate-val: translateY(-50%); - } - @else if $axis != y { - $translate-val: translateX(-50%); - } - - -webkit-transform: $translate-val; - -ms-transform: $translate-val; - transform: $translate-val; -} - -%scut-center-transform { - @include scut-center-transform; -} - -%scut-center-transform-x { - @include scut-center-transform(x); -} - -%scut-center-transform-y { - @include scut-center-transform(y); -} - -@mixin scut-fill ( - $width-height: false -) { - - position: absolute; - left: 0; - top: 0; - @if $width-height { - width: 100%; - height: 100%; - } - @else { - right: 0; - bottom: 0; - } - -} - -%scut-fill { - @include scut-fill; -} -@mixin scut-list-custom ( - $content: "\2022", - $marker-width: 0.75em, - $pad: 0, - $no-margin: false -) { - - $content-val: null; - $counter: index($content, count); - @if $counter { - @if length($content) == 3 { - $content-val: counter(scutlistcounter, nth($content, 3))nth($content,2); - } - @else if length($content) == 2 { - $content-val: counter(scutlistcounter)nth($content,2); - } - @else { - $content-val: counter(scutlistcounter); - } - } - @else { - $content-val: $content; - } - - padding-left: $marker-width + $pad; - list-style-type: none; - - @if $no-margin { - margin-top: 0; - margin-bottom: 0; - } - - & > li { - position: relative; - @if $counter { - counter-increment: scutlistcounter; - } - &:before { - content: $content-val; - display: block; - position: absolute; - top: 0; - left: -$marker-width; - width: $marker-width; - @content; - } - } - -} -// Depends on `list-floated`, which depends in turn on `list-unstyled` and `clearfix`. - -@mixin scut-list-divided ( - $divider: "|", - $space: 0.5em, - $dir: left, - $height: false, - $no-margin: true -) { - - @include scut-list-floated($dir: $dir, $no-margin: $no-margin); - - $pseudo: if($dir == left, 'before', 'after'); - - // If an explicit height is passed, - // things are different: All
  • s - // need the pseudo-element (to force height), - // but the first's must be hidden. - - @if $height { - & > li { - height: $height; - } - & > li:#{$pseudo} { - height: $height; - content: $divider; - display: inline-block; - vertical-align: middle; - @content; - } - & > li:first-child:#{$pseudo} { - width: 0; - overflow: hidden; - } - } - - & > li + li:#{$pseudo} { - @if not($height) { - content: $divider; - display: inline-block; - @content; - } - margin-left: $space; - margin-right: $space; - } - -} - -%scut-list-bar { - @include scut-list-divided; -} - -%scut-list-breadcrumb { - @include scut-list-divided("/"); -} -// Depends on `list-unstyled`. - -@mixin scut-list-inline ( - $space: false, - $no-margin: true -) { - - @include scut-list-unstyled($no-margin); - - & > li { - display: inline-block; - } - - @if $space { - & > li + li { - margin-left: $space; - } - } - -} - -%scut-list-inline { - @include scut-list-inline; -} -// Depends on `list-unstyled`. - -@mixin scut-list-punctuated ( - $divider: ", ", - $display: inline, - $no-margin: true -) { - - @include scut-list-unstyled($no-margin); - - & > li { - display: $display; - &:not(:last-child):after { - content: $divider; - } - } - -} - -%scut-list-comma { - @include scut-list-punctuated; -} -@mixin scut-margin ( - $margin -) { - - @if length($margin) == 1 and $margin != n { - margin-top: $margin; - margin-right: $margin; - margin-bottom: $margin; - margin-left: $margin; - } - - @if length($margin) == 2 { - $margin-y: nth($margin, 1); - $margin-x: nth($margin, 2); - @if $margin-y != n { - margin-top: $margin-y; - margin-bottom: $margin-y; - } - @if $margin-x != n { - margin-left: $margin-x; - margin-right: $margin-x; - } - } - - @if length($margin) == 3 { - $margin-y-top: nth($margin, 1); - $margin-x: nth($margin, 2); - $margin-y-bottom: nth($margin, 3); - @if $margin-y-top != n { - margin-top: $margin-y-top; - } - @if $margin-x != n { - margin-right: $margin-x; - margin-left: $margin-x; - } - @if $margin-y-bottom != n { - margin-bottom: $margin-y-bottom; - } - } - - @if length($margin) == 4 { - $margin-top: nth($margin, 1); - $margin-right: nth($margin, 2); - $margin-bottom: nth($margin, 3); - $margin-left: nth($margin, 4); - @if $margin-top != n { - margin-top: $margin-top; - } - @if $margin-right != n { - margin-right: $margin-right; - } - @if $margin-bottom != n { - margin-bottom: $margin-bottom; - } - @if $margin-left != n { - margin-left: $margin-left; - } - } - -} -@mixin scut-padding ( - $padding -) { - - @if length($padding) == 1 and $padding != n { - padding-top: $padding; - padding-right: $padding; - padding-bottom: $padding; - padding-left: $padding; - } - - @if length($padding) == 2 { - $padding-y: nth($padding, 1); - $padding-x: nth($padding, 2); - @if $padding-y != n { - padding-top: $padding-y; - padding-bottom: $padding-y; - } - @if $padding-x != n { - padding-left: $padding-x; - padding-right: $padding-x; - } - } - - @if length($padding) == 3 { - $padding-y-top: nth($padding, 1); - $padding-x: nth($padding, 2); - $padding-y-bottom: nth($padding, 3); - @if $padding-y-top != n { - padding-top: $padding-y-top; - } - @if $padding-x != n { - padding-right: $padding-x; - padding-left: $padding-x; - } - @if $padding-y-bottom != n { - padding-bottom: $padding-y-bottom; - } - } - - @if length($padding) == 4 { - $padding-top: nth($padding, 1); - $padding-right: nth($padding, 2); - $padding-bottom: nth($padding, 3); - $padding-left: nth($padding, 4); - @if $padding-top != n { - padding-top: $padding-top; - } - @if $padding-right != n { - padding-right: $padding-right; - } - @if $padding-bottom != n { - padding-bottom: $padding-bottom; - } - @if $padding-left != n { - padding-left: $padding-left; - } - } -} -// Depends on `positioning-coordinates`. - -@mixin scut-absolute ( - $coordinates: 0 n n 0 -) { - - position: absolute; - @include scut-coords($coordinates); - -} - -%scut-absolute { - @include scut-absolute; -} -// Depends on `positioning-coordinates`. - -@mixin scut-fixed ( - $coordinates: 0 n n 0 -) { - - position: fixed; - @include scut-coords($coordinates); - -} - -%scut-fixed { - @include scut-fixed; -} -// Depends on `positioning-coordinates`. - -@mixin scut-relative ( - $coordinates: n n n n -) { - - position: relative; - @include scut-coords($coordinates); - -} -@mixin scut-ratio-box ( - $ratio: 1/1 -) { - - overflow: hidden; - position: relative; - - // The container's height, as a percentage of the - // container's width, is set by assigning - // padding-top to a pseudo-element. - &:before { - content: ""; - display: block; - height: 0; - padding-top: (1 / $ratio) * 100%; - } - -} - -%scut-ratio-box { - @include scut-ratio-box; -} -@mixin scut-size( - $size -) { - - @if length($size) == 1 { - width: $size; - height: $size; - } - @else if length($size) == 2 { - width: nth($size, 1); - height: nth($size, 2); - } - -} -@mixin scut-sticky-footer-fixed ( - $height, - $wrapper: ".wrapper", - $footer: ".scut-sticky" -) { - - html, - body { - height: 100%; - margin: 0; - padding: 0; - } - - #{$wrapper} { - min-height: 100%; - margin-bottom: -$height; - &:after { - content: ""; - display: block; - } - } - - #{$wrapper}:after, - #{$footer} { - height: $height; - } - -} - -// deprecated -@mixin scut-sticky-footer ( - $height, - $wrapper: ".wrapper", - $footer: ".scut-sticky" -){ - @include scut-sticky-footer-fixed($height, $wrapper, $footer); -} -@mixin scut-sticky-footer-fluid ( - $wrapper: ".wrapper", - $footer: ".scut-sticky" -) { - - html, - body { - height: 100%; - margin: 0; - padding: 0; - } - - #{$wrapper} { - display: table; - height: 100%; - width: 100%; - } - - #{$footer} { - display: table-row; - height: 1px; - } - -} -@mixin scut-vcenter-ib ( - $inner... -) { - - // The inner element is vertically centered - // by middle-aligning it with an inline pseudo-element - // whose height is 100%. - - &:before { - content: ""; - height: 100%; - display: inline-block; - vertical-align: middle; - // A small negative right margin is set - // to account for the default - // word-spacing of inline-block. - margin-right: -0.25em; - } - - $inner: if(length($inner) == 0, ".scut-inner", $inner); - @each $cell-selector in $inner { - $cell-selector: unquote($cell-selector); - & > #{$cell-selector} { - display: inline-block; - vertical-align: middle; - } - } - -} - -%scut-vcenter-ib { - @include scut-vcenter-ib; -} - -@mixin scut-vcenter-lh ( - $height -) { - - height: $height; - line-height: $height; - -} -@mixin scut-vcenter-td ( - $inner... -) { - - display: table; - - $inner: if(length($inner) == 0, ".scut-inner", $inner); - @each $cell-selector in $inner { - $cell-selector: unquote($cell-selector); - & > #{$cell-selector} { - display: table-cell; - vertical-align: middle; - } - } - -} - - -%scut-vcenter-td { - @include scut-vcenter-td; -} - -// Depends on scut-center-transform - -@mixin scut-vcenter-tt () { - @include scut-center-transform(y); -} - -%scut-vcenter-tt { - @include scut-vcenter-tt; -} -// space -$scut-space: "\0020"; -// non-breaking space -$scut-nbsp: "\00a0"; - -// quotation mark -$scut-quot: "\0022"; -// left single curly quote -$scut-lsquo: "\2018"; -// right single curly quote -$scut-rsquo: "\2019"; -// left double curly quote -$scut-ldquo: "\201C"; -// right double curly quote -$scut-rdquo: "\201D"; -// left single angle quote (guillemet) -$scut-lsaquo: "\2039"; -// right single angle quote (guillemet) -$scut-rsaquo: "\203A"; -// left double angle quote (guillemet) -$scut-laquo: "\00ab"; -// right double angle quote (guillemet) -$scut-raquo: "\00bb"; - -// em dash (mutton) -$scut-mdash: "\2014"; -// en dash (nut) -$scut-ndash: "\2013"; -// hyphen -$scut-hyphen: "\2010"; - -// ampersand -$scut-amp: "\0026"; -// greater than -$scut-gt: "\003e"; -// less than -$scut-lt: "\003c"; -// times -$scut-times: "\00D7"; -// big times -$scut-bigtimes: "\2715"; -// checkmark -$scut-checkmark: "\2713"; - -// section sign (double S, hurricane, sectional symbol, the legal doughnut, signum sectionis) -$scut-sect: "\00a7"; -// paragraph symbol (pilcrow) -$scut-para: "\00b6"; - -// middot (interpunct, interpoint) -$scut-middot: "\00b7"; -// o-slash (slashed o) -$scut-oslash: "\00f8"; -// bullet -$scut-bull: "\2022"; -// white bullet -$scut-whibull: "\25E6"; -// horizontal ellipsis -$scut-hellip: "\2026"; -// vertical ellipsis -$scut-vellip: "\22EE"; -// midline horizontal ellipsis -$scut-midhellip: "\22EF"; - -// up-pointing triangle -$scut-utri: "\25b2"; -// down-pointing triangle -$scut-dtri: "\25bc"; -// left-pointing triangle -$scut-ltri: "\25c0"; -// right-pointing triangle -$scut-rtri: "\25b6"; -// up-pointing small triangle -$scut-ustri: "\25b4"; -// down-pointing small triangle -$scut-dstri: "\25be"; -// left-pointing small triangle -$scut-lstri: "\25c2"; -// right-pointing small triangle -$scut-rstri: "\25b8"; -// diamond -$scut-diamond: "\25c6"; -// fisheye -$scut-fisheye: "\25c9"; -// bullseye -$scut-bullseye: "\25ce"; -// circle -$scut-circle: "\25cf"; -// white circle -$scut-whitecircle: "\25cb"; -// square -$scut-square: "\25a0"; -// white square -$scut-whitesquare: "\25a1"; -// small square -$scut-ssquare: "\25aa"; -// small white square -$scut-swhitesquare: "\25ab"; -@function main-src($formats, $file-path, $font-family) { - // Return the list of `src` values, in order, that - // a good `@font-face` will need, including only - // those formats specified in the list `$formats`. - $result: (); - @if index($formats, eot) { - $eot-val: url('#{$file-path}.eot?#iefix') format('embedded-opentype'); - $result: append($result, $eot-val, comma); - } - @if index($formats, woff2) { - $woff2-val: url('#{$file-path}.woff2') format('woff2'); - $result: append($result, $woff2-val, comma); - } - @if index($formats, woff) { - $woff-val: url('#{$file-path}.woff') format('woff'); - $result: append($result, $woff-val, comma); - } - @if index($formats, ttf) { - $ttf-val: url('#{$file-path}.ttf') format('truetype'); - $result: append($result, $ttf-val, comma); - } - @if index($formats, svg) { - $svg-val: url('#{$file-path}.svg##{$font-family}') format('svg'); - $result: append($result, $svg-val, comma); - } - @return $result; -} - -@mixin scut-font-face ( - $font-family, - $file-path, - $weight: normal, - $style: normal, - $formats: eot woff2 woff ttf svg -) { - - @if index('italic' 'oblique', $weight) { - $style: $weight; - $weight: normal; - } - - @font-face { - font-family: $font-family; - font-weight: $weight; - font-style: $style; - - @if index($formats, eot) { - src: url('#{$file-path}.eot'); - } - src: main-src($formats, $file-path, $font-family); - } - -} - -@mixin scut-hanging-indent ( - $indent: 1em -) { - - // padding-left creates the indent, - // while text-indent pulls the first line - // back to the edge. - - padding-left: $indent; - text-indent: -$indent; - -} - -%scut-hanging-indent { - @include scut-hanging-indent; -} -@mixin scut-indented-ps ( - $indent: 1.5em, - $no-first-indent: true -) { - - p { - margin: 0; - text-indent: $indent; - } - - @if $no-first-indent { - p:first-of-type { - text-indent: 0; - } - } - -} - -%scut-indented-ps { - @include scut-indented-ps; -} -@mixin scut-key-val ( - $divider: ":", - $pad: 0.25em, - $indent: 1em, - $spacing: 0, - $pad-left: 0 -) { - - & > dt { - clear: both; - float: left; - &:after { - content: $divider; - margin-right: $pad; - @if $pad-left != 0 { - margin-left: $pad-left; - } - } - } - - & > dd { - margin-left: $indent; - @if $spacing != 0 { - margin-bottom: $spacing; - } - } - -} - -%scut-key-val { - @include scut-key-val; -} -@mixin scut-link-bb ( - $color: inherit, - $style: solid, - $width: 1px -) { - - text-decoration: none; - - border-bottom-width: $width; - border-bottom-style: $style; - @if $color != inherit { - border-bottom-color: $color; - } - -} - -%scut-link-bb { - @include scut-link-bb; -} -// SCUT LINK UNSTYLED -// http://davidtheclark.github.io/scut/#link-unstyled - -@mixin scut-link-unstyled() { - - text-decoration: none; - color: inherit; - -} - -%scut-link-unstyled { - @include scut-link-unstyled(); -} - -@mixin scut-reverse-italics ( - $elements: null -) { - - $element-list: em, cite, i; - font-style: italic; - #{join($element-list, $elements)} { - font-style: normal; - } - -} - -%scut-reverse-italics { - @include scut-reverse-italics; -} - -@mixin scut-side-lined ( - $height: 1px, - $space: 0.5em, - $color: inherit, - $style: solid, - $v-adjust: false, - $double: false -) { - - display: block; - overflow: hidden; - text-align: center; - - &:before, - &:after { - content: ""; - display: inline-block; - vertical-align: middle; - position: relative; - width: 50%; - - border-top-style: $style; - border-top-width: $height; - - @if $color != inherit { - border-top-color: $color; - } - - @if $v-adjust != false { - bottom: $v-adjust; - } - - @if $double != false { - height: $double; - border-bottom-style: $style; - border-bottom-width: $height; - @if $color != inherit { - border-bottom-color: $color; - } - } - } - - &:before { - right: $space; - margin-left: -50%; - } - &:after { - left: $space; - margin-right: -50%; - } - -} - -%scut-side-lined { - @include scut-side-lined; -} -@mixin scut-truncate { - - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - -} - -%scut-truncate { - @include scut-truncate; -} \ No newline at end of file diff --git a/web-ui/app/scss/views/_action-bar.scss b/web-ui/app/scss/views/_action-bar.scss deleted file mode 100644 index 40e677b0..00000000 --- a/web-ui/app/scss/views/_action-bar.scss +++ /dev/null @@ -1,159 +0,0 @@ -#top-pane { - height: auto; - overflow: hidden; - background: $top_pane; - border-top: 1px solid $top_pane; - - #list-actions { - width: 100%; - height: 34px; - margin: 0; - border-top: 1px solid $white; - border-bottom: 2px solid lighten($top_pane, 30%); - background: $white; - clear: both; - overflow: hidden; - padding-left: 10px; - - li { - display: inline-block; - margin: 1px -3px; - vertical-align: top; - - input[type=checkbox] { - @include check-box; - - margin: 7px 13px 7px; - } - - select { - padding: 1px 3px; - margin: 0; - } - - input[type=button] { - margin: 2px; - padding: 4px 10px; - background: $background_light_grey; - color: $dark_grey; - text-transform: uppercase; - font-weight: 400; - font-size: 0.8em; - opacity: 0.7; - border: 1px solid darken($contrast, 10%); - - @include border-radius(1px); - - @include btn-transition; - - &:hover { - opacity: 1; - } - - &[disabled=disabled] { - opacity: 0.5; - cursor: default; - } - } - } - - #pagination-trigger { - cursor: pointer; - margin: 4px 12px 0 5px; - - span { - padding-left: 5px; - } - } - } - - #compose-search-trigger { - padding: 4px; - } - - #actions { - ul { - margin: 0; - - li { - display: inline-block; - margin-right: -5px; - - a { - transition: background-color 150ms ease-out; - background: $top_pane; - color: $white; - font-size: 1.5em; - display: block; - padding: 14px 20px; - margin: 0 1px 0px; - opacity: 0.35; - - &.selected { - background: $top_pane; - opacity: 1; - cursor: default; - } - - &:hover { - opacity: 1; - } - } - } - } - } - - #search-trigger { - padding: 5px; - padding-left: 0; - - input { - margin: 0; - padding: 8px 30px; - color: $navigation_background; - background: white; - border: none; - transition: background-color 150ms ease-out; - - &:hover { - background: darken(white, 2%); - } - - &:focus { - background: darken(white, 5%); - } - } - - form:before { - font-family: "FontAwesome"; - content: "\f002"; - position: absolute; - padding: 0 10px; - top: 15px; - color: $medium_light_grey; - } - } -} - -#refresh-mails-trigger { - i { - margin-top: 3px; - cursor: pointer; - opacity: 0.9; - padding: 4px; - - &:hover { - opacity: 1; - - &:after { - content: "\f021"; - } - - &:before { - content: attr(data-label); - font-size: 0.8em; - padding-right: 5px; - } - } - } -} diff --git a/web-ui/app/scss/views/_close-button.scss b/web-ui/app/scss/views/_close-button.scss deleted file mode 100644 index 37171c18..00000000 --- a/web-ui/app/scss/views/_close-button.scss +++ /dev/null @@ -1,22 +0,0 @@ -.close-mail-button { - $button-size: 27px; - - margin-right: 3px; - float: left; - background: $lighter_gray; - color: $medium_light_grey; - width: $button-size; - height: $button-size; - padding: 0; - border-radius: 0; - - &:hover, &:focus, &:active { - background-color: darken($lighter_gray, 2); - color: darken($medium_light_grey, 10); - } - - i { - padding: 0; - margin: 0; - } -} diff --git a/web-ui/app/scss/views/_compose-button.scss b/web-ui/app/scss/views/_compose-button.scss deleted file mode 100644 index 81e0bb33..00000000 --- a/web-ui/app/scss/views/_compose-button.scss +++ /dev/null @@ -1,27 +0,0 @@ -// COMPOSE BUTTON -#compose { - margin-bottom: 5px; - padding-right: 4px; - #compose-trigger { - width: 100%; - display: inline-block; - padding: 5px; - #compose-mails-trigger { - background: $action_buttons; - color: $white; - padding: 10px 30px; - text-align: center; - font-weight: 400; - font-size: 1.2em; - width: 100%; - height: 100%; - margin-bottom: 0px; - @include btn-transition; - &:hover { - background: lighten($action_buttons, 10%); - cursor: pointer; - } - } - } -} - diff --git a/web-ui/app/scss/views/_compose-view.scss b/web-ui/app/scss/views/_compose-view.scss deleted file mode 100644 index 9e120357..00000000 --- a/web-ui/app/scss/views/_compose-view.scss +++ /dev/null @@ -1,451 +0,0 @@ -.compose-view { - overflow: auto; - - &__buttons { - &-attachment { - cursor: pointer; - margin-left: 18px; - padding-top: 0px; - display: inline; - border: 1px $contrast solid; - background: $background_light_grey; - padding: 7px 4px; - font-size: 0.8em; - - span { - -ms-transform: rotate(224deg); - -webkit-transform: rotate(224deg); - transform: rotate(224deg); - outline: 0; - } - - i.fa-paperclip { - font-size: 1.7em; - } - - &--busy { - color: lighten($recipients_font_color, 10%); - cursor: progress; - } - } - } - - &__attachments { - &-wrapper { - padding: 0; - margin-top: 30px; - } - - &-list { - &-item { - display: block; - position: relative; - margin-bottom: 8px; - padding: 5px; - border: 1px solid $border_light_grey; - border-radius: 2px; - background-color: $contrast; - - &-label { - color: $attachment_text; - text-decoration: none; - - &:hover, &:focus { - color: $attachment_icon; - outline: none; - } - } - - &-icon { - color: #a2a2a2; - float: right; - margin-top: 7px; - cursor: pointer; - } - - &-progress { - width: 0%; - position: absolute; - right: 0; - left: 0; - top: 0; - bottom: 0; - min-height: 100%; - - &-bar { - height: 100%; - background-color: rgba($light_blue, 0.3); - } - } - - } - - &--upload { - display: none; - } - - } - - &-error { - background-color: $background_light_grey; - border-radius: 2px; - border: 1px solid $error; - display: block; - font-size: 0.9rem; - margin-bottom: 20px; - padding: 5px; - width: 100%; - - &-close { - float: left; - margin: 5px 5px 0 0; - } - - & > * { - color: $error; - } - - & > a { - display: inline-block; - text-decoration: underline; - padding: 5px; - } - } - } - -} - -// COMPOSE PANE -#compose-box, #draft-box, #reply-box, #feedback-box { - div.floatlabel { - position: relative; - } - - .input-container { - padding: 1px; - } - - label, span { - color: $recipients_font_color; - padding: 0.5rem; - display: inline-block; - } - - label { - padding: 13px 10px; - } - - span { - padding: 3px; - - &.attachment-size { - color: $attachment_size; - cursor: pointer; - } - } - - label.floatlabel { - padding: 0.4rem !important; - position: absolute; - font-size: 0.6rem; - transition: all 0.1s linear; - opacity: 0; - font-weight: bold; - } - - label.showfloatlabel { - color: $light_blue !important; - top: -0.3rem; - opacity: 1; - } - - input, textarea { - margin: 0; - border: none; - transition: all 0.1s linear; - } - - input.showfloatlabel, textarea.showfloatlabel { - padding-top: 1rem !important; - } - - input#subject, #feedback-subject { - font-size: 1.6875rem; - line-height: 1.4; - border-top: 1px solid $lighter_gray; - } - - #feedback-subject { - color: $dark_grey; - } - - textarea { - border-bottom: 2px solid $lighter_gray; - min-height: 400px; - font-family: inherit; - font-weight: normal; - font-size: 1rem; - line-height: 1.6; - text-rendering: optimizeLegibility; - } - - &.reply-box, &.forward-box { - margin: 0; - - h4 { - font-size: 0.9em; - font-style: italic; - color: $medium_grey; - margin: 2px 0; - clear: both; - cursor: pointer; - - &:hover { - background: $contrast; - } - } - - textarea { - min-height: 200px; - margin: 10px 0; - } - - p { - padding: 5px; - margin: 10px 0; - font-style: italic; - cursor: pointer; - - &:hover { - background: $contrast; - } - } - } - - button.close-mail-button { - margin: 1px; - } - - .buttons-group { - margin-top: 0px; - } - - .recipients-area { - -webkit-appearance: none; - background-color: white; - font-family: inherit; - display: flex; - flex-wrap: wrap; - font-size: 0.898em; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - position: relative; - - .compose-column-label { - width: 5%; - display: inline-block; - } - - .compose-column-recipients { - width: 95%; - display: inline-block; - } - - .recipients-label { - width: 100%; - height: 100%; - } - - .recipients-navigation-handler { - z-index: -1; - position: absolute; - top: -200px; - } - - .twitter-typeahead { - flex: 1 1 50px; - - .tt-dropdown-menu { - background: $dark_white; - - div div { - padding: 8px; - - &:hover { - background: $background_dropdown_grey; - } - } - } - } - - .invalid-format { - border-bottom: 1px dotted $error; - } - - input[type=text] { - vertical-align: top; - height: 35px; - margin-left: 1px; - font-size: 0.9em; - width: 100%; - } - - .fixed-recipient { - display: inline-block; - margin-right: -3px; - flex: none; - position: relative; - - .recipient-value { - &.selected { - border: 1px solid $medium_dark_grey; - } - - &:before { - font-family: FontAwesome; - padding-right: 6px; - font-size: 1.4em; - } - - &.encrypted { - border-bottom-color: $will_be_encrypted; - - &:before { - color: $will_be_encrypted; - content: "\f023 "; - } - } - - &.not-encrypted { - border-bottom-color: $wont_be_encrypted; - - &:before { - color: $wont_be_encrypted; - content: "\f09c"; - } - } - - &.deleting span { - text-decoration: line-through; - } - - & span { - margin: 0px; - padding: 0px 0px 0px 0px; - vertical-align: top; - cursor: pointer; - } - - margin: 3px; - padding: 5px; - background-color: $background_light_grey; - border: 1px solid $border_light_grey; - border-radius: 2px; - } - - .recipient-del { - position: relative; - color: $recipients_font_color; - - &:hover, &:focus { - color: $recipients_font_color; - } - - &:before { - margin-left: 0.4em; - font-weight: bold; - content: "x"; - } - - &.deleteTooltip:hover:after { - position: absolute; - content: attr(data-label); - font-size: 0.5rem; - - @include tooltip(25px, 0px); - } - } - } - - input.recipients-input:focus { - background-color: $dark_white !important; - border-color: $medium_light_grey; - outline: none; - width: 270px; - } - } - - .collapse { - display: block; - position: absolute; - right: 10px; - padding-right: 15px; - padding-left: 15px; - font-family: 'FontAwesome'; - font-weight: bolder; - font-size: larger; - cursor: pointer; - } - - .collapse + input, .collapse + input + * { - display: none; - } - - .collapse + input:checked + * { - display: block; - } -} - -#reply-section { - padding-left: 30px; - - .reply-container { - margin: 10px 0; - padding: 10px; - border: 1px dashed darken($contrast, 10%); - - @include btn-transition; - } - - button { - margin: 0; - } - - #all-recipients { - color: $black; - } - - #all-recipients:focus { - background-color: darken($contrast, 10%); - } - - #reply-button, #reply-all-button, #forward-button { - text-align: center; - font-weight: 100; - font-size: 1.1em; - background: $white; - color: $medium_light_grey; - padding: 25px; - margin: 0; - - @include border-radius(0); - - &:hover { - background: darken($contrast, 5%); - cursor: pointer; - } - } -} - -.buttons-group { - clear: both; - margin: 20px 0 0; - padding: 0; -} - -#draft-save-status { - float: right; - padding: 0.4rem 1.1rem; - color: $lighter_blue; -} diff --git a/web-ui/app/scss/views/_mail-list.scss b/web-ui/app/scss/views/_mail-list.scss deleted file mode 100644 index f5c4c60f..00000000 --- a/web-ui/app/scss/views/_mail-list.scss +++ /dev/null @@ -1,124 +0,0 @@ -.mail-list-entry { - @include scut-clearfix; - - border-bottom: 1px solid white; - transition: background-color 150ms ease-out; - font-weight: bold; - height: 80px; - position: relative; - - // Workaround: - // Foundation is of the opinion that a 1.6 line height for all lists - // is a totally good idea. Please remove when Foundation is gone - line-height: normal; - - &.status-read { - font-weight: normal; - color: $attachment_text; - - .mail-list-entry__checkbox::after { - display: none; - } - } - - - &.selected { - background: $light_blue; - z-index: 10; // overlay the box-shadow of the right page (z-index: 2) - - &:hover { - background: $light_blue; - } - - a { - color: $white; - } - } - - &:hover { - background: darken($contrast, 5%); - } - - &__checkbox { - margin-right: 5px; - display: block; - float: left; - margin: { - top: 8px; - left: 20px; - } - - &::after { - content: ''; - display: inline-block; - width: 8px; - height: 8px; - -moz-border-radius: 15px; - -webkit-border-radius: 15px; - border-radius: 15px; - background-color: $bullet-blue; - position: absolute; - left: 48px; - top: 13px; - } - - & > input[type=checkbox] { - @include check-box; - } - } - - &__item { - display: block; - color: $dark_grey; - padding: 8px 10px 10px 67px; - height: 100%; - - &-from { - white-space: nowrap; - font-size: 0.8em; - overflow: hidden; - text-overflow: ellipsis; - display: inline-block; - } - - &-date { - font-size: 0.7em; - float: right; - display: inline-block; - } - - &-subject { - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - width: 85%; - - &-icon { - color: $light_gray; - } - } - - &-attachment { - width: 14px; - text-align: right; - display: inline-block; - float: right; - color: $light_gray; - } - - &-tags { - @include tags; - - // Workaround: - // Foundation is of the opinion that a 1.6 line height and a 0.6 rem margin-bottom - // for all lists is a totally good idea. Please remove when Foundation is gone - line-height: normal; - margin-bottom: 0; - } - - &:hover, &:focus, &:active { - color: $dark_grey; - } - } -} diff --git a/web-ui/app/scss/views/_message-panel.scss b/web-ui/app/scss/views/_message-panel.scss deleted file mode 100644 index 4a0a7a6b..00000000 --- a/web-ui/app/scss/views/_message-panel.scss +++ /dev/null @@ -1,26 +0,0 @@ -.message-panel { - width: 100%; - margin: 10px auto; - position: fixed; - z-index: 10000; - text-align: center; - - &__growl { - padding: 5px 60px; - - &--success { - background: $warning; - color: darken($warning, 50%); - border: 1px solid darken($warning, 10%); - @include box-shadow(1px 1px 3px darken($warning, 60%)); - } - - &--error { - font-weight: bold; - color: white; - background: $error; - border: 1px solid darken($error, 10%); - @include box-shadow(1px 1px 3px darken($error, 60%)); - } - } -} diff --git a/web-ui/app/scss/views/_navigation.scss b/web-ui/app/scss/views/_navigation.scss deleted file mode 100644 index 2c33a791..00000000 --- a/web-ui/app/scss/views/_navigation.scss +++ /dev/null @@ -1,589 +0,0 @@ -#logo { - color: $white; -} - -#logout { - color: $white; - cursor: pointer; -} - -#user-settings-box { - position: fixed; - z-index: 10; - - & > div { - position: fixed; - left: 70px; - bottom: 0px; - z-index: 1; - padding: 10px 16px 10px 18px; - background-color: rgba($dark_slate_gray, 0.9); - min-width: 230px; - - &.extra-bottom-space { - bottom: 33px; - } - - header { - border-bottom: 1px solid white; - margin-bottom: 10px; - } - - #user-settings-close { - float: right; - } - - h1, i { - font-size: 1.2em; - color: white; - line-height: 1.2em; - } - - h2 { - font-size: 1.1em; - color: white; - line-height: 1.1em; - display: inline; - margin-left: 5px; - } - - i.fa-user { - margin-right: 10px; - float: left; - } - - i.fa-close { - margin-left: 10px; - float: right; - cursor: pointer; - } - - p { - font-size: 1.1em; - color: $light_orange; - } - } -} - -@keyframes hideshow { - 0% { - fill: lighten($logo_color, 30); - } - - 25% { - opacity: 1; - } - - 100% { - opacity: 0; - } -} - -.logo-part-animation-off { - animation: none; -} - -.logo-part-animation-on { - animation: hideshow 0.6s ease infinite; - opacity: 1; - - &:nth-child(2) { - opacity: 0; - animation-delay: 0.1s; - } - - &:nth-child(3) { - animation-delay: 0.2s; - } - - &:nth-child(4) { - animation-delay: 0.3s; - } - - &:nth-child(5) { - animation-delay: 0.4s; - } - - &:nth-child(6) { - animation-delay: 0.5s; - } -} - -.arrow-box:before { - right: 100%; - top: 65%; - border: 20px solid transparent; - content: " "; - height: 0; - width: 0; - position: absolute; - pointer-events: none; - border-right-color: rgba($dark_slate_gray, 0.9); - margin-top: -20px; -} - -.side-nav-toggle, .side-nav-toggle-icon { - color: white; - cursor: pointer; - - &:hover, &:focus { - color: white; - } - - background: $navigation_background; - - &.logout { - color: $action_buttons; - } -} - -.side-nav-toggle-icon { - padding: 6px 0px 8px 19px; - display: block; - left: 0; - top: 0; - position: relative; - - .fa-navicon { - font-size: 24px; - &:before { - margin-left: -5px; - } - } -} - -.left-off-canvas-logo { - svg { - width: 162px; - height: 56px; - padding-left: 6px; - padding-top: 2px; - - path, polygon, rect { - fill: $logo_color; - } - } -} - -.collapsed-nav { - width: 50px; - position: absolute; - height: 100vh; - background: $navigation_background; - - ul.shortcuts { - li { - position: relative; - margin-bottom: 5px; - opacity: 0.8; - - &.selected { - background: $contrast; - opacity: 1; - cursor: default; - - a { - color: $navigation_background; - } - } - - @include searching(6px, 26px, $medium_dark_grey, 0.9em); - - a { - display: block; - position: relative; - font-size: 1.4em; - padding: 5px; - color: white; - text-align: center; - - &:hover { - background: darken($contrast, 10%); - color: $navigation_background; - - @include btn-transition; - - &.logout { - color: $black; - background: $action_buttons; - } - } - - &[title]:hover:after { - content: attr(title); - - @include tooltip; - } - } - } - } - - #custom-tags-shortcuts { - li { - border-top: 1px solid $lighter_gray; - } - } - - div.shortcut-label { - font-size: xx-small; - text-transform: uppercase; - text-align: center; - } -} - -.move-right { - ul.shortcuts { - li { - display: none; - } - } -} - -.left-off-canvas-menu { - width: 222px; - -webkit-backface-visibility: hidden; - box-sizing: content-box; - left: 0; - top: 0; - bottom: 0; - position: absolute; - overflow-y: auto; -} - -.left-off-canvas-menu * { - -webkit-backface-visibility: hidden; -} - -.off-canvas-wrap { - -webkit-backface-visibility: hidden; - position: relative; - width: 100%; - overflow: hidden; -} - -.off-canvas-wrap.move-right, .off-canvas-wrap.move-left { - min-height: 100%; - -webkit-overflow-scrolling: touch; -} - -.inner-wrap { - -webkit-backface-visibility: hidden; - width: 100%; -} - -.inner-wrap:before, .inner-wrap:after { - content: " "; - display: table; -} - -.inner-wrap:after { - clear: both; -} - -.off-canvas-wrap.content { - -webkit-ransition: -webkit-transform 500ms ease; - -moz-transition: -moz-transform 500ms ease; - -ms-transition: -ms-transform 500ms ease; - -o-transition: -o-transform 500ms ease; - transition: transform 500ms ease; - - &.move-right { - -webkit-transform: translate3d(10rem, 0, 0); - -moz-transform: translate3d(10rem, 0, 0); - -ms-transform: translate3d(10rem, 0, 0); - -o-transform: translate3d(10rem, 0, 0); - transform: translate3d(10rem, 0, 0); - - #user-settings-box > div { - left: 20px; - } - } -} - -.move-right .exit-off-canvas { - -webkit-backface-visibility: hidden; - transition: background 300ms ease; - cursor: pointer; - display: block; - position: absolute; - background: rgba(255, 255, 255, 0.2); - top: 0; - bottom: 0; - left: 0; - right: 0; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} - -@media only screen and (min-width: 40.063em) { - .move-right .exit-off-canvas:hover { - background: rgba(255, 255, 255, 0.05); - } -} - -.off-canvas-wrap.move-right.menu { - position: absolute; -} - -.off-canvas-wrap.content { - left: 50px; - padding-right: 50px; -} - -.offcanvas-overlap .left-off-canvas-menu, .offcanvas-overlap .right-off-canvas-menu { - -ms-transform: none; - -webkit-transform: none; - -moz-transform: none; - -o-transform: none; - transform: none; -} - -.offcanvas-overlap .exit-offcanvas-menu { - -webkit-backface-visibility: hidden; - transition: background 300ms ease; - cursor: pointer; - display: block; - position: absolute; - background: rgba(255, 255, 255, 0.2); - top: 0; - bottom: 0; - left: 0; - right: 0; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} - -div.side-nav-bottom { - width: 100%; - position: fixed; - bottom: 20px; - background-color: $navigation_background; - - .version { - padding-left: 55px; - padding-bottom: 3px; - } -} - -#left-pane nav { - border-right: 1px solid lighten($navigation_background, 10%); - - ul#default-tag-list, #custom-tag-list { - li { - transition: background-color 150ms ease-out; - padding: 2px 10px; - cursor: pointer; - - &:hover { - background: $light_gray; - color: $navigation_background; - } - - &.selected { - font-weight: bold; - background: $contrast; - color: $navigation_background; - } - } - } - - ul#default-tag-list { - - span.tag-label { - padding-left: 2px; - } - - li { - padding: 5px 10px 5px 18px; - position: relative; - - @include searching(4px, 19px, $dark_grey, 0.7em); - - &:before { - font-size: 1.5em; - font-family: "FontAwesome"; - margin-right: 16px; - font-weight: normal; - position: relative; - top: 2px; - margin-left: -3px; - } - - &:after { - padding-left: 10px; - } - - &:nth-child(1) { - &:before { - content: "\f01c"; - } - } - - &:nth-child(2) { - &:before { - font-family: "icomoon"; - content: "\e900"; - margin-left: -5px; - } - } - - &:nth-child(3) { - &:before { - content: "\f040"; - } - } - - &:nth-child(4) { - &:before { - content: "\f014"; - } - } - - &:nth-child(5) { - &:before { - content: "\f187"; - margin-left: -5px; - } - } - } - } - - ul#custom-tag-list { - visibility: hidden; - opacity: 0; - transition-duration: 500ms; - height: 100%; - max-height: 220px; - overflow: auto; - background-color: lighten($navigation_background, 1); - - li { - white-space: nowrap; - overflow: hidden; - font-size: 0.8em; - padding: 5px 10px 5px 15px; - - &.custom-tag { - text-overflow: ellipsis; - } - - span.tag-label { - padding: 5px 20px 5px 38px; - } - } - - .unread-count, .total-count { - padding: 1px 4px; - position: relative; - } - - } - - ul#custom-tag-list.expanded { - visibility: visible; - opacity: 1; - } - - div.tags-icon { - border-top: 1px solid white; - padding-top: 25px; - margin-bottom: 20px; - - i { - font-size: 1.5em; - font-family: "FontAwesome"; - margin-right: 13px; - font-weight: normal; - position: relative; - top: 2px; - left: 16px; - } - - span.tag-label { - font-size: 0.9rem; - padding-left: 16px; - margin-bottom: 10px; - } - } - - ul#logout, ul#feedback, ul#user-settings-icon { - margin-bottom: 0; - - li { - background-color: $navigation_background; - padding: 5px 10px; - position: relative; - - @include searching(4px, 19px, $dark_grey, 0.7em); - - &:hover { - color: $navigation_background; - } - - div { - padding-left: 7px; - - &:before { - font-size: 1.5em; - font-family: "FontAwesome"; - margin-right: 13px; - font-weight: normal; - position: relative; - top: 2px; - } - } - } - } - - ul { - &#logout li { - color: $action_buttons; - - &:hover { - background-color: $action_buttons; - } - } - - &#user-settings-icon { - li { - color: white; - - &:hover { - background-color: white; - } - } - } - - &#feedback { - margin-bottom: 0; - - li { - color: $light_orange; - - &:hover { - background-color: $light_orange; - } - } - } - } - - h3 { - color: white; - text-transform: uppercase; - font-size: 0.6em; - padding: 5px; - font-weight: 600; - margin: 0 10px; - border-bottom: 1px dotted lighten($navigation_background, 10%); - } -} - -.unread-count { - @extend .mail-count; - - background: $secondary_callout; -} - -.total-count { - @extend .mail-count; - - background: $medium_light_grey; -} diff --git a/web-ui/app/scss/views/_no-mails-available.scss b/web-ui/app/scss/views/_no-mails-available.scss deleted file mode 100644 index bf5d256a..00000000 --- a/web-ui/app/scss/views/_no-mails-available.scss +++ /dev/null @@ -1,3 +0,0 @@ -.no-mails-available-pane { - @extend .no-content-placeholder; -} diff --git a/web-ui/app/scss/views/_no-message-selected.scss b/web-ui/app/scss/views/_no-message-selected.scss deleted file mode 100644 index 0e367bf2..00000000 --- a/web-ui/app/scss/views/_no-message-selected.scss +++ /dev/null @@ -1,14 +0,0 @@ -.no-message-selected-pane { - background: $contrast; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - - &__text { - @extend .no-content-placeholder; - - margin-bottom: 40px; // aligns label with "no results for XYZ" - } -} diff --git a/web-ui/app/scss/views/_read-view.scss b/web-ui/app/scss/views/_read-view.scss deleted file mode 100644 index f69d51a5..00000000 --- a/web-ui/app/scss/views/_read-view.scss +++ /dev/null @@ -1,165 +0,0 @@ -.mail-read-view { - $component-vertical-spacing: 10px; - $view-top-spacing: 3px; - - // NB! Setting overflow: hidden on an element causes - // a new float context to be created, so elements that - // are floated inside an element that has overflow: hidden - // applied are cleared. - overflow: hidden; - - hr { - margin: 0; - } - - &__header { - @include scut-clearfix; - - font-size: 0.9em; - margin: 0; - margin: $view-top-spacing 0 $component-vertical-spacing 0; - - &-recipients { - display: inline; - margin-bottom: 5px; - line-height: 1.5em; - - &-separator { - margin: 0 10px; - } - - &--highlight-sender { - font-weight: bold; - } - } - - &-date { - display: inline; - float: right; - } - - &-subject { - display: inline; - float: left; - max-width: 80%; - } - - &-actions { - display: inline; - float: right; - max-width: 20%; - background: $white; - white-space: nowrap; - margin-top: $component-vertical-spacing; - - &-button { - color: $medium_light_grey; - background-color: inherit; - display: inline; - border: 1px solid $lighter_gray; - line-height: 2em; - - margin-bottom: 0; - - i { - // workaround: remove padding and margin inserted by font-awesome - margin: 0; - padding: 0; - } - - &:hover, &:active, &:focus { - @include btn-transition; - - background: darken($contrast, 5%); - color: inherit; - } - - &--reply { - padding: 0 20px; - margin-right: -4px; // force buttons together - - } - - &--more { - padding: 0 5px; - } - } - - &-dropdown { - $container-right-padding: 10px; - - background: inherit; - position: absolute; - border: 1px solid $lighter_gray; - right: $container-right-padding; - - &-entry { - box-sizing: border-box; - background: inherit; - padding: 5px 10px; - display: block; - border-bottom: 1px solid $lighter_gray; - - &:last-child { - border-bottom: none; - } - - &:hover { - cursor: pointer; - background: $contrast; - } - } - } - } - - &-tags { - @include tags-editable; - - clear: both; - margin: 0 0 10px; - } - } - - &__body { - margin: $component-vertical-spacing 0; - width: 100%; - border: none; - } - - &__attachments { - margin: $component-vertical-spacing 0; - - &-header { - font-weight: bold; - } - - &-item { - display: block; - margin-bottom: 8px; - padding: 5px; - border: 1px solid $border_light_grey; - border-radius: 2px; - background-color: $background_light_grey; - - &-label { - color: $attachment_text; - text-decoration: none; - - &:hover, &:focus { - i.download-icon { - color: lighten($attachment_icon, 15); - } - - color: $attachment_icon; - outline: none; - } - } - - &-download { - color: #a2a2a2; - float: right; - margin-top: 5px; - } - } - } -} diff --git a/web-ui/app/scss/views/_security-labels.scss b/web-ui/app/scss/views/_security-labels.scss deleted file mode 100644 index ac966ded..00000000 --- a/web-ui/app/scss/views/_security-labels.scss +++ /dev/null @@ -1,67 +0,0 @@ -.security-status { - margin: 0 0 5px; - - &__label { - display: inline-block; - padding: 2px 6px; - white-space: nowrap; - background: $success; - color: $white; - border-radius: 12px; - - &:before { - font-family: FontAwesome; - } - - &--encrypted { - &:before { - content: "\f023"; - } - - &--with-error { - background: $attention; - &:before { - content: "\f023 \f057"; - } - } - } - - &--not-encrypted { - background: $attention; - - &:before { - content: "\f09c"; - } - } - - &--signed { - &:before { - content: "\f00c"; - } - - &--revoked, &--expired { - background: $attention; - - &:before { - content: "\f05e"; - } - } - - &--not-trusted { - background: $error; - - &:before { - content: "\f05e"; - } - } - } - - &--not-signed { - background: $attention; - - &:before { - content: "\f05e"; - } - } - } -} diff --git a/web-ui/app/templates/compose/attachment_item.hbs b/web-ui/app/templates/compose/attachment_item.hbs deleted file mode 100644 index 7a64f6f5..00000000 --- a/web-ui/app/templates/compose/attachment_item.hbs +++ /dev/null @@ -1,4 +0,0 @@ -
  • - {{ this.name }} ({{ formatSize this.size}}) - {{#if removable}}{{/if}} -
  • diff --git a/web-ui/app/templates/compose/attachment_upload_item.hbs b/web-ui/app/templates/compose/attachment_upload_item.hbs deleted file mode 100644 index eb6c4ba6..00000000 --- a/web-ui/app/templates/compose/attachment_upload_item.hbs +++ /dev/null @@ -1,5 +0,0 @@ -
  • -
    - {{ this.name }} ({{ formatSize this.size}}) - -
  • diff --git a/web-ui/app/templates/compose/attachments_list.hbs b/web-ui/app/templates/compose/attachments_list.hbs deleted file mode 100644 index 6f34df9e..00000000 --- a/web-ui/app/templates/compose/attachments_list.hbs +++ /dev/null @@ -1,14 +0,0 @@ -
    - - - -
    -
      - {{#each attachments }} - {{> attachment_item this }} - {{/each }} -
    -
      -
      - -
      diff --git a/web-ui/app/templates/compose/compose_box.hbs b/web-ui/app/templates/compose/compose_box.hbs deleted file mode 100644 index fcfbeaaf..00000000 --- a/web-ui/app/templates/compose/compose_box.hbs +++ /dev/null @@ -1,32 +0,0 @@ - - -
      - - {{> recipients }} - - -
      - - -
      -
      - - -
      - - {{> attachments_list }} - -
      - -
      - -
      -
      - -
      diff --git a/web-ui/app/templates/compose/feedback_box.hbs b/web-ui/app/templates/compose/feedback_box.hbs deleted file mode 100644 index 346a6192..00000000 --- a/web-ui/app/templates/compose/feedback_box.hbs +++ /dev/null @@ -1,18 +0,0 @@ - - -
      -
      - Feedback -
      - -
      - - -
      - -
      - -
      -
      diff --git a/web-ui/app/templates/compose/fixed_recipient.hbs b/web-ui/app/templates/compose/fixed_recipient.hbs deleted file mode 100644 index 8b01717c..00000000 --- a/web-ui/app/templates/compose/fixed_recipient.hbs +++ /dev/null @@ -1,8 +0,0 @@ -
      - -
      - {{ address }} -
      -
      - -
      diff --git a/web-ui/app/templates/compose/inline_box.hbs b/web-ui/app/templates/compose/inline_box.hbs deleted file mode 100644 index c9c114ec..00000000 --- a/web-ui/app/templates/compose/inline_box.hbs +++ /dev/null @@ -1,20 +0,0 @@ -
      -

      {{subject}}

      - -
      - - -
      - {{t 'to'}}: {{formatRecipients recipients}} - - -{{> recipients }} - -{{> attachments_list }} - -
      - -
      - -
      -
      diff --git a/web-ui/app/templates/compose/no_mails_available.hbs b/web-ui/app/templates/compose/no_mails_available.hbs deleted file mode 100644 index c61152a4..00000000 --- a/web-ui/app/templates/compose/no_mails_available.hbs +++ /dev/null @@ -1,7 +0,0 @@ -
      - {{#if forSearch }} - {{t 'no-results-for'}}: '{{ forSearch }}'. - {{else}} - {{t 'no-emails-in'}} '{{t tag}}'. - {{/if}} -
      diff --git a/web-ui/app/templates/compose/no_message_selected.hbs b/web-ui/app/templates/compose/no_message_selected.hbs deleted file mode 100644 index 0b9beaf8..00000000 --- a/web-ui/app/templates/compose/no_message_selected.hbs +++ /dev/null @@ -1,3 +0,0 @@ -
      -
      {{t 'nothing-selected'}}.
      -
      diff --git a/web-ui/app/templates/compose/recipient_input.hbs b/web-ui/app/templates/compose/recipient_input.hbs deleted file mode 100644 index 9416f11f..00000000 --- a/web-ui/app/templates/compose/recipient_input.hbs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/web-ui/app/templates/compose/recipients.hbs b/web-ui/app/templates/compose/recipients.hbs deleted file mode 100644 index 43aced1c..00000000 --- a/web-ui/app/templates/compose/recipients.hbs +++ /dev/null @@ -1,33 +0,0 @@ - diff --git a/web-ui/app/templates/compose/reply_section.hbs b/web-ui/app/templates/compose/reply_section.hbs deleted file mode 100644 index 45203d87..00000000 --- a/web-ui/app/templates/compose/reply_section.hbs +++ /dev/null @@ -1,6 +0,0 @@ -
      - - - - -
      diff --git a/web-ui/app/templates/compose/upload_attachment_failed.hbs b/web-ui/app/templates/compose/upload_attachment_failed.hbs deleted file mode 100644 index dbb1437b..00000000 --- a/web-ui/app/templates/compose/upload_attachment_failed.hbs +++ /dev/null @@ -1,6 +0,0 @@ -
      - - Upload failed. This file exceeds the 1MB limit. - Choose another file - Dismiss -
      diff --git a/web-ui/app/templates/feedback/feedback_trigger.hbs b/web-ui/app/templates/feedback/feedback_trigger.hbs deleted file mode 100644 index 7f3f8ef1..00000000 --- a/web-ui/app/templates/feedback/feedback_trigger.hbs +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/web-ui/app/templates/mail_actions/actions_box.hbs b/web-ui/app/templates/mail_actions/actions_box.hbs deleted file mode 100644 index 68a8d0bf..00000000 --- a/web-ui/app/templates/mail_actions/actions_box.hbs +++ /dev/null @@ -1,7 +0,0 @@ -
    • -
    • -
    • -
    • -
    • -
    • -
    • diff --git a/web-ui/app/templates/mail_actions/compose_trigger.hbs b/web-ui/app/templates/mail_actions/compose_trigger.hbs deleted file mode 100644 index 06f05fca..00000000 --- a/web-ui/app/templates/mail_actions/compose_trigger.hbs +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/web-ui/app/templates/mail_actions/pagination_trigger.hbs b/web-ui/app/templates/mail_actions/pagination_trigger.hbs deleted file mode 100644 index cbd8a089..00000000 --- a/web-ui/app/templates/mail_actions/pagination_trigger.hbs +++ /dev/null @@ -1,3 +0,0 @@ - -{{ currentPage }} - diff --git a/web-ui/app/templates/mail_actions/refresh_trigger.hbs b/web-ui/app/templates/mail_actions/refresh_trigger.hbs deleted file mode 100644 index dffc7090..00000000 --- a/web-ui/app/templates/mail_actions/refresh_trigger.hbs +++ /dev/null @@ -1,3 +0,0 @@ -
      - -
      diff --git a/web-ui/app/templates/mail_actions/trash_actions_box.hbs b/web-ui/app/templates/mail_actions/trash_actions_box.hbs deleted file mode 100644 index 4e0ec332..00000000 --- a/web-ui/app/templates/mail_actions/trash_actions_box.hbs +++ /dev/null @@ -1,5 +0,0 @@ -
    • -
    • -
    • -
    • -
    • diff --git a/web-ui/app/templates/mails/draft.hbs b/web-ui/app/templates/mails/draft.hbs deleted file mode 100644 index 808ce3ff..00000000 --- a/web-ui/app/templates/mails/draft.hbs +++ /dev/null @@ -1,41 +0,0 @@ -
      - -
      - - -
      -
      - {{t 'to'}}: - {{#if header.to }} - {{ header.to }} - {{else}} - {{t 'no-recipient'}} - {{/if}} -
      - - {{ formatDate header.date }} -
      -
      -
      - - {{#if header.subject }} - {{header.subject}} - {{else}} - {{t 'no-subject'}} - {{/if}} -
      - - {{#if attachments}} -
      - {{/if}} -
      -
        - {{#each tagsForListView }} -
      • {{ this }}
      • - {{/each }} -
      -
      - - - - diff --git a/web-ui/app/templates/mails/full_view.hbs b/web-ui/app/templates/mails/full_view.hbs deleted file mode 100644 index 40bfd4a2..00000000 --- a/web-ui/app/templates/mails/full_view.hbs +++ /dev/null @@ -1,83 +0,0 @@ - - -
      -
      - - -
      - {{#if signatureStatus}} - - {{t signatureStatus.label }} - - {{/if}} - {{#if encryptionStatus}} - - {{t encryptionStatus.label }} - - {{/if}} -
      - -
      - - {{#if header.from }} - {{ header.from }} - {{else}} - {{t 'you'}} - {{/if}} - - - {{{formatRecipients header}}} -
      - -
      - {{ formatDate header.date }} -
      - -
      - -
      -

      {{ header.subject }}

      -
      - - - -
        -
      • - -
      • - - {{#each tags }} -
      • {{ this }}
      • - {{/each }} - -
      • - -
      • - -
      • - -
      • -
      -
      - - - - {{#if attachments}} -
      - -
      -

      {{ attachments.length }} attachment(s):

      - -
      - {{/if}} -
      diff --git a/web-ui/app/templates/mails/mail_actions.hbs b/web-ui/app/templates/mails/mail_actions.hbs deleted file mode 100644 index 0adfe853..00000000 --- a/web-ui/app/templates/mails/mail_actions.hbs +++ /dev/null @@ -1,6 +0,0 @@ - - -
        -
      • {{t 'reply-to-all'}}
      • -
      • {{t 'delete-this-message'}}
      • -
      diff --git a/web-ui/app/templates/mails/sent.hbs b/web-ui/app/templates/mails/sent.hbs deleted file mode 100644 index 158b20c8..00000000 --- a/web-ui/app/templates/mails/sent.hbs +++ /dev/null @@ -1,36 +0,0 @@ -
      - -
      - -
      -
      - {{t 'to'}}: - {{#if header.to }} - {{ header.to }} - {{else}} - {{t 'no-recipient'}} - {{/if}} -
      - - {{ formatDate header.date }} -
      -
      -
      - {{#if header.subject }} - {{header.subject}} - {{else}} - {{t 'no-subject'}} - {{/if}} -
      - - {{#if attachments}} -
      - {{/if}} -
      -
        - {{#each tagsForListView }} -
      • {{ this }}
      • - {{/each }} -
      -
      - diff --git a/web-ui/app/templates/mails/single.hbs b/web-ui/app/templates/mails/single.hbs deleted file mode 100644 index aaede844..00000000 --- a/web-ui/app/templates/mails/single.hbs +++ /dev/null @@ -1,28 +0,0 @@ -
      - -
      - -
      -
      - {{#if header.from }} - {{ header.from }} - {{else}} - {{t "you"}} - {{/if}} -
      - - {{ formatDate header.date }} -
      -
      -
      {{ header.subject }}
      - - {{#if attachments}} -
      - {{/if}} -
      -
        - {{#each tagsForListView }} -
      • {{ this }}
      • - {{/each }} -
      -
      diff --git a/web-ui/app/templates/mails/trash.hbs b/web-ui/app/templates/mails/trash.hbs deleted file mode 100644 index f8947b15..00000000 --- a/web-ui/app/templates/mails/trash.hbs +++ /dev/null @@ -1,32 +0,0 @@ -
      - -
      - -
      -
      - {{#if header.from }} - {{ header.from }} - {{else}} - {{t "you"}} - {{/if}} -
      - - {{ formatDate header.date }} -
      -
      -
      - - {{ header.subject }} -
      - - {{#if attachments}} -
      - {{/if}} -
      -
        - {{#each tagsForListView }} -
      • {{ this }}
      • - {{/each }} -
      -
      - diff --git a/web-ui/app/templates/page/logout.hbs b/web-ui/app/templates/page/logout.hbs deleted file mode 100644 index 0cc079bc..00000000 --- a/web-ui/app/templates/page/logout.hbs +++ /dev/null @@ -1,9 +0,0 @@ -
        -
        - -
      • -
        - {{t 'logout'}} -
      • -
        -
      diff --git a/web-ui/app/templates/page/logout_shortcut.hbs b/web-ui/app/templates/page/logout_shortcut.hbs deleted file mode 100644 index 043ab0dc..00000000 --- a/web-ui/app/templates/page/logout_shortcut.hbs +++ /dev/null @@ -1,6 +0,0 @@ -
    • - - -
      {{t 'logout'}}
      -
      -
    • diff --git a/web-ui/app/templates/page/user_settings_box.hbs b/web-ui/app/templates/page/user_settings_box.hbs deleted file mode 100644 index 2152b779..00000000 --- a/web-ui/app/templates/page/user_settings_box.hbs +++ /dev/null @@ -1,10 +0,0 @@ -
      - - -

      {{t 'user-account'}}

      - -
      -

      {{t 'email-address'}}

      -

      {{ account_email }}

      -

      {{t 'public-key-fingerprint'}}

      -

      {{ formatFingerPrint fingerprint }}

      diff --git a/web-ui/app/templates/page/user_settings_icon.hbs b/web-ui/app/templates/page/user_settings_icon.hbs deleted file mode 100644 index 8f2f9215..00000000 --- a/web-ui/app/templates/page/user_settings_icon.hbs +++ /dev/null @@ -1,8 +0,0 @@ - diff --git a/web-ui/app/templates/page/version.hbs b/web-ui/app/templates/page/version.hbs deleted file mode 100644 index 5f43f78a..00000000 --- a/web-ui/app/templates/page/version.hbs +++ /dev/null @@ -1,2 +0,0 @@ -{{t 'version'}}: UNKNOWN_VERSION
      - diff --git a/web-ui/app/templates/search/search_trigger.hbs b/web-ui/app/templates/search/search_trigger.hbs deleted file mode 100644 index 2261d154..00000000 --- a/web-ui/app/templates/search/search_trigger.hbs +++ /dev/null @@ -1,3 +0,0 @@ -
      - -
      diff --git a/web-ui/app/templates/tags/shortcut.hbs b/web-ui/app/templates/tags/shortcut.hbs deleted file mode 100644 index 1e82d6a9..00000000 --- a/web-ui/app/templates/tags/shortcut.hbs +++ /dev/null @@ -1,9 +0,0 @@ -
    • - - {{#if displayBadge }} - {{ count }} - {{/if}} - -
      {{ tagName }}
      -
      -
    • diff --git a/web-ui/app/templates/tags/tag.hbs b/web-ui/app/templates/tags/tag.hbs deleted file mode 100644 index ca397b9a..00000000 --- a/web-ui/app/templates/tags/tag.hbs +++ /dev/null @@ -1,3 +0,0 @@ -
    • - {{> tag_inner }} -
    • diff --git a/web-ui/app/templates/tags/tag_inner.hbs b/web-ui/app/templates/tags/tag_inner.hbs deleted file mode 100644 index 2e0958cb..00000000 --- a/web-ui/app/templates/tags/tag_inner.hbs +++ /dev/null @@ -1,4 +0,0 @@ -{{ tagName }} -{{#if displayBadge }} -{{ count }} -{{/if}} diff --git a/web-ui/app/templates/tags/tag_list.hbs b/web-ui/app/templates/tags/tag_list.hbs deleted file mode 100644 index 92a73283..00000000 --- a/web-ui/app/templates/tags/tag_list.hbs +++ /dev/null @@ -1,6 +0,0 @@ -
        -
        - - {{t 'tags.tags'}} -
        -
          diff --git a/web-ui/app/templates/user_alerts/message.hbs b/web-ui/app/templates/user_alerts/message.hbs deleted file mode 100644 index abba1f91..00000000 --- a/web-ui/app/templates/user_alerts/message.hbs +++ /dev/null @@ -1 +0,0 @@ -{{ message.content }} diff --git a/web-ui/config/add_git_version.sh b/web-ui/config/add_git_version.sh index a0abefc0..abda150a 100755 --- a/web-ui/config/add_git_version.sh +++ b/web-ui/config/add_git_version.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -TEMPLATE_FILE="app/js/generated/hbs/templates.js" +TEMPLATE_FILE="public/js/generated/hbs/templates.js" COMMITISH=$(git rev-parse --short HEAD) COMMITDATE=$(git show -s --format=%cd) diff --git a/web-ui/config/buildoptions.js b/web-ui/config/buildoptions.js index 63c91653..2e7ba028 100644 --- a/web-ui/config/buildoptions.js +++ b/web-ui/config/buildoptions.js @@ -16,11 +16,11 @@ */ ({ - baseUrl: '../app', + baseUrl: '../public', wrap: true, almond: true, optimize: 'none', - mainConfigFile: '../app/js/main.js', + mainConfigFile: '../public/js/main.js', out: '../.tmp/app.concatenated.js', include: ['js/main'], name: 'bower_components/almond/almond' diff --git a/web-ui/config/compass.rb b/web-ui/config/compass.rb index 9c52a04e..bc0301a3 100644 --- a/web-ui/config/compass.rb +++ b/web-ui/config/compass.rb @@ -17,14 +17,14 @@ # Set this to the root of your project when deployed: -target_dir = ENV['PIXELATED_BUILD'] == 'package' ? 'dist' : 'app' +target_dir = ENV['PIXELATED_BUILD'] == 'package' ? 'dist' : 'public' http_path = "/" css_dir = "#{target_dir}/css" -sass_dir = "app/scss" -images_dir = "app/images" -javascripts_dir = "app/js" +sass_dir = "public/scss" +images_dir = "public/images" +javascripts_dir = "public/js" # You can select your preferred output style here (can be overridden via the command line): # output_style = :expanded or :nested or :compact or :compressed diff --git a/web-ui/config/control-tower.yml b/web-ui/config/control-tower.yml index 874e1b64..2d4d5865 100644 --- a/web-ui/config/control-tower.yml +++ b/web-ui/config/control-tower.yml @@ -1,3 +1,3 @@ --- -include_pattern: 'app/js/**/*.js' +include_pattern: 'public/js/**/*.js' exclude_pattern: '' diff --git a/web-ui/config/imagemin.js b/web-ui/config/imagemin.js index 2b2c87e0..bbc589a8 100644 --- a/web-ui/config/imagemin.js +++ b/web-ui/config/imagemin.js @@ -17,7 +17,7 @@ const imagemin = require('imagemin'); -imagemin(['app/images/*.{gif,jpg,png,svg}'], +imagemin(['public/images/*.{gif,jpg,png,svg}'], 'dist/images' ).then(files => { console.log('Images list:') diff --git a/web-ui/config/package.sh b/web-ui/config/package.sh index 165eeff3..d4208ecb 100644 --- a/web-ui/config/package.sh +++ b/web-ui/config/package.sh @@ -34,7 +34,7 @@ mkdir -p dist # copy files -cd app +cd public cp --parents \ 404.html \ fonts/* \ @@ -47,19 +47,19 @@ cd - # concat js files and minify for app.min.js cat \ -app/bower_components/modernizr/modernizr.js \ -app/bower_components/lodash/dist/lodash.js \ -app/bower_components/jquery/dist/jquery.js \ -app/bower_components/jquery-ui/jquery-ui.js \ -app/bower_components/jquery-file-upload/js/jquery.fileupload.js \ -app/js/lib/highlightRegex.js \ -app/bower_components/handlebars/handlebars.js \ -app/bower_components/typeahead.js/dist/typeahead.bundle.js \ -app/bower_components/foundation/js/foundation.js \ -app/bower_components/foundation/js/foundation/foundation.reveal.js \ -app/bower_components/foundation/js/foundation/foundation.offcanvas.js \ -app/js/foundation/initialize_foundation.js \ -app/bower_components/iframe-resizer/js/iframeResizer.js \ +public/bower_components/modernizr/modernizr.js \ +public/bower_components/lodash/dist/lodash.js \ +public/bower_components/jquery/dist/jquery.js \ +public/bower_components/jquery-ui/jquery-ui.js \ +public/bower_components/jquery-file-upload/js/jquery.fileupload.js \ +public/js/lib/highlightRegex.js \ +public/bower_components/handlebars/handlebars.js \ +public/bower_components/typeahead.js/dist/typeahead.bundle.js \ +public/bower_components/foundation/js/foundation.js \ +public/bower_components/foundation/js/foundation/foundation.reveal.js \ +public/bower_components/foundation/js/foundation/foundation.offcanvas.js \ +public/js/foundation/initialize_foundation.js \ +public/bower_components/iframe-resizer/js/iframeResizer.js \ .tmp/app.concatenated.js > dist/app.js node_modules/.bin/minify dist/app.js > dist/app.min.js rm dist/app.js @@ -72,7 +72,7 @@ fi # concat js files and minify for sandbox.min.js cat \ -app/js/sandbox.js \ -app/bower_components/iframe-resizer/js/iframeResizer.contentWindow.js > dist/sandbox.js +public/js/sandbox.js \ +public/bower_components/iframe-resizer/js/iframeResizer.contentWindow.js > dist/sandbox.js node_modules/.bin/minify dist/sandbox.js > dist/sandbox.min.js rm dist/sandbox.js diff --git a/web-ui/karma.conf.js b/web-ui/karma.conf.js index 52b54f57..93628052 100644 --- a/web-ui/karma.conf.js +++ b/web-ui/karma.conf.js @@ -17,51 +17,51 @@ module.exports = function (config) { // list of files / patterns to load in the browser files: [ // loaded without require - 'app/bower_components/lodash/dist/lodash.js', - 'app/bower_components/jquery/dist/jquery.js', - 'app/bower_components/jquery-ui/jquery-ui.min.js', - 'app/bower_components/jquery-file-upload/js/jquery.fileupload.js', - 'app/bower_components/jasmine-jquery/lib/jasmine-jquery.js', - 'app/bower_components/jasmine-flight/lib/jasmine-flight.js', - 'app/bower_components/jasmine-jquery/lib/jasmine-jquery.js', - 'app/bower_components/handlebars/handlebars.min.js', - 'app/bower_components/modernizr/modernizr.js', - 'app/bower_components/foundation/js/foundation.js', - 'app/bower_components/foundation/js/foundation/foundation.reveal.js', - 'app/bower_components/foundation/js/foundation/foundation.offcanvas.js', - 'app/js/lib/highlightRegex.js', + 'public/bower_components/lodash/dist/lodash.js', + 'public/bower_components/jquery/dist/jquery.js', + 'public/bower_components/jquery-ui/jquery-ui.min.js', + 'public/bower_components/jquery-file-upload/js/jquery.fileupload.js', + 'public/bower_components/jasmine-jquery/lib/jasmine-jquery.js', + 'public/bower_components/jasmine-flight/lib/jasmine-flight.js', + 'public/bower_components/jasmine-jquery/lib/jasmine-jquery.js', + 'public/bower_components/handlebars/handlebars.min.js', + 'public/bower_components/modernizr/modernizr.js', + 'public/bower_components/foundation/js/foundation.js', + 'public/bower_components/foundation/js/foundation/foundation.reveal.js', + 'public/bower_components/foundation/js/foundation/foundation.offcanvas.js', + 'public/js/lib/highlightRegex.js', // hack to load RequireJS after the shim libs 'node_modules/requirejs/require.js', 'node_modules/karma-requirejs/lib/adapter.js', // loaded with require - {pattern: 'app/bower_components/DOMPurify/dist/purify.min.js', included: false}, - {pattern: 'app/bower_components/he/he.js', included: false}, - {pattern: 'app/bower_components/flight/**/*.js', included: false}, - {pattern: 'app/bower_components/i18next/**/*.js', included: false}, - {pattern: 'app/bower_components/i18next-xhr-backend/**/*.js', included: false}, - {pattern: 'app/bower_components/i18next-browser-languagedetector/**/*.js', included: false}, - {pattern: 'app/bower_components/quoted-printable/*.js', included: false}, - {pattern: 'app/bower_components/utf8/utf8.js', included: false}, - {pattern: 'app/locales/**/*.json', included: false}, - {pattern: 'app/js/**/*.js', included: false}, + {pattern: 'public/bower_components/DOMPurify/dist/purify.min.js', included: false}, + {pattern: 'public/bower_components/he/he.js', included: false}, + {pattern: 'public/bower_components/flight/**/*.js', included: false}, + {pattern: 'public/bower_components/i18next/**/*.js', included: false}, + {pattern: 'public/bower_components/i18next-xhr-backend/**/*.js', included: false}, + {pattern: 'public/bower_components/i18next-browser-languagedetector/**/*.js', included: false}, + {pattern: 'public/bower_components/quoted-printable/*.js', included: false}, + {pattern: 'public/bower_components/utf8/utf8.js', included: false}, + {pattern: 'public/locales/**/*.json', included: false}, + {pattern: 'public/js/**/*.js', included: false}, {pattern: 'test/test_data.js', included: false}, {pattern: 'test/custom_matchers.js', included: false}, {pattern: 'test/features.js', included: false}, {pattern: 'test/spec/**/*.spec.js', included: false}, - {pattern: 'app/sandbox.html', included: true, served: true}, + {pattern: 'public/sandbox.html', included: true, served: true}, 'test/test-main.js' ], proxies: { - '/sandbox/sandbox.html': '/base/app/sandbox.html', + '/sandbox/sandbox.html': '/base/public/sandbox.html', }, // list of files to exclude exclude: [ - 'app/js/main.js' + 'public/js/main.js' ], // test results reporter to use @@ -69,7 +69,7 @@ module.exports = function (config) { reporters: ['dots', 'junit', 'coverage'], preprocessors: { - 'app/js/!(lib)/**/*.js': ['coverage'] + 'public/js/!(lib)/**/*.js': ['coverage'] }, // enable / disable watching file and executing tests whenever any file changes diff --git a/web-ui/package.json b/web-ui/package.json index b937502f..3fa1d294 100644 --- a/web-ui/package.json +++ b/web-ui/package.json @@ -31,19 +31,19 @@ "debug": "npm run build && node_modules/karma/bin/karma start --browsers Chrome $GRUNT_OPTS", "watch": "npm run compass-watch & npm run handlebars-watch", "watch-test": "node_modules/karma/bin/karma start", - "handlebars": "mkdir -p app/js/generated/hbs/ && node_modules/handlebars/bin/handlebars app/templates/**/*.hbs > app/js/generated/hbs/templates.js --namespace=window.Pixelated --root .", - "handlebars-watch": "node_modules/.bin/watch 'npm run handlebars' app/templates", + "handlebars": "mkdir -p public/js/generated/hbs/ && node_modules/handlebars/bin/handlebars public/templates/**/*.hbs > public/js/generated/hbs/templates.js --namespace=window.Pixelated --root .", + "handlebars-watch": "node_modules/.bin/watch 'npm run handlebars' public/templates", "compass": "compass compile", "compass-watch": "compass watch", "build": "npm run clean && npm run handlebars && npm run add_git_version && npm run compass && npm run build-signup", "build-signup": "babel src/js -d lib/js && browserify lib/js/index.js >public/signup.js", - "jshint": "node_modules/jshint/bin/jshint --config=.jshintrc app test", - "clean": "rm -rf .tmp/ 'dist/*' app/js/generated/hbs/* app/css/*", + "jshint": "node_modules/jshint/bin/jshint --config=.jshintrc public test", + "clean": "rm -rf .tmp/ 'dist/*' public/js/generated/hbs/* public/css/*", "buildmain": "node_modules/requirejs/bin/r.js -o config/buildoptions.js", "package": "/bin/bash config/package.sh", "imagemin": "node config/imagemin.js", - "minify_html": "node_modules/.bin/html-minifier app/index.html --collapse-whitespace | sed 's|.*||' > dist/index.html", - "minify_sandbox": "node_modules/.bin/html-minifier app/sandbox.html --collapse-whitespace | sed 's|.*||' > dist/sandbox.html", + "minify_html": "node_modules/.bin/html-minifier public/index.html --collapse-whitespace | sed 's|.*||' > dist/index.html", + "minify_sandbox": "node_modules/.bin/html-minifier public/sandbox.html --collapse-whitespace | sed 's|.*||' > dist/sandbox.html", "add_git_version": "/bin/bash config/add_git_version.sh" }, "dependencies": { diff --git a/web-ui/public/404.html b/web-ui/public/404.html new file mode 100644 index 00000000..fdace4ab --- /dev/null +++ b/web-ui/public/404.html @@ -0,0 +1,157 @@ + + + + + Page Not Found :( + + + +
          +

          Not found :(

          +

          Sorry, but the page you were trying to view does not exist.

          +

          It looks like this was the result of either:

          +
            +
          • a mistyped address
          • +
          • an out-of-date link
          • +
          + + +
          + + diff --git a/web-ui/public/favicon.ico b/web-ui/public/favicon.ico new file mode 100644 index 00000000..e69de29b diff --git a/web-ui/public/fonts/OpenSans-Bold.woff b/web-ui/public/fonts/OpenSans-Bold.woff new file mode 100644 index 00000000..dacf3c9c Binary files /dev/null and b/web-ui/public/fonts/OpenSans-Bold.woff differ diff --git a/web-ui/public/fonts/OpenSans-BoldItalic.woff b/web-ui/public/fonts/OpenSans-BoldItalic.woff new file mode 100644 index 00000000..a4e29c0f Binary files /dev/null and b/web-ui/public/fonts/OpenSans-BoldItalic.woff differ diff --git a/web-ui/public/fonts/OpenSans-Extrabold.woff b/web-ui/public/fonts/OpenSans-Extrabold.woff new file mode 100644 index 00000000..7a2e352b Binary files /dev/null and b/web-ui/public/fonts/OpenSans-Extrabold.woff differ diff --git a/web-ui/public/fonts/OpenSans-ExtraboldItalic.woff b/web-ui/public/fonts/OpenSans-ExtraboldItalic.woff new file mode 100644 index 00000000..ce3ab2e7 Binary files /dev/null and b/web-ui/public/fonts/OpenSans-ExtraboldItalic.woff differ diff --git a/web-ui/public/fonts/OpenSans-Italic.woff b/web-ui/public/fonts/OpenSans-Italic.woff new file mode 100644 index 00000000..c5f6bac1 Binary files /dev/null and b/web-ui/public/fonts/OpenSans-Italic.woff differ diff --git a/web-ui/public/fonts/OpenSans-Light.woff b/web-ui/public/fonts/OpenSans-Light.woff new file mode 100644 index 00000000..eb601d70 Binary files /dev/null and b/web-ui/public/fonts/OpenSans-Light.woff differ diff --git a/web-ui/public/fonts/OpenSans-Semibold.woff b/web-ui/public/fonts/OpenSans-Semibold.woff new file mode 100644 index 00000000..56c44944 Binary files /dev/null and b/web-ui/public/fonts/OpenSans-Semibold.woff differ diff --git a/web-ui/public/fonts/OpenSans-SemiboldItalic.woff b/web-ui/public/fonts/OpenSans-SemiboldItalic.woff new file mode 100644 index 00000000..3a439fc3 Binary files /dev/null and b/web-ui/public/fonts/OpenSans-SemiboldItalic.woff differ diff --git a/web-ui/public/fonts/OpenSans.woff b/web-ui/public/fonts/OpenSans.woff new file mode 100644 index 00000000..77706fa6 Binary files /dev/null and b/web-ui/public/fonts/OpenSans.woff differ diff --git a/web-ui/public/fonts/OpenSansLight-Italic.woff b/web-ui/public/fonts/OpenSansLight-Italic.woff new file mode 100644 index 00000000..3f9f088f Binary files /dev/null and b/web-ui/public/fonts/OpenSansLight-Italic.woff differ diff --git a/web-ui/public/fonts/icomoon.ttf b/web-ui/public/fonts/icomoon.ttf new file mode 100644 index 00000000..61315d04 Binary files /dev/null and b/web-ui/public/fonts/icomoon.ttf differ diff --git a/web-ui/public/fonts/icomoon.woff b/web-ui/public/fonts/icomoon.woff new file mode 100644 index 00000000..82f11748 Binary files /dev/null and b/web-ui/public/fonts/icomoon.woff differ diff --git a/web-ui/public/images/LOADING-transparent.gif b/web-ui/public/images/LOADING-transparent.gif new file mode 100644 index 00000000..ac9abcde Binary files /dev/null and b/web-ui/public/images/LOADING-transparent.gif differ diff --git a/web-ui/public/images/fa-sent.svg b/web-ui/public/images/fa-sent.svg new file mode 100644 index 00000000..a4b4bea4 --- /dev/null +++ b/web-ui/public/images/fa-sent.svg @@ -0,0 +1,15 @@ + + + + fa-sent + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/web-ui/public/images/favicon.png b/web-ui/public/images/favicon.png new file mode 100644 index 00000000..e14841c7 Binary files /dev/null and b/web-ui/public/images/favicon.png differ diff --git a/web-ui/public/images/logo.svg b/web-ui/public/images/logo.svg new file mode 100644 index 00000000..6c2d8989 --- /dev/null +++ b/web-ui/public/images/logo.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/web-ui/public/images/pixelated-symbol-blue-transparent-01.png b/web-ui/public/images/pixelated-symbol-blue-transparent-01.png new file mode 100644 index 00000000..96b92155 Binary files /dev/null and b/web-ui/public/images/pixelated-symbol-blue-transparent-01.png differ diff --git a/web-ui/public/index.html b/web-ui/public/index.html new file mode 100644 index 00000000..4b6a81a0 --- /dev/null +++ b/web-ui/public/index.html @@ -0,0 +1,113 @@ + + + + + + +$account_email - Pixelated Mail + + + + + + + + + + +
          +
          +
          +
          + +
          + + +
          +
          +
          +
          +
          +
          +
          +
          +
          +
            +
            + +
            +
              +
            +
            +
            +
            + +
            +
            +
            +
            + + + + + + + + + + + + + + + + + + + + diff --git a/web-ui/public/js/dispatchers/left_pane_dispatcher.js b/web-ui/public/js/dispatchers/left_pane_dispatcher.js new file mode 100644 index 00000000..0037a88f --- /dev/null +++ b/web-ui/public/js/dispatchers/left_pane_dispatcher.js @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'page/router/url_params', + 'page/events' + ], + + function(defineComponent, urlParams, events) { + 'use strict'; + + return defineComponent(leftPaneDispatcher); + + function leftPaneDispatcher() { + var initialized = false; + + this.refreshTagList = function (ev, data) { + this.trigger(document, events.tags.want, { caller: this.$node, skipMailListRefresh: data.skipMailListRefresh }); + }; + + this.loadTags = function (ev, data) { + this.trigger(document, events.ui.tagList.load, data); + }; + + this.selectTag = function (ev, data) { + var tag = (data && data.tag) || urlParams.getTag(); + this.trigger(document, events.ui.tag.select, { tag: tag, skipMailListRefresh: data.skipMailListRefresh }); + }; + + this.pushUrlState = function (ev, data) { + if (initialized) { + this.trigger(document, events.router.pushState, data); + } + initialized = true; + }; + + this.after('initialize', function () { + //this.on(this.$node, events.tags.received, this.loadTags); + this.on(document, events.dispatchers.tags.refreshTagList, this.refreshTagList); + this.on(document, events.ui.tags.loaded, this.selectTag); + this.on(document, events.ui.tag.selected, this.pushUrlState); + this.on(document, events.ui.tag.select, this.pushUrlState); + this.trigger(document, events.tags.want, { caller: this.$node }); + }); + } + } +); diff --git a/web-ui/public/js/dispatchers/middle_pane_dispatcher.js b/web-ui/public/js/dispatchers/middle_pane_dispatcher.js new file mode 100644 index 00000000..12222aec --- /dev/null +++ b/web-ui/public/js/dispatchers/middle_pane_dispatcher.js @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(['flight/lib/component', 'page/events', 'helpers/triggering', 'mail_view/ui/no_mails_available_pane'], function(defineComponent, events, triggering, NoMailsAvailablePane) { + 'use strict'; + + return defineComponent(function() { + this.defaultAttrs({ + middlePane: '#middle-pane', + noMailsAvailablePane: 'no-mails-available-pane' + }); + + this.createChildDiv = function (component_id) { + var child_div = $('
            ', {id: component_id}); + this.select('middlePane').append(child_div); + return child_div; + }; + + this.resetChildDiv = function(component_id) { + $('#' + component_id).remove(); + }; + + this.refreshMailList = function (ev, data) { + this.trigger(document, events.ui.mails.fetchByTag, data); + }; + + this.cleanSelected = function(ev, data) { + this.trigger(document, events.ui.mails.cleanSelected); + }; + + this.resetScroll = function() { + this.select('middlePane').scrollTop(0); + }; + + this.updateMiddlePaneHeight = function() { + var vh = $(window).height(); + var top = $('#main').outerHeight() + $('#top-pane').outerHeight(); + this.select('middlePane').css({height: (vh - top) + 'px'}); + }; + + this.onMailsChange = function (ev, data) { + this.resetChildDiv(this.attr.noMailsAvailablePane); + if (data.mails.length > 0) { + NoMailsAvailablePane.teardownAll(); + } else { + var child_div = this.createChildDiv(this.attr.noMailsAvailablePane); + NoMailsAvailablePane.attachTo(child_div, {tag: data.tag, forSearch: data.forSearch}); + } + }; + + this.after('initialize', function () { + this.on(document, events.dispatchers.middlePane.refreshMailList, this.refreshMailList); + this.on(document, events.dispatchers.middlePane.cleanSelected, this.cleanSelected); + this.on(document, events.dispatchers.middlePane.resetScroll, this.resetScroll); + this.on(document, events.mails.available, this.onMailsChange); + + this.updateMiddlePaneHeight(); + $(window).on('resize', this.updateMiddlePaneHeight.bind(this)); + }); + }); +}); diff --git a/web-ui/public/js/dispatchers/right_pane_dispatcher.js b/web-ui/public/js/dispatchers/right_pane_dispatcher.js new file mode 100644 index 00000000..870bcd92 --- /dev/null +++ b/web-ui/public/js/dispatchers/right_pane_dispatcher.js @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'mail_view/ui/compose_box', + 'mail_view/ui/mail_view', + 'mail_view/ui/reply_section', + 'mail_view/ui/draft_box', + 'mail_view/ui/no_message_selected_pane', + 'mail_view/ui/feedback_box', + 'page/events' + ], + + function(defineComponent, ComposeBox, MailView, ReplySection, DraftBox, NoMessageSelectedPane, FeedbackBox, events) { + 'use strict'; + + return defineComponent(rightPaneDispatcher); + + function rightPaneDispatcher() { + this.defaultAttrs({ + rightPane: '#right-pane', + composeBox: 'compose-box', + feedbackBox: 'feedback-box', + mailView: 'mail-view', + noMessageSelectedPane: 'no-message-selected-pane', + replySection: 'reply-section', + draftBox: 'draft-box', + currentTag: '' + }); + + this.createAndAttach = function(newContainer) { + var stage = $('
            ', { id: newContainer }); + this.select('rightPane').append(stage); + return stage; + }; + + this.reset = function (newContainer) { + this.trigger(document, events.dispatchers.rightPane.clear); + this.select('rightPane').empty(); + var stage = this.createAndAttach(newContainer); + return stage; + }; + + this.openComposeBox = function() { + var stage = this.reset(this.attr.composeBox); + ComposeBox.attachTo(stage, {currentTag: this.attr.currentTag}); + }; + + this.openFeedbackBox = function() { + var stage = this.reset(this.attr.feedbackBox); + FeedbackBox.attachTo(stage); + }; + + this.openMail = function(ev, data) { + var stage = this.reset(this.attr.mailView); + MailView.attachTo(stage, data); + + var replySectionContainer = this.createAndAttach(this.attr.replySection); + ReplySection.attachTo(replySectionContainer, { ident: data.ident }); + }; + + this.initializeNoMessageSelectedPane = function () { + var stage = this.reset(this.attr.noMessageSelectedPane); + NoMessageSelectedPane.attachTo(stage); + this.trigger(document, events.dispatchers.middlePane.cleanSelected); + }; + + this.openNoMessageSelectedPane = function(ev, data) { + this.initializeNoMessageSelectedPane(); + + this.trigger(document, events.router.pushState, { tag: this.attr.currentTag, isDisplayNoMessageSelected: true }); + }; + + this.openDraft = function (ev, data) { + var stage = this.reset(this.attr.draftBox); + DraftBox.attachTo(stage, { mailIdent: data.ident, currentTag: this.attr.currentTag }); + }; + + this.selectTag = function(ev, data) { + this.trigger(document, events.ui.tags.loaded, {tag: data.tag}); + }; + + this.saveTag = function(ev, data) { + this.attr.currentTag = data.tag; + }; + + this.after('initialize', function () { + this.on(document, events.dispatchers.rightPane.openComposeBox, this.openComposeBox); + this.on(document, events.dispatchers.rightPane.openDraft, this.openDraft); + this.on(document, events.ui.mail.open, this.openMail); + this.on(document, events.dispatchers.rightPane.openFeedbackBox, this.openFeedbackBox); + this.on(document, events.dispatchers.rightPane.openNoMessageSelected, this.openNoMessageSelectedPane); + this.on(document, events.dispatchers.rightPane.selectTag, this.selectTag); + this.on(document, events.ui.tag.selected, this.saveTag); + this.on(document, events.ui.tag.select, this.saveTag); + this.on(document, events.dispatchers.rightPane.openNoMessageSelectedWithoutPushState, this.initializeNoMessageSelectedPane); + this.initializeNoMessageSelectedPane(); + }); + } + } +); diff --git a/web-ui/public/js/features/features.js b/web-ui/public/js/features/features.js new file mode 100644 index 00000000..f71d56ea --- /dev/null +++ b/web-ui/public/js/features/features.js @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['helpers/monitored_ajax'], function(monitoredAjax) { + 'use strict'; + var cachedDisabledFeatures; + var cachedMultiUserFeatures; + + function getDisabledFeatures() { + cachedDisabledFeatures = cachedDisabledFeatures || fetchFeatures().disabled_features; + return cachedDisabledFeatures; + } + + function getMultiUserFeatures() { + cachedMultiUserFeatures = cachedMultiUserFeatures || fetchFeatures().multi_user; + return cachedMultiUserFeatures; + } + + function fetchFeatures() { + var features; + monitoredAjax(this, '/features', { + async: false, + success: function (results) { + features = results; + }, + error: function () { + console.error('Could not load feature toggles'); + } + }); + return features; + } + + return { + isEnabled: function (featureName) { + return ! _.contains(getDisabledFeatures(), featureName); + }, + isAutoRefreshEnabled: function () { + return this.isEnabled('autoRefresh'); + }, + isLogoutEnabled: function () { + return _.has(getMultiUserFeatures(), 'logout'); + }, + getLogoutUrl: function () { + return getMultiUserFeatures().logout; + } + }; +}); diff --git a/web-ui/public/js/feedback/feedback_cache.js b/web-ui/public/js/feedback/feedback_cache.js new file mode 100644 index 00000000..a5d92266 --- /dev/null +++ b/web-ui/public/js/feedback/feedback_cache.js @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define([], function() { + 'use strict'; + + return (function() { + var feedbackCache = ''; + return { + resetCache: function () { + feedbackCache = ''; + }, + setCache: function(feedback) { + feedbackCache = feedback; + }, + getCache: function() { + return feedbackCache; + } + }; + })(); +}); diff --git a/web-ui/public/js/feedback/feedback_trigger.js b/web-ui/public/js/feedback/feedback_trigger.js new file mode 100644 index 00000000..598f9060 --- /dev/null +++ b/web-ui/public/js/feedback/feedback_trigger.js @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['flight/lib/component', 'views/templates', 'page/events', 'features'], + function (defineComponent, templates, events, features) { + 'use strict'; + + return defineComponent(function () { + this.render = function () { + this.$node.html(templates.feedback.feedback()); + }; + + this.onClick = function() { + this.trigger(document, events.dispatchers.rightPane.openFeedbackBox); + }; + + this.after('initialize', function () { + if (features.isEnabled('feedback')) { + this.render(); + this.on('click', this.onClick); + } + }); + + }); +}); diff --git a/web-ui/public/js/foundation/initialize_foundation.js b/web-ui/public/js/foundation/initialize_foundation.js new file mode 100644 index 00000000..42405dfe --- /dev/null +++ b/web-ui/public/js/foundation/initialize_foundation.js @@ -0,0 +1,5 @@ + +(function() { + 'use strict'; + $(document).foundation(); +})(); diff --git a/web-ui/public/js/foundation/off_canvas.js b/web-ui/public/js/foundation/off_canvas.js new file mode 100644 index 00000000..66334470 --- /dev/null +++ b/web-ui/public/js/foundation/off_canvas.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(['flight/lib/component', 'page/events'], function (defineComponent, events) { + 'use strict'; + return defineComponent(function() { + + this.closeSlider = function (ev){ + $('.off-canvas-wrap.content').removeClass('move-right'); + this.toggleTagsVisibility(); + }; + + this.toggleSlideContent = function (ev) { + ev.preventDefault(); + $('.left-off-canvas-toggle').click(); + this.toggleTagsVisibility(); + }; + + this.toggleTagsVisibility = function () { + if ($('.off-canvas-wrap.content').hasClass('move-right')) { + $('#custom-tag-list').addClass('expanded'); + } else { + $('#custom-tag-list').removeClass('expanded'); + } + }; + + this.after('initialize', function () { + this.on($('#middle-pane-container'), 'click', this.closeSlider); + this.on($('#right-pane'), 'click', this.closeSlider); + this.on($('.side-nav-toggle'), 'click', this.toggleSlideContent); + }); + }); +}); diff --git a/web-ui/public/js/helpers/browser.js b/web-ui/public/js/helpers/browser.js new file mode 100644 index 00000000..dacf2263 --- /dev/null +++ b/web-ui/public/js/helpers/browser.js @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define([], function () { + + 'use strict'; + + function redirect(url) { + window.location.replace(url); + } + + function getCookie(name) { + var value = '; ' + document.cookie; + var parts = value.split('; ' + name + '='); + if (parts.length === 2) { return parts.pop().split(';').shift(); } + } + + return { + redirect: redirect, + getCookie: getCookie + }; +}); diff --git a/web-ui/public/js/helpers/contenttype.js b/web-ui/public/js/helpers/contenttype.js new file mode 100644 index 00000000..a1e5361a --- /dev/null +++ b/web-ui/public/js/helpers/contenttype.js @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +/* jshint curly: false */ +define([], function () { + 'use strict'; + var exports = {}; + + // Licence: PUBLIC DOMAIN + // Author: Austin Wright + + function MediaType(s, p){ + this.type = ''; + this.params = {}; + var c, i, n; + if(typeof s==='string'){ + c = splitQuotedString(s); + this.type = c.shift(); + for(i=0; i=0){ + offset = [delim,quote].reduce(findNextChar, 1/0); + if(offset===1/0) break; + switch(str[offset]){ + case quote: + // Skip to end of quoted string + while(1){ + offset=str.indexOf(quote, offset+1); + if(offset<0) break; + if(str[offset-1]==='\\') continue; + break; + } + continue; + case delim: + res.push(str.substr(start, offset-start).trim()); + start = ++offset; + break; + } + } + res.push(str.substr(start).trim()); + return res; + } + exports.splitQuotedString = splitQuotedString; + + // Split a list of content types found in an Accept header + // Maybe use it like: splitContentTypes(request.headers.accept).map(parseMedia) + function splitContentTypes(str){ + return splitQuotedString(str, ','); + } + exports.splitContentTypes = splitContentTypes; + + function parseMedia(str){ + var o = new MediaType(str); + if(o.q===undefined) o.q=1; + return o; + } + exports.parseMedia = parseMedia; + + // Pick an ideal representation to send given a list of representations to choose from and the client-preferred list + function select(reps, accept){ + var cr = {q:0}; + var ca = {q:0}; + var cq = 0; + for(var i=0; i=0){ + if(aq*rq>cq){ + ca = a; + cr = r; + cq = ca.q*cr.q; + if(cq===1 && cr.type) return cr; + } + } + } + } + return cr.type&&cr; + } + exports.select = select; + + // Determine if one media type is a subset of another + // If a is a superset of b (b is smaller than a), return 1 + // If b is a superset of a, return -1 + // If they are the exact same, return 0 + // If they are disjoint, return null + function mediaCmp(a, b){ + if(a.type==='*/*' && b.type!=='*/*') return 1; + else if(a.type!=='*/*' && b.type==='*/*') return -1; + var ac = (a.type||'').split('/'); + var bc = (b.type||'').split('/'); + if(ac[0]==='*' && bc[0]!=='*') return 1; + if(ac[0]!=='*' && bc[0]==='*') return -1; + if(a.type!==b.type) return null; + var ap = a.params || {}; + var bp = b.params || {}; + var ak = Object.keys(ap); + var bk = Object.keys(bp); + if(ak.length < bk.length) return 1; + if(ak.length > bk.length) return -1; + var k = ak.concat(bk).sort(); + var dir = 0; + for(var n in ap){ + if(ap[n] && !bp[n]){ if(dir<0) return null; else dir=1; } + if(!ap[n] && bp[n]){ if(dir>0) return null; else dir=-1; } + } + return dir; + } + exports.mediaCmp = mediaCmp; + + return exports; +}); diff --git a/web-ui/public/js/helpers/iterator.js b/web-ui/public/js/helpers/iterator.js new file mode 100644 index 00000000..236c7a40 --- /dev/null +++ b/web-ui/public/js/helpers/iterator.js @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(function () { + 'use strict'; + + return Iterator; + + function Iterator(elems, startingIndex) { + + this.index = startingIndex || 0; + this.elems = elems; + + this.hasPrevious = function () { + return this.index !== 0; + }; + + this.hasNext = function () { + return this.index < this.elems.length - 1; + }; + + this.previous = function () { + return this.elems[--this.index]; + }; + + this.next = function () { + return this.elems[++this.index]; + }; + + this.current = function () { + return this.elems[this.index]; + }; + + this.hasElements = function () { + return this.elems.length > 0; + }; + + this.removeCurrent = function () { + var removed = this.current(), + toRemove = this.index; + + if(!this.hasNext()) { this.index--; } + this.elems.remove(toRemove); + return removed; + }; + } +}); diff --git a/web-ui/public/js/helpers/monitored_ajax.js b/web-ui/public/js/helpers/monitored_ajax.js new file mode 100644 index 00000000..bbf85c45 --- /dev/null +++ b/web-ui/public/js/helpers/monitored_ajax.js @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['page/events', 'views/i18n', 'helpers/browser'], function (events, i18n, browser) { + + 'use strict'; + + var messages = { + timeout: 'error.timeout', + error: 'error.general', + parseerror: 'error.parse' + }; + + function monitoredAjax(on, url, config) { + config = config || {}; + config.timeout = 60 * 1000; + + var originalBeforeSend = config.beforeSend; + config.beforeSend = function () { + if (originalBeforeSend) { + originalBeforeSend(); + } + }; + + config.headers = {'X-XSRF-TOKEN': browser.getCookie('XSRF-TOKEN')}; + + var originalComplete = config.complete; + config.complete = function () { + if (originalComplete) { + originalComplete(); + } + }; + + return $.ajax(url, config).fail(function (xmlhttprequest, textstatus, message) { + if (!config.skipErrorMessage) { + var msg = (xmlhttprequest.responseJSON && xmlhttprequest.responseJSON.message) || + messages[textstatus] || messages.error; + on.trigger(document, events.ui.userAlerts.displayMessage, {message: i18n.t(msg), class: 'error'}); + } + + if (xmlhttprequest.status === 302) { + var redirectUrl = xmlhttprequest.getResponseHeader('Location'); + browser.redirect(redirectUrl); + } else if (xmlhttprequest.status === 401) { + browser.redirect('/'); + } + + }.bind(this)); + } + + return monitoredAjax; + +}); diff --git a/web-ui/public/js/helpers/sanitizer.js b/web-ui/public/js/helpers/sanitizer.js new file mode 100644 index 00000000..443e8602 --- /dev/null +++ b/web-ui/public/js/helpers/sanitizer.js @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['DOMPurify', 'he'], function (DOMPurify, he) { + 'use strict'; + + /** + * Sanitizes a mail body to safe-to-display HTML + */ + var sanitizer = {}; + + sanitizer.whitelist = [{ + // highlight tag open + pre: '<em class="search-highlight">', + post: '' + }, { + // highlight tag close + pre: '</em>', + post: '' + }]; + + /** + * Adds html line breaks to a plaintext with line breaks (incl carriage return) + * + * @param {string} textPlainBody Plaintext input + * @returns {string} Plaintext with HTML line breals (
            ) + */ + sanitizer.addLineBreaks = function (textPlainBody) { + return textPlainBody.replace(/(\r)?\n/g, '
            ').replace(/( )? /g, '
            '); + }; + + /** + * Runs a given dirty body through DOMPurify, thereby removing + * potentially hazardous XSS attacks. Please be advised that this + * will not act as a privacy leak prevention. Contained contents + * will still point to remote sources. + * + * For future reference: Running DOMPurify with these parameters + * can help mitigate some of the most widely used privacy leaks. + * FORBID_TAGS: ['style', 'svg', 'audio', 'video', 'math'], + * FORBID_ATTR: ['src'] + * + * @param {string} dirtyBody The unsanitized string + * @return {string} Safe-to-display HTML string + */ + sanitizer.purifyHtml = function (dirtyBody) { + return DOMPurify.sanitize(dirtyBody, { + SAFE_FOR_JQUERY: true, + SAFE_FOR_TEMPLATES: true + }); + }; + + /** + * Runs a given dirty body through he, thereby encoding everything + * as HTML entities. + * + * @param {string} dirtyBody The unsanitized string + * @return {string} Safe-to-display HTML string + */ + sanitizer.purifyText = function (dirtyBody) { + var escapedBody = he.encode(dirtyBody, { + encodeEverything: true + }); + + this.whitelist.forEach(function(entry) { + while (escapedBody.indexOf(entry.pre) > -1) { + escapedBody = escapedBody.replace(entry.pre, entry.post); + } + }); + + return escapedBody; + }; + + /** + * Calls #purify and #addLineBreaks to turn untrusted mail body content + * into safe-to-display HTML. + * + * NB: HTML content is preferred to plaintext content. + * + * @param {object} mail Pixelated Mail Object + * @return {string} Safe-to-display HTML string + */ + sanitizer.sanitize = function (mail) { + var body; + + if (mail.htmlBody) { + body = this.purifyHtml(mail.htmlBody); + } else { + body = this.purifyText(mail.textPlainBody); + body = this.addLineBreaks(body); + } + + return body; + }; + + /** + * Add hooks to DOMPurify for opening links in new windows + */ + DOMPurify.addHook('afterSanitizeAttributes', function (node) { + // set all elements owning target to target=_blank + if ('target' in node) { + node.setAttribute('target', '_blank'); + } + + // set non-HTML/MathML links to xlink:show=new + if (!node.hasAttribute('target') && (node.hasAttribute('xlink:href') || node.hasAttribute('href'))) { + node.setAttribute('xlink:show', 'new'); + } + }); + + return sanitizer; +}); diff --git a/web-ui/public/js/helpers/triggering.js b/web-ui/public/js/helpers/triggering.js new file mode 100644 index 00000000..d26d9fc6 --- /dev/null +++ b/web-ui/public/js/helpers/triggering.js @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define([], function() { + 'use strict'; + + return function(that, event, data, on) { + return function() { + if(on) { + that.trigger(on, event, data || {}); + } else { + that.trigger(event, data || {}); + } + }; + }; +}); diff --git a/web-ui/public/js/helpers/view_helper.js b/web-ui/public/js/helpers/view_helper.js new file mode 100644 index 00000000..ed9e0559 --- /dev/null +++ b/web-ui/public/js/helpers/view_helper.js @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'helpers/contenttype', + 'views/i18n', + 'quoted-printable/quoted-printable', + 'utf8/utf8', + 'helpers/sanitizer' + ], + function(contentType, i18n, quotedPrintable, utf8, sanitizer) { + 'use strict'; + + function formatStatusClasses(ss) { + return _.map(ss, function(s) { + return 'status-' + s; + }).join(' '); + } + + function formatMailBody(mail) { + return sanitizer.sanitize(mail); + } + + function moveCaretToEnd(el) { + if (typeof el.selectionStart === 'number') { + el.selectionStart = el.selectionEnd = el.value.length; + } else if (typeof el.createTextRange !== 'undefined') { + el.focus(); + var range = el.createTextRange(); + range.collapse(false); + range.select(); + } + } + + function fixedSizeNumber(num, size) { + var res = num.toString(); + while(res.length < size) { + res = '0' + res; + } + return res; + } + + function createTodayDate() { + var today = new Date(); + today.setHours(0); + today.setMinutes(0); + today.setSeconds(0); + return today; + } + + function moveCaretToEndOfText() { + var self = this; + + moveCaretToEnd(self); + window.setTimeout(function() { + moveCaretToEnd(self); + }, 1); + } + + function prependFrom(mail) { + return i18n.t( + 'reply-author-line', {'date': new Date(mail.header.date).toString(), 'from': mail.header.from} + ); + } + + function quoteMail(mail) { + return '\n\n' + prependFrom(mail) + mail.textPlainBody.replace(/^/mg, '> '); + } + + function formatDate(dateString) { + var date = new Date(dateString); + var today = createTodayDate(); + if (date.getTime() > today.getTime()) { + return fixedSizeNumber(date.getHours(), 2) + ':' + fixedSizeNumber(date.getMinutes(), 2); + } else { + return '' + date.getFullYear() + '-' + fixedSizeNumber(date.getMonth() + 1, 2) + '-' + fixedSizeNumber(date.getDate(), 2); + } + } + + function formatSize(bytes) { + var e = Math.floor(Math.log(bytes) / Math.log(1024)); + return (bytes / Math.pow(1024, e)).toFixed(2) + ' ' + ' KMGTP'.charAt(e) + 'b'; + } + + + function formatFingerPrint(fingerprint) { + fingerprint = fingerprint || ''; + return fingerprint.replace(/(.{4})/g, '$1 ').trim(); + } + + function getSinceDate(sinceDate){ + var commitDate = new Date(sinceDate); + var number = Date.now(); + var millisecondsSince = number - commitDate; + + var SECONDS = 1000, + MIN = 60 * SECONDS, + HOUR = MIN * 60, + DAY = HOUR * 24, + WEEK = DAY * 7, + MONTH = WEEK * 4, + YEAR = DAY * 365; + + var years = Math.floor(millisecondsSince / YEAR); + if (years >= 1){ + return years + ' year(s)'; + } + + var months = Math.floor(millisecondsSince / MONTH); + if (months >= 1) { + return months + ' month(s)'; + } + + var weeks = Math.floor(millisecondsSince / WEEK); + if (weeks >= 1) { + return weeks + ' week(s)'; + } + + var days = Math.floor(millisecondsSince / DAY); + if (days >= 1) { + return days + ' day(s)'; + } + + var hours = Math.floor(millisecondsSince / HOUR); + if (hours >= 1) { + return hours + ' hour(s)'; + } + + var minutes = Math.floor(millisecondsSince / MIN); + return minutes + ' minute(s)'; + } + + Handlebars.registerHelper('formatDate', formatDate); + Handlebars.registerHelper('formatSize', formatSize); + Handlebars.registerHelper('formatStatusClasses', formatStatusClasses); + Handlebars.registerHelper('formatFingerPrint', formatFingerPrint); + Handlebars.registerHelper('sinceDate', getSinceDate); + + return { + formatStatusClasses: formatStatusClasses, + formatSize: formatSize, + formatMailBody: formatMailBody, + formatFingerPrint: formatFingerPrint, + moveCaretToEndOfText: moveCaretToEndOfText, + quoteMail: quoteMail, + sinceDate: getSinceDate, + i18n: i18n + }; +}); diff --git a/web-ui/public/js/lib/highlightRegex.js b/web-ui/public/js/lib/highlightRegex.js new file mode 100644 index 00000000..17caaa23 --- /dev/null +++ b/web-ui/public/js/lib/highlightRegex.js @@ -0,0 +1,127 @@ +/* + * jQuery Highlight Regex Plugin v0.1.2 + * + * Based on highlight v3 by Johann Burkard + * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html + * + * (c) 2009-13 Jacob Rothstein + * MIT license + */ + +;(function( $ ) { + + + + var normalize = function( node ) { + if ( ! ( node && node.childNodes )) return + + var children = $.makeArray( node.childNodes ) + , prevTextNode = null + + $.each( children, function( i, child ) { + if ( child.nodeType === 3 ) { + if ( child.nodeValue === "" ) { + + node.removeChild( child ) + + } else if ( prevTextNode !== null ) { + + prevTextNode.nodeValue += child.nodeValue; + node.removeChild( child ) + + } else { + + prevTextNode = child + + } + } else { + prevTextNode = null + + if ( child.childNodes ) { + normalize( child ) + } + } + }) + } + + + + + $.fn.highlightRegex = function( regex, options ) { + + if ( typeof regex === 'object' && !(regex.constructor.name == 'RegExp' || regex instanceof RegExp ) ) { + options = regex + regex = undefined + } + + if ( typeof options === 'undefined' ) options = {} + + options.className = options.className || 'highlight' + options.tagType = options.tagType || 'span' + options.attrs = options.attrs || {} + + if ( typeof regex === 'undefined' || regex.source === '' ) { + + $( this ).find( options.tagType + '.' + options.className ).each( function() { + + $( this ).replaceWith( $( this ).text() ) + + normalize( $( this ).parent().get( 0 )) + + }) + + } else { + + $( this ).each( function() { + + var elt = $( this ).get( 0 ) + + normalize( elt ) + + $.each( $.makeArray( elt.childNodes ), function( i, searchnode ) { + + var spannode, middlebit, middleclone, pos, match, parent + + normalize( searchnode ) + + if ( searchnode.nodeType == 3 ) { + + // don't re-highlight the same node over and over + if ( $(searchnode).parent(options.tagType + '.' + options.className).length ) { + return; + } + + while ( searchnode.data && + ( pos = searchnode.data.search( regex )) >= 0 ) { + + match = searchnode.data.slice( pos ).match( regex )[ 0 ] + + if ( match.length > 0 ) { + + spannode = document.createElement( options.tagType ) + spannode.className = options.className + $(spannode).attr(options.attrs) + + parent = searchnode.parentNode + middlebit = searchnode.splitText( pos ) + searchnode = middlebit.splitText( match.length ) + middleclone = middlebit.cloneNode( true ) + + spannode.appendChild( middleclone ) + parent.replaceChild( spannode, middlebit ) + + } else break + } + + } else { + + $( searchnode ).highlightRegex( regex, options ) + + } + }) + }) + } + + return $( this ) + } +})( jQuery ); diff --git a/web-ui/public/js/lib/html4-defs.js b/web-ui/public/js/lib/html4-defs.js new file mode 100644 index 00000000..1ec575da --- /dev/null +++ b/web-ui/public/js/lib/html4-defs.js @@ -0,0 +1,640 @@ +// Copyright Google Inc. +// Licensed under the Apache Licence Version 2.0 +// Autogenerated at Mon Jul 14 18:51:33 BRT 2014 +// @overrides window +// @provides html4 +define([], function() { +var html4 = {}; +html4.atype = { + 'NONE': 0, + 'URI': 1, + 'URI_FRAGMENT': 11, + 'SCRIPT': 2, + 'STYLE': 3, + 'HTML': 12, + 'ID': 4, + 'IDREF': 5, + 'IDREFS': 6, + 'GLOBAL_NAME': 7, + 'LOCAL_NAME': 8, + 'CLASSES': 9, + 'FRAME_TARGET': 10, + 'MEDIA_QUERY': 13 +}; +html4[ 'atype' ] = html4.atype; +html4.ATTRIBS = { + '*::class': 9, + '*::dir': 0, + '*::draggable': 0, + '*::hidden': 0, + '*::id': 4, + '*::inert': 0, + '*::itemprop': 0, + '*::itemref': 6, + '*::itemscope': 0, + '*::lang': 0, + '*::onblur': 2, + '*::onchange': 2, + '*::onclick': 2, + '*::ondblclick': 2, + '*::onerror': 2, + '*::onfocus': 2, + '*::onkeydown': 2, + '*::onkeypress': 2, + '*::onkeyup': 2, + '*::onload': 2, + '*::onmousedown': 2, + '*::onmousemove': 2, + '*::onmouseout': 2, + '*::onmouseover': 2, + '*::onmouseup': 2, + '*::onreset': 2, + '*::onscroll': 2, + '*::onselect': 2, + '*::onsubmit': 2, + '*::ontouchcancel': 2, + '*::ontouchend': 2, + '*::ontouchenter': 2, + '*::ontouchleave': 2, + '*::ontouchmove': 2, + '*::ontouchstart': 2, + '*::onunload': 2, + '*::spellcheck': 0, + '*::style': 3, + '*::tabindex': 0, + '*::title': 0, + '*::translate': 0, + 'a::accesskey': 0, + 'a::coords': 0, + 'a::href': 1, + 'a::hreflang': 0, + 'a::name': 7, + 'a::onblur': 2, + 'a::onfocus': 2, + 'a::shape': 0, + 'a::target': 10, + 'a::type': 0, + 'area::accesskey': 0, + 'area::alt': 0, + 'area::coords': 0, + 'area::href': 1, + 'area::nohref': 0, + 'area::onblur': 2, + 'area::onfocus': 2, + 'area::shape': 0, + 'area::target': 10, + 'audio::controls': 0, + 'audio::loop': 0, + 'audio::mediagroup': 5, + 'audio::muted': 0, + 'audio::preload': 0, + 'audio::src': 1, + 'bdo::dir': 0, + 'blockquote::cite': 1, + 'br::clear': 0, + 'button::accesskey': 0, + 'button::disabled': 0, + 'button::name': 8, + 'button::onblur': 2, + 'button::onfocus': 2, + 'button::type': 0, + 'button::value': 0, + 'canvas::height': 0, + 'canvas::width': 0, + 'caption::align': 0, + 'col::align': 0, + 'col::char': 0, + 'col::charoff': 0, + 'col::span': 0, + 'col::valign': 0, + 'col::width': 0, + 'colgroup::align': 0, + 'colgroup::char': 0, + 'colgroup::charoff': 0, + 'colgroup::span': 0, + 'colgroup::valign': 0, + 'colgroup::width': 0, + 'command::checked': 0, + 'command::command': 5, + 'command::disabled': 0, + 'command::icon': 1, + 'command::label': 0, + 'command::radiogroup': 0, + 'command::type': 0, + 'data::value': 0, + 'del::cite': 1, + 'del::datetime': 0, + 'details::open': 0, + 'dir::compact': 0, + 'div::align': 0, + 'dl::compact': 0, + 'fieldset::disabled': 0, + 'font::color': 0, + 'font::face': 0, + 'font::size': 0, + 'form::accept': 0, + 'form::action': 1, + 'form::autocomplete': 0, + 'form::enctype': 0, + 'form::method': 0, + 'form::name': 7, + 'form::novalidate': 0, + 'form::onreset': 2, + 'form::onsubmit': 2, + 'form::target': 10, + 'h1::align': 0, + 'h2::align': 0, + 'h3::align': 0, + 'h4::align': 0, + 'h5::align': 0, + 'h6::align': 0, + 'hr::align': 0, + 'hr::noshade': 0, + 'hr::size': 0, + 'hr::width': 0, + 'iframe::align': 0, + 'iframe::frameborder': 0, + 'iframe::height': 0, + 'iframe::marginheight': 0, + 'iframe::marginwidth': 0, + 'iframe::width': 0, + 'img::align': 0, + 'img::alt': 0, + 'img::border': 0, + 'img::height': 0, + 'img::hspace': 0, + 'img::ismap': 0, + 'img::name': 7, + 'img::src': 1, + 'img::usemap': 11, + 'img::vspace': 0, + 'img::width': 0, + 'input::accept': 0, + 'input::accesskey': 0, + 'input::align': 0, + 'input::alt': 0, + 'input::autocomplete': 0, + 'input::checked': 0, + 'input::disabled': 0, + 'input::inputmode': 0, + 'input::ismap': 0, + 'input::list': 5, + 'input::max': 0, + 'input::maxlength': 0, + 'input::min': 0, + 'input::multiple': 0, + 'input::name': 8, + 'input::onblur': 2, + 'input::onchange': 2, + 'input::onfocus': 2, + 'input::onselect': 2, + 'input::pattern': 0, + 'input::placeholder': 0, + 'input::readonly': 0, + 'input::required': 0, + 'input::size': 0, + 'input::src': 1, + 'input::step': 0, + 'input::type': 0, + 'input::usemap': 11, + 'input::value': 0, + 'ins::cite': 1, + 'ins::datetime': 0, + 'label::accesskey': 0, + 'label::for': 5, + 'label::onblur': 2, + 'label::onfocus': 2, + 'legend::accesskey': 0, + 'legend::align': 0, + 'li::type': 0, + 'li::value': 0, + 'map::name': 7, + 'menu::compact': 0, + 'menu::label': 0, + 'menu::type': 0, + 'meter::high': 0, + 'meter::low': 0, + 'meter::max': 0, + 'meter::min': 0, + 'meter::value': 0, + 'ol::compact': 0, + 'ol::reversed': 0, + 'ol::start': 0, + 'ol::type': 0, + 'optgroup::disabled': 0, + 'optgroup::label': 0, + 'option::disabled': 0, + 'option::label': 0, + 'option::selected': 0, + 'option::value': 0, + 'output::for': 6, + 'output::name': 8, + 'p::align': 0, + 'pre::width': 0, + 'progress::max': 0, + 'progress::min': 0, + 'progress::value': 0, + 'q::cite': 1, + 'select::autocomplete': 0, + 'select::disabled': 0, + 'select::multiple': 0, + 'select::name': 8, + 'select::onblur': 2, + 'select::onchange': 2, + 'select::onfocus': 2, + 'select::required': 0, + 'select::size': 0, + 'source::type': 0, + 'table::align': 0, + 'table::bgcolor': 0, + 'table::border': 0, + 'table::cellpadding': 0, + 'table::cellspacing': 0, + 'table::frame': 0, + 'table::rules': 0, + 'table::summary': 0, + 'table::width': 0, + 'tbody::align': 0, + 'tbody::char': 0, + 'tbody::charoff': 0, + 'tbody::valign': 0, + 'td::abbr': 0, + 'td::align': 0, + 'td::axis': 0, + 'td::bgcolor': 0, + 'td::char': 0, + 'td::charoff': 0, + 'td::colspan': 0, + 'td::headers': 6, + 'td::height': 0, + 'td::nowrap': 0, + 'td::rowspan': 0, + 'td::scope': 0, + 'td::valign': 0, + 'td::width': 0, + 'textarea::accesskey': 0, + 'textarea::autocomplete': 0, + 'textarea::cols': 0, + 'textarea::disabled': 0, + 'textarea::inputmode': 0, + 'textarea::name': 8, + 'textarea::onblur': 2, + 'textarea::onchange': 2, + 'textarea::onfocus': 2, + 'textarea::onselect': 2, + 'textarea::placeholder': 0, + 'textarea::readonly': 0, + 'textarea::required': 0, + 'textarea::rows': 0, + 'textarea::wrap': 0, + 'tfoot::align': 0, + 'tfoot::char': 0, + 'tfoot::charoff': 0, + 'tfoot::valign': 0, + 'th::abbr': 0, + 'th::align': 0, + 'th::axis': 0, + 'th::bgcolor': 0, + 'th::char': 0, + 'th::charoff': 0, + 'th::colspan': 0, + 'th::headers': 6, + 'th::height': 0, + 'th::nowrap': 0, + 'th::rowspan': 0, + 'th::scope': 0, + 'th::valign': 0, + 'th::width': 0, + 'thead::align': 0, + 'thead::char': 0, + 'thead::charoff': 0, + 'thead::valign': 0, + 'tr::align': 0, + 'tr::bgcolor': 0, + 'tr::char': 0, + 'tr::charoff': 0, + 'tr::valign': 0, + 'track::default': 0, + 'track::kind': 0, + 'track::label': 0, + 'track::srclang': 0, + 'ul::compact': 0, + 'ul::type': 0, + 'video::controls': 0, + 'video::height': 0, + 'video::loop': 0, + 'video::mediagroup': 5, + 'video::muted': 0, + 'video::poster': 1, + 'video::preload': 0, + 'video::src': 1, + 'video::width': 0 +}; +html4[ 'ATTRIBS' ] = html4.ATTRIBS; +html4.eflags = { + 'OPTIONAL_ENDTAG': 1, + 'EMPTY': 2, + 'CDATA': 4, + 'RCDATA': 8, + 'UNSAFE': 16, + 'FOLDABLE': 32, + 'SCRIPT': 64, + 'STYLE': 128, + 'VIRTUALIZED': 256 +}; +html4[ 'eflags' ] = html4.eflags; +html4.ELEMENTS = { + 'a': 0, + 'abbr': 0, + 'acronym': 0, + 'address': 0, + 'applet': 272, + 'area': 2, + 'article': 0, + 'aside': 0, + 'audio': 0, + 'b': 0, + 'base': 274, + 'basefont': 274, + 'bdi': 0, + 'bdo': 0, + 'big': 0, + 'blockquote': 0, + 'body': 305, + 'br': 2, + 'button': 0, + 'canvas': 0, + 'caption': 0, + 'center': 0, + 'cite': 0, + 'code': 0, + 'col': 2, + 'colgroup': 1, + 'command': 2, + 'data': 0, + 'datalist': 0, + 'dd': 1, + 'del': 0, + 'details': 0, + 'dfn': 0, + 'dialog': 272, + 'dir': 0, + 'div': 0, + 'dl': 0, + 'dt': 1, + 'em': 0, + 'fieldset': 0, + 'figcaption': 0, + 'figure': 0, + 'font': 0, + 'footer': 0, + 'form': 0, + 'frame': 274, + 'frameset': 272, + 'h1': 0, + 'h2': 0, + 'h3': 0, + 'h4': 0, + 'h5': 0, + 'h6': 0, + 'head': 305, + 'header': 0, + 'hgroup': 0, + 'hr': 2, + 'html': 305, + 'i': 0, + 'iframe': 4, + 'img': 2, + 'input': 2, + 'ins': 0, + 'isindex': 274, + 'kbd': 0, + 'keygen': 274, + 'label': 0, + 'legend': 0, + 'li': 1, + 'link': 274, + 'map': 0, + 'mark': 0, + 'menu': 0, + 'meta': 274, + 'meter': 0, + 'nav': 0, + 'nobr': 0, + 'noembed': 276, + 'noframes': 276, + 'noscript': 276, + 'object': 272, + 'ol': 0, + 'optgroup': 0, + 'option': 1, + 'output': 0, + 'p': 1, + 'param': 274, + 'pre': 0, + 'progress': 0, + 'q': 0, + 's': 0, + 'samp': 0, + 'script': 84, + 'section': 0, + 'select': 0, + 'small': 0, + 'source': 2, + 'span': 0, + 'strike': 0, + 'strong': 0, + 'style': 148, + 'sub': 0, + 'summary': 0, + 'sup': 0, + 'table': 0, + 'tbody': 1, + 'td': 1, + 'textarea': 8, + 'tfoot': 1, + 'th': 1, + 'thead': 1, + 'time': 0, + 'title': 280, + 'tr': 1, + 'track': 2, + 'tt': 0, + 'u': 0, + 'ul': 0, + 'var': 0, + 'video': 0, + 'wbr': 2 +}; +html4[ 'ELEMENTS' ] = html4.ELEMENTS; +html4.ELEMENT_DOM_INTERFACES = { + 'a': 'HTMLAnchorElement', + 'abbr': 'HTMLElement', + 'acronym': 'HTMLElement', + 'address': 'HTMLElement', + 'applet': 'HTMLAppletElement', + 'area': 'HTMLAreaElement', + 'article': 'HTMLElement', + 'aside': 'HTMLElement', + 'audio': 'HTMLAudioElement', + 'b': 'HTMLElement', + 'base': 'HTMLBaseElement', + 'basefont': 'HTMLBaseFontElement', + 'bdi': 'HTMLElement', + 'bdo': 'HTMLElement', + 'big': 'HTMLElement', + 'blockquote': 'HTMLQuoteElement', + 'body': 'HTMLBodyElement', + 'br': 'HTMLBRElement', + 'button': 'HTMLButtonElement', + 'canvas': 'HTMLCanvasElement', + 'caption': 'HTMLTableCaptionElement', + 'center': 'HTMLElement', + 'cite': 'HTMLElement', + 'code': 'HTMLElement', + 'col': 'HTMLTableColElement', + 'colgroup': 'HTMLTableColElement', + 'command': 'HTMLCommandElement', + 'data': 'HTMLElement', + 'datalist': 'HTMLDataListElement', + 'dd': 'HTMLElement', + 'del': 'HTMLModElement', + 'details': 'HTMLDetailsElement', + 'dfn': 'HTMLElement', + 'dialog': 'HTMLDialogElement', + 'dir': 'HTMLDirectoryElement', + 'div': 'HTMLDivElement', + 'dl': 'HTMLDListElement', + 'dt': 'HTMLElement', + 'em': 'HTMLElement', + 'fieldset': 'HTMLFieldSetElement', + 'figcaption': 'HTMLElement', + 'figure': 'HTMLElement', + 'font': 'HTMLFontElement', + 'footer': 'HTMLElement', + 'form': 'HTMLFormElement', + 'frame': 'HTMLFrameElement', + 'frameset': 'HTMLFrameSetElement', + 'h1': 'HTMLHeadingElement', + 'h2': 'HTMLHeadingElement', + 'h3': 'HTMLHeadingElement', + 'h4': 'HTMLHeadingElement', + 'h5': 'HTMLHeadingElement', + 'h6': 'HTMLHeadingElement', + 'head': 'HTMLHeadElement', + 'header': 'HTMLElement', + 'hgroup': 'HTMLElement', + 'hr': 'HTMLHRElement', + 'html': 'HTMLHtmlElement', + 'i': 'HTMLElement', + 'iframe': 'HTMLIFrameElement', + 'img': 'HTMLImageElement', + 'input': 'HTMLInputElement', + 'ins': 'HTMLModElement', + 'isindex': 'HTMLUnknownElement', + 'kbd': 'HTMLElement', + 'keygen': 'HTMLKeygenElement', + 'label': 'HTMLLabelElement', + 'legend': 'HTMLLegendElement', + 'li': 'HTMLLIElement', + 'link': 'HTMLLinkElement', + 'map': 'HTMLMapElement', + 'mark': 'HTMLElement', + 'menu': 'HTMLMenuElement', + 'meta': 'HTMLMetaElement', + 'meter': 'HTMLMeterElement', + 'nav': 'HTMLElement', + 'nobr': 'HTMLElement', + 'noembed': 'HTMLElement', + 'noframes': 'HTMLElement', + 'noscript': 'HTMLElement', + 'object': 'HTMLObjectElement', + 'ol': 'HTMLOListElement', + 'optgroup': 'HTMLOptGroupElement', + 'option': 'HTMLOptionElement', + 'output': 'HTMLOutputElement', + 'p': 'HTMLParagraphElement', + 'param': 'HTMLParamElement', + 'pre': 'HTMLPreElement', + 'progress': 'HTMLProgressElement', + 'q': 'HTMLQuoteElement', + 's': 'HTMLElement', + 'samp': 'HTMLElement', + 'script': 'HTMLScriptElement', + 'section': 'HTMLElement', + 'select': 'HTMLSelectElement', + 'small': 'HTMLElement', + 'source': 'HTMLSourceElement', + 'span': 'HTMLSpanElement', + 'strike': 'HTMLElement', + 'strong': 'HTMLElement', + 'style': 'HTMLStyleElement', + 'sub': 'HTMLElement', + 'summary': 'HTMLElement', + 'sup': 'HTMLElement', + 'table': 'HTMLTableElement', + 'tbody': 'HTMLTableSectionElement', + 'td': 'HTMLTableDataCellElement', + 'textarea': 'HTMLTextAreaElement', + 'tfoot': 'HTMLTableSectionElement', + 'th': 'HTMLTableHeaderCellElement', + 'thead': 'HTMLTableSectionElement', + 'time': 'HTMLTimeElement', + 'title': 'HTMLTitleElement', + 'tr': 'HTMLTableRowElement', + 'track': 'HTMLTrackElement', + 'tt': 'HTMLElement', + 'u': 'HTMLElement', + 'ul': 'HTMLUListElement', + 'var': 'HTMLElement', + 'video': 'HTMLVideoElement', + 'wbr': 'HTMLElement' +}; +html4[ 'ELEMENT_DOM_INTERFACES' ] = html4.ELEMENT_DOM_INTERFACES; +html4.ueffects = { + 'NOT_LOADED': 0, + 'SAME_DOCUMENT': 1, + 'NEW_DOCUMENT': 2 +}; +html4[ 'ueffects' ] = html4.ueffects; +html4.URIEFFECTS = { + 'a::href': 2, + 'area::href': 2, + 'audio::src': 1, + 'blockquote::cite': 0, + 'command::icon': 1, + 'del::cite': 0, + 'form::action': 2, + 'img::src': 1, + 'input::src': 1, + 'ins::cite': 0, + 'q::cite': 0, + 'video::poster': 1, + 'video::src': 1 +}; +html4[ 'URIEFFECTS' ] = html4.URIEFFECTS; +html4.ltypes = { + 'UNSANDBOXED': 2, + 'SANDBOXED': 1, + 'DATA': 0 +}; +html4[ 'ltypes' ] = html4.ltypes; +html4.LOADERTYPES = { + 'a::href': 2, + 'area::href': 2, + 'audio::src': 2, + 'blockquote::cite': 2, + 'command::icon': 1, + 'del::cite': 2, + 'form::action': 2, + 'img::src': 1, + 'input::src': 1, + 'ins::cite': 2, + 'q::cite': 2, + 'video::poster': 1, + 'video::src': 2 +}; +html4[ 'LOADERTYPES' ] = html4.LOADERTYPES; + +return html4 +}); diff --git a/web-ui/public/js/mail_list/domain/refresher.js b/web-ui/public/js/mail_list/domain/refresher.js new file mode 100644 index 00000000..38c9cde5 --- /dev/null +++ b/web-ui/public/js/mail_list/domain/refresher.js @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(['flight/lib/component', 'page/events', 'features'], function(defineComponent, events, features) { + 'use strict'; + + return defineComponent(refresher); + + function refresher() { + this.defaultAttrs({ + interval: 20000 + }); + + this.setupRefresher = function() { + setTimeout(this.doRefresh.bind(this), this.attr.interval); + }; + + this.doRefresh = function() { + this.trigger(document, events.ui.mails.refresh); + this.setupRefresher(); + }; + + this.after('initialize', function () { + if (features.isAutoRefreshEnabled()) { + this.setupRefresher(); + } + }); + } + } +); diff --git a/web-ui/public/js/mail_list/ui/mail_item_factory.js b/web-ui/public/js/mail_list/ui/mail_item_factory.js new file mode 100644 index 00000000..7205d35c --- /dev/null +++ b/web-ui/public/js/mail_list/ui/mail_item_factory.js @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'mail_list/ui/mail_items/generic_mail_item', + 'mail_list/ui/mail_items/draft_item', + 'mail_list/ui/mail_items/sent_item' + ], + function (GenericMailItem, DraftItem, SentItem) { + 'use strict'; + + var MAIL_ITEM_TYPE = { + 'drafts': DraftItem, + 'sent': SentItem, + 'trash': GenericMailItem + }; + + var TEMPLATE_TYPE = { + 'drafts': 'draft', + 'sent': 'sent', + 'trash': 'trash' + }; + + var createAndAttach = function (nodeToAttachTo, mail, currentMailIdent, currentTag, isChecked) { + var mailItemContainer = $('
          • ', { id: 'mail-' + mail.ident}); + nodeToAttachTo.append(mailItemContainer); + + mail.currentTag = currentTag; + var mailToCreate = MAIL_ITEM_TYPE[mail.mailbox] || GenericMailItem; + mailToCreate.attachTo(mailItemContainer, { + mail: mail, + selected: mail.ident === currentMailIdent, + tag: currentTag, + isChecked: isChecked, + templateType: TEMPLATE_TYPE[mail.mailbox] || 'single' + }); + + }; + + return { + createAndAttach: createAndAttach + }; + } +); diff --git a/web-ui/public/js/mail_list/ui/mail_items/draft_item.js b/web-ui/public/js/mail_list/ui/mail_items/draft_item.js new file mode 100644 index 00000000..57fbafd5 --- /dev/null +++ b/web-ui/public/js/mail_list/ui/mail_items/draft_item.js @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'helpers/view_helper', + 'mail_list/ui/mail_items/mail_item', + 'page/events' + ], + + function (defineComponent, viewHelpers, mailItem, events) { + 'use strict'; + + return defineComponent(draftItem, mailItem); + + function draftItem() { + this.triggerOpenMail = function (ev) { + if (this.isOpeningOnANewTab(ev)) { + return; + } + this.trigger(document, events.dispatchers.rightPane.openDraft, { ident: this.attr.mail.ident }); + this.trigger(document, events.ui.mail.updateSelected, { ident: this.attr.mail.ident }); + this.trigger(document, events.router.pushState, { mailIdent: this.attr.mail.ident }); + ev.preventDefault(); // don't let the hashchange trigger a popstate + }; + + this.after('initialize', function () { + this.render(); + + if (this.attr.isChecked) { + this.checkCheckbox(); + } + + this.on(document, events.ui.composeBox.newMessage, this.doUnselect); + this.on(document, events.ui.mail.updateSelected, this.updateSelected); + this.on(document, events.mails.teardown, this.teardown); + }); + } + } +); diff --git a/web-ui/public/js/mail_list/ui/mail_items/generic_mail_item.js b/web-ui/public/js/mail_list/ui/mail_items/generic_mail_item.js new file mode 100644 index 00000000..939f7e1b --- /dev/null +++ b/web-ui/public/js/mail_list/ui/mail_items/generic_mail_item.js @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'helpers/view_helper', + 'mail_list/ui/mail_items/mail_item', + 'page/events' + ], + + function (defineComponent, viewHelpers, mailItem, events) { + 'use strict'; + + return defineComponent(genericMailItem, mailItem); + + function genericMailItem() { + this.status = { + READ: 'read' + }; + + this.triggerOpenMail = function (ev) { + if (this.isOpeningOnANewTab(ev)) { + updateMailStatusToRead.call(this); + return; + } + this.trigger(document, events.ui.mail.open, { ident: this.attr.mail.ident }); + this.trigger(document, events.router.pushState, { mailIdent: this.attr.mail.ident }); + ev.preventDefault(); // don't let the hashchange trigger a popstate + }; + + function updateMailStatusToRead() { + if (!_.contains(this.attr.mail.status, this.status.READ)) { + var mail_read_data = { ident: this.attr.mail.ident, tags: this.attr.mail.tags, mailbox: this.attr.mail.mailbox }; + this.trigger(document, events.mail.read, mail_read_data); + this.attr.mail.status.push(this.status.READ); + this.$node.addClass(viewHelpers.formatStatusClasses(this.attr.mail.status)); + } + } + + this.openMail = function (ev, data) { + if (data.ident !== this.attr.mail.ident) { + return; + } + updateMailStatusToRead.call(this); + + this.trigger(document, events.ui.mail.updateSelected, { ident: this.attr.mail.ident }); + }; + + this.updateTags = function(ev, data) { + if(data.ident === this.attr.mail.ident){ + this.attr.tags = data.tags; + if(!_.contains(this.attr.tags, this.attr.tag)) { + this.teardown(); + } else { + this.render(); + } + } + }; + + this.deleteMail = function(ev, data) { + if(data.mail.ident === this.attr.mail.ident){ + this.teardown(); + } + }; + + this.after('initialize', function () { + this.render(); + + if (this.attr.isChecked) { + this.checkCheckbox(); + } + + this.on(document, events.ui.composeBox.newMessage, this.doUnselect); + this.on(document, events.ui.mail.open, this.openMail); + this.on(document, events.ui.mail.updateSelected, this.updateSelected); + this.on(document, events.mails.teardown, this.teardown); + this.on(document, events.mail.tags.update, this.updateTags); + this.on(document, events.mail.delete, this.deleteMail); + }); + } + } +); diff --git a/web-ui/public/js/mail_list/ui/mail_items/mail_item.js b/web-ui/public/js/mail_list/ui/mail_items/mail_item.js new file mode 100644 index 00000000..be664289 --- /dev/null +++ b/web-ui/public/js/mail_list/ui/mail_items/mail_item.js @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'helpers/view_helper', + 'views/templates', + 'page/events' + ], + function (viewHelper, templates, events) { + + 'use strict'; + function mailItem() { + this.updateSelected = function (ev, data) { + if (data.ident === this.attr.mail.ident) { this.doSelect(); } + else { this.doUnselect(); } + }; + + this.isOpeningOnANewTab = function (ev) { + return ev.metaKey || ev.ctrlKey || ev.which === 2; + }; + + this.doSelect = function () { + this.$node.addClass('selected'); + }; + + this.doUnselect = function () { + this.$node.removeClass('selected'); + }; + + this.doMailChecked = function (ev) { + if (ev.target.checked) { + this.checkCheckbox(); + } else { + this.uncheckCheckbox(); + } + }; + + this.checkboxElement = function () { + return this.$node.find('input[type=checkbox]'); + }; + + this.checkCheckbox = function () { + this.checkboxElement().prop('checked', true); + this.trigger(document, events.ui.mail.checked, { mail: this.attr.mail}); + }; + + this.uncheckCheckbox = function () { + this.checkboxElement().prop('checked', false); + this.trigger(document, events.ui.mail.unchecked, { mail: this.attr.mail}); + }; + + this.render = function () { + this.attr.mail.tagsForListView = _.without(this.attr.mail.tags, this.attr.tag); + var mailItemHtml = templates.mails[this.attr.templateType](this.attr.mail); + this.$node.html(mailItemHtml); + this.$node.addClass("mail-list-entry"); + this.$node.addClass(viewHelper.formatStatusClasses(this.attr.mail.status)); + if (this.attr.selected) { this.doSelect(); } + this.on(this.$node.find('a'), 'click', this.triggerOpenMail); + }; + + this.after('initialize', function () { + this.on(this.$node.find('input[type=checkbox]'), 'change', this.doMailChecked); + this.on(document, events.ui.mails.cleanSelected, this.doUnselect); + this.on(document, events.ui.tag.select, this.doUnselect); + this.on(document, events.ui.tag.select, this.uncheckCheckbox); + this.on(document, events.ui.mails.uncheckAll, this.uncheckCheckbox); + this.on(document, events.ui.mails.checkAll, this.checkCheckbox); + }); + } + + return mailItem; +}); diff --git a/web-ui/public/js/mail_list/ui/mail_items/sent_item.js b/web-ui/public/js/mail_list/ui/mail_items/sent_item.js new file mode 100644 index 00000000..9e511068 --- /dev/null +++ b/web-ui/public/js/mail_list/ui/mail_items/sent_item.js @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'mail_list/ui/mail_items/mail_item', + 'page/events' + ], + + function (defineComponent, mailItem, events) { + 'use strict'; + + return defineComponent(sentItem, mailItem); + + function sentItem() { + this.triggerOpenMail = function (ev) { + if (this.isOpeningOnANewTab(ev)) { + return; + } + this.trigger(document, events.ui.mail.open, { ident: this.attr.mail.ident }); + this.trigger(document, events.router.pushState, { mailIdent: this.attr.mail.ident }); + ev.preventDefault(); // don't let the hashchange trigger a popstate + }; + + this.openMail = function (ev, data) { + if (data.ident !== this.attr.mail.ident) { + return; + } + this.trigger(document, events.ui.mail.updateSelected, { ident: this.attr.mail.ident }); + }; + + this.after('initialize', function () { + this.render(); + + if (this.attr.isChecked) { + this.checkCheckbox(); + } + + this.on(document, events.ui.composeBox.newMessage, this.doUnselect); + this.on(document, events.ui.mail.open, this.openMail); + this.on(document, events.ui.mail.updateSelected, this.updateSelected); + this.on(document, events.mails.teardown, this.teardown); + }); + } + } +); diff --git a/web-ui/public/js/mail_list/ui/mail_list.js b/web-ui/public/js/mail_list/ui/mail_list.js new file mode 100644 index 00000000..af4821a8 --- /dev/null +++ b/web-ui/public/js/mail_list/ui/mail_list.js @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'flight/lib/utils', + 'mail_list/ui/mail_item_factory', + 'page/router/url_params', + 'page/events' + ], + + function (defineComponent, utils, MailItemFactory, urlParams, events) { + 'use strict'; + + return defineComponent(mailList); + + function mailList () { + var openMailEventFor = function (tag) { + return tag === 'drafts' ? events.dispatchers.rightPane.openDraft : events.ui.mail.open; + }; + + this.defaultAttrs({ + mail: '.mail', + currentMailIdent: '', + urlParams: urlParams, + initialized: false, + checkedMails: {} + }); + + this.appendMail = function (mail) { + var isChecked = mail.ident in this.attr.checkedMails; + MailItemFactory.createAndAttach(this.$node, mail, this.attr.currentMailIdent, this.attr.currentTag, isChecked); + }; + + this.resetMailList = function () { + this.trigger(document, events.mails.teardown); + this.$node.empty(); + }; + + this.triggerMailOpenForPopState = function (data) { + if (data.mailIdent) { + this.trigger(document, openMailEventFor(data.tag), { ident: data.mailIdent }); + } + }; + + this.shouldSelectEmailFromUrlMailIdent = function () { + return this.attr.urlParams.hasMailIdent(); + }; + + this.selectMailBasedOnUrlMailIdent = function () { + var mailIdent = this.attr.urlParams.getMailIdent(); + this.trigger(document, openMailEventFor(this.attr.currentTag), { ident: mailIdent }); + this.trigger(document, events.router.pushState, { tag: this.attr.currentTag, mailIdent: mailIdent }); + }; + + this.updateCurrentTagAndMail = function (data) { + if (data.ident) { + this.attr.currentMailIdent = data.ident; + } + + this.attr.currentTag = data.tag || this.attr.currentTag; + + this.updateCheckAllCheckbox(); + }; + + this.renderMails = function (mails) { + _.each(mails, this.appendMail, this); + this.trigger(document, events.search.highlightResults, {where: '#mail-list'}); + this.trigger(document, events.search.highlightResults, {where: '.mail-read-view__header'}); + }; + + this.triggerScrollReset = function () { + this.trigger(document, events.dispatchers.middlePane.resetScroll); + }; + + this.showMails = function (event, data) { + this.updateCurrentTagAndMail(data); + this.refreshMailList(null, data); + this.triggerMailOpenForPopState(data); + this.openMailFromUrl(); + }; + + this.refreshMailList = function (ev, data) { + if (ev) { // triggered by the event, so we need to refresh the tag list + this.trigger(document, events.dispatchers.tags.refreshTagList, { skipMailListRefresh: true }); + } + this.resetMailList(); + this.renderMails(data.mails); + }; + + this.updateSelected = function (ev, data) { + if (data.ident !== this.attr.currentMailIdent) { + this.attr.currentMailIdent = data.ident; + } + }; + + this.cleanSelected = function () { + this.attr.currentMailIdent = ''; + this.triggerScrollReset(); + }; + + this.respondWithCheckedMails = function (ev, caller) { + this.trigger(caller, events.ui.mail.hereChecked, {checkedMails: this.attr.checkedMails}); + }; + + this.updateCheckAllCheckbox = function () { + this.trigger(document, events.ui.mails.hasMailsChecked, _.keys(this.attr.checkedMails).length > 0); + }; + + this.addToCheckedMails = function (ev, data) { + this.attr.checkedMails[data.mail.ident] = data.mail; + this.updateCheckAllCheckbox(); + }; + + this.removeFromCheckedMails = function (ev, data) { + if (data.mails) { + _.each(data.mails, function (mail) { + delete this.attr.checkedMails[mail.ident]; + }, this); + } else { + delete this.attr.checkedMails[data.mail.ident]; + } + this.updateCheckAllCheckbox(); + }; + + this.refreshWithScroll = function () { + this.trigger(document, events.ui.mails.refresh); + this.triggerScrollReset(); + }; + + this.refreshAfterSaveDraft = function () { + if (this.attr.currentTag === 'drafts') { + this.refreshWithScroll(); + } + }; + + this.refreshAfterMailSent = function () { + if (this.attr.currentTag === 'drafts' || this.attr.currentTag === 'sent') { + this.refreshWithScroll(); + } + }; + + this.after('initialize', function () { + this.on(document, events.ui.mails.cleanSelected, this.cleanSelected); + this.on(document, events.ui.tag.select, this.cleanSelected); + + this.on(document, events.mails.available, this.showMails); + this.on(document, events.mails.availableForRefresh, this.refreshMailList); + + this.on(document, events.mail.draftSaved, this.refreshAfterSaveDraft); + this.on(document, events.mail.sent, this.refreshAfterMailSent); + + this.on(document, events.ui.mail.updateSelected, this.updateSelected); + this.on(document, events.ui.mail.wantChecked, this.respondWithCheckedMails); + this.on(document, events.ui.mail.checked, this.addToCheckedMails); + this.on(document, events.ui.mail.unchecked, this.removeFromCheckedMails); + + this.openMailFromUrl = utils.once(function () { + if (this.shouldSelectEmailFromUrlMailIdent()) { + this.selectMailBasedOnUrlMailIdent(); + } + }); + + }); + } + } +); diff --git a/web-ui/public/js/mail_list_actions/ui/archive_many_trigger.js b/web-ui/public/js/mail_list_actions/ui/archive_many_trigger.js new file mode 100644 index 00000000..b148cdce --- /dev/null +++ b/web-ui/public/js/mail_list_actions/ui/archive_many_trigger.js @@ -0,0 +1,29 @@ +define( + [ + 'flight/lib/component', + 'views/templates', + 'mixins/with_enable_disable_on_event', + 'page/events' + ], + + function(definecomponent, templates, withEnableDisableOnEvent, events) { + 'use strict'; + + return definecomponent(archiveManyTrigger, withEnableDisableOnEvent(events.ui.mails.hasMailsChecked)); + function archiveManyTrigger() { + + this.getMailsToArchive = function() { + this.trigger(document, events.ui.mail.wantChecked, this.$node); + }; + + this.archiveManyEmails = function(event, data) { + this.trigger(document, events.mail.archiveMany, data); + }; + + this.after('initialize', function () { + this.on('click', this.getMailsToArchive); + this.on(events.ui.mail.hereChecked, this.archiveManyEmails); + }); + } + } +); diff --git a/web-ui/public/js/mail_list_actions/ui/compose_trigger.js b/web-ui/public/js/mail_list_actions/ui/compose_trigger.js new file mode 100644 index 00000000..ec79cb26 --- /dev/null +++ b/web-ui/public/js/mail_list_actions/ui/compose_trigger.js @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'page/events' + ], + + function(defineComponent, templates, events) { + 'use strict'; + + return defineComponent(composeTrigger); + + function composeTrigger() { + + this.defaultAttrs({}); + + this.render = function() { + this.$node.html(templates.mailActions.composeTrigger); + }; + + this.enableComposing = function(event, data) { + this.trigger(document, events.dispatchers.rightPane.openComposeBox); + }; + + this.showEmailSuccess = function () { + this.trigger(document, events.ui.userAlerts.displayMessage, {message: 'Your message was sent!', class: 'success'}); + }; + + this.showEmailError = function (ev, data) { + this.trigger(document, events.ui.userAlerts.displayMessage, {message: 'Error, message not sent: ' + data.responseJSON.message, class: 'error'}); + }; + + this.after('initialize', function () { + this.render(); + this.on('click', this.enableComposing); + this.on(document, events.mail.sent, this.showEmailSuccess); + this.on(document, events.mail.send_failed, this.showEmailError); + }); + } + } +); diff --git a/web-ui/public/js/mail_list_actions/ui/delete_many_trigger.js b/web-ui/public/js/mail_list_actions/ui/delete_many_trigger.js new file mode 100644 index 00000000..dd2f67a5 --- /dev/null +++ b/web-ui/public/js/mail_list_actions/ui/delete_many_trigger.js @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'mixins/with_enable_disable_on_event', + 'page/events' + ], + + function(defineComponent, templates, withEnableDisableOnEvent, events) { + 'use strict'; + + return defineComponent(deleteManyTrigger, withEnableDisableOnEvent(events.ui.mails.hasMailsChecked)); + + function deleteManyTrigger() { + this.defaultAttrs({}); + + this.getMailsToDelete = function(event) { + this.trigger(document, events.ui.mail.wantChecked, this.$node); + }; + + this.deleteManyEmails = function (event, data) { + this.trigger(document, events.ui.mail.deleteMany, data); + }; + + this.after('initialize', function () { + this.on('click', this.getMailsToDelete); + this.on(events.ui.mail.hereChecked, this.deleteManyEmails); + }); + } + } +); diff --git a/web-ui/public/js/mail_list_actions/ui/mail_list_actions.js b/web-ui/public/js/mail_list_actions/ui/mail_list_actions.js new file mode 100644 index 00000000..69e5fde4 --- /dev/null +++ b/web-ui/public/js/mail_list_actions/ui/mail_list_actions.js @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'views/templates', + 'page/events', + 'page/router/url_params', + 'mail_list_actions/ui/compose_trigger', + 'mail_list_actions/ui/refresh_trigger', + 'mail_list/domain/refresher', + 'mail_list_actions/ui/toggle_check_all_trigger', + 'mail_list_actions/ui/pagination_trigger', + 'mail_list_actions/ui/delete_many_trigger', + 'mail_list_actions/ui/recover_many_trigger', + 'mail_list_actions/ui/archive_many_trigger', + 'mail_list_actions/ui/mark_many_as_read_trigger', + 'mail_list_actions/ui/mark_as_unread_trigger' + ], + + function ( + defineComponent, + templates, + events, + urlParams, + composeTrigger, + refreshTrigger, + refresher, + toggleCheckAllMailTrigger, + paginationTrigger, + deleteManyTrigger, + recoverManyTrigger, + archiveManyTrigger, + markManyAsReadTrigger, + markAsUnreadTrigger + ) { + 'use strict'; + return defineComponent(mailsActions); + + function mailsActions() { + this.render = function() { + this.$node.html(this.getActionsBoxTemplate()); + refreshTrigger.attachTo('#refresh-trigger'); + composeTrigger.attachTo('#compose-trigger'); + toggleCheckAllMailTrigger.attachTo('#toggle-check-all-emails'); + paginationTrigger.attachTo('#pagination-trigger'); + deleteManyTrigger.attachTo('#delete-selected'); + recoverManyTrigger.attachTo('#recover-selected'); + archiveManyTrigger.attachTo('#archive-selected'); + markManyAsReadTrigger.attachTo('#mark-selected-as-read'); + markAsUnreadTrigger.attachTo('#mark-selected-as-unread'); + refresher.attachTo(document); + }; + + this.getCurrentTag = function () { + return this.attr.currentTag || urlParams.getTag(); + }; + + this.updateCurrentTag = function (ev, data) { + this.attr.currentTag = data.tag; + this.render(); + }; + + this.getActionsBoxTemplate = function () { + if(this.getCurrentTag() === 'trash') { + return templates.mailActions.trashActionsBox(); + } else { + return templates.mailActions.actionsBox(); + } + }; + + this.after('initialize', function () { + this.on(document, events.ui.tag.select, this.updateCurrentTag); + this.render(); + }); + } + } +); diff --git a/web-ui/public/js/mail_list_actions/ui/mark_as_unread_trigger.js b/web-ui/public/js/mail_list_actions/ui/mark_as_unread_trigger.js new file mode 100644 index 00000000..2584e453 --- /dev/null +++ b/web-ui/public/js/mail_list_actions/ui/mark_as_unread_trigger.js @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'mixins/with_enable_disable_on_event', + 'page/events' + ], + + function(defineComponent, templates, withEnableDisableOnEvent, events) { + 'use strict'; + + return defineComponent(markAsUnreadTrigger, withEnableDisableOnEvent(events.ui.mails.hasMailsChecked)); + + function markAsUnreadTrigger() { + this.defaultAttrs({}); + + this.getMailsToMarkAsUnread = function(event) { + this.trigger(document, events.ui.mail.wantChecked, this.$node); + }; + + this.markManyEmailsAsUnread = function (event, data) { + this.trigger(document, events.mail.unread, data); + }; + + this.after('initialize', function () { + this.on('click', this.getMailsToMarkAsUnread); + this.on(events.ui.mail.hereChecked, this.markManyEmailsAsUnread); + }); + } + } +); diff --git a/web-ui/public/js/mail_list_actions/ui/mark_many_as_read_trigger.js b/web-ui/public/js/mail_list_actions/ui/mark_many_as_read_trigger.js new file mode 100644 index 00000000..c16a2229 --- /dev/null +++ b/web-ui/public/js/mail_list_actions/ui/mark_many_as_read_trigger.js @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'mixins/with_enable_disable_on_event', + 'page/events' + ], + + function(defineComponent, templates, withEnableDisableOnEvent, events) { + 'use strict'; + + return defineComponent(markManyAsReadTrigger, withEnableDisableOnEvent(events.ui.mails.hasMailsChecked)); + + function markManyAsReadTrigger() { + this.defaultAttrs({}); + + this.getMailsToMarkAsRead = function(event) { + this.trigger(document, events.ui.mail.wantChecked, this.$node); + }; + + this.markManyEmailsAsRead = function (event, data) { + this.trigger(document, events.mail.read, data); + }; + + this.after('initialize', function () { + this.on('click', this.getMailsToMarkAsRead); + this.on(events.ui.mail.hereChecked, this.markManyEmailsAsRead); + }); + } + } +); diff --git a/web-ui/public/js/mail_list_actions/ui/pagination_trigger.js b/web-ui/public/js/mail_list_actions/ui/pagination_trigger.js new file mode 100644 index 00000000..3bc13d40 --- /dev/null +++ b/web-ui/public/js/mail_list_actions/ui/pagination_trigger.js @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'page/events' + ], + + function(defineComponent, templates, events) { + 'use strict'; + + return defineComponent(paginationTrigger); + + function paginationTrigger() { + this.defaultAttrs({ + previous: '#left-arrow', + next: '#right-arrow', + currentPage: '#current-page' + }); + + this.renderWithPageNumber = function(pageNumber) { + this.$node.html(templates.mailActions.paginationTrigger({ + currentPage: pageNumber + })); + this.on(this.attr.previous, 'click', this.previousPage); + this.on(this.attr.next, 'click', this.nextPage); + }; + + this.render = function() { + this.renderWithPageNumber(1); + }; + + this.updatePageDisplay = function(event, data) { + this.renderWithPageNumber(data.currentPage); + }; + + this.previousPage = function(event) { + this.trigger(document, events.ui.page.previous); + }; + + this.nextPage = function(event) { + this.trigger(document, events.ui.page.next); + }; + + this.after('initialize', function () { + this.render(); + this.on(document, events.ui.page.changed, this.updatePageDisplay); + }); + } + } +); diff --git a/web-ui/public/js/mail_list_actions/ui/recover_many_trigger.js b/web-ui/public/js/mail_list_actions/ui/recover_many_trigger.js new file mode 100644 index 00000000..e0a32094 --- /dev/null +++ b/web-ui/public/js/mail_list_actions/ui/recover_many_trigger.js @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'mixins/with_enable_disable_on_event', + 'page/events' + ], + + function(defineComponent, templates, withEnableDisableOnEvent, events) { + 'use strict'; + + return defineComponent(recoverManyTrigger, withEnableDisableOnEvent(events.ui.mails.hasMailsChecked)); + + function recoverManyTrigger() { + this.defaultAttrs({}); + + this.getMailsToRecover = function(event) { + this.trigger(document, events.ui.mail.wantChecked, this.$node); + }; + + this.recoverManyEmails = function (event, data) { + this.trigger(document, events.ui.mail.recoverMany, data); + }; + + this.after('initialize', function () { + this.on('click', this.getMailsToRecover); + this.on(events.ui.mail.hereChecked, this.recoverManyEmails); + }); + } + } +); diff --git a/web-ui/public/js/mail_list_actions/ui/refresh_trigger.js b/web-ui/public/js/mail_list_actions/ui/refresh_trigger.js new file mode 100644 index 00000000..a16270d2 --- /dev/null +++ b/web-ui/public/js/mail_list_actions/ui/refresh_trigger.js @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'page/events' + ], + + function(defineComponent, templates, events) { + 'use strict'; + + return defineComponent(refreshTrigger); + + function refreshTrigger() { + this.render = function() { + this.$node.html(templates.mailActions.refreshTrigger); + }; + + this.refresh = function(event) { + this.trigger(document, events.ui.mails.refresh); + }; + + this.after('initialize', function () { + this.render(); + this.on('click', this.refresh); + }); + } + } +); diff --git a/web-ui/public/js/mail_list_actions/ui/toggle_check_all_trigger.js b/web-ui/public/js/mail_list_actions/ui/toggle_check_all_trigger.js new file mode 100644 index 00000000..71c65346 --- /dev/null +++ b/web-ui/public/js/mail_list_actions/ui/toggle_check_all_trigger.js @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'page/events' + ], + + function(defineComponent, events) { + 'use strict'; + + return defineComponent(toggleCheckAllEmailsTrigger); + + function toggleCheckAllEmailsTrigger() { + this.defaultAttrs({ }); + + this.toggleCheckAll = function(event) { + if (this.$node.prop('checked')) { + this.trigger(document, events.ui.mails.checkAll); + } else { + this.trigger(document, events.ui.mails.uncheckAll); + } + }; + + this.setCheckbox = function (event, state) { + this.$node.prop('checked', state); + }; + + this.after('initialize', function () { + this.on('click', this.toggleCheckAll); + this.on(document, events.ui.mails.hasMailsChecked, this.setCheckbox); + }); + } + } +); diff --git a/web-ui/public/js/mail_view/data/feedback_sender.js b/web-ui/public/js/mail_view/data/feedback_sender.js new file mode 100644 index 00000000..2232dbe4 --- /dev/null +++ b/web-ui/public/js/mail_view/data/feedback_sender.js @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'helpers/monitored_ajax', + 'page/events' + ], + function (defineComponent, monitoredAjax, events) { + 'use strict'; + + return defineComponent(function () { + this.defaultAttrs({ + feedbackResource: '/feedback' + }); + + this.successSubmittingFeedback = function() { + this.trigger(document, events.feedback.submitted); + }; + + this.submitFeedback = function(event, data) { + monitoredAjax.call(_, this, this.attr.feedbackResource, { + type: 'POST', + dataType: 'json', + contentType: 'application/json; charset=utf-8', + data: JSON.stringify(data) + }).done(this.successSubmittingFeedback()); + }; + + this.after('initialize', function () { + this.on(document, events.feedback.submit, this.submitFeedback); + }); + + }); +}); diff --git a/web-ui/public/js/mail_view/data/mail_builder.js b/web-ui/public/js/mail_view/data/mail_builder.js new file mode 100644 index 00000000..7a478dd8 --- /dev/null +++ b/web-ui/public/js/mail_view/data/mail_builder.js @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['services/model/mail'], function (mailModel) { + 'use strict'; + + var mail; + + function recipients(mail, place, v) { + if (v !== '' && !_.isUndefined(v)) { + if (_.isArray(v)) { + mail[place] = v; + } else { + mail[place] = v.split(' '); + } + } else { + mail[place] = []; + } + } + + return { + newMail: function (ident) { + ident = _.isUndefined(ident) ? '' : ident; + + mail = { + header: { + to: [], + cc: [], + bcc: [], + from: undefined, + subject: '' + }, + tags: [], + body: '', + attachments: [], + ident: ident + }; + return this; + }, + + subject: function (subject) { + mail.header.subject = subject; + return this; + }, + + body: function (body) { + mail.body = body; + return this; + }, + + to: function (to) { + recipients(mail.header, 'to', to); + return this; + }, + + cc: function (cc) { + recipients(mail.header, 'cc', cc); + return this; + }, + + bcc: function (bcc) { + recipients(mail.header, 'bcc', bcc); + return this; + }, + + header: function (name, value) { + mail.header[name] = value; + return this; + }, + + tag: function (tag) { + if (_.isUndefined(tag)) { + tag = 'drafts'; + } + mail.tags.push(tag); + return this; + }, + + attachment: function (attachmentList) { + mail.attachments = attachmentList; + return this; + }, + + build: function () { + return mailModel.create(mail); + } + }; +}); diff --git a/web-ui/public/js/mail_view/data/mail_sender.js b/web-ui/public/js/mail_view/data/mail_sender.js new file mode 100644 index 00000000..8bb01f70 --- /dev/null +++ b/web-ui/public/js/mail_view/data/mail_sender.js @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'mail_view/data/mail_builder', + 'page/events', + 'helpers/monitored_ajax', + 'features' + ], + function (defineComponent, mailBuilder, events, monitoredAjax, features) { + 'use strict'; + + return defineComponent(mailSender); + + function mailSender() { + function successSendingMail(on){ + return function(result) { + on.trigger(document, events.mail.sent, result); + }; + } + + function failureSendingMail(on) { + return function(result) { + on.trigger(document, events.mail.send_failed, result); + }; + } + + function successSaveDraft(on){ + return function(result){ + on.trigger(document, events.mail.draftSaved, result); + }; + } + + this.defaultAttrs({ + mailsResource: '/mails' + }); + + this.sendMail = function(event, data) { + this.trigger(events.dispatchers.rightPane.openNoMessageSelected); + monitoredAjax.call(_, this, this.attr.mailsResource, { + type: 'POST', + dataType: 'json', + contentType: 'application/json; charset=utf-8', + data: JSON.stringify(data) + }).done(successSendingMail(this)).fail(failureSendingMail(this)); + + }; + + this.saveMail = function(mail) { + return monitoredAjax.call(_, this, this.attr.mailsResource, { + type: 'PUT', + dataType: 'json', + contentType: 'application/json; charset=utf-8', + data: JSON.stringify(mail), + skipErrorMessage: true + }); + }; + + this.saveDraft = function(event, data) { + this.saveMail(data) + .done(successSaveDraft(this)); + }; + + this.saveMailWithCallback = function(event, data) { + this.saveMail(data.mail) + .done(function(result) { return data.callback(result); }) + .fail(function(result) { return data.callback(result); }); + }; + + this.after('initialize', function () { + this.on(events.mail.send, this.sendMail); + if(features.isEnabled('saveDraft')) { + this.on(events.mail.saveDraft, this.saveDraft); + } + this.on(document, events.mail.save, this.saveMailWithCallback); + }); + } + }); diff --git a/web-ui/public/js/mail_view/ui/attachment_icon.js b/web-ui/public/js/mail_view/ui/attachment_icon.js new file mode 100644 index 00000000..e04fc02a --- /dev/null +++ b/web-ui/public/js/mail_view/ui/attachment_icon.js @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'page/events', + 'features' + ], + + function (defineComponent, events, features) { + 'use strict'; + + return defineComponent(function () { + this.render = function () { + this.$node.html(''); + }; + + this.triggerUploadAttachment = function () { + this.trigger(document, events.mail.startUploadAttachment); + }; + + this.uploadInProgress = function (ev, data) { + this.attr.busy = true; + this.$node.addClass('busy'); + }; + + this.uploadFinished = function (ev, data) { + this.attr.busy = false; + this.$node.removeClass('busy'); + }; + + this.after('initialize', function () { + if (features.isEnabled('attachment')) { + this.render(); + this.on(document, events.mail.uploadingAttachment, this.uploadInProgress); + this.on(document, events.mail.uploadedAttachment, this.uploadFinished); + this.on(document, events.mail.failedUploadAttachment, this.uploadFinished); + } + this.on(this.$node, 'click', function() { + if (!this.attr.busy) { + this.triggerUploadAttachment(); + } + }); + }); + }); + }); diff --git a/web-ui/public/js/mail_view/ui/attachment_list.js b/web-ui/public/js/mail_view/ui/attachment_list.js new file mode 100644 index 00000000..4ef64960 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/attachment_list.js @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2015 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'views/templates', + 'page/events', + 'helpers/view_helper', + 'helpers/monitored_ajax' + ], + + function (templates, events, viewHelper, monitoredAjax) { + 'use strict'; + + function attachmentList() { + this.defaultAttrs({ + inputFileUpload: '#fileupload', + attachmentListItem: '#attachment-list-item', + attachmentUploadItem: '#attachment-upload-item', + attachmentUploadItemProgress: '#attachment-upload-item-progress', + attachmentUploadItemAbort: '#attachment-upload-item-abort', + attachmentBaseUrl: '/attachment', + attachments: [], + closeIcon: '#upload-error-close', + uploadError: '#upload-error', + dismissButton: '#dismiss-button', + uploadFileButton: '#upload-file-button' + }); + + var ONE_MEGABYTE = 1024*1024; + var ATTACHMENT_SIZE_LIMIT = 5*ONE_MEGABYTE; + + this.showAttachment = function (ev, data) { + this.trigger(document, events.mail.appendAttachment, data); + this.renderAttachmentListView(data); + }; + + this.addAttachment = function (event, data) { + this.attr.attachments.push(data); + }; + + this.renderAttachmentListView = function (data) { + var currentHtml = this.select('attachmentListItem').html(); + var item = this.buildAttachmentListItem(data); + this.select('attachmentListItem').append(item); + }; + + this.buildAttachmentListItem = function (attachment) { + var attachmentData = {ident: attachment.ident, + encoding: attachment.encoding, + name: attachment.name, + size: attachment.size, + removable: true}; + + var element = $(templates.compose.attachmentItem(attachmentData)); + var self = this; + element.find('i.remove-icon').bind('click', function(event) { + var element = $(this); + var ident = element.closest('li').attr('data-ident'); + self.trigger(document, events.mail.removeAttachment, {ident: ident, element: element}); + event.preventDefault(); + }); + return element; + }; + + this.performPreUploadCheck = function(e, data) { + if (data.originalFiles[0].size > ATTACHMENT_SIZE_LIMIT) { + return false; + } + + return true; + }; + + this.removeUploadError = function() { + var uploadError = this.select('uploadError'); + if (uploadError) { + uploadError.remove(); + } + }; + + this.showUploadError = function () { + var self = this; + + var html = $(templates.compose.uploadAttachmentFailed()); + html.insertAfter(self.select('attachmentListItem')); + + self.on(self.select('closeIcon'), 'click', dismissUploadFailed); + self.on(self.select('dismissButton'), 'click', dismissUploadFailed); + self.on(self.select('uploadFileButton'), 'click', uploadAnotherFile); + + function dismissUploadFailed(event) { + event.preventDefault(); + self.select('uploadError').remove(); + } + + function uploadAnotherFile(event) { + event.preventDefault(); + self.trigger(document, events.mail.startUploadAttachment); + } + }; + + this.showUploadProgressBar = function(e, data) { + var element = $(templates.compose.attachmentUploadItem({ + name: data.originalFiles[0].name, + size: data.originalFiles[0].size + })); + this.select('attachmentUploadItem').append(element); + this.select('attachmentUploadItem').show(); + }; + + this.hideUploadProgressBar = function() { + this.select('attachmentUploadItem').hide(); + this.select('attachmentUploadItem').empty(); + }; + + this.attachUploadAbort = function(e, data) { + this.on(this.select('attachmentUploadItemAbort'), 'click', function(e) { + data.abort(); + e.preventDefault(); + }); + }; + + this.detachUploadAbort = function() { + this.off(this.select('attachmentUploadItemAbort'), 'click'); + }; + + this.addJqueryFileUploadConfig = function() { + var self = this; + + self.removeUploadError(); + + this.select('inputFileUpload').fileupload({ + add: function(e, data) { + if (self.performPreUploadCheck(e, data)) { + self.showUploadProgressBar(e, data); + self.attachUploadAbort(e, data); + data.submit(); + } else { + self.showUploadError(); + } + }, + url: self.attr.attachmentBaseUrl, + dataType: 'json', + done: function (e, response) { + self.detachUploadAbort(); + self.hideUploadProgressBar(); + self.trigger(document, events.mail.uploadedAttachment, response.result); + }, + fail: function(e, data){ + self.detachUploadAbort(); + self.hideUploadProgressBar(); + self.trigger(document, events.mail.failedUploadAttachment); + }, + progressall: function (e, data) { + var progressRate = parseInt(data.loaded / data.total * 100, 10); + self.select('attachmentUploadItemProgress').css('width', progressRate + '%'); + } + }).bind('fileuploadstart', function (e) { + self.trigger(document, events.mail.uploadingAttachment); + }); + }; + + this.startUpload = function () { + this.addJqueryFileUploadConfig(); + this.select('inputFileUpload').click(); + }; + + this.removeAttachmentFromList = function(ident) { + for (var i = 0; i < this.attr.attachments.length; i++) { + if (this.attr.attachments[i].ident === ident) { + this.attr.attachments.remove(i); + break; + } + } + }; + + this.destroyAttachmentElement = function(element) { + element.closest('li').remove(); + }; + + this.removeAttachments = function(event, data) { + this.removeAttachmentFromList(data.ident); + this.destroyAttachmentElement(data.element); + }; + + this.after('initialize', function () { + this.addJqueryFileUploadConfig(); + this.on(document, events.mail.uploadedAttachment, this.showAttachment); + this.on(document, events.mail.startUploadAttachment, this.startUpload); + this.on(document, events.mail.appendAttachment, this.addAttachment); + this.on(document, events.mail.removeAttachment, this.removeAttachments); + }); + } + + return attachmentList; + }); diff --git a/web-ui/public/js/mail_view/ui/compose_box.js b/web-ui/public/js/mail_view/ui/compose_box.js new file mode 100644 index 00000000..101dc939 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/compose_box.js @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'mixins/with_mail_edit_base', + 'page/events', + 'mail_view/data/mail_builder' + ], + + function (defineComponent, templates, withMailEditBase, events, mailBuilder) { + 'use strict'; + + return defineComponent(composeBox, withMailEditBase); + + function composeBox() { + + this.defaultAttrs({ + 'closeButton': '.close-mail-button' + }); + + this.showNoMessageSelected = function() { + this.trigger(events.dispatchers.rightPane.openNoMessageSelected); + }; + + this.buildMail = function(tag) { + return this.builtMail(tag).build(); + }; + + this.builtMail = function(tag) { + return mailBuilder.newMail(this.attr.ident) + .subject(this.select('subjectBox').val()) + .to(this.attr.recipientValues.to) + .cc(this.attr.recipientValues.cc) + .bcc(this.attr.recipientValues.bcc) + .body(this.select('bodyBox').val()) + .attachment(this.attr.attachments) + .tag(tag); + }; + + this.renderComposeBox = function() { + this.render(templates.compose.box, {}); + this.enableFloatlabel('input.floatlabel'); + this.enableFloatlabel('textarea.floatlabel'); + this.select('recipientsFields').show(); + this.on(this.select('closeButton'), 'click', this.showNoMessageSelected); + this.enableAutoSave(); + }; + + this.mailDeleted = function(event, data) { + if (_.contains(_.pluck(data.mails, 'ident'), this.attr.ident)) { + this.trigger(events.dispatchers.rightPane.openNoMessageSelected); + } + }; + + this.discardDraft = function () { + this.trigger(events.dispatchers.rightPane.openNoMessageSelected); + }; + + this.after('initialize', function () { + this.renderComposeBox(); + + this.select('toBox').focus(); + this.on(document, events.mail.deleted, this.mailDeleted); + this.on(document, events.mail.sent, this.showNoMessageSelected); + }); + } + } +); diff --git a/web-ui/public/js/mail_view/ui/draft_box.js b/web-ui/public/js/mail_view/ui/draft_box.js new file mode 100644 index 00000000..afe31914 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/draft_box.js @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'mixins/with_mail_edit_base', + 'page/events', + 'mail_view/data/mail_builder' + ], + + function (defineComponent, templates, withMailEditBase, events, mailBuilder) { + 'use strict'; + + return defineComponent(draftBox, withMailEditBase); + + function draftBox() { + this.defaultAttrs({ + closeMailButton: '.close-mail-button' + }); + + this.showNoMessageSelected = function() { + this.trigger(events.dispatchers.rightPane.openNoMessageSelected); + }; + + this.buildMail = function(tag) { + return this.builtMail(tag).build(); + }; + + this.builtMail = function(tag) { + return mailBuilder.newMail(this.attr.ident) + .subject(this.select('subjectBox').val()) + .to(this.attr.recipientValues.to) + .cc(this.attr.recipientValues.cc) + .bcc(this.attr.recipientValues.bcc) + .body(this.select('bodyBox').val()) + .attachment(this.attr.attachments) + .tag(tag); + }; + + this.renderDraftBox = function(ev, data) { + var mail = data.mail; + var body = mail.textPlainBody; + this.attr.ident = mail.ident; + this.render(templates.compose.box, { + recipients: { + to: mail.header.to, + cc: mail.header.cc, + bcc: mail.header.bcc + }, + subject: mail.header.subject, + body: body, + attachments: this.convertToRemovableAttachments(mail.attachments) + }); + + var self = this; + this.$node.find('i.remove-icon').bind('click', function(event) { + var element = $(this); + var ident = element.closest('li').attr('data-ident'); + self.trigger(document, events.mail.removeAttachment, {ident: ident, element: element}); + event.preventDefault(); + }); + + this.enableFloatlabel('input.floatlabel'); + this.enableFloatlabel('textarea.floatlabel'); + this.select('recipientsFields').show(); + this.select('bodyBox').focus(); + this.select('tipMsg').hide(); + this.enableAutoSave(); + this.bindCollapse(); + this.on(this.select('closeMailButton'), 'click', this.showNoMessageSelected); + }; + + this.convertToRemovableAttachments = function(attachments) { + return attachments.map(function(attachment) { + attachment.removable = true; + return attachment; + }); + }; + + this.mailDeleted = function(event, data) { + if (_.contains(_.pluck(data.mails, 'ident'), this.attr.ident)) { + this.trigger(events.dispatchers.rightPane.openNoMessageSelected); + } + }; + + this.after('initialize', function () { + this.on(this, events.mail.here, this.renderDraftBox); + this.on(document, events.mail.sent, this.showNoMessageSelected); + this.on(document, events.mail.deleted, this.mailDeleted); + this.trigger(document, events.mail.want, { mail: this.attr.mailIdent, caller: this }); + }); + } + } +); diff --git a/web-ui/public/js/mail_view/ui/draft_save_status.js b/web-ui/public/js/mail_view/ui/draft_save_status.js new file mode 100644 index 00000000..47751d91 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/draft_save_status.js @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'page/events', + 'views/i18n' + ], + + function (defineComponent, events, i18n) { + 'use strict'; + + return defineComponent(draftSaveStatus); + + function draftSaveStatus() { + this.setMessage = function(msg) { + var node = this.$node; + return function () { node.text(msg); }; + }; + + this.after('initialize', function () { + this.on(document, events.mail.saveDraft, this.setMessage(i18n.t('draft-saving'))); + this.on(document, events.mail.draftSaved, this.setMessage(i18n.t('draft-saved'))); + this.on(document, events.ui.mail.changedSinceLastSave, this.setMessage('')); + }); + } + } +); diff --git a/web-ui/public/js/mail_view/ui/feedback_box.js b/web-ui/public/js/mail_view/ui/feedback_box.js new file mode 100644 index 00000000..4e00ece8 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/feedback_box.js @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2015 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['flight/lib/component', 'views/templates', 'page/events', 'features', 'feedback/feedback_cache'], + function (defineComponent, templates, events, features, feedbackCache) { + 'use strict'; + + return defineComponent(function () { + this.defaultAttrs({ + 'closeButton': '.close-mail-button', + 'submitButton': '#send-button', + 'textBox': '#text-box', + }); + + this.render = function () { + this.$node.html(templates.compose.feedback()); + }; + + this.startCachingData = function () { + this.select('textBox').val(feedbackCache.getCache()); + this.select('textBox').on('change', this.cacheFeedbackData.bind(this)); + }; + + + this.cacheFeedbackData = function () { + feedbackCache.setCache(this.select('textBox').val()); + }; + + this.showNoMessageSelected = function () { + this.trigger(document, events.dispatchers.rightPane.openNoMessageSelected); + }; + + this.submitFeedback = function () { + var feedback = this.select('textBox').val(); + this.trigger(document, events.feedback.submit, {feedback: feedback}); + feedbackCache.resetCache(); + }; + + this.showSuccessMessage = function () { + this.trigger(document, events.ui.userAlerts.displayMessage, {message: 'Thanks for your feedback!'}); + }; + + this.after('initialize', function () { + if (features.isEnabled('feedback')) { + this.render(); + this.startCachingData(); + this.on(document, events.feedback.submitted, this.showNoMessageSelected); + this.on(document, events.feedback.submitted, this.showSuccessMessage); + this.on(this.select('closeButton'), 'click', this.showNoMessageSelected); + this.on(this.select('submitButton'), 'click', this.submitFeedback); + } + }); + + }); + }); diff --git a/web-ui/public/js/mail_view/ui/forward_box.js b/web-ui/public/js/mail_view/ui/forward_box.js new file mode 100644 index 00000000..a34bd55d --- /dev/null +++ b/web-ui/public/js/mail_view/ui/forward_box.js @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'helpers/view_helper', + 'mixins/with_hide_and_show', + 'mixins/with_compose_inline', + 'page/events', + 'views/i18n' + ], + + function (defineComponent, viewHelper, withHideAndShow, withComposeInline, events, i18n) { + 'use strict'; + + return defineComponent(forwardBox, withHideAndShow, withComposeInline); + + function forwardBox() { + var fwd = function(v) { return i18n.t('fwd') + ': ' + v; }; + + this.fetchTargetMail = function (ev) { + this.trigger(document, events.mail.want, { mail: this.attr.ident, caller: this }); + }; + + this.setupForwardBox = function() { + var mail = this.attr.mail; + this.attr.subject = fwd(mail.header.subject); + this.attr.attachments = mail.attachments; + + this.renderInlineCompose('forward-box', { + subject: this.attr.subject, + recipients: { to: [], cc: []}, + body: viewHelper.quoteMail(mail), + attachments: this.convertToRemovableAttachments(mail.attachments) + }); + + var self = this; + this.$node.find('i.remove-icon').bind('click', function(event) { + var element = $(this); + var ident = element.closest('li').attr('data-ident'); + self.trigger(document, events.mail.removeAttachment, {ident: ident}); + event.preventDefault(); + }); + + this.on(this.select('subjectDisplay'), 'click', this.showSubjectInput); + this.select('recipientsDisplay').hide(); + this.select('recipientsFields').show(); + }; + + this.convertToRemovableAttachments = function(attachments) { + return attachments.map(function(attachment) { + attachment.removable = true; + return attachment; + }); + }; + + this.showSubjectInput = function() { + this.select('subjectDisplay').hide(); + this.select('subjectInput').show(); + this.select('subjectInput').focus(); + }; + + this.buildMail = function(tag) { + var builder = this.builtMail(tag).subject(this.select('subjectInput').val()); + + var headersToFwd = ['bcc', 'cc', 'date', 'from', 'message_id', 'reply_to', 'sender', 'to']; + var header = this.attr.mail.header; + _.each(headersToFwd, function (h) { + if (!_.isUndefined(header[h])) { + builder.header('resent_' + h, header[h]); + } + }); + + return builder.build(); + }; + + this.after('initialize', function () { + this.setupForwardBox(); + }); + } + } +); diff --git a/web-ui/public/js/mail_view/ui/mail_actions.js b/web-ui/public/js/mail_view/ui/mail_actions.js new file mode 100644 index 00000000..65cd0aaa --- /dev/null +++ b/web-ui/public/js/mail_view/ui/mail_actions.js @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'views/templates', + 'page/events' + ], + + function (defineComponent, templates, events) { + 'use strict'; + + return defineComponent(mailActions); + + function mailActions() { + + this.defaultAttrs({ + replyButtonTop: '#reply-button-top', + viewMoreActions: '#view-more-actions', + replyAllButtonTop: '#reply-all-button-top', + deleteButtonTop: '#delete-button-top', + moreActions: '#more-actions' + }); + + + this.displayMailActions = function () { + + this.$node.html(templates.mails.mailActions()); + + this.select('moreActions').hide(); + + this.on(this.select('replyButtonTop'), 'click', function () { + this.trigger(document, events.ui.replyBox.showReply); + }.bind(this)); + + this.on(this.select('replyAllButtonTop'), 'click', function () { + this.trigger(document, events.ui.replyBox.showReplyAll); + this.select('moreActions').hide(); + }.bind(this)); + + this.on(this.select('deleteButtonTop'), 'click', function () { + this.trigger(document, events.ui.mail.delete, {mail: this.attr.mail}); + this.select('moreActions').hide(); + }.bind(this)); + + this.on(this.select('viewMoreActions'), 'click', function () { + this.select('moreActions').toggle(); + }.bind(this)); + + this.on(this.select('viewMoreActions'), 'blur', function (event) { + var replyButtonTopHover = this.select('replyAllButtonTop').is(':hover'); + var deleteButtonTopHover = this.select('deleteButtonTop').is(':hover'); + + if (replyButtonTopHover || deleteButtonTopHover) { + event.preventDefault(); + } else { + this.select('moreActions').hide(); + } + }.bind(this)); + + }; + + this.after('initialize', function () { + this.on(document, events.dispatchers.rightPane.clear, this.teardown); + this.displayMailActions(); + }); + } + } +); diff --git a/web-ui/public/js/mail_view/ui/mail_view.js b/web-ui/public/js/mail_view/ui/mail_view.js new file mode 100644 index 00000000..3408c8af --- /dev/null +++ b/web-ui/public/js/mail_view/ui/mail_view.js @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'views/templates', + 'mail_view/ui/mail_actions', + 'helpers/view_helper', + 'mixins/with_hide_and_show', + 'mixins/with_mail_tagging', + 'mixins/with_mail_sandbox', + 'page/events', + 'views/i18n' + ], + + function (defineComponent, templates, mailActions, viewHelpers, withHideAndShow, withMailTagging, withMailSandbox, events, i18n) { + 'use strict'; + + return defineComponent(mailView, mailActions, withHideAndShow, withMailTagging, withMailSandbox); + + function mailView() { + this.defaultAttrs({ + tags: '.mail-read-view__header-tags-tag', + newTagInput: '#new-tag-input', + newTagButton: '#new-tag-button', + addNew: '.mail-read-view__header-tags-new-button', + trashButton: '#trash-button', + archiveButton: '#archive-button', + closeMailButton: '.close-mail-button' + }); + + this.displayMail = function (event, data) { + this.attr.mail = data.mail; + + var signed, encrypted, attachments; + + data.mail.security_casing = data.mail.security_casing || {}; + signed = this.checkSigned(data.mail); + encrypted = this.checkEncrypted(data.mail); + attachments = data.mail.attachments.map(function (attachment) { + attachment.received = true; + return attachment; + }); + + if(data.mail.mailbox === 'sent') { + encrypted = undefined; + signed = undefined; + } + + this.$node.html(templates.mails.fullView({ + header: data.mail.header, + body: [], + statuses: viewHelpers.formatStatusClasses(data.mail.status), + ident: data.mail.ident, + tags: data.mail.tags, + encryptionStatus: encrypted, + signatureStatus: signed, + attachments: attachments + })); + + this.showMailOnSandbox(this.attr.mail); + + this.attachTagCompletion(this.attr.mail); + + this.select('tags').on('click', function (event) { + this.removeTag($(event.target).text()); + }.bind(this)); + + this.addTagLoseFocus(); + this.on(this.select('newTagButton'), 'click', this.showNewTagInput); + this.on(this.select('newTagInput'), 'keydown', this.handleKeyDown); + this.on(this.select('newTagInput'), 'blur', this.addTagLoseFocus); + this.on(this.select('trashButton'), 'click', this.moveToTrash); + this.on(this.select('closeMailButton'), 'click', this.openNoMessageSelectedPane); + + mailActions.attachTo('#mail-actions', data); + this.resetScroll(); + }; + + this.resetScroll = function(){ + $('#right-pane').scrollTop(0); + }; + + this.checkEncrypted = function(mail) { + if(_.isEmpty(mail.security_casing.locks)) { + return { + cssClass: 'security-status__label--not-encrypted', + label: 'not-encrypted' + }; + } + + var statusClass = ['security-status__label--encrypted']; + var statusLabel; + + var hasAnyEncryptionInfo = _.any(mail.security_casing.locks, function (lock) { + return lock.state === 'valid'; + }); + + if(hasAnyEncryptionInfo) { + statusLabel = 'encrypted'; + } else { + statusClass.push('--with-error'); + statusLabel = 'encryption-error'; + } + + return { + cssClass: statusClass.join(''), + label: statusLabel + }; + }; + + this.checkSigned = function(mail) { + var statusNotSigned = { + cssClass: 'security-status__label--not-signed', + label: 'not-signed' + }; + + if(_.isEmpty(mail.security_casing.imprints)) { + return statusNotSigned; + } + + var hasNoSignatureInformation = _.any(mail.security_casing.imprints, function (imprint) { + return imprint.state === 'no_signature_information'; + }); + + if(hasNoSignatureInformation) { + return statusNotSigned; + } + + var statusClass = ['security-status__label--signed']; + var statusLabel = ['signed']; + + if(_.any(mail.security_casing.imprints, function(imprint) { return imprint.state === 'from_revoked'; })) { + statusClass.push('--revoked'); + statusLabel.push('signature-revoked'); + } + + if(_.any(mail.security_casing.imprints, function(imprint) { return imprint.state === 'from_expired'; })) { + statusClass.push('--expired'); + statusLabel.push('signature-expired'); + } + + if(this.isNotTrusted(mail)) { + statusClass.push('--not-trusted'); + statusLabel.push('signature-not-trusted'); + } + + return { + cssClass: statusClass.join(''), + label: statusLabel.join(' ') + }; + }; + + this.isNotTrusted = function(mail){ + return _.any(mail.security_casing.imprints, function(imprint) { + if(_.isNull(imprint.seal)){ + return true; + } + var currentTrust = _.isUndefined(imprint.seal.trust) ? imprint.seal.validity : imprint.seal.trust; + return currentTrust === 'no_trust'; + }); + }; + + this.openNoMessageSelectedPane = function(ev, data) { + this.trigger(document, events.dispatchers.rightPane.openNoMessageSelected); + }; + + this.handleKeyDown = function(event) { + var ENTER_KEY = 13; + var ESC_KEY = 27; + + if (event.which === ENTER_KEY){ + event.preventDefault(); + if (this.select('newTagInput').val().trim() !== '') { + this.createNewTag(); + } + } else if (event.which === ESC_KEY) { + event.preventDefault(); + this.addTagLoseFocus(); + } + }; + + this.addTagLoseFocus = function () { + this.select('newTagInput').hide(); + this.select('newTagInput').typeahead('val', ''); + this.select('addNew').show(); + }; + + this.showNewTagInput = function () { + this.select('newTagInput').show(); + this.select('newTagInput').focus(); + this.select('addNew').hide(); + }; + + this.removeTag = function (tag) { + tag = tag.toString(); + var filteredTags = _.without(this.attr.mail.tags, tag); + this.updateTags(this.attr.mail, filteredTags); + this.trigger(document, events.dispatchers.tags.refreshTagList); + }; + + this.moveToTrash = function(){ + this.trigger(document, events.ui.mail.delete, { mail: this.attr.mail }); + }; + + this.tagsUpdated = function(ev, data) { + data = data || {}; + this.attr.mail.tags = data.tags; + this.displayMail({}, { mail: this.attr.mail }); + }; + + this.mailDeleted = function(ev, data) { + if (_.contains(_.pluck(data.mails, 'ident'), this.attr.mail.ident)) { + this.openNoMessageSelectedPane(); + } + }; + + this.fetchMailToShow = function () { + this.trigger(events.mail.want, {mail: this.attr.ident, caller: this}); + }; + + this.highlightMailContent = function (event, data) { + // we can't directly manipulate the iFrame to highlight the content + // so we need to take an indirection where we directly manipulate + // the mail content to accomodate the highlighting + this.trigger(document, events.mail.highlightMailContent, data); + }; + + this.after('initialize', function () { + this.on(this, events.mail.notFound, this.openNoMessageSelectedPane); + this.on(this, events.mail.here, this.highlightMailContent); + this.on(document, events.mail.display, this.displayMail); + this.on(document, events.dispatchers.rightPane.clear, this.teardown); + this.on(document, events.mail.tags.updated, this.tagsUpdated); + this.on(document, events.mail.deleted, this.mailDeleted); + this.fetchMailToShow(); + }); + } + } +); diff --git a/web-ui/public/js/mail_view/ui/no_mails_available_pane.js b/web-ui/public/js/mail_view/ui/no_mails_available_pane.js new file mode 100644 index 00000000..c62c6b30 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/no_mails_available_pane.js @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'mixins/with_hide_and_show', + 'page/events' + ], + + function(defineComponent, templates, withHideAndShow, events) { + 'use strict'; + + //return defineComponent(noMailsAvailablePane, withHideAndShow); + return defineComponent(noMailsAvailablePane); + + function noMailsAvailablePane() { + this.defaultAttrs({ + tag: null, + forSearch: '' + }); + + var mailsQueryMatch = /-?in:"?[\w]+"?|tag:"[\w]+"/g; + + this.render = function() { + this.attr.tag = 'tags.' + this.attr.tag; + this.attr.forSearch = this.attr.forSearch.replace(mailsQueryMatch, '').trim(); + this.$node.html(templates.noMailsAvailable(this.attr)); + }; + + this.after('initialize', function () { + this.render(); + }); + } + } +); diff --git a/web-ui/public/js/mail_view/ui/no_message_selected_pane.js b/web-ui/public/js/mail_view/ui/no_message_selected_pane.js new file mode 100644 index 00000000..a5fc2393 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/no_message_selected_pane.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'mixins/with_hide_and_show', + 'page/events' + ], + + function(defineComponent, templates, withHideAndShow, events) { + 'use strict'; + + return defineComponent(noMessageSelectedPane, withHideAndShow); + + function noMessageSelectedPane() { + this.render = function() { + this.$node.html(templates.noMessageSelected()); + }; + + this.after('initialize', function () { + this.render(); + this.on(document, events.dispatchers.rightPane.clear, this.teardown); + }); + } + } +); diff --git a/web-ui/public/js/mail_view/ui/recipients/recipient.js b/web-ui/public/js/mail_view/ui/recipients/recipient.js new file mode 100644 index 00000000..c13a52b1 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/recipients/recipient.js @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'views/templates', + 'page/events' + ], + + function (defineComponent, templates, events) { + 'use strict'; + + return defineComponent(recipient); + + function recipient() { + this.renderAndPrepend = function (nodeToPrependTo, recipient) { + var html = $(templates.compose.fixedRecipient(recipient)); + html.insertBefore(nodeToPrependTo.children().last()); + var component = new this.constructor(); + component.initialize(html, recipient); + component.attr.recipient = recipient; + return component; + }; + + this.recipientDelActions = function () { + this.on(this.$node.find('.recipient-del'), 'click', function (event) { + this.doSelect(); + this.trigger(events.ui.recipients.deleteRecipient, this); + event.preventDefault(); + }); + + this.on(this.$node.find('.recipient-del'), 'mouseover', function () { + this.$node.find('.recipient-value').addClass('deleting'); + this.$node.find('.recipient-del').addClass('deleteTooltip'); + }); + + this.on(this.$node.find('.recipient-del'), 'mouseout', function () { + this.$node.find('.recipient-value').removeClass('deleting'); + this.$node.find('.recipient-del').removeClass('deleteTooltip'); + }); + }; + + this.destroy = function () { + this.$node.remove(); + this.teardown(); + }; + + this.doSelect = function () { + this.$node.find('.recipient-value').addClass('selected'); + }; + + this.doUnselect = function () { + this.$node.find('.recipient-value').removeClass('selected'); + }; + + this.isSelected = function () { + return this.$node.find('.recipient-value').hasClass('selected'); + }; + + this.sinalizeInvalid = function () { + this.$node.find('.recipient-value>span').addClass('invalid-format'); + }; + + this.discoverEncryption = function () { + this.$node.addClass('discover-encryption'); + var p = $.getJSON('/keys?search=' + this.attr.address).promise(); + p.done(function () { + this.$node.find('.recipient-value').addClass('encrypted'); + this.$node.removeClass('discover-encryption'); + }.bind(this)); + p.fail(function () { + this.$node.find('.recipient-value').addClass('not-encrypted'); + this.$node.removeClass('discover-encryption'); + }.bind(this)); + }; + + this.getMailAddress = function() { + return this.$node.find('input[type=hidden]').val(); + }; + + this.triggerEditRecipient = function(event, element) { + this.trigger(this.$node.closest('.recipients-area'), events.ui.recipients.clickToEdit, this); + }; + + this.after('initialize', function () { + this.recipientDelActions(); + this.on('click', this.triggerEditRecipient); + + if (this.attr.invalidAddress){ + this.sinalizeInvalid(); + } else { + this.discoverEncryption(); + } + }); + } + } +); diff --git a/web-ui/public/js/mail_view/ui/recipients/recipients.js b/web-ui/public/js/mail_view/ui/recipients/recipients.js new file mode 100644 index 00000000..2caa8d14 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/recipients/recipients.js @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'views/templates', + 'page/events', + 'helpers/iterator', + 'mail_view/ui/recipients/recipients_input', + 'mail_view/ui/recipients/recipient', + 'mail_view/ui/recipients/recipients_iterator' + ], + function (defineComponent, templates, events, Iterator, RecipientsInput, Recipient, RecipientsIterator) { + 'use strict'; + + return defineComponent(recipients); + + function recipients() { + this.defaultAttrs({ + navigationHandler: '.recipients-navigation-handler', + recipientsList: '.recipients-list' + }); + + function getAddresses(recipients) { + return _.flatten(_.map(recipients, function (e) { return e.attr.address;})); + } + + function moveLeft() { this.attr.iterator.moveLeft(); } + function moveRight() { this.attr.iterator.moveRight(); } + function deleteCurrentRecipient() { + this.attr.iterator.deleteCurrent(); + this.addressesUpdated(); + } + + function editCurrentRecipient(event, recipient) { + var mailAddr = this.attr.iterator.current().getMailAddress(); + this.attr.iterator.deleteCurrent(); + this.attr.input.$node.val(mailAddr).focus(); + this.unselectAllRecipients(); + this.addressesUpdated(); + } + + this.clickToEditRecipient = function(event, recipient) { + this.attr.iterator = null; + var mailAddr = recipient.getMailAddress(); + + var position = this.getRecipientPosition(recipient); + this.attr.recipients.splice(position, 1); + recipient.destroy(); + + this.addressesUpdated(); + this.unselectAllRecipients(); + this.attr.input.$node.val(mailAddr).focus(); + }; + + this.getRecipientPosition = function(recipient) { + return recipient.$node.closest('.recipients-area').find('.fixed-recipient').index(recipient.$node); + }; + + this.unselectAllRecipients = function() { + this.$node.find('.recipient-value.selected').removeClass('selected'); + }; + + var SPECIAL_KEYS_ACTIONS = { + 8: deleteCurrentRecipient, + 46: deleteCurrentRecipient, + 32: editCurrentRecipient, + 13: editCurrentRecipient, + 37: moveLeft, + 39: moveRight + }; + + this.addRecipient = function(recipient) { + var newRecipient = Recipient.prototype.renderAndPrepend(this.$node.find(this.attr.recipientsList), recipient); + this.attr.recipients.push(newRecipient); + }; + + this.recipientEntered = function (event, recipient) { + this.addRecipient(recipient); + this.addressesUpdated(); + }; + + this.invalidRecipientEntered = function(event, recipient) { + recipient.invalidAddress = true; + this.addRecipient(recipient); + }; + + this.deleteRecipient = function (event, recipient) { + this.attr.iterator = null; + var position = this.getRecipientPosition(recipient); + + this.attr.recipients.splice(position, 1); + recipient.destroy(); + + this.addressesUpdated(); + }; + + this.deleteLastRecipient = function () { + this.attr.recipients.pop().destroy(); + this.addressesUpdated(); + }; + + this.enterNavigationMode = function () { + this.attr.iterator = new RecipientsIterator({ + elements: this.attr.recipients, + exitInput: this.attr.input.$node + }); + + this.attr.iterator.current().doSelect(); + this.attr.input.$node.blur(); + this.select('navigationHandler').focus(); + }; + + this.leaveNavigationMode = function () { + if(this.attr.iterator) { this.attr.iterator.current().unselect(); } + this.attr.iterator = null; + }; + + this.selectLastRecipient = function () { + if (this.attr.recipients.length === 0) { return; } + this.enterNavigationMode(); + }; + + this.attachInput = function () { + this.attr.input = RecipientsInput.prototype.attachAndReturn(this.$node.find('input[type=text]'), this.attr.name); + }; + + this.processSpecialKey = function (event) { + if(SPECIAL_KEYS_ACTIONS.hasOwnProperty(event.which)) { SPECIAL_KEYS_ACTIONS[event.which].apply(this); } + }; + + this.initializeAddresses = function () { + _.each(_.flatten(this.attr.addresses), function (address) { + this.addRecipient({ address: address, name: this.attr.name }); + }.bind(this)); + }; + + this.addressesUpdated = function() { + this.trigger(document, events.ui.recipients.updated, {recipientsName: this.attr.name, newRecipients: getAddresses(this.attr.recipients)}); + }; + + this.doCompleteRecipients = function () { + var address = this.attr.input.$node.val(); + if (!_.isEmpty(address)) { + var recipient = Recipient.prototype.renderAndPrepend(this.$node, { name: this.attr.name, address: address }); + this.attr.recipients.push(recipient); + this.attr.input.$node.val(''); + } + + this.trigger(document, events.ui.recipients.updated, { + recipientsName: this.attr.name, + newRecipients: getAddresses(this.attr.recipients), + skipSaveDraft: true + }); + + }; + + this.after('initialize', function () { + this.attr.recipients = []; + this.attachInput(); + this.initializeAddresses(); + + this.on(events.ui.recipients.deleteRecipient, this.deleteRecipient); + this.on(events.ui.recipients.deleteLast, this.deleteLastRecipient); + this.on(events.ui.recipients.selectLast, this.selectLastRecipient); + this.on(events.ui.recipients.entered, this.recipientEntered); + this.on(events.ui.recipients.enteredInvalid, this.invalidRecipientEntered); + this.on(events.ui.recipients.clickToEdit, this.clickToEditRecipient); + + this.on(document, events.ui.recipients.doCompleteInput, this.doCompleteRecipients); + + this.on(this.attr.input.$node, 'focus', this.leaveNavigationMode); + this.on(this.select('navigationHandler'), 'keydown', this.processSpecialKey); + + this.on(document, events.dispatchers.rightPane.clear, this.teardown); + }); + } + }); diff --git a/web-ui/public/js/mail_view/ui/recipients/recipients_input.js b/web-ui/public/js/mail_view/ui/recipients/recipients_input.js new file mode 100644 index 00000000..8a9c4eaf --- /dev/null +++ b/web-ui/public/js/mail_view/ui/recipients/recipients_input.js @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define([ + 'flight/lib/component', + 'page/events', + 'features' + ], + function (defineComponent, events, features) { + 'use strict'; + + function recipientsInput() { + var EXIT_KEY_CODE_MAP = { + 8: 'backspace', + 37: 'left' + }, + ENTER_ADDRESS_KEY_CODE_MAP = { + 9: 'tab', + 186: 'semicolon', + 188: 'comma', + 13: 'enter', + 27: 'esc' + }, + EVENT_FOR = { + 8: events.ui.recipients.deleteLast, + 37: events.ui.recipients.selectLast + }, + self; + + var simpleAddressMatch = /[^<\w,;]?([^\s<;,]+@[\w-]+\.[^\s>;,]+)/; + var canonicalAddressMatch = /([^,;\s][^,;@]+<[^\s;,]+@[\w-]+\.[^\s;,]+>)/; + var emailAddressMatch = new RegExp([simpleAddressMatch.source, '|', canonicalAddressMatch.source].join(''), 'g'); + + var extractContactNames = function (response) { + return _.map(response, function(a) { return { value: a }; }); + }; + + function createEmailCompleter() { + var emailCompleter = new Bloodhound({ + datumTokenizer: function (d) { + return [d.value]; + }, + queryTokenizer: function (q) { + return [q.trim()]; + }, + remote: { + url: '/contacts?q=%QUERY', + filter: extractContactNames + } + }); + emailCompleter.initialize(); + return emailCompleter; + } + + function reset(node) { + node.typeahead('val', ''); + } + + function caretIsInTheBeginningOfInput(input) { + return input.selectionStart === 0; + } + + function isExitKey(keyPressed) { + return EXIT_KEY_CODE_MAP.hasOwnProperty(keyPressed); + } + + function isEnterAddressKey(keyPressed) { + return ENTER_ADDRESS_KEY_CODE_MAP.hasOwnProperty(keyPressed); + } + + this.processSpecialKey = function (event) { + var keyPressed = event.which; + + if (isExitKey(keyPressed) && caretIsInTheBeginningOfInput(this.$node[0])) { + this.trigger(EVENT_FOR[keyPressed]); + return; + } + + if (!event.shiftKey && isEnterAddressKey(keyPressed)) { + this.tokenizeRecipient(event); + + if ((keyPressed !== 9 /* tab */)) { + event.preventDefault(); + } + } + + }; + + this.tokenizeRecipient = function (event) { + if (_.isEmpty(this.$node.val().trim())) { + return; + } + + this.recipientSelected(null, {value: this.$node.val() }); + event.preventDefault(); + }; + + this.recipientSelected = function (event, data) { + var value = (data && data.value) || this.$node.val(); + + var validAddresses = this.extractValidAddresses(value); + var invalidAddresses = this.extractInvalidAddresses(value); + + this.triggerEventForEach(validAddresses, events.ui.recipients.entered); + this.triggerEventForEach(invalidAddresses, events.ui.recipients.enteredInvalid); + + reset(this.$node); + }; + + this.triggerEventForEach = function (addresses, event) { + var that = this; + _.each(addresses, function(address) { + if (!_.isEmpty(address.trim())) { + that.trigger(that.$node, event, { name: that.attr.name, address: address.trim() }); + } + }); + }; + + this.extractValidAddresses = function(rawAddresses) { + return rawAddresses.match(emailAddressMatch); + }; + + this.extractInvalidAddresses = function(rawAddresses) { + return rawAddresses.replace(emailAddressMatch, '').split(/[,;]/); + }; + + this.init = function () { + this.$node.typeahead({ + hint: true, + highlight: true, + minLength: 1 + }, { + source: createEmailCompleter().ttAdapter(), + templates: { + suggestion: function (o) { return _.escape(o.value); } + } + }); + }; + + this.attachAndReturn = function (node, name) { + var input = new this.constructor(); + input.initialize(node, { name: name}); + return input; + }; + + this.warnSendButtonOfInputState = function () { + var toTrigger = _.isEmpty(this.$node.val()) ? events.ui.recipients.inputFieldIsEmpty : events.ui.recipients.inputFieldHasCharacters; + this.trigger(document, toTrigger, { name: this.attr.name }); + }; + + this.after('initialize', function () { + self = this; + this.init(); + this.on('typeahead:selected typeahead:autocompleted', this.recipientSelected); + this.on(this.$node, 'focusout', this.tokenizeRecipient); + this.on(this.$node, 'keydown', this.processSpecialKey); + this.on(this.$node, 'keyup', this.warnSendButtonOfInputState); + + this.on(document, events.dispatchers.rightPane.clear, this.teardown); + }); + } + + return defineComponent(recipientsInput); + + } +); diff --git a/web-ui/public/js/mail_view/ui/recipients/recipients_iterator.js b/web-ui/public/js/mail_view/ui/recipients/recipients_iterator.js new file mode 100644 index 00000000..624ac4f5 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/recipients/recipients_iterator.js @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['helpers/iterator'], function (Iterator) { + 'use strict'; + + return RecipientsIterator; + + function RecipientsIterator(options) { + + this.iterator = new Iterator(options.elements, options.elements.length - 1); + this.input = options.exitInput; + + this.current = function () { + return this.iterator.current(); + }; + + this.moveLeft = function () { + if (this.iterator.hasPrevious()) { + this.iterator.current().doUnselect(); + this.iterator.previous().doSelect(); + } + }; + + this.moveRight = function () { + this.iterator.current().doUnselect(); + if (this.iterator.hasNext()) { + this.iterator.next().doSelect(); + } else { + this.input.focus(); + } + }; + + this.deleteCurrent = function () { + this.iterator.removeCurrent().destroy(); + + if (this.iterator.hasElements()) { + this.iterator.current().doSelect(); + } else { + this.input.focus(); + } + }; + } + +}); diff --git a/web-ui/public/js/mail_view/ui/reply_box.js b/web-ui/public/js/mail_view/ui/reply_box.js new file mode 100644 index 00000000..a174d185 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/reply_box.js @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'helpers/view_helper', + 'mixins/with_hide_and_show', + 'mixins/with_compose_inline', + 'page/events', + 'views/i18n' + ], + + function (defineComponent, viewHelper, withHideAndShow, withComposeInline, events, i18n) { + 'use strict'; + + return defineComponent(replyBox, withHideAndShow, withComposeInline); + + function replyBox() { + this.defaultAttrs({ + replyType: 'reply', + draftReply: false, + mail: null, + mailBeingRepliedIdent: undefined + }); + + this.getRecipients = function() { + if (this.attr.replyType === 'replyall') { + return this.attr.mail.replyToAllAddress(); + } else { + return this.attr.mail.replyToAddress(); + } + }; + + var re = function(v) { return i18n.t('re') + ': ' + v; }; + + this.setupReplyBox = function() { + var recipients, body; + + if (this.attr.draftReply){ + this.attr.ident = this.attr.mail.ident; + this.attr.mailBeingRepliedIdent = this.attr.mail.draft_reply_for; + + recipients = this.attr.mail.recipients(); + body = this.attr.mail.body; + this.attr.subject = this.attr.mail.header.subject; + } else { + this.attr.mailBeingRepliedIdent = this.attr.mail.ident; + recipients = this.getRecipients(); + body = viewHelper.quoteMail(this.attr.mail); + this.attr.subject = re(this.attr.mail.header.subject); + } + + this.attr.recipientValues.to = recipients.to; + this.attr.recipientValues.cc = recipients.cc; + + this.renderInlineCompose('reply-box', { + recipients: recipients, + subject: this.attr.subject, + body: body + }); + + this.on(this.select('recipientsDisplay'), 'click keydown', this.showRecipientFields); + this.on(this.select('subjectDisplay'), 'click', this.showSubjectInput); + }; + + this.showRecipientFields = function(ev, data) { + if(!ev.keyCode || ev.keyCode === 13){ + this.select('recipientsDisplay').hide(); + this.select('recipientsFields').show(); + $('#recipients-to-area .tt-input').focus(); + } + }; + + this.showSubjectInput = function() { + this.select('subjectDisplay').hide(); + this.select('subjectInput').show(); + this.select('subjectInput').focus(); + }; + + this.buildMail = function(tag) { + var builder = this.builtMail(tag).subject(this.select('subjectInput').val()); + if(!_.isUndefined(this.attr.mail.header.message_id)) { + builder.header('in_reply_to', this.attr.mail.header.message_id); + } + + if(!_.isUndefined(this.attr.mail.header.list_id)) { + builder.header('list_id', this.attr.mail.header.list_id); + } + + var mail = builder.build(); + mail.setDraftReplyFor(this.attr.mailBeingRepliedIdent); + + return mail; + }; + + this.after('initialize', function () { + this.setupReplyBox(); + }); + } + } +); diff --git a/web-ui/public/js/mail_view/ui/reply_section.js b/web-ui/public/js/mail_view/ui/reply_section.js new file mode 100644 index 00000000..cbe64205 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/reply_section.js @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'mail_view/ui/reply_box', + 'mail_view/ui/forward_box', + 'mixins/with_hide_and_show', + 'mixins/with_feature_toggle', + 'page/events' + ], + + function (defineComponent, templates, ReplyBox, ForwardBox, withHideAndShow, withFeatureToggle, events) { + 'use strict'; + + return defineComponent(replySection, withHideAndShow, withFeatureToggle('replySection')); + + function replySection() { + this.defaultAttrs({ + replyButton: '#reply-button', + replyAllButton: '#reply-all-button', + forwardButton: '#forward-button', + replyBox: '#reply-box', + replyType: 'reply', + replyContainer: '.reply-container' + }); + + this.showReply = function() { + this.attr.replyType = 'reply'; + this.fetchEmailToReplyTo(); + }; + + this.showReplyAll = function() { + this.attr.replyType = 'replyall'; + this.fetchEmailToReplyTo(); + }; + + this.showForward = function() { + this.attr.replyType = 'forward'; + this.fetchEmailToReplyTo(); + }; + + this.render = function () { + this.$node.html(templates.compose.replySection); + + this.on(this.select('replyButton'), 'click', this.showReply); + this.on(this.select('replyAllButton'), 'click', this.showReplyAll); + this.on(this.select('forwardButton'), 'click', this.showForward); + }; + + this.checkForDraftReply = function() { + this.render(); + this.hideContainer(); + + this.trigger(document, events.mail.draftReply.want, {ident: this.attr.ident}); + }; + + this.fetchEmailToReplyTo = function (ev) { + this.trigger(document, events.mail.want, { mail: this.attr.ident, caller: this }); + }; + + this.showDraftReply = function(ev, data) { + this.showContainer(); + this.hideButtons(); + ReplyBox.attachTo(this.select('replyBox'), { mail: data.mail, draftReply: true }); + }; + + this.showReplyComposeBox = function (ev, data) { + this.showContainer(); + this.hideButtons(); + if(this.attr.replyType === 'forward') { + ForwardBox.attachTo(this.select('replyBox'), { mail: data.mail }); + } else { + ReplyBox.attachTo(this.select('replyBox'), { mail: data.mail, replyType: this.attr.replyType }); + } + }; + + this.hideContainer = function() { + this.select('replyContainer').hide(); + }; + + this.showContainer = function() { + this.select('replyContainer').show(); + }; + + this.hideButtons = function() { + this.select('replyButton').hide(); + this.select('replyAllButton').hide(); + this.select('forwardButton').hide(); + }; + + this.showButtons = function () { + this.showContainer(); + this.select('replyBox').empty(); + this.select('replyButton').show(); + this.select('replyAllButton').show(); + this.select('forwardButton').show(); + }; + + this.after('initialize', function () { + this.on(document, events.ui.replyBox.showReply, this.showReply); + this.on(document, events.ui.replyBox.showReplyAll, this.showReplyAll); + this.on(document, events.ui.composeBox.trashReply, this.showButtons); + this.on(this, events.mail.here, this.showReplyComposeBox); + this.on(document, events.dispatchers.rightPane.clear, this.teardown); + + this.on(document, events.ui.replyBox.showReplyContainer, this.showContainer); + this.on(document, events.mail.draftReply.here, this.showDraftReply); + + this.checkForDraftReply(); + }); + } + } +); diff --git a/web-ui/public/js/mail_view/ui/send_button.js b/web-ui/public/js/mail_view/ui/send_button.js new file mode 100644 index 00000000..66fe1233 --- /dev/null +++ b/web-ui/public/js/mail_view/ui/send_button.js @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define([ + 'flight/lib/component', + 'flight/lib/utils', + 'page/events', + 'helpers/view_helper' + ], + function (defineComponent, utils, events, viewHelper) { + 'use strict'; + + return defineComponent(sendButton); + + function sendButton() { + var RECIPIENTS_BOXES_COUNT = 3; + + this.enableButton = function () { + this.$node.prop('disabled', false); + }; + + this.disableButton = function () { + this.$node.prop('disabled', true); + }; + + this.atLeastOneInputFieldHasRecipients = function () { + return _.any(_.values(this.attr.recipients), function (e) { return !_.isEmpty(e); }); + }; + + this.atLeastOneInputFieldHasCharacters = function () { + return _.any(_.values(this.attr.inputFieldHasCharacters), function (e) { return e === true; }); + }; + + this.updateButton = function () { + if (this.attr.sendingInProgress === false) { + if (this.attr.uploading === false && (this.atLeastOneInputFieldHasCharacters() || this.atLeastOneInputFieldHasRecipients())) { + this.enableButton(); + } else { + this.disableButton(); + } + } + }; + + this.inputFieldIsEmpty = function (ev, data) { + this.attr.inputFieldHasCharacters[data.name] = false; + this.updateButton(); + }; + + this.inputFieldHasCharacters = function (ev, data) { + this.attr.inputFieldHasCharacters[data.name] = true; + this.updateButton(); + }; + + this.uploadInProgress = function (ev, data) { + this.attr.uploading = true; + this.updateButton(); + }; + + this.uploadFinished = function (ev, data) { + this.attr.uploading = false; + this.updateButton(); + }; + + this.updateRecipientsForField = function (ev, data) { + this.attr.recipients[data.recipientsName] = data.newRecipients; + this.attr.inputFieldHasCharacters[data.recipientsName] = false; + + this.updateButton(); + }; + + this.updateRecipientsAndSendMail = function () { + + this.on(document, events.ui.mail.recipientsUpdated, utils.countThen(RECIPIENTS_BOXES_COUNT, function () { + this.trigger(document, events.ui.mail.send); + this.off(document, events.ui.mail.recipientsUpdated); + }.bind(this))); + + this.disableButton(); + this.$node.text(viewHelper.i18n.t('sending-mail')); + + this.attr.sendingInProgress = true; + + this.trigger(document, events.ui.recipients.doCompleteInput); + }; + + this.resetButton = function () { + this.attr.sendingInProgress = false; + this.attr.uploading = false; + this.$node.html(viewHelper.i18n.t('send')); + this.enableButton(); + }; + + this.after('initialize', function () { + this.attr.recipients = {}; + this.attr.inputFieldHasCharacters = {}; + this.resetButton(); + + this.on(document, events.ui.recipients.inputFieldHasCharacters, this.inputFieldHasCharacters); + this.on(document, events.ui.recipients.inputFieldIsEmpty, this.inputFieldIsEmpty); + this.on(document, events.ui.recipients.updated, this.updateRecipientsForField); + + this.on(this.$node, 'click', this.updateRecipientsAndSendMail); + + this.on(document, events.mail.uploadingAttachment, this.uploadInProgress); + this.on(document, events.mail.uploadedAttachment, this.uploadFinished); + this.on(document, events.mail.failedUploadAttachment, this.uploadFinished); + + this.on(document, events.dispatchers.rightPane.clear, this.teardown); + this.on(document, events.ui.sendbutton.enable, this.resetButton); + this.on(document, events.mail.send_failed, this.resetButton); + + this.disableButton(); + }); + } + + } +); diff --git a/web-ui/public/js/main.js b/web-ui/public/js/main.js new file mode 100644 index 00000000..b8836a6b --- /dev/null +++ b/web-ui/public/js/main.js @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +requirejs.config({ + baseUrl: '../assets/', + paths: { + 'mail_list': 'js/mail_list', + 'page': 'js/page', + 'feedback': 'js/feedback', + 'flight': 'bower_components/flight', + 'DOMPurify': 'bower_components/DOMPurify/dist/purify.min', + 'he': 'bower_components/he/he', + 'hbs': 'js/generated/hbs', + 'helpers': 'js/helpers', + 'lib': 'js/lib', + 'views': 'js/views', + 'tags': 'js/tags', + 'mail_list_actions': 'js/mail_list_actions', + 'user_alerts': 'js/user_alerts', + 'mail_view': 'js/mail_view', + 'dispatchers': 'js/dispatchers', + 'services': 'js/services', + 'mixins': 'js/mixins', + 'search': 'js/search', + 'foundation': 'js/foundation', + 'features': 'js/features/features', + 'i18next': 'bower_components/i18next/i18next', + 'i18nextXHRBackend': 'bower_components/i18next-xhr-backend/i18nextXHRBackend', + 'i18nextBrowserLanguageDetector': 'bower_components/i18next-browser-languagedetector/i18nextBrowserLanguageDetector', + 'quoted-printable': 'bower_components/quoted-printable', + 'utf8': 'bower_components/utf8', + 'user_settings': 'js/user_settings' + } +}); + +require([ + 'flight/lib/compose', + 'flight/lib/debug' +], function(compose, debug){ + 'use strict'; + debug.enable(true); + debug.events.logAll(); +}); + +require( + [ + 'flight/lib/compose', + 'flight/lib/registry', + 'flight/lib/advice', + 'flight/lib/logger', + 'flight/lib/debug', + 'page/events', + 'page/default', + 'js/monkey_patching/all' + ], + + function(compose, registry, advice, withLogging, debug, events, initializeDefault, _monkeyPatched) { + 'use strict'; + + window.Pixelated = window.Pixelated || {}; + window.Pixelated.events = events; + + compose.mixin(registry, [advice.withAdvice, withLogging]); + + debug.enable(true); + debug.events.logAll(); + + initializeDefault(''); + } +); diff --git a/web-ui/public/js/mixins/with_auto_refresh.js b/web-ui/public/js/mixins/with_auto_refresh.js new file mode 100644 index 00000000..c75fda45 --- /dev/null +++ b/web-ui/public/js/mixins/with_auto_refresh.js @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['features'], + function (features) { + 'use strict'; + + function withAutoRefresh(refreshMethod) { + return function () { + this.defaultAttrs({ + refreshInterval: 15000 + }); + + this.setupRefresher = function () { + clearTimeout(this.attr.refreshTimer); + this.attr.refreshTimer = setTimeout(function () { + this[refreshMethod](); + this.setupRefresher(); + }.bind(this), this.attr.refreshInterval); + }; + + this.after('initialize', function () { + if (features.isAutoRefreshEnabled()) { + this.setupRefresher(); + } + }); + }; + } + + return withAutoRefresh; + } +); + diff --git a/web-ui/public/js/mixins/with_compose_inline.js b/web-ui/public/js/mixins/with_compose_inline.js new file mode 100644 index 00000000..b8266f28 --- /dev/null +++ b/web-ui/public/js/mixins/with_compose_inline.js @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'page/events', + 'views/templates', + 'mail_view/data/mail_builder', + 'mixins/with_mail_edit_base' + ], + function(events, templates, mailBuilder, withMailEditBase) { + 'use strict'; + + function withComposeInline() { + this.defaultAttrs({ + subjectDisplay: '#reply-subject', + subjectInput: '#subject-container input', + forwardBox: '#forward-box', + recipientsDisplay: '#all-recipients' + }); + + this.openMail = function(ev, data) { + this.trigger(document, events.ui.mail.open, {ident: this.attr.mail.ident}); + }; + + this.trashReply = function() { + this.trigger(document, events.ui.composeBox.trashReply); + this.teardown(); + }; + + this.builtMail = function(tag) { + return mailBuilder.newMail(this.attr.ident) + .subject(this.select('subjectBox').val()) + .to(this.attr.recipientValues.to) + .cc(this.attr.recipientValues.cc) + .bcc(this.attr.recipientValues.bcc) + .body(this.select('bodyBox').val()) + .attachment(this.attr.attachments) + .tag(tag); + }; + + this.renderInlineCompose = function(className, viewData) { + this.show(); + this.render(templates.compose.inlineBox, viewData); + + this.$node.addClass(className); + this.select('bodyBox').focus(); + + this.enableAutoSave(); + }; + + this.updateIdent = function(ev, data) { + this.attr.mail.ident = data.ident; + }; + + this.discardDraft = function() { + this.trashReply(); + }; + + this.after('initialize', function () { + this.on(document, events.mail.sent, this.openMail); + this.on(document, events.mail.deleted, this.trashReply); + this.on(document, events.mail.draftSaved, this.updateIdent); + }); + + withMailEditBase.call(this); + } + + return withComposeInline; + }); diff --git a/web-ui/public/js/mixins/with_enable_disable_on_event.js b/web-ui/public/js/mixins/with_enable_disable_on_event.js new file mode 100644 index 00000000..5b28a67b --- /dev/null +++ b/web-ui/public/js/mixins/with_enable_disable_on_event.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define([], + function () { + 'use strict'; + + function withEnableDisableOnEvent(ev) { + return function () { + this.disableElement = function () { + this.$node.attr('disabled', 'disabled'); + }; + + this.enableElement = function () { + this.$node.removeAttr('disabled'); + }; + + this.toggleEnabled = function (ev, enable) { + if (enable) { + this.enableElement(); + } else { + this.disableElement(); + } + }; + + this.after('initialize', function () { + this.on(document, ev, this.toggleEnabled); + }); + }; + } + + return withEnableDisableOnEvent; + } +); diff --git a/web-ui/public/js/mixins/with_feature_toggle.js b/web-ui/public/js/mixins/with_feature_toggle.js new file mode 100644 index 00000000..195b08bc --- /dev/null +++ b/web-ui/public/js/mixins/with_feature_toggle.js @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['features'], + function(features) { + 'use strict'; + + function withFeatureToggle(componentName, behaviorForFeatureOff) { + return function() { + + this.around('initialize', _.bind(function(basicInitialize, node, attrs) { + if(features.isEnabled(componentName)) { + return basicInitialize(node, attrs); + } + else if (behaviorForFeatureOff){ + behaviorForFeatureOff.call(this); + + return this; + } + }, this)); + }; + } + + return withFeatureToggle; + +}); diff --git a/web-ui/public/js/mixins/with_hide_and_show.js b/web-ui/public/js/mixins/with_hide_and_show.js new file mode 100644 index 00000000..c8902f61 --- /dev/null +++ b/web-ui/public/js/mixins/with_hide_and_show.js @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(function(require) { + 'use strict'; + + function withHideAndShow() { + this.hide = function () { + this.$node.hide(); + }; + this.show = function () { + this.$node.show(); + }; + } + + return withHideAndShow; + +}); diff --git a/web-ui/public/js/mixins/with_mail_edit_base.js b/web-ui/public/js/mixins/with_mail_edit_base.js new file mode 100644 index 00000000..a088080e --- /dev/null +++ b/web-ui/public/js/mixins/with_mail_edit_base.js @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/compose', + 'helpers/view_helper', + 'mail_view/ui/recipients/recipients', + 'mail_view/ui/draft_save_status', + 'page/events', + 'views/i18n', + 'mail_view/ui/send_button', + 'mail_view/ui/attachment_icon', + 'mail_view/ui/attachment_list', + 'flight/lib/utils' + ], + function (compose, viewHelper, Recipients, DraftSaveStatus, events, i18n, SendButton, AttachmentIcon, attachmentList, utils) { + 'use strict'; + + function withMailEditBase() { + + this.defaultAttrs({ + bodyBox: '#text-box', + sendButton: '#send-button', + attachmentButton: '#attachment-button', + attachmentList: '#attachment-list', + cancelButton: '#cancel-button', + trashButton: '#trash-button', + toArea: '#recipients-to-area', + toBox: '#recipients-to-box', + ccArea: '#recipients-cc-area', + bccArea: '#recipients-bcc-area', + ccsTrigger: '#ccs-trigger', + bccsTrigger: '#bccs-trigger', + toTrigger: '#to-trigger', + subjectBox: '#subject', + tipMsg: '.tip-msg', + draftSaveStatus: '#draft-save-status', + recipientsFields: '#recipients-fields', + currentTag: '', + recipientValues: {to: [], cc: [], bcc: []}, + saveDraftInterval: 3000 + }); + + this.attachRecipients = function (context) { + Recipients.attachTo(this.select('toArea'), {name: 'to', addresses: context.recipients.to}); + Recipients.attachTo(this.select('ccArea'), {name: 'cc', addresses: context.recipients.cc || []}); + Recipients.attachTo(this.select('bccArea'), {name: 'bcc', addresses: context.recipients.bcc || []}); + }; + + function thereAreRecipientsToDisplay() { + + var allRecipients = _.chain(this.attr.recipientValues). + values(). + flatten(). + remove(undefined). + value(); + + return !_.isEmpty(allRecipients); + } + + this.warnSendButtonOfRecipients = function () { + if (thereAreRecipientsToDisplay.call(this)) { + _.forOwn(this.attr.recipientValues, function (recipients, recipientsType) { + if (!_.isUndefined(recipients) && !_.isEmpty(recipients)) { + var recipientsUpdatedData = { + newRecipients: recipients, + recipientsName: recipientsType + }; + this.trigger(document, events.ui.recipients.updated, recipientsUpdatedData); + } + }.bind(this)); + } + }; + + this.render = function (template, context) { + this.$node.html(template(context)); + + if (!context || _.isEmpty(context)) { + context.recipients = {to: [], cc: [], bcc: []}; + } + this.attr.recipientValues = context.recipients; + this.attr.attachments = context.attachments || []; + this.attachRecipients(context); + + this.on(this.select('trashButton'), 'click', this.discardMail); + SendButton.attachTo(this.select('sendButton')); + AttachmentIcon.attachTo(this.select('attachmentButton')); + + this.warnSendButtonOfRecipients(); + }; + + this.enableAutoSave = function () { + this.select('bodyBox').on('input', this.monitorInput.bind(this)); + this.select('subjectBox').on('input', this.monitorInput.bind(this)); + this.on(document, events.mail.appendAttachment, this.monitorInput.bind(this)); + this.on(document, events.mail.removeAttachment, this.monitorInput.bind(this)); + DraftSaveStatus.attachTo(this.select('draftSaveStatus')); + }; + + this.monitorInput = function () { + this.trigger(events.ui.mail.changedSinceLastSave); + this.cancelPostponedSaveDraft(); + var mail = this.buildMail(); + this.postponeSaveDraft(mail); + }; + + this.discardMail = function () { + this.cancelPostponedSaveDraft(); + if (this.attr.ident) { + var mail = this.buildMail(); + this.trigger(document, events.ui.mail.delete, {mail: mail}); + } else { + this.trigger(document, events.ui.mail.discard); + } + }; + + this.trim_recipient = function (recipients) { + return recipients.map(function (recipient) { + return recipient.trim(); + }); + }; + + this.sendMail = function () { + this.cancelPostponedSaveDraft(); + var mail = this.buildMail('sent'); + + if (allRecipientsAreEmails(mail)) { + mail.header.to = this.trim_recipient(mail.header.to); + mail.header.cc = this.trim_recipient(mail.header.cc); + mail.header.bcc = this.trim_recipient(mail.header.bcc); + this.trigger(events.mail.send, mail); + } else { + this.trigger( + events.ui.userAlerts.displayMessage, + {message: i18n.t('recipients-not-valid')} + ); + this.trigger(events.mail.send_failed); + } + }; + + this.buildAndSaveDraft = function () { + var mail = this.buildMail(); + this.saveDraft(mail); + }; + + this.recipientsUpdated = function (ev, data) { + this.attr.recipientValues[data.recipientsName] = data.newRecipients; + this.trigger(document, events.ui.mail.recipientsUpdated); + if (data.skipSaveDraft) { + return; + } + + var mail = this.buildMail(); + this.postponeSaveDraft(mail); + }; + + this.saveDraft = function (mail) { + this.cancelPostponedSaveDraft(); + this.trigger(document, events.mail.saveDraft, mail); + }; + + this.cancelPostponedSaveDraft = function () { + clearTimeout(this.attr.timeout); + }; + + this.postponeSaveDraft = function (mail) { + this.cancelPostponedSaveDraft(); + + this.attr.timeout = window.setTimeout(_.bind(function () { + this.saveDraft(mail); + }, this), this.attr.saveDraftInterval); + }; + + this.draftSaved = function (event, data) { + this.attr.ident = data.ident; + }; + + this.validateAnyRecipient = function () { + return !_.isEmpty(_.flatten(_.values(this.attr.recipientValues))); + }; + + function allRecipientsAreEmails(mail) { + var allRecipients = mail.header.to.concat(mail.header.cc).concat(mail.header.bcc); + return _.isEmpty(allRecipients) ? false : _.all(allRecipients, emailFormatChecker); + } + + function emailFormatChecker(email) { + var emailFormat = /[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailFormat.test(email); + } + + this.saveTag = function (ev, data) { + this.attr.currentTag = data.tag; + }; + + this.mailSent = function () { + this.trigger(document, events.ui.userAlerts.displayMessage, {message: 'Your message was sent!'}); + }; + + this.enableFloatlabel = function (element) { + var showClass = 'showfloatlabel'; + $(element).bind('keyup', function () { + var label = $(this).prev('label'); + if (this.value !== '') { + label.addClass(showClass); + $(this).addClass(showClass); + } else { + label.removeClass(showClass); + $(this).removeClass(showClass); + } + }); + }; + + this.toggleRecipientsArrows = function () { + $('#cc-bcc-collapse').toggleClass('fa-angle-down'); + $('#cc-bcc-collapse').toggleClass('fa-angle-up'); + }; + + this.before('initialize', function () { + if (!this.discardDraft) { + this.discardDraft = function () { + }; + } + }); + + this.bindCollapse = function () { + this.on($('#cc-bcc-collapse'), 'click', this.toggleRecipientsArrows); + }; + + this.after('initialize', function () { + this.on(document, events.dispatchers.rightPane.clear, this.teardown); + this.on(document, events.ui.recipients.updated, this.recipientsUpdated); + this.on(document, events.mail.draftSaved, this.draftSaved); + this.on(document, events.mail.sent, this.mailSent); + + this.on(document, events.ui.mail.send, this.sendMail); + + this.on(document, events.ui.mail.discard, this.discardDraft); + this.on(document, events.ui.tag.selected, this.saveTag); + this.on(document, events.ui.tag.select, this.saveTag); + this.bindCollapse(); + }); + + compose.mixin(this, [attachmentList]); + } + + return withMailEditBase; + }); diff --git a/web-ui/public/js/mixins/with_mail_sandbox.js b/web-ui/public/js/mixins/with_mail_sandbox.js new file mode 100644 index 00000000..1a51840d --- /dev/null +++ b/web-ui/public/js/mixins/with_mail_sandbox.js @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + ['helpers/view_helper', 'page/events'], + function(viewHelpers, events) { + 'use strict'; + + function withMailSandbox() { + this.showMailOnSandbox = function(mail) { + var that = this; + var $iframe = $("#read-sandbox"); + var iframe = $iframe[0]; + var content = viewHelpers.formatMailBody(mail); + + window.addEventListener('message', function(e) { + if (e.origin === 'null' && e.source === iframe.contentWindow) { + that.trigger(document, events.ui.replyBox.showReplyContainer); + that.trigger(document, events.search.highlightResults, {where: '.mail-read-view__header'}); + } + }); + + iframe.onload = function() { + if ($iframe.iFrameResize) { + // use iframe-resizer to dynamically adapt iframe size to its content + var config = { + resizedCallback: scaleToFit, + checkOrigin: false + }; + $iframe.iFrameResize(config); + } + + iframe.contentWindow.postMessage({ + html: content + }, '*'); + + // transform scale iframe to fit container width + // necessary if iframe is wider than container + function scaleToFit() { + var parentWidth = $iframe.parent().width(); + var w = $iframe.width(); + var scale = 'none'; + + // only scale html mails + if (mail && mail.htmlBody && (w > parentWidth)) { + scale = parentWidth / w; + scale = 'scale(' + scale + ',' + scale + ')'; + } + + $iframe.css({ + '-webkit-transform-origin': '0 0', + '-moz-transform-origin': '0 0', + '-ms-transform-origin': '0 0', + 'transform-origin': '0 0', + '-webkit-transform': scale, + '-moz-transform': scale, + '-ms-transform': scale, + 'transform': scale + }); + } + }; + }; + } + + return withMailSandbox; + } +); diff --git a/web-ui/public/js/mixins/with_mail_tagging.js b/web-ui/public/js/mixins/with_mail_tagging.js new file mode 100644 index 00000000..1fc1c3bd --- /dev/null +++ b/web-ui/public/js/mixins/with_mail_tagging.js @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + ['page/events', 'features'], + function (events, features) { + 'use strict'; + function withMailTagging () { + this.updateTags = function(mail, tags) { + this.trigger(document, events.mail.tags.update, {ident: mail.ident, tags: tags}); + }; + + this.attachTagCompletion = function(mail) { + this.tagFilter = function (parsedResult) { + var filtered = _.filter(parsedResult, function (tag) {return ! _.contains(mail.tags, tag.name); }); + return _.map(filtered, function(tag) { return {value: Handlebars.Utils.escapeExpression(tag.name)}; }); + }; + + this.tagCompleter = new Bloodhound({ + datumTokenizer: function(d) { return [d.value]; }, + queryTokenizer: function(q) { return [q.trim()]; }, + remote: { + url: '/tags?skipDefaultTags=true&q=%QUERY', + filter: this.tagFilter + } + }); + + this.tagCompleter.initialize(); + + this.select('newTagInput').typeahead({ + hint: true, + highlight: true, + minLength: 1 + }, { + source: this.tagCompleter.ttAdapter() + }); + }; + + this.createNewTag = function () { + var tagsCopy = this.attr.mail.tags.slice(); + tagsCopy.push(this.select('newTagInput').val()); + this.tagCompleter.clear(); + this.tagCompleter.clearPrefetchCache(); + this.tagCompleter.clearRemoteCache(); + this.updateTags(this.attr.mail, _.uniq(tagsCopy)); + }; + + this.after('displayMail', function () { + this.on(this.select('newTagInput'), 'typeahead:selected typeahead:autocompleted', this.createNewTag); + }); + } + + return withMailTagging; + } +); diff --git a/web-ui/public/js/monkey_patching/all.js b/web-ui/public/js/monkey_patching/all.js new file mode 100644 index 00000000..2c29c9a1 --- /dev/null +++ b/web-ui/public/js/monkey_patching/all.js @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +require(['js/monkey_patching/array', 'js/monkey_patching/post_message'], function () {}); diff --git a/web-ui/public/js/monkey_patching/array.js b/web-ui/public/js/monkey_patching/array.js new file mode 100644 index 00000000..d0ccc4b8 --- /dev/null +++ b/web-ui/public/js/monkey_patching/array.js @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +(function () { + 'use strict'; + + // Array Remove - By John Resig (MIT Licensed) + Array.prototype.remove = function (from, to) { + var rest = this.slice((to || from) + 1 || this.length); + this.length = from < 0 ? this.length + from : from; + return this.push.apply(this, rest); + }; + +}()); diff --git a/web-ui/public/js/monkey_patching/post_message.js b/web-ui/public/js/monkey_patching/post_message.js new file mode 100644 index 00000000..363ce581 --- /dev/null +++ b/web-ui/public/js/monkey_patching/post_message.js @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +/* + * origin window.postMessage fails with non serializable objects, so we fallback to console.log to do the job + */ +(function () { + 'use strict'; + + var originalPostMessage = window.postMessage; + window.postMessage = function(a, b) { + try { + originalPostMessage(a, b); + } catch (e) { + console.log(a, b); + } + }; + +}()); diff --git a/web-ui/public/js/page/default.js b/web-ui/public/js/page/default.js new file mode 100644 index 00000000..ecaedfd8 --- /dev/null +++ b/web-ui/public/js/page/default.js @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'mail_view/ui/compose_box', + 'mail_list_actions/ui/mail_list_actions', + 'user_alerts/ui/user_alerts', + 'mail_list/ui/mail_list', + 'mail_view/ui/no_message_selected_pane', + 'mail_view/ui/no_mails_available_pane', + 'mail_view/ui/mail_view', + 'mail_view/ui/mail_actions', + 'mail_view/ui/reply_section', + 'mail_view/data/mail_sender', + 'services/mail_service', + 'services/delete_service', + 'services/recover_service', + 'tags/ui/tag_list', + 'tags/data/tags', + 'page/router', + 'dispatchers/right_pane_dispatcher', + 'dispatchers/middle_pane_dispatcher', + 'dispatchers/left_pane_dispatcher', + 'search/search_trigger', + 'search/results_highlighter', + 'foundation/off_canvas', + 'page/pane_contract_expand', + 'views/i18n', + 'views/recipientListFormatter', + 'flight/lib/logger', + 'user_settings/data/user_settings', + 'user_settings/ui/user_settings_icon', + 'page/logout', + 'page/logout_shortcut', + 'feedback/feedback_trigger', + 'mail_view/ui/feedback_box', + 'mail_view/data/feedback_sender', + 'page/version', + 'page/unread_count_title', + 'page/pix_logo', + 'helpers/browser' + ], + + function ( + composeBox, + mailListActions, + userAlerts, + mailList, + noMessageSelectedPane, + noMailsAvailablePane, + mailView, + mailViewActions, + replyButton, + mailSender, + mailService, + deleteService, + recoverService, + tagList, + tags, + router, + rightPaneDispatcher, + middlePaneDispatcher, + leftPaneDispatcher, + searchTrigger, + resultsHighlighter, + offCanvas, + paneContractExpand, + viewI18n, + recipientListFormatter, + withLogging, + userSettings, + userSettingsIcon, + logout, + logoutShortcut, + feedback, + feedbackBox, + feedbackSender, + version, + unreadCountTitle, + pixLogo, + browser) { + + 'use strict'; + function initialize(path) { + viewI18n.init(path + '/assets/'); + viewI18n.loaded(function() { + paneContractExpand.attachTo(document); + + userAlerts.attachTo('#user-alerts'); + + mailList.attachTo('#mail-list'); + mailListActions.attachTo('#list-actions'); + + searchTrigger.attachTo('#search-trigger'); + resultsHighlighter.attachTo(document); + + mailSender.attachTo(document); + + mailService.attachTo(document); + deleteService.attachTo(document); + recoverService.attachTo(document); + + tags.attachTo(document); + tagList.attachTo('#tag-list'); + + router.attachTo(document); + + rightPaneDispatcher.attachTo(document); + middlePaneDispatcher.attachTo(document); + leftPaneDispatcher.attachTo(document); + + offCanvas.attachTo(document); + userSettings.attachTo(document); + userSettingsIcon.attachTo('#user-settings-icon'); + logout.attachTo('#logout'); + logoutShortcut.attachTo('#logout-shortcut'); + version.attachTo('.version'); + + feedback.attachTo('#feedback'); + feedbackSender.attachTo(document); + + unreadCountTitle.attachTo(document); + + pixLogo.attachTo(document); + + $.ajaxSetup({headers: {'X-XSRF-TOKEN': browser.getCookie('XSRF-TOKEN')}}); + }); + } + + return initialize; + } +); diff --git a/web-ui/public/js/page/events.js b/web-ui/public/js/page/events.js new file mode 100644 index 00000000..68a6aad1 --- /dev/null +++ b/web-ui/public/js/page/events.js @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(function () { + 'use strict'; + + var events = { + router: { + pushState: 'router:pushState' + }, + ui: { + sendbutton: { + enable: 'ui:sendbutton:enable' + }, + middlePane: { + expand: 'ui:middlePane:expand', + contract: 'ui:middlePane:contract' + }, + userAlerts: { + displayMessage: 'ui:userAlerts:displayMessage' + }, + tag: { + selected: 'ui:tagSelected', + select: 'ui:tagSelect', + }, + tags: { + loaded: 'ui:tagsLoaded' + }, + tagList: { + load: 'ui:tagList:load' + }, + mails: { + refresh: 'ui:mails:refresh', + fetchByTag: 'ui:mails:fetchByTag', + cleanSelected: 'ui:mails:cleanSelected', + checkAll: 'ui:mails:checkAll', + uncheckAll: 'ui:mails:uncheckAll', + hasMailsChecked: 'ui:mails:hasMailsChecked' + }, + mail: { + open: 'ui:mail:open', + updateSelected: 'ui:mail:updateSelected', + delete: 'ui:mail:delete', + deleteMany: 'ui:mail:deleteMany', + recoverMany: 'ui:mail:recoverMany', + archiveMany: 'ui:mail:archiveMany', + wantChecked: 'ui:mail:wantChecked', + hereChecked: 'ui:mail:hereChecked', + checked: 'ui:mail:checked', + discard: 'ui:mail:discard', + unchecked: 'ui:mail:unchecked', + changedSinceLastSave: 'ui:mail:changedSinceLastSave', + send: 'ui:mail:send', + recipientsUpdated: 'ui:mail:recipientsUpdated' + }, + page: { + previous: 'ui:page:previous', + next: 'ui:page:next', + changed: 'ui:page:changed', + spinLogo: 'ui:page:spinLogo', + stopSpinningLogo: 'ui:page:stopSpinningLogo' + }, + composeBox: { + newMessage: 'ui:composeBox:newMessage', + newReply: 'ui:composeBox:newReply', + trashReply: 'ui:composeBox:trashReply', + requestCancelReply: 'ui:composeBox:requestCancelReply' + }, + replyBox: { + showReply: 'ui:replyBox:showReply', + showReplyAll: 'ui:replyBox:showReplyAll', + showReplyContainer: 'ui:replyBox:showReplyContainer', + }, + recipients: { + entered: 'ui:recipients:entered', + enteredInvalid: 'ui:recipients:enteredInvalid', + updated: 'ui:recipients:updated', + editRecipient: 'ui:recipients:editRecipient', + deleteRecipient: 'ui:recipients:deleteRecipient', + deleteLast: 'ui:recipients:deleteLast', + selectLast: 'ui:recipients:selectLast', + unselectAll: 'ui:recipients:unselectAll', + addressesExist: 'ui:recipients:addressesExist', + inputFieldHasCharacters: 'ui:recipients:inputFieldHasCharacters', + inputFieldIsEmpty: 'ui:recipients:inputFieldIsEmpty', + doCompleteInput: 'ui:recipients:doCompleteInput', + doCompleteRecipients: 'ui:recipients:doCompleteRecipients', + clickToEdit: 'ui:recipients:clickToEdit' + }, + userSettingsBox: { + toggle: 'ui:userSettingsBox:toggle' + } + }, + search: { + perform: 'search:perform', + results: 'search:results', + empty: 'search:empty', + highlightResults: 'search:highlightResults', + resetHighlight: 'search:resetHighlight' + }, + feedback: { + submit: 'feedback:submit', + submitted: 'feedback:submitted' + }, + userSettings: { + here: 'userSettings:here', + getInfo: 'userSettings:getInfo', + destroyPopup: 'userSettings:destroyPopup' + }, + mail: { + here: 'mail:here', + want: 'mail:want', + display: 'mail:display', + highlightMailContent: 'mail:highlightMailContent', + send: 'mail:send', + send_failed: 'mail:send_failed', + sent: 'mail:sent', + read: 'mail:read', + unread: 'mail:unread', + delete: 'mail:delete', + deleteMany: 'mail:deleteMany', + archiveMany: 'mail:archiveMany', + recoverMany: 'mail:recoverMany', + deleted: 'mail:deleted', + saveDraft: 'draft:save', + draftSaved: 'draft:saved', + draftReply: { + want: 'mail:draftReply:want', + here: 'mail:draftReply:here', + notFound: 'mail:draftReply:notFound' + }, + notFound: 'mail:notFound', + save: 'mail:saved', + tags: { + update: 'mail:tags:update', + updated: 'mail:tags:updated' + }, + uploadedAttachment: 'mail:uploaded:attachment', + uploadingAttachment: 'mail:uploading:attachment', + startUploadAttachment: 'mail:start:upload:attachment', + failedUploadAttachment: 'mail:failed:upload:attachment', + appendAttachment: 'mail:append:attachment', + resetAttachments: 'mail:reset:attachments', + removeAttachment: 'mail:remove:attachment' + }, + mails: { + available: 'mails:available', + availableForRefresh: 'mails:available:refresh', + teardown: 'mails:teardown' + }, + tags: { + want: 'tags:want', + received: 'tags:received', + teardown: 'tags:teardown', + shortcuts: { + teardown: 'tags:shortcuts:teardown' + } + }, + route: { + toUrl: 'route:toUrl' + }, + + components: { + composeBox: { + open: 'components:composeBox:open', + close: 'components:composeBox:close' + }, + mailPane: { + open: 'components:mailPane:open', + close: 'components:mailPane:close' + }, + mailView: { + show: 'components:mailView:show', + close: 'components:mailView:close' + }, + replySection: { + initialize: 'components:replySection:initialize', + close: 'components:replySection:close' + }, + noMessageSelectedPane: { + open: 'components:noMessageSelectedPane:open', + close: 'components:noMessageSelectedPane:close' + } + }, + + dispatchers: { + rightPane: { + openComposeBox: 'dispatchers:rightPane:openComposeBox', + openFeedbackBox: 'dispatchers:rightPane:openFeedbackBox', + openNoMessageSelected: 'dispatchers:rightPane:openNoMessageSelected', + openNoMessageSelectedWithoutPushState: 'dispatchers:rightPane:openNoMessageSelectedWithoutPushState', + refreshMailList: 'dispatchers:rightPane:refreshMailList', + openDraft: 'dispatchers:rightPane:openDraft', + selectTag: 'dispatchers:rightPane:selectTag', + clear: 'dispatchers:rightPane:clear' + }, + middlePane: { + refreshMailList: 'dispatchers:middlePane:refreshMailList', + cleanSelected: 'dispatchers:middlePane:unselect', + resetScroll: 'dispatchers:middlePane:resetScroll' + }, + tags: { + refreshTagList: 'dispatchers:tag:refresh' + } + } + }; + + return events; +}); diff --git a/web-ui/public/js/page/logout.js b/web-ui/public/js/page/logout.js new file mode 100644 index 00000000..81b57db2 --- /dev/null +++ b/web-ui/public/js/page/logout.js @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(['flight/lib/component', 'features', 'views/templates', 'helpers/browser'], + function (defineComponent, features, templates, browser) { + 'use strict'; + + return defineComponent(function () { + + this.defaultAttrs({form: '#logout-form'}); + + this.render = function () { + var logoutHTML = templates.page.logout({ logout_url: features.getLogoutUrl(), + csrf_token: browser.getCookie('XSRF-TOKEN')}); + this.$node.html(logoutHTML); + }; + + this.logout = function(){ + this.select('form').submit(); + }; + + this.after('initialize', function () { + if (features.isLogoutEnabled()) { + this.render(); + this.on(this.$node, 'click', this.logout); + } + }); + + }); +}); diff --git a/web-ui/public/js/page/logout_shortcut.js b/web-ui/public/js/page/logout_shortcut.js new file mode 100644 index 00000000..10a69c7d --- /dev/null +++ b/web-ui/public/js/page/logout_shortcut.js @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(['flight/lib/component', 'features', 'views/templates'], function (defineComponent, features, templates) { + 'use strict'; + + return defineComponent(function () { + + this.render = function () { + if (features.isLogoutEnabled()) { + var logoutShortcutHTML = templates.page.logoutShortcut(); + this.$node.html(logoutShortcutHTML); + } + }; + + this.after('initialize', function () { + this.render(); + }); + }); +}); diff --git a/web-ui/public/js/page/pane_contract_expand.js b/web-ui/public/js/page/pane_contract_expand.js new file mode 100644 index 00000000..9bb435c4 --- /dev/null +++ b/web-ui/public/js/page/pane_contract_expand.js @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['flight/lib/component', 'page/events'], function (describeComponent, events) { + 'use strict'; + + return describeComponent(paneContractExpand); + + function paneContractExpand() { + this.defaultAttrs({ + RIGHT_PANE_EXPAND_CLASSES: 'small-7 medium-7 large-7 columns', + RIGHT_PANE_CONTRACT_CLASSES: 'small-7 medium-4 large-4 columns', + MIDDLE_PANE_EXPAND_CLASSES: 'small-5 medium-8 large-8 columns no-padding', + MIDDLE_PANE_CONTRACT_CLASSES: 'small-5 medium-5 large-5 columns no-padding' + }); + + this.expandMiddlePaneContractRightPane = function () { + $('#middle-pane-container').attr('class', this.attr.MIDDLE_PANE_EXPAND_CLASSES); + $('#right-pane').attr('class', this.attr.RIGHT_PANE_CONTRACT_CLASSES); + }; + + this.contractMiddlePaneExpandRightPane = function () { + $('#middle-pane-container').attr('class', this.attr.MIDDLE_PANE_CONTRACT_CLASSES); + $('#right-pane').attr('class', this.attr.RIGHT_PANE_EXPAND_CLASSES); + }; + + this.after('initialize', function () { + this.on(document, events.ui.mail.open, this.contractMiddlePaneExpandRightPane); + this.on(document, events.dispatchers.rightPane.openComposeBox, this.contractMiddlePaneExpandRightPane); + this.on(document, events.dispatchers.rightPane.openDraft, this.contractMiddlePaneExpandRightPane); + this.on(document, events.dispatchers.rightPane.openFeedbackBox, this.contractMiddlePaneExpandRightPane); + this.on(document, events.dispatchers.rightPane.openNoMessageSelected, this.expandMiddlePaneContractRightPane); + this.expandMiddlePaneContractRightPane(); + }); + + } +}); diff --git a/web-ui/public/js/page/pix_logo.js b/web-ui/public/js/page/pix_logo.js new file mode 100644 index 00000000..ad17f3be --- /dev/null +++ b/web-ui/public/js/page/pix_logo.js @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'page/events' + ], + + function(defineComponent, events) { + 'use strict'; + + return defineComponent(pixLogo); + + function pixLogo() { + this.turnAnimationOn = function () { + $('.logo-part-animation-off').attr('class', 'logo-part-animation-on'); + }; + + this.turnAnimationOff = function () { + setTimeout(function(){ + $('.logo-part-animation-on').attr('class', 'logo-part-animation-off'); + }, 600); + }; + + this.triggerSpinLogo = function (ev, data) { + this.trigger(document, events.ui.page.spinLogo); + }; + + this.triggerStopSpinningLogo = function(ev, data) { + this.trigger(document, events.ui.page.stopSpinningLogo); + }; + + this.after('initialize', function () { + this.on(document, events.ui.page.spinLogo, this.turnAnimationOn); + this.on(document, events.ui.page.stopSpinningLogo, this.turnAnimationOff); + + this.on(document, events.ui.tag.select, this.triggerSpinLogo); + this.on(document, events.mails.available, this.triggerStopSpinningLogo); + this.on(document, events.mail.saveDraft, this.triggerSpinLogo); + this.on(document, events.mail.draftSaved, this.triggerStopSpinningLogo); + this.on(document, events.ui.mail.open, this.triggerSpinLogo); + this.on(document, events.dispatchers.rightPane.openDraft, this.triggerSpinLogo); + this.on(document, events.search.perform, this.triggerSpinLogo); + this.on(document, events.mail.want, this.triggerStopSpinningLogo); + }); + } + } +); diff --git a/web-ui/public/js/page/router.js b/web-ui/public/js/page/router.js new file mode 100644 index 00000000..ce0d7d04 --- /dev/null +++ b/web-ui/public/js/page/router.js @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(['flight/lib/component', 'page/events', 'page/router/url_params'], function (defineComponent, events, urlParams) { + 'use strict'; + + return defineComponent(function () { + this.defaultAttrs({ + history: window.history + }); + + function createHash(data) { + var hash = '/#/' + data.tag; + if (!_.isUndefined(data.mailIdent)) { + hash += '/mail/' + data.mailIdent; + } + return hash; + } + + function createState(data, previousState) { + return { + tag: data.tag || (previousState && previousState.tag) || urlParams.defaultTag(), + mailIdent: data.mailIdent, + query: data.query, + isDisplayNoMessageSelected: !!data.isDisplayNoMessageSelected + }; + } + + this.pushState = function (ev, data) { + if (!data.fromPopState) { + var nextState = createState(data, this.attr.history.state); + this.attr.history.pushState(nextState, '', createHash(nextState)); + } + }; + + this.popState = function (ev) { + var state = ev.state || {}; + + this.trigger(document, events.ui.tag.select, { + tag: state.tag || urlParams.getTag(), + mailIdent: state.mailIdent, + fromPopState: true + }); + + if (ev.state.isDisplayNoMessageSelected) { + this.trigger(document, events.dispatchers.rightPane.openNoMessageSelectedWithoutPushState); + } + }; + + this.after('initialize', function () { + this.on(document, events.router.pushState, this.pushState); + this.on(document, events.ui.tag.select, this.pushState); + this.on(document, events.search.perform, this.pushState); + this.on(document, events.search.empty, this.pushState); + window.onpopstate = this.popState.bind(this); + }); + }); +}); diff --git a/web-ui/public/js/page/router/url_params.js b/web-ui/public/js/page/router/url_params.js new file mode 100644 index 00000000..4fa11c6d --- /dev/null +++ b/web-ui/public/js/page/router/url_params.js @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define([], function () { + 'use strict'; + + function defaultTag() { + return 'inbox'; + } + + function getDocumentHash() { + return document.location.hash.replace(/\/$/, ''); + } + + function hashTag(hash) { + if (hasMailIdent(hash)) { + return /\/(.+)\/mail\/[-\w]+$/.exec(getDocumentHash())[1]; + } + return hash.substring(2); + } + + + function getTag() { + if (document.location.hash !== '') { + return hashTag(getDocumentHash()); + } + return defaultTag(); + } + + function hasMailIdent() { + return getDocumentHash().match(/mail\/[-\w]+$/); + } + + function getMailIdent() { + return /mail\/([-\w]+)$/.exec(getDocumentHash())[1]; + } + + return { + getTag: getTag, + hasMailIdent: hasMailIdent, + getMailIdent: getMailIdent, + defaultTag: defaultTag + }; +}); diff --git a/web-ui/public/js/page/unread_count_title.js b/web-ui/public/js/page/unread_count_title.js new file mode 100644 index 00000000..89dcd47d --- /dev/null +++ b/web-ui/public/js/page/unread_count_title.js @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + + +define( + [ + 'flight/lib/component', + 'page/events', + ], + + function (defineComponent, events) { + 'use strict'; + + return defineComponent(function () { + this.getTitleText = function () { + return document.title; + }; + + this.updateCount = function (ev, data) { + var unread = data.mails.filter(function (mail) { + return mail.status.indexOf('read') === -1; + }).length; + + var tag = this.toTitleCase(data.tag); + var counter = unread > 0 ? ' (' + unread + ') - ' : ' - '; + document.title = tag + counter + this.rawTitle; + }; + + this.toTitleCase = function (str) { + return str.replace(/\b\w/g, function (txt) { return txt.toUpperCase(); }); + }; + + this.after('initialize', function () { + this.rawTitle = document.title; + this.on(document, events.mails.available, this.updateCount); + }); + + }); +}); diff --git a/web-ui/public/js/page/version.js b/web-ui/public/js/page/version.js new file mode 100644 index 00000000..9fd5e629 --- /dev/null +++ b/web-ui/public/js/page/version.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(['flight/lib/component', 'views/templates', 'helpers/view_helper'], function (defineComponent, templates, viewHelper) { + 'use strict'; + + return defineComponent(function () { + this.defaultAttrs({ + 'sinceDate': '#version-date' + }); + + this.render = function () { + this.$node.html(templates.page.version()); + this.renderCommitDate(); + }; + + this.renderCommitDate = function(){ + var since = this.select('sinceDate').attr('data-since'), + commitDate = viewHelper.sinceDate(since); + this.select('sinceDate').html(commitDate + ' ago'); + }; + + this.after('initialize', function () { + this.render(); + }); + + }); +}); diff --git a/web-ui/public/js/sandbox.js b/web-ui/public/js/sandbox.js new file mode 100644 index 00000000..33b16ea4 --- /dev/null +++ b/web-ui/public/js/sandbox.js @@ -0,0 +1,11 @@ +(function () { + 'use strict'; + + window.onmessage = function (e) { + if (e.data.html) { + document.body.innerHTML = e.data.html; + var mainWindow = e.source; + mainWindow.postMessage('data ok', e.origin); + } + }; +})(); diff --git a/web-ui/public/js/search/results_highlighter.js b/web-ui/public/js/search/results_highlighter.js new file mode 100644 index 00000000..831be0cd --- /dev/null +++ b/web-ui/public/js/search/results_highlighter.js @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'page/events' + ], function (defineComponent, events) { + + 'use strict'; + + return defineComponent(resultsHighlighter); + + function resultsHighlighter(){ + this.defaultAttrs({ + keywords: [] + }); + + this.getKeywordsSearch = function (event, data) { + this.attr.keywords = data.query.split(' ').map(function(keyword) { + return keyword.toLowerCase(); + }); + }; + + this.highlightResults = function (event, data) { + var domIdent = data.where; + if(this.attr.keywords) { + _.each(this.attr.keywords, function (keyword) { + keyword = escapeRegExp(keyword); + $(domIdent).highlightRegex(new RegExp(keyword, 'i'), { + tagType: 'em', + className: 'search-highlight' + }); + }); + } + }; + + this.clearHighlights = function (event, data) { + this.attr.keywords = []; + _.each($('em.search-highlight'), function(highlighted) { + var jqueryHighlighted = $(highlighted); + var text = jqueryHighlighted.text(); + jqueryHighlighted.replaceWith(text); + }); + }; + + this.highlightString = function (string) { + _.each(this.attr.keywords, function (keyword) { + keyword = escapeRegExp(keyword); + var regex = new RegExp('(' + keyword + ')', 'ig'); + string = string.replace(regex, '$1'); + }); + return string; + }; + + /* + * Alter data.mail.textPlainBody to highlight each of this.attr.keywords + * and pass it back to the mail_view when done + */ + this.highlightMailContent = function(ev, data){ + var mail = data.mail; + mail.textPlainBody = this.highlightString(mail.textPlainBody); + this.trigger(document, events.mail.display, data); + }; + + /* + * Escapes the special charaters used regular expressions that + * would cause problems with strings in the RegExp constructor + */ + function escapeRegExp(string){ + return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + } + + this.after('initialize', function () { + this.on(document, events.search.perform, this.getKeywordsSearch); + this.on(document, events.ui.tag.select, this.clearHighlights); + this.on(document, events.search.resetHighlight, this.clearHighlights); + + this.on(document, events.search.highlightResults, this.highlightResults); + this.on(document, events.mail.highlightMailContent, this.highlightMailContent); + }); + } +}); diff --git a/web-ui/public/js/search/search_trigger.js b/web-ui/public/js/search/search_trigger.js new file mode 100644 index 00000000..2aff027c --- /dev/null +++ b/web-ui/public/js/search/search_trigger.js @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'views/templates', + 'page/events', + 'views/i18n' + ], function (defineComponent, templates, events, i18n) { + + 'use strict'; + + return defineComponent(searchTrigger); + + function searchTrigger() { + this.defaultAttrs({ + input: 'input[type=search]', + form: 'form', + searchResultsPrefix: 'search-results-for' + }); + + this.render = function() { + this.$node.html(templates.search.trigger()); + }; + + this.search = function(ev, data) { + this.trigger(document, events.search.resetHighlight); + ev.preventDefault(); + var input = this.select('input'); + var value = input.val(); + input.blur(); + if(!_.isEmpty(value)){ + this.trigger(document, events.search.perform, { query: value }); + } else { + this.trigger(document, events.search.empty); + } + }; + + this.clearInput = function() { + this.select('input').val(''); + }; + + this.showOnlySearchTerms = function(event){ + var value = this.select('input').val(); + var searchTerms = value.slice((i18n.t(this.attr.searchResultsPrefix) + ': ').length); + this.select('input').val(searchTerms); + }; + + this.showSearchTermsAndPlaceHolder = function(event){ + var value = this.select('input').val(); + if (value.length > 0){ + this.select('input').val(i18n.t(this.attr.searchResultsPrefix) + ': ' + value); + } + }; + + this.after('initialize', function () { + this.render(); + this.on(this.select('form'), 'submit', this.search); + this.on(this.select('input'), 'focus', this.showOnlySearchTerms); + this.on(this.select('input'), 'blur', this.showSearchTermsAndPlaceHolder); + this.on(document, events.ui.tag.selected, this.clearInput); + this.on(document, events.ui.tag.select, this.clearInput); + }); + } + } +); diff --git a/web-ui/public/js/services/delete_service.js b/web-ui/public/js/services/delete_service.js new file mode 100644 index 00000000..0dfc1bdb --- /dev/null +++ b/web-ui/public/js/services/delete_service.js @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['flight/lib/component', 'page/events', 'views/i18n'], function (defineComponent, events, i18n) { + 'use strict'; + + return defineComponent(function() { + + this.successDeleteMessageFor = function(mail) { + return mail.isInTrash() ? + i18n.t('delete-single') : + i18n.t('trash-single'); + }; + + this.successDeleteManyMessageFor = function(mail) { + return mail.isInTrash() ? + i18n.t('delete-bulk') : + i18n.t('trash-bulk'); + }; + + this.deleteEmail = function (event, data) { + this.trigger(document, events.mail.delete, { + mail: data.mail, + successMessage: this.successDeleteMessageFor(data.mail) + }); + }; + + this.deleteManyEmails = function (event, data) { + var emails = _.values(data.checkedMails), + firstEmail = emails[_.first(_.keys(emails))]; + + this.trigger(document, events.mail.deleteMany, { + mails: emails, + successMessage: this.successDeleteManyMessageFor(firstEmail) + }); + + }; + + this.after('initialize', function () { + this.on(document, events.ui.mail.delete, this.deleteEmail); + this.on(document, events.ui.mail.deleteMany, this.deleteManyEmails); + }); + + }); +}); diff --git a/web-ui/public/js/services/mail_service.js b/web-ui/public/js/services/mail_service.js new file mode 100644 index 00000000..5e4bd4f3 --- /dev/null +++ b/web-ui/public/js/services/mail_service.js @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'views/i18n', + 'services/model/mail', + 'helpers/monitored_ajax', + 'page/events', + 'features', + 'mixins/with_auto_refresh', + 'page/router/url_params' + ], function (defineComponent, i18n, Mail, monitoredAjax, events, features, withAutoRefresh, urlParams) { + + 'use strict'; + + return defineComponent(mailService, withAutoRefresh('refreshMails')); + + function mailService() { + var that; + + this.defaultAttrs({ + mailsResource: '/mails', + singleMailResource: '/mail', + currentTag: '', + lastQuery: '', + currentPage: 1, + numPages: 1, + pageSize: 25 + }); + + this.errorMessage = function (msg) { + return function () { + that.trigger(document, events.ui.userAlerts.displayMessage, { message: msg }); + }; + }; + + this.updateTags = function (ev, data) { + var ident = data.ident; + + var success = function (data) { + this.refreshMails(); + $(document).trigger(events.mail.tags.updated, { ident: ident, tags: data.tags }); + $(document).trigger(events.dispatchers.tags.refreshTagList, { skipMailListRefresh: true }); + }; + + var failure = function (resp) { + var msg = i18n.t('failed-change-tags'); + if (resp.status === 403) { + msg = i18n.t('invalid-tag-name'); + } + this.trigger(document, events.ui.userAlerts.displayMessage, { message: msg }); + }; + + monitoredAjax(this, '/mail/' + ident + '/tags', { + type: 'POST', + contentType: 'application/json; charset=utf-8', + data: JSON.stringify({newtags: data.tags}) + }).done(success.bind(this)).fail(failure.bind(this)); + + }; + + this.readMail = function (ev, data) { + var mailIdents; + if (data.checkedMails) { + mailIdents = _.map(data.checkedMails, function (mail) { + return mail.ident; + }); + } else { + mailIdents = [data.ident]; + } + monitoredAjax(this, '/mails/read', { + type: 'POST', + data: JSON.stringify({idents: mailIdents}) + }).done(this.triggerMailsRead(data.checkedMails)); + }; + + this.unreadMail = function (ev, data) { + var mailIdents; + if (data.checkedMails) { + mailIdents = _.map(data.checkedMails, function (mail) { + return mail.ident; + }); + } else { + mailIdents = [data.ident]; + } + monitoredAjax(this, '/mails/unread', { + type: 'POST', + data: JSON.stringify({idents: mailIdents}) + }).done(this.triggerMailsRead(data.checkedMails)); + }; + + this.triggerMailsRead = function (mails) { + return _.bind(function () { + this.refreshMails(); + this.trigger(document, events.ui.mails.uncheckAll); + }, this); + }; + + this.triggerDeleted = function (dataToDelete) { + return _.bind(function () { + var mails = dataToDelete.mails || [dataToDelete.mail]; + + this.refreshMails(); + this.trigger(document, events.ui.userAlerts.displayMessage, { message: dataToDelete.successMessage}); + this.trigger(document, events.ui.mails.uncheckAll); + this.trigger(document, events.mail.deleted, { mails: mails }); + }, this); + }; + + this.triggerRecovered = function (dataToRecover) { + return _.bind(function () { + var mails = dataToRecover.mails || [dataToRecover.mail]; + + this.refreshMails(); + this.trigger(document, events.ui.userAlerts.displayMessage, { message: i18n.t(dataToRecover.successMessage)}); + this.trigger(document, events.ui.mails.uncheckAll); + }, this); + }; + + this.triggerArchived = function (dataToArchive) { + return _.bind(function (response) { + this.refreshMails(); + this.trigger(document, events.ui.userAlerts.displayMessage, { message: i18n.t(response.successMessage)}); + this.trigger(document, events.ui.mails.uncheckAll); + }, this); + }; + + this.archiveManyMails = function(event, dataToArchive) { + var mailIdents = _.map(dataToArchive.checkedMails, function (mail) { + return mail.ident; + }); + monitoredAjax(this, '/mails/archive', { + type: 'POST', + dataType: 'json', + contentType: 'application/json; charset=utf-8', + data: JSON.stringify({idents: mailIdents}) + }).done(this.triggerArchived(dataToArchive)) + .fail(this.errorMessage(i18n.t('failed-archive'))); + }; + + this.deleteMail = function (ev, data) { + monitoredAjax(this, '/mail/' + data.mail.ident, + {type: 'DELETE'}) + .done(this.triggerDeleted(data)) + .fail(this.errorMessage(i18n.t('failed-delete-single'))); + }; + + this.deleteManyMails = function (ev, data) { + var dataToDelete = data; + var mailIdents = _.map(data.mails, function (mail) { + return mail.ident; + }); + + monitoredAjax(this, '/mails/delete', { + type: 'POST', + dataType: 'json', + contentType: 'application/json; charset=utf-8', + data: JSON.stringify({idents: mailIdents}) + }).done(this.triggerDeleted(dataToDelete)) + .fail(this.errorMessage(i18n.t('failed-delete-bulk'))); + }; + + this.recoverManyMails = function (ev, data) { + var dataToRecover = data; + var mailIdents = _.map(data.mails, function (mail) { + return mail.ident; + }); + + monitoredAjax(this, '/mails/recover', { + type: 'POST', + dataType: 'json', + contentType: 'application/json; charset=utf-8', + data: JSON.stringify({idents: mailIdents}) + }).done(this.triggerRecovered(dataToRecover)) + .fail(this.errorMessage(i18n.t('Could not move emails to inbox'))); + }; + + function compileQuery(data) { + var query = 'tag:"' + that.attr.currentTag + '"'; + + if (data.tag === 'all') { + query = 'in:all'; + } + return query; + } + + this.fetchByTag = function (ev, data) { + this.attr.currentTag = data.tag; + this.attr.lastQuery = compileQuery(data); + this.updateCurrentPageNumber(1); + + this.refreshMails(); + }; + + this.newSearch = function (ev, data) { + this.attr.lastQuery = data.query; + this.attr.currentTag = 'all'; + this.refreshMails(); + }; + + this.mailFromJSON = function (mail) { + return Mail.create(mail); + }; + + this.parseMails = function (data) { + data.mails = _.map(data.mails, this.mailFromJSON, this); + + return data; + }; + + function escaped(s) { + return encodeURIComponent(s); + } + + this.excludeTrashedEmailsForDraftsAndSent = function (query) { + if (query === 'tag:"drafts"' || query === 'tag:"sent"') { + return query + ' -in:"trash"'; + } + return query; + }; + + this.refreshMails = function () { + var url = this.attr.mailsResource + '?q=' + escaped(this.attr.lastQuery) + '&p=' + this.attr.currentPage + '&w=' + this.attr.pageSize; + + this.attr.lastQuery = this.excludeTrashedEmailsForDraftsAndSent(this.attr.lastQuery); + + monitoredAjax(this, url, { dataType: 'json' }) + .done(function (data) { + this.attr.numPages = Math.ceil(data.stats.total / this.attr.pageSize); + this.trigger(document, events.mails.available, _.merge({tag: this.attr.currentTag, forSearch: this.attr.lastQuery }, this.parseMails(data))); + }.bind(this)) + .fail(function () { + this.trigger(document, events.ui.userAlerts.displayMessage, { message: i18n.t('failed-fetch-messages'), class: 'error' }); + }.bind(this)); + }; + + function createSingleMailUrl(mailsResource, ident) { + return mailsResource + '/' + ident; + } + + this.fetchSingle = function (event, data) { + var fetchUrl = createSingleMailUrl(this.attr.singleMailResource, data.mail); + + monitoredAjax(this, fetchUrl, { dataType: 'json' }) + .done(function (mail) { + if (_.isNull(mail)) { + this.trigger(data.caller, events.mail.notFound); + return; + } + + this.trigger(data.caller, events.mail.here, { mail: this.mailFromJSON(mail) }); + }.bind(this)); + }; + + this.previousPage = function () { + if (this.attr.currentPage > 1) { + this.updateCurrentPageNumber(this.attr.currentPage - 1); + this.refreshMails(); + } + }; + + this.nextPage = function () { + if (this.attr.currentPage < (this.attr.numPages)) { + this.updateCurrentPageNumber(this.attr.currentPage + 1); + this.refreshMails(); + } + }; + + this.updateCurrentPageNumber = function (newCurrentPage) { + this.attr.currentPage = newCurrentPage; + this.trigger(document, events.ui.page.changed, { + currentPage: this.attr.currentPage, + numPages: this.attr.numPages + }); + }; + + this.wantDraftReplyForMail = function (ev, data) { + if (!features.isEnabled('draftReply')) { + this.trigger(document, events.mail.draftReply.notFound); + return; + } + + monitoredAjax(this, '/draft_reply_for/' + data.ident, { dataType: 'json' }) + .done(function (mail) { + if (_.isNull(mail)) { + this.trigger(document, events.mail.draftReply.notFound); + return; + } + this.trigger(document, events.mail.draftReply.here, { mail: this.mailFromJSON(mail) }); + }.bind(this)); + }; + + this.after('initialize', function () { + that = this; + + if (features.isEnabled('tags')) { + this.on(events.mail.tags.update, this.updateTags); + } + + this.on(document, events.mail.draftReply.want, this.wantDraftReplyForMail); + this.on(document, events.mail.want, this.fetchSingle); + this.on(document, events.mail.read, this.readMail); + this.on(document, events.mail.unread, this.unreadMail); + this.on(document, events.mail.delete, this.deleteMail); + this.on(document, events.mail.deleteMany, this.deleteManyMails); + this.on(document, events.mail.recoverMany, this.recoverManyMails); + this.on(document, events.mail.archiveMany, this.archiveManyMails); + this.on(document, events.search.perform, this.newSearch); + this.on(document, events.ui.tag.selected, this.fetchByTag); + this.on(document, events.ui.tag.select, this.fetchByTag); + this.on(document, events.ui.mails.refresh, this.refreshMails); + this.on(document, events.ui.page.previous, this.previousPage); + this.on(document, events.ui.page.next, this.nextPage); + + this.fetchByTag(null, {tag: urlParams.getTag()}); + }); + } + } +); diff --git a/web-ui/public/js/services/model/mail.js b/web-ui/public/js/services/model/mail.js new file mode 100644 index 00000000..64a10c1c --- /dev/null +++ b/web-ui/public/js/services/model/mail.js @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(['helpers/contenttype'], function (contentType) { + 'use strict'; + function isSentMail() { + return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'SENT'; + } + + function isDraftMail() { + return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'DRAFTS'; + } + + function isInTrash() { + return _.has(this, 'mailbox') && this.mailbox.toUpperCase() === 'TRASH'; + } + + function setDraftReplyFor(ident) { + this.draft_reply_for = ident; + } + + function replyToAddress() { + return { + to: [this.replying.single], + cc: [] + }; + } + + function replyToAllAddress() { + return { + to: this.replying.all['to-field'], + cc: this.replying.all['cc-field'] + }; + } + + function getHeadersFromMailPart (rawBody) { + var lines, headerLines, endOfHeaders, headers; + + lines = rawBody.split('\n'); + endOfHeaders = _.indexOf(lines, ''); + headerLines = lines.slice(0, endOfHeaders); + + headers = _.map(headerLines, function (headerLine) { + return _.map(headerLine.split(':'), function(elem){return elem.trim();}); + }); + + return _.object(headers); + } + + function getBodyFromMailPart (rawBody) { + var lines, endOfHeaders; + + lines = rawBody.split('\n'); + endOfHeaders = _.indexOf(lines, ''); + + return lines.slice(endOfHeaders + 1).join('\n'); + } + + function parseWithHeaders(rawBody) { + return {headers: getHeadersFromMailPart(rawBody), body: getBodyFromMailPart(rawBody)}; + } + + function getMailMultiParts () { + var mediaType = this.getMailMediaType(); + var boundary = '--' + mediaType.params.boundary + '\n'; + var finalBoundary = '--' + mediaType.params.boundary + '--'; + + var bodyParts = this.body.split(finalBoundary)[0].split(boundary); + + bodyParts = _.reject(bodyParts, function(bodyPart) { return _.isEmpty(bodyPart.trim()); }); + + return _.map(bodyParts, parseWithHeaders); + } + + function getMailMediaType () { + return new contentType.MediaType(this.header.content_type); + } + + function isMailMultipartAlternative () { + return this.getMailMediaType().type === 'multipart/alternative'; + } + + function availableBodyPartsContentType () { + var bodyParts = this.getMailMultiParts(); + + return _.pluck(_.pluck(bodyParts, 'headers'), 'Content-Type'); + } + + function getMailPartByContentType (contentType) { + var bodyParts = this.getMailMultiParts(); + + return _.findWhere(bodyParts, {headers: {'Content-Type': contentType}}); + } + + return { + create: function (mail) { + if (!mail) { return; } + + mail.isSentMail = isSentMail; + mail.isDraftMail = isDraftMail; + mail.isInTrash = isInTrash; + mail.setDraftReplyFor = setDraftReplyFor; + mail.replyToAddress = replyToAddress; + mail.replyToAllAddress = replyToAllAddress; + mail.getMailMediaType = getMailMediaType; + mail.isMailMultipartAlternative = isMailMultipartAlternative; + mail.getMailMultiParts = getMailMultiParts; + mail.availableBodyPartsContentType = availableBodyPartsContentType; + mail.getMailPartByContentType = getMailPartByContentType; + return mail; + } + }; +}); diff --git a/web-ui/public/js/services/recover_service.js b/web-ui/public/js/services/recover_service.js new file mode 100644 index 00000000..d7d9cdc9 --- /dev/null +++ b/web-ui/public/js/services/recover_service.js @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['flight/lib/component', 'page/events', 'views/i18n'], function (defineComponent, events, i18n) { + 'use strict'; + + return defineComponent(function() { + + this.recoverManyEmails = function (event, data) { + var emails = _.values(data.checkedMails); + + this.trigger(document, events.mail.recoverMany, { + mails: emails, + successMessage: i18n.t('Your messages were moved to inbox!') + }); + + }; + + this.after('initialize', function () { + this.on(document, events.ui.mail.recoverMany, this.recoverManyEmails); + }); + + }); +}); diff --git a/web-ui/public/js/style_guide/main.js b/web-ui/public/js/style_guide/main.js new file mode 100644 index 00000000..32c213cf --- /dev/null +++ b/web-ui/public/js/style_guide/main.js @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +$(document).ready(function(){ + 'use strict'; + $('a[href*=#]').click(function() { + if (location.pathname.replace(/^\//,'') === this.pathname.replace(/^\//,'') && + location.hostname === this.hostname) { + var $target = $(this.hash); + $target = $target.length && $target || + $('[name=' + this.hash.slice(1) +']'); + if ($target.length) { + var targetOffset = $target.offset().top; + $('html,body') + .animate({scrollTop: targetOffset}, 500); + return false; + } + } + }); +}); diff --git a/web-ui/public/js/tags/data/tags.js b/web-ui/public/js/tags/data/tags.js new file mode 100644 index 00000000..31703b2a --- /dev/null +++ b/web-ui/public/js/tags/data/tags.js @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(['flight/lib/component', 'page/events', 'helpers/monitored_ajax', 'mixins/with_feature_toggle', 'mixins/with_auto_refresh'], function (defineComponent, events, monitoredAjax, withFeatureToggle, withAutoRefresh) { + 'use strict'; + + var DataTags = defineComponent(dataTags, withFeatureToggle('tags', function() { + $(document).trigger(events.ui.mails.refresh); + }), withAutoRefresh('refreshTags')); + + DataTags.all = { + name: 'all', + ident: '8752888923742657436', + query: 'in:all', + default: true, + counts:{ + total:0, + read:0, + starred:0, + replied:0 + } + }; + + function dataTags() { + function sendTagsBackTo(on) { + return function(data) { + data.push(DataTags.all); + on.trigger(document, events.tags.received, {tags: data}); + }; + } + + this.defaultAttrs({ + tagsResource: '/tags' + }); + + this.fetchTags = function(event, params) { + monitoredAjax(this, this.attr.tagsResource) + .done(sendTagsBackTo(this)); + }; + + this.refreshTags = function() { + var notTriggeredByEvent = null; + this.fetchTags(notTriggeredByEvent); + }; + + this.after('initialize', function () { + this.on(document, events.tags.want, this.fetchTags); + this.on(document, events.mail.sent, this.fetchTags); + }); + } + + return DataTags; +}); diff --git a/web-ui/public/js/tags/ui/tag.js b/web-ui/public/js/tags/ui/tag.js new file mode 100644 index 00000000..37814cfc --- /dev/null +++ b/web-ui/public/js/tags/ui/tag.js @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define( + [ + 'flight/lib/component', + 'views/templates', + 'page/events', + 'views/i18n' + ], + + function (defineComponent, templates, events, i18n) { + 'use strict'; + + var Tag = defineComponent(tag); + + Tag.appendedTo = function (parent, data) { + var res = new this(); + res.renderAndAttach(parent, data); + return res; + }; + + return Tag; + + function tag() { + + var ALWAYS_HIDE_BADGE_FOR = ['sent', 'trash', 'all']; + var TOTAL_BADGE = ['drafts']; + + this.displayBadge = function(tag) { + if(_.include(ALWAYS_HIDE_BADGE_FOR, tag.name)) { return false; } + if(this.badgeType(tag) === 'total') { + return tag.counts.total > 0; + } else { + return (tag.counts.total - tag.counts.read) > 0; + } + }; + + this.badgeType = function(tag) { + return _.include(TOTAL_BADGE, tag.name) ? 'total' : 'unread'; + }; + + this.doUnselect = function () { + this.$node.removeClass('selected'); + }; + + this.doSelect = function () { + this.$node.addClass('selected'); + }; + + this.selectTag = function (ev, data) { + this.attr.currentTag = data.tag; + if (data.tag === this.attr.tag.name) { + this.doSelect(); + } + else { + this.doUnselect(); + } + }; + + this.selectTagAll = function () { + this.selectTag(null, {tag: 'all'}); + }; + + this.viewFor = function (tag, template, currentTag) { + return template({ + tagName: tag.default ? i18n.t('tags.' + tag.name) : tag.name, + ident: this.hashIdent(tag.ident), + count: this.badgeType(tag) === 'total' ? tag.counts.total : (tag.counts.total - tag.counts.read), + displayBadge: this.displayBadge(tag), + badgeType: this.badgeType(tag), + icon: tag.icon, + selected: tag.name === currentTag ? 'selected' : '' + }); + }; + + this.decreaseReadCountIfMatchingTag = function (ev, data) { + var mailbox_and_tags = _.flatten([data.tags, data.mailbox]); + if (_.contains(mailbox_and_tags, this.attr.tag.name)) { + this.attr.tag.counts.read++; + this.$node.html(this.viewFor(this.attr.tag, templates.tags.tagInner, this.attr.currentTag)); + if (!_.isUndefined(this.attr.shortcut)) { + this.attr.shortcut.reRender(); + } + } + }; + + this.triggerSelect = function () { + this.trigger(document, events.ui.tag.select, { tag: this.attr.tag.name }); + + this.removeSearchingClass(); + }; + + this.addSearchingClass = function() { + if (this.attr.tag.name === 'all'){ + this.$node.addClass('searching'); + } + }; + + this.hashIdent = function(ident) { + if (typeof ident === 'undefined') { + return ''; + } + if (typeof ident === 'number') { + return ident; + } + if (ident.match(/^[a-zA-Z0-9]+$/)) { + return ident; + } + + /*jslint bitwise: true */ + return Math.abs(String(ident).split('').reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a;},0)); + }; + + this.removeSearchingClass = function() { + if (this.attr.tag.name === 'all'){ + this.$node.removeClass('searching'); + } + }; + + this.after('initialize', function () { + this.on('click', this.triggerSelect); + this.on(document, events.mail.read, this.decreaseReadCountIfMatchingTag); + this.on(document, events.search.perform, this.addSearchingClass); + this.on(document, events.search.empty, this.removeSearchingClass); + + this.on(document, events.ui.tag.select, this.selectTag); + this.on(document, events.search.perform, this.selectTagAll); + this.on(document, events.search.empty, this.selectTagAll); + }); + + this.renderAndAttach = function (parent, data) { + var rendered = this.viewFor(data.tag, templates.tags.tag, data.currentTag); + parent.append(rendered); + this.initialize('#tag-' + this.hashIdent(data.tag.ident), data); + this.on(parent, events.tags.teardown, this.teardown); + }; + } + } +); diff --git a/web-ui/public/js/tags/ui/tag_base.js b/web-ui/public/js/tags/ui/tag_base.js new file mode 100644 index 00000000..9dc1ccbb --- /dev/null +++ b/web-ui/public/js/tags/ui/tag_base.js @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(['views/i18n', 'page/events'], function(i18n, events) { + 'use strict'; + + function tagBase() { + var ALWAYS_HIDE_BADGE_FOR = ['sent', 'trash', 'all']; + var TOTAL_BADGE = ['drafts']; + + this.displayBadge = function(tag) { + if(_.include(ALWAYS_HIDE_BADGE_FOR, tag.name)) { return false; } + if(this.badgeType(tag) === 'total') { + return tag.counts.total > 0; + } else { + return (tag.counts.total - tag.counts.read) > 0; + } + }; + + this.badgeType = function(tag) { + return _.include(TOTAL_BADGE, tag.name) ? 'total' : 'unread'; + }; + + this.doUnselect = function () { + this.$node.removeClass('selected'); + }; + + this.doSelect = function () { + this.$node.addClass('selected'); + }; + + this.selectTag = function (ev, data) { + this.attr.currentTag = data.tag; + if (data.tag === this.attr.tag.name) { + this.doSelect(); + } + else { + this.doUnselect(); + } + }; + + this.selectTagAll = function () { + this.selectTag(null, {tag: 'all'}); + }; + + this.after('initialize', function () { + this.on(document, events.ui.tag.select, this.selectTag); + this.on(document, events.search.perform, this.selectTagAll); + this.on(document, events.search.empty, this.selectTagAll); + }); + } + + return tagBase; + +}); diff --git a/web-ui/public/js/tags/ui/tag_list.js b/web-ui/public/js/tags/ui/tag_list.js new file mode 100644 index 00000000..a2172c6d --- /dev/null +++ b/web-ui/public/js/tags/ui/tag_list.js @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'tags/ui/tag', + 'views/templates', + 'page/events', + 'page/router/url_params' + ], + + function(defineComponent, Tag, templates, events, urlParams) { + 'use strict'; + + var ICON_FOR = { + 'inbox': 'inbox', + 'sent': 'send', + 'drafts': 'pencil', + 'trash': 'trash-o', + 'all': 'archive' + }; + + var ORDER = { + 'inbox': '0', + 'sent': '1', + 'drafts': '2', + 'trash': '3', + 'all': '4' + }; + + return defineComponent(tagList); + + function tagOrder(nm) { + return ORDER[nm.name] || '999' + nm.name; + } + + function tagList() { + this.defaultAttrs({ + defaultTagList: '#default-tag-list', + customTagList: '#custom-tag-list' + }); + + function renderTag(tag, defaultList, customList) { + var list = tag.default ? defaultList : customList; + + var tagComponent = Tag.appendedTo(list, {tag: tag, currentTag: this.getCurrentTag()}); + } + + function resetTagList(lists) { + _.each(lists, function (list) { + this.trigger(list, events.tags.teardown); + list.empty(); + }.bind(this)); + + } + + this.renderTagList = function(tags) { + var defaultList = this.select('defaultTagList'); + var customList = this.select('customTagList'); + + resetTagList.call(this, [defaultList, customList]); + + tags.forEach(function (tag) { + renderTag.call(this, tag, defaultList, customList); + }.bind(this)); + }; + + this.displayTags = function(ev, data) { + this.renderTagList(_.sortBy(data.tags, tagOrder)); + }; + + this.getCurrentTag = function () { + return this.attr.currentTag || urlParams.getTag(); + }; + + this.updateCurrentTag = function(ev, data) { + this.attr.currentTag = data.tag; + }; + + this.renderTagListTemplate = function () { + this.$node.html(templates.tags.tagList()); + }; + + this.after('initialize', function() { + this.on(document, events.tags.received, this.displayTags); + this.on(document, events.ui.tag.select, this.updateCurrentTag); + this.renderTagListTemplate(); + }); + } + } +); diff --git a/web-ui/public/js/user_alerts/ui/user_alerts.js b/web-ui/public/js/user_alerts/ui/user_alerts.js new file mode 100644 index 00000000..e944a7a5 --- /dev/null +++ b/web-ui/public/js/user_alerts/ui/user_alerts.js @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'mixins/with_hide_and_show', + 'page/events' + ], + + function(defineComponent, templates, withHideAndShow, events) { + 'use strict'; + + return defineComponent(userAlerts, withHideAndShow); + + function userAlerts() { + this.defaultAttrs({ + dismissTimeout: 3000 + }); + + this.render = function(message) { + this.$node.html(templates.userAlerts.message(message)); + this.show(); + setTimeout(this.hide.bind(this), this.attr.dismissTimeout); + }; + + + this.displayMessage = function(ev, data) { + this.render({ + message: { + content: data.message, + class: 'message-panel__growl--' + (data.class || 'success') + } + }); + }; + + this.after('initialize', function() { + this.on(document, events.ui.userAlerts.displayMessage, this.displayMessage); + }); + } + } +); + diff --git a/web-ui/public/js/user_settings/data/user_settings.js b/web-ui/public/js/user_settings/data/user_settings.js new file mode 100644 index 00000000..dac29cec --- /dev/null +++ b/web-ui/public/js/user_settings/data/user_settings.js @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2015 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'helpers/monitored_ajax', + 'page/events' + ], + function (defineComponent, monitoredAjax, events) { + 'use strict'; + + return defineComponent(function() { + this.defaultAttrs({ + userSettingsResource: '/user-settings', + userSettings: {} + }); + + this.sendInfo = function() { + this.trigger(document, events.userSettings.here, this.attr.userSettings); + }; + + this.getUserSettings = function() { + var getUserSettingsSuccess = function (userSettings) { + this.attr.userSettings = userSettings; + }; + + monitoredAjax(this, this.attr.userSettingsResource, { + type: 'GET', + contentType: 'application/json; charset=utf-8' + }).done(getUserSettingsSuccess.bind(this)); + }; + + this.after('initialize', function() { + this.getUserSettings(); + this.on(document, events.userSettings.getInfo, this.sendInfo); + }); + }); +}); diff --git a/web-ui/public/js/user_settings/ui/user_settings_box.js b/web-ui/public/js/user_settings/ui/user_settings_box.js new file mode 100644 index 00000000..d3de23ed --- /dev/null +++ b/web-ui/public/js/user_settings/ui/user_settings_box.js @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'features', + 'views/templates', + 'page/events', + 'helpers/monitored_ajax' + ], function (defineComponent, features, templates, events, monitoredAjax) { + + 'use strict'; + + return defineComponent(function () { + this.defaultAttrs({ + close: '#user-settings-close', + userSettingsBoxContainer: '#user-settings-box' + }); + + this.render = function (event, userSettings) { + if (features.isLogoutEnabled()) { + this.$node.addClass('extra-bottom-space'); + } + + this.$node.addClass('arrow-box'); + this.$node.html(templates.page.userSettingsBox(userSettings)); + + this.on(this.attr.close, 'click', function() { + this.trigger(document, events.userSettings.destroyPopup); + }); + + this.on(document, 'click', function(e) { + var userSettingsBoxContainer = $(this.attr.userSettingsBoxContainer).get(0); + var target = e.target || e.srcElement; + + if (target !== userSettingsBoxContainer && !isChildOf(target, userSettingsBoxContainer)) { + this.destroy(); + } + }); + + function isChildOf(child, parent) { + if (child.parentNode === parent) { + return true; + } else if (child.parentNode === null) { + return false; + } else { + return isChildOf(child.parentNode, parent); + } + } + }; + + this.destroy = function () { + this.$node.remove(); + this.teardown(); + }; + + this.after('initialize', function () { + this.on(document, events.userSettings.here, this.render); + this.on(document, events.userSettings.destroyPopup, this.destroy); + this.trigger(document, events.userSettings.getInfo); + }); + }); +}); diff --git a/web-ui/public/js/user_settings/ui/user_settings_icon.js b/web-ui/public/js/user_settings/ui/user_settings_icon.js new file mode 100644 index 00000000..a6385dc1 --- /dev/null +++ b/web-ui/public/js/user_settings/ui/user_settings_icon.js @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define( + [ + 'flight/lib/component', + 'views/templates', + 'page/events', + 'user_settings/ui/user_settings_box' + ], function (defineComponent, templates, events, userSettingsBox) { + 'use strict'; + + return defineComponent(function () { + this.defaultAttrs({ + userSettingsBox: $('#user-settings-box') + }); + + this.render = function () { + this.$node.html(templates.page.userSettingsIcon()); + }; + + this.toggleUserSettingsBox = function() { + if(this.attr.userSettingsBox.children().length === 0) { + var div = $('
            '); + $(this.attr.userSettingsBox).append(div); + userSettingsBox.attachTo(div); + this.attr.userSettingsInfo = userSettingsBox; + } else { + this.trigger(document, events.userSettings.destroyPopup); + } + }; + + this.triggerToggleUserSettingsBox = function(e) { + this.trigger(document, events.ui.userSettingsBox.toggle); + e.stopPropagation(); + }; + + this.after('initialize', function () { + this.render(); + this.on('click', this.triggerToggleUserSettingsBox); + this.on(document, events.ui.userSettingsBox.toggle, this.toggleUserSettingsBox); + }); + }); +}); diff --git a/web-ui/public/js/views/i18n.js b/web-ui/public/js/views/i18n.js new file mode 100644 index 00000000..29a1beca --- /dev/null +++ b/web-ui/public/js/views/i18n.js @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ +define(['i18next', + 'i18nextXHRBackend', + 'i18nextBrowserLanguageDetector'], +function(i18n, i18n_backend, I18n_detector) { + 'use strict'; + + var detector = new I18n_detector(); + var detect = detector.detect.bind(detector); + + detector.detect = function(detectionOrder) { + var result = detect(detectionOrder); + return result.replace('-', '_'); + }; + + function t(i18n_key, options) { + var result = i18n.t(i18n_key, options); + var safe_string = new Handlebars.SafeString(result); + return safe_string.string; + } + + function loaded(callback) { + i18n.on('loaded', function(loaded) { + callback(); + }); + } + + function init(path) { + i18n + .use(i18n_backend) + .use(detector) + .init({ + fallbackLng: 'en_US', + backend: { + loadPath: path + 'locales/{{lng}}/{{ns}}.json' + } + }); + // Handlebars.registerHelper('t', self.bind(self)); + Handlebars.registerHelper('t', t); + } + + return { + t: t, + init: init, + loaded: loaded + }; +}); diff --git a/web-ui/public/js/views/recipientListFormatter.js b/web-ui/public/js/views/recipientListFormatter.js new file mode 100644 index 00000000..0b887142 --- /dev/null +++ b/web-ui/public/js/views/recipientListFormatter.js @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(function() { + 'use strict'; + Handlebars.registerHelper('formatRecipients', function (header) { + function wrapWith(begin, end) { + return function (x) { + return begin + Handlebars.Utils.escapeExpression(x) + end; + }; + } + + var to = _.map(header.to, wrapWith('', '')); + var cc = _.map(header.cc, wrapWith('cc: ', '')); + var bcc = _.map(header.bcc, wrapWith('bcc: ', '')); + + return new Handlebars.SafeString(to.concat(cc, bcc).join(', ')); + }); +}); diff --git a/web-ui/public/js/views/templates.js b/web-ui/public/js/views/templates.js new file mode 100644 index 00000000..8792f8cb --- /dev/null +++ b/web-ui/public/js/views/templates.js @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2014 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +define(['hbs/templates'], function (templates) { + 'use strict'; + + var Templates = { + compose: { + box: window.Pixelated['public/templates/compose/compose_box.hbs'], + inlineBox: window.Pixelated['public/templates/compose/inline_box.hbs'], + replySection: window.Pixelated['public/templates/compose/reply_section.hbs'], + recipientInput: window.Pixelated['public/templates/compose/recipient_input.hbs'], + fixedRecipient: window.Pixelated['public/templates/compose/fixed_recipient.hbs'], + recipients: window.Pixelated['public/templates/compose/recipients.hbs'], + feedback: window.Pixelated['public/templates/compose/feedback_box.hbs'], + attachmentsList: window.Pixelated['public/templates/compose/attachments_list.hbs'], + attachmentItem: window.Pixelated['public/templates/compose/attachment_item.hbs'], + attachmentUploadItem: window.Pixelated['public/templates/compose/attachment_upload_item.hbs'], + uploadAttachmentFailed: window.Pixelated['public/templates/compose/upload_attachment_failed.hbs'] + }, + tags: { + tagList: window.Pixelated['public/templates/tags/tag_list.hbs'], + tag: window.Pixelated['public/templates/tags/tag.hbs'], + tagInner: window.Pixelated['public/templates/tags/tag_inner.hbs'], + shortcut: window.Pixelated['public/templates/tags/shortcut.hbs'] + }, + userAlerts: { + message: window.Pixelated['public/templates/user_alerts/message.hbs'] + }, + mails: { + single: window.Pixelated['public/templates/mails/single.hbs'], + fullView: window.Pixelated['public/templates/mails/full_view.hbs'], + mailActions: window.Pixelated['public/templates/mails/mail_actions.hbs'], + draft: window.Pixelated['public/templates/mails/draft.hbs'], + sent: window.Pixelated['public/templates/mails/sent.hbs'], + trash: window.Pixelated['public/templates/mails/trash.hbs'] + }, + mailActions: { + actionsBox: window.Pixelated['public/templates/mail_actions/actions_box.hbs'], + trashActionsBox: window.Pixelated['public/templates/mail_actions/trash_actions_box.hbs'], + composeTrigger: window.Pixelated['public/templates/mail_actions/compose_trigger.hbs'], + refreshTrigger: window.Pixelated['public/templates/mail_actions/refresh_trigger.hbs'], + paginationTrigger: window.Pixelated['public/templates/mail_actions/pagination_trigger.hbs'] + }, + noMessageSelected: window.Pixelated['public/templates/compose/no_message_selected.hbs'], + noMailsAvailable: window.Pixelated['public/templates/compose/no_mails_available.hbs'], + search: { + trigger: window.Pixelated['public/templates/search/search_trigger.hbs'] + }, + page: { + userSettingsIcon: window.Pixelated['public/templates/page/user_settings_icon.hbs'], + userSettingsBox: window.Pixelated['public/templates/page/user_settings_box.hbs'], + logout: window.Pixelated['public/templates/page/logout.hbs'], + logoutShortcut: window.Pixelated['public/templates/page/logout_shortcut.hbs'], + version: window.Pixelated['public/templates/page/version.hbs'] + }, + feedback: { + feedback: window.Pixelated['public/templates/feedback/feedback_trigger.hbs'] + } + }; + + Handlebars.registerPartial('tag_inner', Templates.tags.tagInner); + Handlebars.registerPartial('recipients', Templates.compose.recipients); + Handlebars.registerPartial('attachments_list', Templates.compose.attachmentsList); + Handlebars.registerPartial('attachments_upload', Templates.compose.attachmentsList); + Handlebars.registerPartial('attachment_item', Templates.compose.attachmentItem); + Handlebars.registerPartial('attachment_upload_item', Templates.compose.attachmentUploadItem); + Handlebars.registerPartial('uploadAttachmentFailed', Templates.compose.uploadAttachmentFailed); + + return Templates; +}); diff --git a/web-ui/public/locales/en_US/translation.json b/web-ui/public/locales/en_US/translation.json new file mode 100644 index 00000000..3e006156 --- /dev/null +++ b/web-ui/public/locales/en_US/translation.json @@ -0,0 +1,72 @@ +{ + "compose": "Compose", + "re": "Re", + "fwd": "Fwd", + "trash-single": "Your message was moved to trash!", + "trash-bulk": "Your messages were moved to trash!", + "your-message-was-archived": "Your message was archived", + "delete-single": "Your message was permanently deleted!", + "delete-bulk": "Your messages were permanently deleted!", + "draft-saving": "Saving to Drafts...", + "draft-saved": "Draft saved", + "recipients-not-valid": "One or more of the recipients are not valid emails", + "failed-change-tags": "Could not change mail tags", + "invalid-tag-name": "Invalid tag name", + "failed-delete-single": "Could not delete email", + "failed-delete-bulk": "Could not delete emails", + "failed-fetch-messages": "Could not fetch messages", + "failed-archive": "Could not archive emails", + "to": "to", + "cc": "CC", + "bcc": "BCC", + "body": "Body", + "subject": "Subject", + "send": "Send", + "reply": "Reply", + "reply-to-all": "Reply to all", + "delete-this-message": "Delete this message", + "mark-as-read": "Mark as read", + "mark-as-unread": "Mark as unread", + "delete": "Delete", + "archive": "Archive", + "nothing-selected": "Nothing selected", + "add-tag-placeholder": "Press Enter to add tag", + "no-subject": "", + "no-recipient": "", + "you": "you", + "encrypted": "Encrypted", + "not-encrypted": "Not encrypted", + "signed": "Verified sender", + "not-signed": "Not signed", + "sending-mail": "Sending...", + "trash-button": "Delete it", + "search-placeholder" : "Search...", + "search-results-for": "Search results for", + "forward": "Forward", + "feedback-placeholder": "Tell us what you liked, didn't like, what is missing and generally what you think about Pixelated.", + "user-account": "My Account", + "email-address": "Email address", + "public-key-fingerprint": "Public key fingerprint", + "version": "version", + "logout": "Logout", + "delete-permanently": "Delete Permanently", + "move-to-inbox": "Move to Inbox", + "reply-author-line": "On {{date}}, <{{from}}> wrote:\n", + "refresh": "refresh", + "click-to-remove": "Click to remove", + "no-results-for": "No results for", + "no-emails-in": "No emails in", + "error": { + "timeout": "A timeout occurred", + "general": "Problems talking to server", + "parse": "Got invalid response from server" + }, + "tags": { + "inbox": "Inbox", + "sent": "Sent", + "drafts": "Drafts", + "trash": "Trash", + "all": "All", + "tags": "Tags" + } +} diff --git a/web-ui/public/locales/pt_BR/translation.json b/web-ui/public/locales/pt_BR/translation.json new file mode 100644 index 00000000..ff766a2b --- /dev/null +++ b/web-ui/public/locales/pt_BR/translation.json @@ -0,0 +1,72 @@ +{ + "compose": "Escrever", + "re": "Res", + "fwd": "Enc", + "trash-single": "Sua mensagem foi movida para a lixeira!", + "trash-bulk": "Suas mensagens foram movidas para a lixeira!", + "your-message-was-archived": "Sua mensagem foi arquivada", + "delete-single": "Sua mensagem foi permanentemente deletada!", + "delete-bulk": "Suas mensagens foram permanentemente deletadas!", + "draft-saving": "Salvando rascunho...", + "draft-saved": "Rascunho salvo", + "recipients-not-valid": "Um ou mais destinatários não são emails válidos", + "failed-change-tags": "Não pode atualizar as tags do email", + "invalid-tag-name": "Nome inválido para tag", + "failed-delete-single": "Não pode deletar o email", + "failed-delete-bulk": "Não foi possível remover os emails", + "failed-fetch-messages": "Não pode receber as mensagens", + "failed-archive": "Não foi possível arquivar os emails", + "to": "para", + "cc": "CC", + "bcc": "CCO", + "body": "Mensagem", + "subject": "Assunto", + "send": "Enviar", + "reply": "Responder", + "reply-to-all": "Responder para todos", + "delete-this-message": "Deletar essa mensagem", + "mark-as-read": "Marcar como lida", + "mark-as-unread": "Marcar como não lida", + "delete": "Deletar", + "archive": "Arquivar", + "nothing-selected": "Nada selecionado", + "add-tag-placeholder": "Aperte enter para adicionar a tag", + "no-subject": "", + "no-recipient": "", + "you": "você", + "encrypted": "Criptografado", + "not-encrypted": "Não criptografado", + "signed": "Rementente verificado", + "not-signed": "Não assinado", + "sending-mail": "Enviando...", + "trash-button": "Deletar", + "search-placeholder" : "Pesquisar...", + "search-results-for": "Resultado da pesquisa por", + "forward": "Encaminhar", + "feedback-placeholder": "Nos diga o que gosta, não gosta, o que está faltando e o que pensa sobre o Pixelated.", + "user-account": "Opções de usuário", + "email-address": "Endereço de email", + "public-key-fingerprint": "Identificação da chave pública", + "version": "versão", + "logout": "Sair", + "delete-permanently": "Excluir permanentemente", + "move-to-inbox": "Mover para Caixa de Entrada", + "reply-author-line": "Em {{date}}, <{{from}}> escreveu:\n", + "refresh": "atualizar", + "click-to-remove": "Pressione para remover", + "no-results-for": "Sem resultados para", + "no-emails-in": "Nenhum email em", + "error": { + "timeout": "A operação excedeu o limite de tempo", + "general": "Problemas ao se comunicar com o servidor", + "parse": "Obteve uma resposta inválida do servidor" + }, + "tags": { + "inbox": "Caixa de Entrada", + "sent": "Enviadas", + "drafts": "Rascunhos", + "trash": "Lixeira", + "all": "Todas", + "tags": "Etiquetas" + } +} diff --git a/web-ui/public/locales/sv_SE/translation.json b/web-ui/public/locales/sv_SE/translation.json new file mode 100644 index 00000000..d4da0711 --- /dev/null +++ b/web-ui/public/locales/sv_SE/translation.json @@ -0,0 +1,42 @@ +{ + "compose": "Skriv nytt", + "re": "Sv", + "fwd": "VB", + "trash-single": "Ditt meddelande har flyttats till papperskorgen!", + "trash-bulk": "Ditt meddelande har arkiverats!", + "recipients-not-valid": "En eller flera mottagare är inte giltiga epost-adresser", + "failed-change-tags": "Kan inte ändra taggar", + "invalid-tag-name": "Ogiltigt taggnamn", + "failed-delete-single": "Kan inte ta bort meddelande", + "failed-fetch-messages": "Kan inte hämta meddelanden", + "to": "till", + "cc": "CC", + "bcc": "BCC", + "body": "Innehåll", + "subject": "Titel", + "send": "Skicka", + "reply": "Svara", + "reply-to-all": "Svara Alla", + "mark-as-read": "Markera som läst", + "delete": "Ta bort", + "archive": "Arkivera", + "nothing-selected": "INGET VALT", + "add-tag-placeholder": "Tryck retur för att skapa", + "no-subject": "", + "no-recipient": "", + "you": "du", + "encrypted": "krypterad", + "not-encrypted": "Meddelandet var läsbart medans det var på väg.", + "signed": "Certifierad avsändare.", + "not-signed": "Avsändaren kunde inte säkert identifieras.", + "search-placeholder" : "Sök...", + "search-results-for": "Sökresultat för", + "forward": "Vidarebefodra", + "tags": { + "inbox": "Inlåda", + "sent": "Skickat", + "drafts": "Utkast", + "trash": "Skräp", + "all": "Alla" + } +} diff --git a/web-ui/public/robots.txt b/web-ui/public/robots.txt new file mode 100644 index 00000000..6b0157e2 --- /dev/null +++ b/web-ui/public/robots.txt @@ -0,0 +1,3 @@ +# robotstxt.org + +User-agent: * \ No newline at end of file diff --git a/web-ui/public/sandbox.html b/web-ui/public/sandbox.html new file mode 100644 index 00000000..8325b0da --- /dev/null +++ b/web-ui/public/sandbox.html @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/web-ui/public/scss/_mixins.scss b/web-ui/public/scss/_mixins.scss new file mode 100644 index 00000000..d3aa0220 --- /dev/null +++ b/web-ui/public/scss/_mixins.scss @@ -0,0 +1,71 @@ +// SHARED MIXINS +@mixin btn-transition { + @include transition-property(background-color); + @include transition-duration(300ms); + @include transition-timing-function(ease-out); +} + +@mixin tooltip($top: 8px, $left: 40px) { + background: rgba(0, 0, 0, 0.7); + color: $white; + position: absolute; + z-index: 2; + left: $left; + top: $top; + font-size: 0.8rem; + padding: 2px 10px; + white-space: nowrap; + @include border-radius(2px); +} + +// FORM MIXINS +@mixin check-box { + background-color: $white; + border: 1px solid $light_gray; + padding: 7px; + margin: 3px 0; + cursor: pointer; + display: inline-block; + position: relative; + @include border-radius(2px); + @include appearance(none); + + &:focus { + outline: none; + border-color: $medium_dark_grey; + } + + &:active, &:checked:active { + } + + &:checked { + background-color: $contrast; + border: 1px solid darken($lighter_gray, 10%); + color: $dark_grey; + } + + &:checked:after { + content: '\2714'; + font-size: 1em; + position: absolute; + bottom: -2px; + left: 1px; + color: $navigation_background; + } +} + + +@mixin searching($top, $left, $color, $size){ + &.searching { + &:after { + font-family: FontAwesome; + content: "\f002"; + font-size: $size; + top: $top; + left: $left; + position: absolute; + color: $color; + text-shadow: -1px 0 $contrast, 0 1px $contrast, 1px 0 $contrast, 0 -1px $contrast; + } + } +} diff --git a/web-ui/public/scss/_others.scss b/web-ui/public/scss/_others.scss new file mode 100644 index 00000000..039d94bd --- /dev/null +++ b/web-ui/public/scss/_others.scss @@ -0,0 +1,72 @@ +.hidden { + display: none; +} + +.no-padding { + padding: 0; +} + +.text-right { + text-align: right; +} + +.search-highlight { + background-color: $search-highlight; +} + +button { + border: 1px solid transparent; + + i { + margin-left: 5px; + } + + &#trash-button { + background: $white; + border: 1px solid $medium_light_grey; + color: $medium_light_grey; + float: right; + margin-left: 5px; + + &:hover, &:focus { + background: $contrast; + } + } + + &.no-style { + background: transparent; + color: $medium_light_grey; + padding: 0; + margin: 0; + + i { + margin: 0; + padding: 0; + vertical-align: middle; + } + } +} + +section { + display: inline-block; + vertical-align: top; + height: 100vh; + overflow-y: scroll; + + &#left-pane { + background-color: $navigation_background; + color: white; + } + + &#middle-pane { + background: $white; + } + + &#right-pane { + padding: 0 10px 60px 0px; + background: $white; + box-shadow: -2px -2px 5px rgba(0, 0, 0, 0.12); + z-index: 2; + overflow-y: auto; + } +} diff --git a/web-ui/public/scss/base/_colors.scss b/web-ui/public/scss/base/_colors.scss new file mode 100644 index 00000000..17333ff9 --- /dev/null +++ b/web-ui/public/scss/base/_colors.scss @@ -0,0 +1,64 @@ +/* Pixelated Color Palette - don't change these! */ +$dark_slate_gray: #3E3A37; +$light_gray: #C2C2C2; +$lighter_blue: #91C2D1; +$light_blue: #3DABC4; +$dark_blue: #178CA6; +$bullet-blue: #5cacde; +$light_orange: #FF9C00; +$dark_orange: #FF7902; + + +/* Side nav background color */ +$navigation_background: $dark_slate_gray; + +/* Action buttons and links */ +$action_buttons: $light_blue; + +/* Primary Highlight*/ +$primary_highlight: $light_orange; + +/* Logo color*/ +$logo_color: $light_orange; + +/* Unread count dialog bubble background color */ +$secondary_callout: darken($primary_highlight, 5); + +/* Grayscale */ +$contrast: #EEE; +$white: #FFF; +$dark_white: #FAFAFA; +$lighter_gray: #DDD; +$medium_light_grey: #999; +$medium_grey: #777; +$medium_dark_grey: #666; +$dark_grey: #333; +$black: #000; +$top_pane: $contrast; +$total_count_bg: #C0B9B9; +$background_dropdown_grey: #f0f0f0; + +$background_light_grey: #F5F5F5; +$border_light_grey: #D9D9D9; + +/* Feedback to Users */ +$warning: #F7E8AF; +$search-highlight: #FFEF29; + +/* Light gray indicator icons */ +$indicator_icon_color: $light_gray; + +$error: #D93C38; +$attention: #F6A41C; +$success: #50BA5B; + +$will_be_encrypted: $success; +$wont_be_encrypted: $attention; +$recipients_font_color: #828282; + +/* Attachments */ +$attachment_text: #555; +$attachment_icon: lighten($attachment_text, 30); +$attachment_size: lighten($attachment_text, 30); +$attachment_area_background: #F5F5F5; +$attachment_area_border: #D9D9D9; diff --git a/web-ui/public/scss/base/_fonts.scss b/web-ui/public/scss/base/_fonts.scss new file mode 100644 index 00000000..dfc56dd8 --- /dev/null +++ b/web-ui/public/scss/base/_fonts.scss @@ -0,0 +1,68 @@ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + src: local('Open Sans Light'), local('OpenSans-Light'), url('/assets/fonts/OpenSans-Light.woff') format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans'), local('OpenSans'), url('/assets/fonts/OpenSans.woff') format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + src: local('Open Sans Semibold'), local('OpenSans-Semibold'), url('/assets/fonts/OpenSans-Semibold.woff') format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 700; + src: local('Open Sans Bold'), local('OpenSans-Bold'), url('/assets/fonts/OpenSans-Bold.woff') format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 800; + src: local('Open Sans Extrabold'), local('OpenSans-Extrabold'), url('/assets/fonts/OpenSans-Extrabold.woff') format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + src: local('Open Sans Light Italic'), local('OpenSansLight-Italic'), url('/assets/fonts/OpenSansLight-Italic.woff') format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + src: local('Open Sans Italic'), local('OpenSans-Italic'), url('/assets/fonts/OpenSans-Italic.woff') format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + src: local('Open Sans Semibold Italic'), local('OpenSans-SemiboldItalic'), url('/assets/fonts/OpenSans-SemiboldItalic.woff') format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 700; + src: local('Open Sans Bold Italic'), local('OpenSans-BoldItalic'), url('/assets/fonts/OpenSans-BoldItalic.woff') format('woff'); +} +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 800; + src: local('Open Sans Extrabold Italic'), local('OpenSans-ExtraboldItalic'), url('/assets/fonts/OpenSans-ExtraboldItalic.woff') format('woff'); +} + +@font-face { + font-family: 'icomoon'; + font-style: normal; + font-weight: 400; + src: url('/assets/fonts/icomoon.woff') format('woff'), url('/assets/fonts/icomoon.ttf') format('truetype'), ; +} + diff --git a/web-ui/public/scss/base/_scaffolding.scss b/web-ui/public/scss/base/_scaffolding.scss new file mode 100644 index 00000000..b8b5fa3b --- /dev/null +++ b/web-ui/public/scss/base/_scaffolding.scss @@ -0,0 +1,10 @@ +html { + height: 100% ; +} + +body { + min-height: 100% ; + overflow: hidden; + background: $white; +} + diff --git a/web-ui/public/scss/mixins/_position-helpers.scss b/web-ui/public/scss/mixins/_position-helpers.scss new file mode 100644 index 00000000..254bfc6c --- /dev/null +++ b/web-ui/public/scss/mixins/_position-helpers.scss @@ -0,0 +1,9 @@ +@mixin absolute-center-unknown-height-width() { + margin: auto; + position: absolute; + left: 50%; + top: 50%; + -ms-transform: translate(-50%, -50%); + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} diff --git a/web-ui/public/scss/mixins/_tags.scss b/web-ui/public/scss/mixins/_tags.scss new file mode 100644 index 00000000..9bb287ea --- /dev/null +++ b/web-ui/public/scss/mixins/_tags.scss @@ -0,0 +1,110 @@ +$tags-font-size: 0.6rem; + +@mixin tags { + & > * { + display: inline; + } + + &-tag { + font-size: $tags-font-size; + font-weight: 700; + background-color: $dark_blue; + color: white; + padding: 2px 4px; + margin: 0 1px; + border-radius: 2px; + } +} + +@mixin tags-editable { + @include tags; + + &-tag:hover { + text-decoration: line-through; + cursor: pointer; + position: relative; + + &:before { + @include tooltip(130%, 25%); + + content: "click to remove"; + text-transform: lowercase; + } + } + + &-label { + vertical-align: bottom; + color: $light_gray; + } + + &-new-button { + font-size: $tags-font-size; + padding: 0; + background: transparent; + border-radius: 2px; + padding: 2px; + + &:hover { + opacity: 1; + background: $lighter_gray; + } + } + + &-name-input { + opacity: 0.6; + transition: background-color 150ms ease-out; + + &:hover { + opacity: 1; + } + + // twitter typeahead classes. those are set via JS, with relatively high specificity, + // hence box-model-related properties are repeated + // https://github.com/twitter/typeahead.js/blob/master/doc/jquery_typeahead.md#class-names + + $suggestion-border: 1px solid darken($contrast, 5%); + $input-field-padding: 1px 5px; + $input-field-margin: 2px; + + & * .tt-input { + border-radius: $input-field-margin; + padding: $input-field-padding; + margin-top: 2px; + font-size: $tags-font-size; + } + + & * .tt-hint { + color: $medium_light_grey; + padding: $input-field-padding; + margin-top: $input-field-margin; + font-size: $tags-font-size; + background: transparent; + } + + & * .tt-dropdown-menu { + min-width: 250px; + padding: 0; + font-size: $tags-font-size; + background-color: $contrast; + border: $suggestion-border; + } + + & * .tt-suggestion { + padding: 5px 10px; + font-size: $tags-font-size; + border-bottom: $suggestion-border; + + &:last-child { + border-bottom: none; + } + + p { + margin: 0; + } + } + + & * .tt-cursor { + background-color: $white; + } + } +} diff --git a/web-ui/public/scss/sandbox.scss b/web-ui/public/scss/sandbox.scss new file mode 100644 index 00000000..3c1be358 --- /dev/null +++ b/web-ui/public/scss/sandbox.scss @@ -0,0 +1,27 @@ +$search-highlight: #FFEF29; + +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: local('Open Sans'), local('OpenSans'), url('/sandbox/fonts/OpenSans.woff') format('woff'); +} + +body { + font-family: "Open Sans", "Microsoft YaHei", "Hiragino Sans GB", "Hiragino Sans GB W3", "微软雅黑", "Helvetica Neue", Arial, sans-serif; + font-size: 13px; + line-height: 1.2em; + background: white; + color: #333; + padding: 0; + margin: 0; + font-weight: normal; + -webkit-font-smoothing: antialiased; + font-style: normal; + box-sizing: border-box; + word-wrap: break-word; +} + +.search-highlight { + background-color: $search-highlight; +} diff --git a/web-ui/public/scss/style.scss b/web-ui/public/scss/style.scss new file mode 100644 index 00000000..e99ab194 --- /dev/null +++ b/web-ui/public/scss/style.scss @@ -0,0 +1,39 @@ +// vendor stylesheets and resets +@import "vendor/reset"; +@import "vendor/scut"; +@import "compass/css3"; +@import "vendor/foundation"; +@import "vendor/customfont"; + +// basic configuration +@import "base/fonts"; +@import "base/colors"; +@import "base/scaffolding"; + +// mixins +@import "mixins/position-helpers"; +@import "mixins/tags"; + +// TODO +@import "mixins"; + +// templates +@import "templates/no-content-placeholder"; +@import "templates/unread-count"; + +// views +@import "views/message-panel"; +@import "views/close-button"; +@import "views/no-message-selected"; +@import "views/no-mails-available"; +@import "views/read-view"; +@import "views/security-labels"; +@import "views/compose-view"; +@import "views/compose-button"; +@import "views/mail-list"; +@import "views/_action-bar.scss"; +@import "views/_navigation.scss"; + +// misc stuff +@import "others"; + diff --git a/web-ui/public/scss/templates/_no-content-placeholder.scss b/web-ui/public/scss/templates/_no-content-placeholder.scss new file mode 100644 index 00000000..c6807011 --- /dev/null +++ b/web-ui/public/scss/templates/_no-content-placeholder.scss @@ -0,0 +1,5 @@ +.no-content-placeholder { + @include absolute-center-unknown-height-width; + + color: $medium_dark_grey; +} diff --git a/web-ui/public/scss/templates/_unread-count.scss b/web-ui/public/scss/templates/_unread-count.scss new file mode 100644 index 00000000..f7852227 --- /dev/null +++ b/web-ui/public/scss/templates/_unread-count.scss @@ -0,0 +1,14 @@ +.mail-count { + background: $white; + border-radius: 50%; + border: 1px solid $white; + color: $white; + font-size: 0.7em; + font-weight: 700; + left: 0; + margin-left: 5px; + opacity: 0.95; + padding: 0px 5px 0; + position: absolute; + top: 1px; +} diff --git a/web-ui/public/scss/vendor/_customfont.scss b/web-ui/public/scss/vendor/_customfont.scss new file mode 100644 index 00000000..d72cca0f --- /dev/null +++ b/web-ui/public/scss/vendor/_customfont.scss @@ -0,0 +1,9 @@ +[class^="icon-"], [class*=" icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'icomoon' !important; + line-height: 1; +} + +.icon-px-sent:before { + content: "\e900"; +} diff --git a/web-ui/public/scss/vendor/_foundation.scss b/web-ui/public/scss/vendor/_foundation.scss new file mode 100644 index 00000000..7918cf26 --- /dev/null +++ b/web-ui/public/scss/vendor/_foundation.scss @@ -0,0 +1,2066 @@ +@import 'compass/css3'; + +meta { + &.foundation-version { + font-family: "/5.2.3/"; + } + &.foundation-mq-small { + font-family: "/only screen/"; + width: 0em; + } + &.foundation-mq-medium { + font-family: "/only screen and (min-width:40.063em)/"; + width: 40.063em; + } + &.foundation-mq-large { + font-family: "/only screen and (min-width:64.063em)/"; + width: 64.063em; + } + &.foundation-mq-xlarge { + font-family: "/only screen and (min-width:90.063em)/"; + width: 90.063em; + } + &.foundation-mq-xxlarge { + font-family: "/only screen and (min-width:120.063em)/"; + width: 120.063em; + } + &.foundation-data-attribute-namespace { + font-family: false; + } +} + +html, body { + height: 100%; +} + +* { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + &:before, &:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } +} + +html { + font-size: 100%; +} + +body { + font-family: "Open Sans", "Microsoft YaHei", "Hiragino Sans GB", "Hiragino Sans GB W3", "微软雅黑", "Helvetica Neue", Arial, sans-serif; + font-size: 13px; + line-height: 1.2em; + background: white; + color: #333; + padding: 0; + margin: 0; + font-weight: normal; + -webkit-font-smoothing: antialiased; + font-style: normal; + position: relative; + cursor: default; +} + +a:hover { + cursor: pointer; +} + +img { + max-width: 100%; + height: auto; + -ms-interpolation-mode: bicubic; +} + +#map_canvas { + img, embed, object { + max-width: none !important; + } +} + +.map_canvas { + img, embed, object { + max-width: none !important; + } +} + +.left { + float: left !important; +} + +.right { + float: right !important; +} + +.clearfix { + &:before { + content: " "; + display: table; + } + &:after { + content: " "; + display: table; + clear: both; + } +} + +.hide { + display: none; +} + +.antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +img { + display: inline-block; + vertical-align: middle; +} + +textarea { + height: auto; + min-height: 50px; + &:focus { + outline: none; + } +} + +select { + width: 100%; +} + +.row { + width: 100%; + margin-left: auto; + margin-right: auto; + margin-top: 0; + margin-bottom: 0; + &:before { + content: " "; + display: table; + } + &:after { + content: " "; + display: table; + clear: both; + } + &.collapse { + > { + .column, .columns { + padding-left: 0; + padding-right: 0; + } + } + .row { + margin-left: 0; + margin-right: 0; + } + } + .row { + width: auto; + margin-left: -0.9375em; + margin-right: -0.9375em; + margin-top: 0; + margin-bottom: 0; + max-width: none; + &:before { + content: " "; + display: table; + } + &:after { + content: " "; + display: table; + clear: both; + } + &.collapse { + width: auto; + margin: 0; + max-width: none; + &:before { + content: " "; + display: table; + } + &:after { + content: " "; + display: table; + clear: both; + } + } + } +} + +.column, .columns { + padding-left: 0.9375em; + padding-right: 0.9375em; + width: 100%; + float: left; +} + +@media only screen { + .small-push-0 { + position: relative; + left: 0%; + right: auto; + } + .small-pull-0 { + position: relative; + right: 0%; + left: auto; + } + .small-push-1 { + position: relative; + left: 8.33333%; + right: auto; + } + .small-pull-1 { + position: relative; + right: 8.33333%; + left: auto; + } + .small-push-2 { + position: relative; + left: 16.66667%; + right: auto; + } + .small-pull-2 { + position: relative; + right: 16.66667%; + left: auto; + } + .small-push-3 { + position: relative; + left: 25%; + right: auto; + } + .small-pull-3 { + position: relative; + right: 25%; + left: auto; + } + .small-push-4 { + position: relative; + left: 33.33333%; + right: auto; + } + .small-pull-4 { + position: relative; + right: 33.33333%; + left: auto; + } + .small-push-5 { + position: relative; + left: 41.66667%; + right: auto; + } + .small-pull-5 { + position: relative; + right: 41.66667%; + left: auto; + } + .small-push-6 { + position: relative; + left: 50%; + right: auto; + } + .small-pull-6 { + position: relative; + right: 50%; + left: auto; + } + .small-push-7 { + position: relative; + left: 58.33333%; + right: auto; + } + .small-pull-7 { + position: relative; + right: 58.33333%; + left: auto; + } + .small-push-8 { + position: relative; + left: 66.66667%; + right: auto; + } + .small-pull-8 { + position: relative; + right: 66.66667%; + left: auto; + } + .small-push-9 { + position: relative; + left: 75%; + right: auto; + } + .small-pull-9 { + position: relative; + right: 75%; + left: auto; + } + .small-push-10 { + position: relative; + left: 83.33333%; + right: auto; + } + .small-pull-10 { + position: relative; + right: 83.33333%; + left: auto; + } + .small-push-11 { + position: relative; + left: 91.66667%; + right: auto; + } + .small-pull-11 { + position: relative; + right: 91.66667%; + left: auto; + } + .column, .columns { + position: relative; + padding-left: 0.9375em; + padding-right: 0.9375em; + float: left; + } + .small-1 { + width: 8.33333%; + } + .small-2 { + width: 16.66667%; + } + .small-3 { + width: 25%; + } + .small-4 { + width: 33.33333%; + } + .small-5 { + width: 41.66667%; + } + .small-6 { + width: 50%; + } + .small-7 { + width: 58.33333%; + } + .small-8 { + width: 66.66667%; + } + .small-9 { + width: 75%; + } + .small-10 { + width: 83.33333%; + } + .small-11 { + width: 91.66667%; + } + .small-12 { + width: 100%; + } + [class*="column"] + [class*="column"] { + &:last-child { + float: right; + } + &.end { + float: left; + } + } + .small-offset-0 { + margin-left: 0% !important; + } + .small-offset-1 { + margin-left: 8.33333% !important; + } + .small-offset-2 { + margin-left: 16.66667% !important; + } + .small-offset-3 { + margin-left: 25% !important; + } + .small-offset-4 { + margin-left: 33.33333% !important; + } + .small-offset-5 { + margin-left: 41.66667% !important; + } + .small-offset-6 { + margin-left: 50% !important; + } + .small-offset-7 { + margin-left: 58.33333% !important; + } + .small-offset-8 { + margin-left: 66.66667% !important; + } + .small-offset-9 { + margin-left: 75% !important; + } + .small-offset-10 { + margin-left: 83.33333% !important; + } + .small-offset-11 { + margin-left: 91.66667% !important; + } + .small-reset-order { + margin-left: 0; + margin-right: 0; + left: auto; + right: auto; + float: left; + } + .column.small-centered, .columns.small-centered { + margin-left: auto; + margin-right: auto; + float: none !important; + } + .column.small-uncentered, .columns.small-uncentered { + margin-left: 0; + margin-right: 0; + float: left !important; + } + .column.small-uncentered.opposite, .columns.small-uncentered.opposite { + float: right; + } +} + +@media only screen and (min-width: 40.063em) { + .medium-push-0 { + position: relative; + left: 0%; + right: auto; + } + .medium-pull-0 { + position: relative; + right: 0%; + left: auto; + } + .medium-push-1 { + position: relative; + left: 8.33333%; + right: auto; + } + .medium-pull-1 { + position: relative; + right: 8.33333%; + left: auto; + } + .medium-push-2 { + position: relative; + left: 16.66667%; + right: auto; + } + .medium-pull-2 { + position: relative; + right: 16.66667%; + left: auto; + } + .medium-push-3 { + position: relative; + left: 25%; + right: auto; + } + .medium-pull-3 { + position: relative; + right: 25%; + left: auto; + } + .medium-push-4 { + position: relative; + left: 33.33333%; + right: auto; + } + .medium-pull-4 { + position: relative; + right: 33.33333%; + left: auto; + } + .medium-push-5 { + position: relative; + left: 41.66667%; + right: auto; + } + .medium-pull-5 { + position: relative; + right: 41.66667%; + left: auto; + } + .medium-push-6 { + position: relative; + left: 50%; + right: auto; + } + .medium-pull-6 { + position: relative; + right: 50%; + left: auto; + } + .medium-push-7 { + position: relative; + left: 58.33333%; + right: auto; + } + .medium-pull-7 { + position: relative; + right: 58.33333%; + left: auto; + } + .medium-push-8 { + position: relative; + left: 66.66667%; + right: auto; + } + .medium-pull-8 { + position: relative; + right: 66.66667%; + left: auto; + } + .medium-push-9 { + position: relative; + left: 75%; + right: auto; + } + .medium-pull-9 { + position: relative; + right: 75%; + left: auto; + } + .medium-push-10 { + position: relative; + left: 83.33333%; + right: auto; + } + .medium-pull-10 { + position: relative; + right: 83.33333%; + left: auto; + } + .medium-push-11 { + position: relative; + left: 91.66667%; + right: auto; + } + .medium-pull-11 { + position: relative; + right: 91.66667%; + left: auto; + } + .column, .columns { + position: relative; + padding-left: 0.9375em; + padding-right: 0.9375em; + float: left; + } + .medium-1 { + width: 8.33333%; + } + .medium-2 { + width: 16.66667%; + } + .medium-3 { + width: 25%; + } + .medium-4 { + width: 33.33333%; + } + .medium-5 { + width: 41.66667%; + } + .medium-6 { + width: 50%; + } + .medium-7 { + width: 58.33333%; + } + .medium-8 { + width: 66.66667%; + } + .medium-9 { + width: 75%; + } + .medium-10 { + width: 83.33333%; + } + .medium-11 { + width: 91.66667%; + } + .medium-12 { + width: 100%; + } + [class*="column"] + [class*="column"] { + &:last-child { + float: right; + } + &.end { + float: left; + } + } + .medium-offset-0 { + margin-left: 0% !important; + } + .medium-offset-1 { + margin-left: 8.33333% !important; + } + .medium-offset-2 { + margin-left: 16.66667% !important; + } + .medium-offset-3 { + margin-left: 25% !important; + } + .medium-offset-4 { + margin-left: 33.33333% !important; + } + .medium-offset-5 { + margin-left: 41.66667% !important; + } + .medium-offset-6 { + margin-left: 50% !important; + } + .medium-offset-7 { + margin-left: 58.33333% !important; + } + .medium-offset-8 { + margin-left: 66.66667% !important; + } + .medium-offset-9 { + margin-left: 75% !important; + } + .medium-offset-10 { + margin-left: 83.33333% !important; + } + .medium-offset-11 { + margin-left: 91.66667% !important; + } + .medium-reset-order { + margin-left: 0; + margin-right: 0; + left: auto; + right: auto; + float: left; + } + .column.medium-centered, .columns.medium-centered { + margin-left: auto; + margin-right: auto; + float: none !important; + } + .column.medium-uncentered, .columns.medium-uncentered { + margin-left: 0; + margin-right: 0; + float: left !important; + } + .column.medium-uncentered.opposite, .columns.medium-uncentered.opposite { + float: right; + } + .push-0 { + position: relative; + left: 0%; + right: auto; + } + .pull-0 { + position: relative; + right: 0%; + left: auto; + } + .push-1 { + position: relative; + left: 8.33333%; + right: auto; + } + .pull-1 { + position: relative; + right: 8.33333%; + left: auto; + } + .push-2 { + position: relative; + left: 16.66667%; + right: auto; + } + .pull-2 { + position: relative; + right: 16.66667%; + left: auto; + } + .push-3 { + position: relative; + left: 25%; + right: auto; + } + .pull-3 { + position: relative; + right: 25%; + left: auto; + } + .push-4 { + position: relative; + left: 33.33333%; + right: auto; + } + .pull-4 { + position: relative; + right: 33.33333%; + left: auto; + } + .push-5 { + position: relative; + left: 41.66667%; + right: auto; + } + .pull-5 { + position: relative; + right: 41.66667%; + left: auto; + } + .push-6 { + position: relative; + left: 50%; + right: auto; + } + .pull-6 { + position: relative; + right: 50%; + left: auto; + } + .push-7 { + position: relative; + left: 58.33333%; + right: auto; + } + .pull-7 { + position: relative; + right: 58.33333%; + left: auto; + } + .push-8 { + position: relative; + left: 66.66667%; + right: auto; + } + .pull-8 { + position: relative; + right: 66.66667%; + left: auto; + } + .push-9 { + position: relative; + left: 75%; + right: auto; + } + .pull-9 { + position: relative; + right: 75%; + left: auto; + } + .push-10 { + position: relative; + left: 83.33333%; + right: auto; + } + .pull-10 { + position: relative; + right: 83.33333%; + left: auto; + } + .push-11 { + position: relative; + left: 91.66667%; + right: auto; + } + .pull-11 { + position: relative; + right: 91.66667%; + left: auto; + } +} + +@media only screen and (min-width: 64.063em) { + .large-push-0 { + position: relative; + left: 0%; + right: auto; + } + .large-pull-0 { + position: relative; + right: 0%; + left: auto; + } + .large-push-1 { + position: relative; + left: 8.33333%; + right: auto; + } + .large-pull-1 { + position: relative; + right: 8.33333%; + left: auto; + } + .large-push-2 { + position: relative; + left: 16.66667%; + right: auto; + } + .large-pull-2 { + position: relative; + right: 16.66667%; + left: auto; + } + .large-push-3 { + position: relative; + left: 25%; + right: auto; + } + .large-pull-3 { + position: relative; + right: 25%; + left: auto; + } + .large-push-4 { + position: relative; + left: 33.33333%; + right: auto; + } + .large-pull-4 { + position: relative; + right: 33.33333%; + left: auto; + } + .large-push-5 { + position: relative; + left: 41.66667%; + right: auto; + } + .large-pull-5 { + position: relative; + right: 41.66667%; + left: auto; + } + .large-push-6 { + position: relative; + left: 50%; + right: auto; + } + .large-pull-6 { + position: relative; + right: 50%; + left: auto; + } + .large-push-7 { + position: relative; + left: 58.33333%; + right: auto; + } + .large-pull-7 { + position: relative; + right: 58.33333%; + left: auto; + } + .large-push-8 { + position: relative; + left: 66.66667%; + right: auto; + } + .large-pull-8 { + position: relative; + right: 66.66667%; + left: auto; + } + .large-push-9 { + position: relative; + left: 75%; + right: auto; + } + .large-pull-9 { + position: relative; + right: 75%; + left: auto; + } + .large-push-10 { + position: relative; + left: 83.33333%; + right: auto; + } + .large-pull-10 { + position: relative; + right: 83.33333%; + left: auto; + } + .large-push-11 { + position: relative; + left: 91.66667%; + right: auto; + } + .large-pull-11 { + position: relative; + right: 91.66667%; + left: auto; + } + .column, .columns { + position: relative; + padding-left: 0.9375em; + padding-right: 0.9375em; + float: left; + } + .large-1 { + width: 8.33333%; + } + .large-2 { + width: 16.66667%; + } + .large-3 { + width: 25%; + } + .large-4 { + width: 33.33333%; + } + .large-5 { + width: 41.66667%; + } + .large-6 { + width: 50%; + } + .large-7 { + width: 58.33333%; + } + .large-8 { + width: 66.66667%; + } + .large-9 { + width: 75%; + } + .large-10 { + width: 83.33333%; + } + .large-11 { + width: 91.66667%; + } + .large-12 { + width: 100%; + } + [class*="column"] + [class*="column"] { + &:last-child { + float: right; + } + &.end { + float: left; + } + } + .large-offset-0 { + margin-left: 0% !important; + } + .large-offset-1 { + margin-left: 8.33333% !important; + } + .large-offset-2 { + margin-left: 16.66667% !important; + } + .large-offset-3 { + margin-left: 25% !important; + } + .large-offset-4 { + margin-left: 33.33333% !important; + } + .large-offset-5 { + margin-left: 41.66667% !important; + } + .large-offset-6 { + margin-left: 50% !important; + } + .large-offset-7 { + margin-left: 58.33333% !important; + } + .large-offset-8 { + margin-left: 66.66667% !important; + } + .large-offset-9 { + margin-left: 75% !important; + } + .large-offset-10 { + margin-left: 83.33333% !important; + } + .large-offset-11 { + margin-left: 91.66667% !important; + } + .large-reset-order { + margin-left: 0; + margin-right: 0; + left: auto; + right: auto; + float: left; + } + .column.large-centered, .columns.large-centered { + margin-left: auto; + margin-right: auto; + float: none !important; + } + .column.large-uncentered, .columns.large-uncentered { + margin-left: 0; + margin-right: 0; + float: left !important; + } + .column.large-uncentered.opposite, .columns.large-uncentered.opposite { + float: right; + } + .push-0 { + position: relative; + left: 0%; + right: auto; + } + .pull-0 { + position: relative; + right: 0%; + left: auto; + } + .push-1 { + position: relative; + left: 8.33333%; + right: auto; + } + .pull-1 { + position: relative; + right: 8.33333%; + left: auto; + } + .push-2 { + position: relative; + left: 16.66667%; + right: auto; + } + .pull-2 { + position: relative; + right: 16.66667%; + left: auto; + } + .push-3 { + position: relative; + left: 25%; + right: auto; + } + .pull-3 { + position: relative; + right: 25%; + left: auto; + } + .push-4 { + position: relative; + left: 33.33333%; + right: auto; + } + .pull-4 { + position: relative; + right: 33.33333%; + left: auto; + } + .push-5 { + position: relative; + left: 41.66667%; + right: auto; + } + .pull-5 { + position: relative; + right: 41.66667%; + left: auto; + } + .push-6 { + position: relative; + left: 50%; + right: auto; + } + .pull-6 { + position: relative; + right: 50%; + left: auto; + } + .push-7 { + position: relative; + left: 58.33333%; + right: auto; + } + .pull-7 { + position: relative; + right: 58.33333%; + left: auto; + } + .push-8 { + position: relative; + left: 66.66667%; + right: auto; + } + .pull-8 { + position: relative; + right: 66.66667%; + left: auto; + } + .push-9 { + position: relative; + left: 75%; + right: auto; + } + .pull-9 { + position: relative; + right: 75%; + left: auto; + } + .push-10 { + position: relative; + left: 83.33333%; + right: auto; + } + .pull-10 { + position: relative; + right: 83.33333%; + left: auto; + } + .push-11 { + position: relative; + left: 91.66667%; + right: auto; + } + .pull-11 { + position: relative; + right: 91.66667%; + left: auto; + } +} + +.inline-list { + margin: 0 auto 1.0625rem auto; + margin-left: -1.375rem; + margin-right: 0; + padding: 0; + list-style: none; + overflow: hidden; + > li { + list-style: none; + float: left; + margin-left: 1.375rem; + display: block; + > * { + display: block; + } + } +} + +.text-left { + text-align: left !important; +} + +.text-right { + text-align: right !important; +} + +.text-center { + text-align: center !important; +} + +.text-justify { + text-align: justify !important; +} + +@media only screen and (max-width: 40em) { + .small-only-text-left { + text-align: left !important; + } + .small-only-text-right { + text-align: right !important; + } + .small-only-text-center { + text-align: center !important; + } + .small-only-text-justify { + text-align: justify !important; + } +} + +@media only screen { + .small-text-left { + text-align: left !important; + } + .small-text-right { + text-align: right !important; + } + .small-text-center { + text-align: center !important; + } + .small-text-justify { + text-align: justify !important; + } +} + +@media only screen and (min-width: 40.063em) and (max-width: 64em) { + .medium-only-text-left { + text-align: left !important; + } + .medium-only-text-right { + text-align: right !important; + } + .medium-only-text-center { + text-align: center !important; + } + .medium-only-text-justify { + text-align: justify !important; + } +} + +@media only screen and (min-width: 40.063em) { + .medium-text-left { + text-align: left !important; + } + .medium-text-right { + text-align: right !important; + } + .medium-text-center { + text-align: center !important; + } + .medium-text-justify { + text-align: justify !important; + } +} + +@media only screen and (min-width: 64.063em) and (max-width: 90em) { + .large-only-text-left { + text-align: left !important; + } + .large-only-text-right { + text-align: right !important; + } + .large-only-text-center { + text-align: center !important; + } + .large-only-text-justify { + text-align: justify !important; + } +} + +@media only screen and (min-width: 64.063em) { + .large-text-left { + text-align: left !important; + } + .large-text-right { + text-align: right !important; + } + .large-text-center { + text-align: center !important; + } + .large-text-justify { + text-align: justify !important; + } +} + +@media only screen and (min-width: 90.063em) and (max-width: 120em) { + .xlarge-only-text-left { + text-align: left !important; + } + .xlarge-only-text-right { + text-align: right !important; + } + .xlarge-only-text-center { + text-align: center !important; + } + .xlarge-only-text-justify { + text-align: justify !important; + } +} + +@media only screen and (min-width: 90.063em) { + .xlarge-text-left { + text-align: left !important; + } + .xlarge-text-right { + text-align: right !important; + } + .xlarge-text-center { + text-align: center !important; + } + .xlarge-text-justify { + text-align: justify !important; + } +} + +@media only screen and (min-width: 120.063em) and (max-width: 99999999em) { + .xxlarge-only-text-left { + text-align: left !important; + } + .xxlarge-only-text-right { + text-align: right !important; + } + .xxlarge-only-text-center { + text-align: center !important; + } + .xxlarge-only-text-justify { + text-align: justify !important; + } +} + +@media only screen and (min-width: 120.063em) { + .xxlarge-text-left { + text-align: left !important; + } + .xxlarge-text-right { + text-align: right !important; + } + .xxlarge-text-center { + text-align: center !important; + } + .xxlarge-text-justify { + text-align: justify !important; + } +} + +/* Typography resets */ + +div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, p, blockquote, th, td { + margin: 0; + padding: 0; +} + +/* Default Link Styles */ + +a { + color: #2ba6cb; + text-decoration: none; + line-height: inherit; + &:hover, &:focus { + color: #258faf; + outline: none; + } + img { + border: none; + } +} + +/* Default paragraph styles */ + +p { + font-family: inherit; + font-weight: normal; + font-size: 0.9rem; + line-height: 1.4; + margin-bottom: 1.25rem; + text-rendering: optimizeLegibility; + &.lead { + font-size: 1.21875rem; + line-height: 1.4; + } + aside { + font-size: 0.875rem; + line-height: 1.35; + font-style: italic; + } +} + +/* Default header styles */ + +h1, h2, h3, h4, h5, h6 { + font-weight: normal; + font-style: normal; + color: #222; + text-rendering: optimizeLegibility; + margin-top: 0.2rem; + margin-bottom: 0.5rem; + line-height: 1.2; +} + +h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { + font-size: 60%; + color: #6f6f6f; + line-height: 0; +} + +h1 { + font-size: 2.125rem; +} + +h2 { + font-size: 1.6875rem; +} + +h3 { + font-size: 1.375rem; +} + +h4, h5 { + font-size: 1.125rem; +} + +h6 { + font-size: 1rem; +} + +.subheader { + line-height: 1.4; + color: #6f6f6f; + font-weight: normal; + margin-top: 0.2rem; + margin-bottom: 0.5rem; +} + +hr { + border: solid #dddddd; + border-width: 1px 0 0; + clear: both; + margin: 1.25rem 0 1.1875rem; + height: 0; +} + +/* Helpful Typography Defaults */ + +em, i { + font-style: italic; + line-height: inherit; +} + +strong, b { + font-weight: bold; + line-height: inherit; +} + +small { + font-size: 60%; + line-height: inherit; +} + +code { + font-family: Consolas, "Liberation Mono", Courier, monospace; + font-weight: bold; + color: #910b0e; +} + +/* Lists */ + +ul, ol, dl { + font-size: 0.9rem; + line-height: 1.6; + margin-bottom: 1.25rem; + list-style-position: outside; + font-family: inherit; +} + +ul { + margin-left: 0; + &.bullets { + margin-left: 1.1rem; + li { + margin-left: 1.25rem; + margin-bottom: 0; + list-style: circle; + } + } + li { + margin-bottom: 0; + list-style: none; + } +} + +/* Abbreviations */ + +abbr, acronym { + text-transform: uppercase; + font-size: 90%; + color: #222222; + border-bottom: 1px dotted #dddddd; + cursor: help; +} + +abbr { + text-transform: none; +} + +/* Blockquotes */ + +blockquote { + margin: 0 0 1.25rem; + padding: 0.5625rem 1.25rem 0 1.1875rem; + border-left: 1px solid #dddddd; + cite { + display: block; + font-size: 0.8125rem; + color: #555555; + &:before { + content: "\2014 \0020"; + } + a { + color: #555555; + &:visited { + color: #555555; + } + } + } + line-height: 1.6; + color: #6f6f6f; + p { + line-height: 1.6; + color: #6f6f6f; + } +} + +/* Microformats */ + +.vcard { + display: inline-block; + margin: 0 0 1.25rem 0; + border: 1px solid #dddddd; + padding: 0.625rem 0.75rem; + li { + margin: 0; + display: block; + } + .fn { + font-weight: bold; + font-size: 0.9375rem; + } +} + +.vevent { + .summary { + font-weight: bold; + } + abbr { + cursor: default; + text-decoration: none; + font-weight: bold; + border: none; + padding: 0 0.0625rem; + } +} + +@media only screen and (min-width: 40.063em) { + h1, h2, h3, h4, h5, h6 { + line-height: 1.2; + } + h1 { + font-size: 2.55rem; + } + h2 { + font-size: 2.3125rem; + } + h3 { + font-size: 1.4875rem; + } + h4 { + font-size: 1.1375rem; + } +} + +/* + * Print styles. + * + * Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ + * Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com) +*/ + +.print-only { + display: none !important; +} + +@media print { + * { + background: transparent !important; + color: black !important; + /* Black prints faster: h5bp.com/s */ + box-shadow: none !important; + text-shadow: none !important; + } + a { + text-decoration: underline; + &:visited { + text-decoration: underline; + } + &[href]:after { + content: " (" attr(href) ")"; + } + } + abbr[title]:after { + content: " (" attr(title) ")"; + } + .ir a:after { + content: ""; + } + a { + &[href^="javascript:"]:after, &[href^="#"]:after { + content: ""; + } + } + pre, blockquote { + border: 1px solid #999999; + page-break-inside: avoid; + } + thead { + display: table-header-group; + /* h5bp.com/t */ + } + tr { + page-break-inside: avoid; + } + img { + page-break-inside: avoid; + max-width: 100% !important; + } + @page { + margin: 0.5cm; + } + + p, h2, h3 { + orphans: 3; + widows: 3; + } + h2, h3 { + page-break-after: avoid; + } + .hide-on-print { + display: none !important; + } + .print-only { + display: block !important; + } + .hide-for-print { + display: none !important; + } + .show-for-print { + display: inherit !important; + } +} + +.reveal-modal-bg { + position: fixed; + height: 100%; + width: 100%; + background: black; + background: rgba(0, 0, 0, 0.45); + z-index: 99; + display: none; + top: 0; + left: 0; +} + +dialog, .reveal-modal { + visibility: hidden; + display: none; + position: absolute; + z-index: 100; + width: 100vw; + top: 0; + left: 0; + background-color: white; + padding: 1.25rem; + border: solid 1px #666666; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); +} + +@media only screen and (max-width: 40em) { + dialog, .reveal-modal { + min-height: 100vh; + } +} + +@media only screen and (min-width: 40.063em) { + dialog, .reveal-modal { + left: 50%; + } +} + +dialog { + .column, .columns { + min-width: 0; + } +} + +.reveal-modal { + .column, .columns { + min-width: 0; + } +} + +dialog > :first-child, .reveal-modal > :first-child { + margin-top: 0; +} + +dialog > :last-child, .reveal-modal > :last-child { + margin-bottom: 0; +} + +@media only screen and (min-width: 40.063em) { + dialog, .reveal-modal { + margin-left: -26%; + width: 50%; + } +} + +@media only screen and (min-width: 40.063em) { + dialog, .reveal-modal { + top: 6.25rem; + } +} + +dialog .close-reveal-modal, .reveal-modal .close-reveal-modal { + font-size: 2.5rem; + line-height: 1; + position: absolute; + top: 0.5rem; + right: 0.6875rem; + color: #aaaaaa; + font-weight: bold; + cursor: pointer; +} + +dialog[open] { + display: block; + visibility: visible; +} + +@media only screen and (min-width: 40.063em) { + dialog, .reveal-modal { + padding: 1.875rem; + } + dialog.radius, .reveal-modal.radius { + border-radius: 3px; + } + dialog.round, .reveal-modal.round { + border-radius: 1000px; + } + dialog.collapse, .reveal-modal.collapse { + padding: 0; + } + dialog.full, .reveal-modal.full { + top: 0; + left: 0; + height: 100vh; + min-height: 100vh; + margin-left: 0 !important; + } +} + +@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { + dialog.tiny, .reveal-modal.tiny { + margin-left: -15%; + width: 30%; + } +} + +@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { + dialog.small, .reveal-modal.small { + margin-left: -20%; + width: 40%; + } +} + +@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { + dialog.medium, .reveal-modal.medium { + margin-left: -30%; + width: 60%; + } +} + +@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { + dialog.large, .reveal-modal.large { + margin-left: -35%; + width: 70%; + } +} + +@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { + dialog.xlarge, .reveal-modal.xlarge { + margin-left: -47.5%; + width: 95%; + } +} + +@media only screen and (min-width: 40.063em) and (min-width: 40.063em) { + dialog.full, .reveal-modal.full { + margin-left: -50vw; + width: 100vw; + } +} + +@media print { + dialog, .reveal-modal { + background: white !important; + } +} + +.label { + font-weight: normal; + text-align: center; + text-decoration: none; + line-height: 1; + white-space: nowrap; + display: inline-block; + position: relative; + margin-bottom: inherit; + padding: 0.25rem 0.5rem 0.375rem; + font-size: 0.6875rem; + background-color: #2ba6cb; + color: white; + &.radius { + border-radius: 3px; + } + &.round { + border-radius: 1000px; + } + &.alert { + background-color: #c60f13; + color: white; + } + &.success { + background-color: #5da423; + color: white; + } + &.secondary { + background-color: #e9e9e9; + color: #333333; + } +} + +button, .button, input[type=button] { + cursor: pointer; + margin: 0 0 1.25rem; + border: none; + position: relative; + text-decoration: none; + text-align: center; + -webkit-appearance: none; + display: inline-block; + padding: 0.4rem 1.1rem; + font-size: 0.9rem; + background-color: #2ba6cb; + border-color: #2285a2; + color: white; + transition: background-color 150ms ease-out; + @include border-radius(2px); + &:hover, &:focus { + background-color: #2285a2; + outline: none; + color: white; + } + &.large { + padding-top: 1.125rem; + padding-right: 2.25rem; + padding-bottom: 1.1875rem; + padding-left: 2.25rem; + font-size: 1.25rem; + } + + &.small { + padding-top: 0.875rem; + padding-right: 1.75rem; + padding-bottom: 0.9375rem; + padding-left: 1.75rem; + font-size: 0.8125rem; + } + + &.tiny { + padding-top: 0.625rem; + padding-right: 1.25rem; + padding-bottom: 0.6875rem; + padding-left: 1.25rem; + font-size: 0.6875rem; + } + + &.expand { + padding-right: 0; + padding-left: 0; + width: 100%; + } + + &.left-align { + text-align: left; + text-indent: 0.75rem; + } + + &.right-align { + text-align: right; + padding-right: 0.75rem; + } + + &.round { + border-radius: 1000px; + } + + &.disabled, &[disabled] { + background-color: #2285a2; + border-color: #2285a2; + color: white; + cursor: default; + opacity: 0.5; + box-shadow: none; + &:hover, &:focus { + background-color: #2285a2; + opacity: 0.5; + } + } +} + + +@media only screen and (min-width: 40.063em) { + button, .button { + display: inline-block; + } +} + +.keystroke, kbd { + background-color: #ededed; + border-color: #dddddd; + color: #222222; + border-style: solid; + border-width: 1px; + margin: 0; + font-family: "Consolas", "Menlo", "Courier", monospace; + font-size: inherit; + padding: 0.125rem 0.25rem 0; + border-radius: 3px; +} + + + +/* We use this to get basic styling on all basic form elements */ +input[type="text"], +input[type="password"], +input[type="date"], +input[type="datetime"], +input[type="datetime-local"], +input[type="month"], +input[type="week"], +input[type="email"], +input[type="number"], +input[type="search"], +input[type="tel"], +input[type="time"], +input[type="url"], +textarea { + -webkit-appearance: none; + background-color: white; + font-family: inherit; + border: 1px solid #cccccc; + color: rgba(0, 0, 0, 0.75); + display: block; + font-size: 0.875rem; + margin: 0 0 1rem 0; + padding: 0.4rem; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + input[type="text"]:focus, + input[type="password"]:focus, + input[type="date"]:focus, + input[type="datetime"]:focus, + input[type="datetime-local"]:focus, + input[type="month"]:focus, + input[type="week"]:focus, + input[type="email"]:focus, + input[type="number"]:focus, + input[type="search"]:focus, + input[type="tel"]:focus, + input[type="time"]:focus, + input[type="url"]:focus, + textarea:focus {} + input[type="text"]:focus, + input[type="password"]:focus, + input[type="date"]:focus, + input[type="datetime"]:focus, + input[type="datetime-local"]:focus, + input[type="month"]:focus, + input[type="week"]:focus, + input[type="email"]:focus, + input[type="number"]:focus, + input[type="search"]:focus, + input[type="tel"]:focus, + input[type="time"]:focus, + input[type="url"]:focus, + textarea:focus { + background: #fafafa; + border-color: #999999; + outline: none; } + input[type="text"][disabled], fieldset[disabled] input[type="text"], + input[type="password"][disabled], fieldset[disabled] + input[type="password"], + input[type="date"][disabled], fieldset[disabled] + input[type="date"], + input[type="datetime"][disabled], fieldset[disabled] + input[type="datetime"], + input[type="datetime-local"][disabled], fieldset[disabled] + input[type="datetime-local"], + input[type="month"][disabled], fieldset[disabled] + input[type="month"], + input[type="week"][disabled], fieldset[disabled] + input[type="week"], + input[type="email"][disabled], fieldset[disabled] + input[type="email"], + input[type="number"][disabled], fieldset[disabled] + input[type="number"], + input[type="search"][disabled], fieldset[disabled] + input[type="search"], + input[type="tel"][disabled], fieldset[disabled] + input[type="tel"], + input[type="time"][disabled], fieldset[disabled] + input[type="time"], + input[type="url"][disabled], fieldset[disabled] + input[type="url"], + textarea[disabled], fieldset[disabled] + textarea { + background-color: #dddddd; } + input[type="text"].radius, + input[type="password"].radius, + input[type="date"].radius, + input[type="datetime"].radius, + input[type="datetime-local"].radius, + input[type="month"].radius, + input[type="week"].radius, + input[type="email"].radius, + input[type="number"].radius, + input[type="search"].radius, + input[type="tel"].radius, + input[type="time"].radius, + input[type="url"].radius, + textarea.radius { + border-radius: 3px; } + +input[type="submit"] { + -webkit-appearance: none; } + +/* Respect enforced amount of rows for textarea */ +textarea[rows] { + height: auto; } + +/* Add height value for select elements to match text input height */ +select { + -webkit-appearance: none !important; + background-color: #fafafa; + background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iM3B4IiB2aWV3Qm94PSIwIDAgNiAzIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA2IDMiIHhtbDpzcGFjZT0icHJlc2VydmUiPjxwb2x5Z29uIHBvaW50cz0iNS45OTIsMCAyLjk5MiwzIC0wLjAwOCwwICIvPjwvc3ZnPg=="); + background-repeat: no-repeat; + background-position: 97% center; + border: 1px solid #cccccc; + padding: 0.5rem; + font-size: 0.875rem; + color: rgba(0, 0, 0, 0.75); + line-height: normal; + border-radius: 0; +} + select.radius { + border-radius: 3px; } + select:hover { + background-color: #f3f3f3; + border-color: #999999; } + +/* Adjust margin for form elements below */ +input[type="file"], +input[type="checkbox"], +input[type="radio"], +select { + margin: 0 0 1rem 0; } + +input[type="checkbox"] + label, +input[type="radio"] + label { + display: inline-block; + + margin-left: 0.5rem; + margin-right: 1rem; + margin-bottom: 0; + vertical-align: baseline; } + +/* Normalize file input width */ +input[type="file"] { + width: 100%; } diff --git a/web-ui/public/scss/vendor/_reset.scss b/web-ui/public/scss/vendor/_reset.scss new file mode 100644 index 00000000..55f8d054 --- /dev/null +++ b/web-ui/public/scss/vendor/_reset.scss @@ -0,0 +1,421 @@ +/*! normalize.css v3.0.1 | MIT License | git.io/normalize */ + +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + font-family: sans-serif; + /* 1 */ + -ms-text-size-adjust: 100%; + /* 2 */ + -webkit-text-size-adjust: 100%; + /* 2 */ +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined for any HTML5 element in IE 8/9. + * Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox. + * Correct `block` display not defined for `main` in IE 11. + */ + +article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { + display: block; +} + +/** + * 1. Correct `inline-block` display not defined in IE 8/9. + * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. + */ + +audio, canvas, progress, video { + display: inline-block; + /* 1 */ + vertical-align: baseline; + /* 2 */ +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address `[hidden]` styling not present in IE 8/9/10. + * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. + */ + +[hidden], template { + display: none; +} + +/* Links + ========================================================================== */ + +/** + * Remove the gray background color from active links in IE 10. + */ + +a { + background: transparent; + &:active, &:hover { + outline: 0; + } +} + +/** + * Improve readability when focused and also mouse hovered in all browsers. + */ + +/* Text-level semantics + ========================================================================== */ + +/** + * Address styling not present in IE 8/9/10/11, Safari, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. + */ + +b, strong { + font-weight: bold; +} + +/** + * Address styling not present in Safari and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove border when inside `a` element in IE 8/9/10. + */ + +img { + border: 0; +} + +/** + * Correct overflow not hidden in IE 9/10/11. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Grouping content + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari. + */ + +figure { + margin: 1em 40px; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} + +/** + * Contain overflow in all browsers. + */ + +pre { + overflow: auto; +} + +/** + * Address odd `em`-unit font size rendering in all browsers. + */ + +code, kbd, pre, samp { + font-family: monospace, monospace; + font-size: 1em; +} + +/* Forms + ========================================================================== */ + +/** + * Known limitation: by default, Chrome and Safari on OS X allow very limited + * styling of `select`, unless a `border` property is set. + */ + +/** + * 1. Correct color not being inherited. + * Known issue: affects color of disabled elements. + * 2. Correct font properties not being inherited. + * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. + */ + +button, input, optgroup, select, textarea { + color: inherit; + /* 1 */ + font: inherit; + /* 2 */ + margin: 0; + /* 3 */ +} + +/** + * Address `overflow` set to `hidden` in IE 8/9/10/11. + */ + +button { + overflow: visible; + text-transform: none; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. + * Correct `select` style inheritance in Firefox. + */ + +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, html input[type="button"] { + -webkit-appearance: button; + /* 2 */ + cursor: pointer; + /* 3 */ +} + +input { + &[type="reset"], &[type="submit"] { + -webkit-appearance: button; + /* 2 */ + cursor: pointer; + /* 3 */ + } +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], html input[disabled] { + cursor: default; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner { + border: 0; + padding: 0; +} + +input { + &::-moz-focus-inner { + border: 0; + padding: 0; + } + line-height: normal; + &[type="checkbox"], &[type="radio"] { + box-sizing: border-box; + /* 1 */ + padding: 0; + /* 2 */ + } + &[type="number"] { + &::-webkit-inner-spin-button, &::-webkit-outer-spin-button { + height: auto; + } + } + &[type="search"] { + -webkit-appearance: textfield; + /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; + /* 2 */ + box-sizing: content-box; + &::-webkit-search-cancel-button, &::-webkit-search-decoration { + -webkit-appearance: none; + } + } +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +/** + * It's recommended that you don't attempt to style these elements. + * Firefox's implementation doesn't respect box-sizing, padding, or width. + * + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + */ + +/** + * Fix the cursor style for Chrome's increment/decrement buttons. For certain + * `font-size` values of the `input`, it causes the cursor style of the + * decrement button to change from `default` to `text`. + */ + +/** + * 1. Address `appearance` set to `searchfield` in Safari and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari and Chrome + * (include `-moz` to future-proof). + */ + +/** + * Remove inner padding and search cancel button in Safari and Chrome on OS X. + * Safari (but not Chrome) clips the cancel button when the search input has + * padding (and `textfield` appearance). + */ + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9/10/11. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; + /* 1 */ + padding: 0; + /* 2 */ +} + +/** + * Remove default vertical scrollbar in IE 8/9/10/11. + */ + +textarea { + overflow: auto; +} + +/** + * Don't inherit the `font-weight` (applied by a rule above). + * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. + */ + +optgroup { + font-weight: bold; +} + +/* Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, th { + padding: 0; +} diff --git a/web-ui/public/scss/vendor/_scut.scss b/web-ui/public/scss/vendor/_scut.scss new file mode 100644 index 00000000..3e16fa65 --- /dev/null +++ b/web-ui/public/scss/vendor/_scut.scss @@ -0,0 +1,1518 @@ +/* +* Scut, a collection of Sass utilities +* to ease and improve our implementations of common style-code patterns. +* v1.3.0 +* Docs at http://davidtheclark.github.io/scut +*/ + +@mixin scut-clearfix { + + &:after { + content: ""; + display: table; + clear: both; + } + +} + +%scut-clearfix { + @include scut-clearfix; +} +@mixin scut-list-unstyled( + $no-margin: true +) { + + list-style-type: none; + padding-left: 0; + + @if $no-margin { + margin-top: 0; + margin-bottom: 0; + } + +} + +%scut-list-unstyled { + @include scut-list-unstyled(); +} +// Depends on `list-unstyled` and `clearfix`. + +@mixin scut-list-floated ( + $space: false, + $dir: left, + $no-margin: true +) { + + @include scut-list-unstyled($no-margin); + @include scut-clearfix; + + & > li { + float: $dir; + } + + @if $space { + & > li + li { + margin-#{$dir}: $space; + } + } + +} + +%scut-list-floated { + @include scut-list-floated; +} + +@function scut-autoOrValue ($val) { + @if $val == a or $val == auto { + @return auto; + } + @else { + @return $val; + } +} + +@mixin scut-coords ( + $coordinates: n n n n +) { + + $top: nth($coordinates, 1); + $right: nth($coordinates, 2); + $bottom: nth($coordinates, 3); + $left: nth($coordinates, 4); + + @if $top != n { + top: scut-autoOrValue($top); + } + @if $right != n { + right: scut-autoOrValue($right); + } + @if $bottom != n { + bottom: scut-autoOrValue($bottom); + } + @if $left != n { + left: scut-autoOrValue($left); + } + +} +@function scut-strip-unit ( + $num +) { + + @return $num / ($num * 0 + 1); + +} +// Depends on `scut-strip-unit`. + +$scut-em-base: 16 !default; + +@function scut-em ( + $pixels, + $base: $scut-em-base +) { + + // $base could be in em or px (no unit = px). + // Adjust accordingly to create a $divisor that + // serves as context for $pixels. + $multiplier: if(unit($base) == em, 16, 1); + $divisor: scut-strip-unit($base) * $multiplier; + + $em-vals: (); + @each $val in $pixels { + $val-in-ems: (scut-strip-unit($val) / $divisor) * 1em; + $em-vals: append($em-vals, $val-in-ems); + } + + @if length($em-vals) == 1 { + // return a single value instead of a list, + // so it can be used in calculations + @return nth($em-vals, 1); + } + @else { + @return $em-vals; + } + +} +// Depends on `scut-strip-unit`. + +$scut-rem-base: 16 !default; + +@function scut-rem ( + $pixels +) { + + $rem-vals: (); + @each $val in $pixels { + $val-in-rems: scut-strip-unit($val) / $scut-rem-base * 1rem; + $rem-vals: append($rem-vals, $val-in-rems); + } + + @if length($rem-vals) == 1 { + // return a single value instead of a list, + // so it can be used in calculations + @return nth($rem-vals, 1); + } + @else { + @return $rem-vals; + } + +} +@mixin scut-border ( + $style, + $sides: n y +) { + + @if length($sides) == 2 { + @if nth($sides, 1) != n { + border-top: $style; + border-bottom: $style; + } + @if nth($sides, 2) != n { + border-left: $style; + border-right: $style; + } + } + + @else if length($sides) == 4 { + @if nth($sides, 1) != n { + border-top: $style; + } + @if nth($sides, 2) != n { + border-right: $style; + } + @if nth($sides, 3) != n { + border-bottom: $style; + } + @if nth($sides, 4) != n { + border-left: $style; + } + } + + @else { + @warn "Scut-border requires a $sides argument of 2 or 4 values." + } + +} +@mixin scut-circle ( + $size, + $color: inherit +) { + + border-radius: 50%; + display: inline-block; + + @if $color == inherit { + // If user wants to inherit the color, + // take advantage of the fact that border + // color defaults to the text color of the element. + border-width: $size / 2; + border-style: solid; + height: 0; + width: 0; + } + @else { + // Otherwise, just use background-color. + background-color: $color; + height: $size; + width: $size; + } + +} +@mixin scut-color-swap ( + $off, + $on, + $duration: 0, + $bg: false +) { + + $transition-properties: null; + $off-is-list: type-of($off) == list; + $on-is-list: type-of($on) == list; + + // If $off IS a list, + // assign color and background-color. + @if $off-is-list { + color: nth($off, 1); + background-color: nth($off, 2); + $transition-properties: background-color, color; + } + + // If $off IS NOT a list and $bg is TRUE, + // assign background-color. + @else if $bg and not($off-is-list) { + background-color: $off; + $transition-properties: background-color; + } + + // If $off IS NOT a list and $bg is FALSE, + // assign color. + @else { + color: $off; + $transition-properties: color; + } + + // Only set-up transition if $duration != 0. + @if $duration != 0 { + transition-property: $transition-properties; + transition-duration: $duration; + } + + &:hover, + &:focus { + + // $on is treated the same as $off, above. + @if $on-is-list { + color: nth($on, 1); + background-color: nth($on, 2); + } + + @else if $bg and not($on-is-list) { + background-color: $on; + } + + @else { + color: $on; + } + } + +} +@mixin scut-hd-bp ( + $ratio: 1.3 +) { + + @media (-o-min-device-pixel-ratio: ($ratio / 1)), + (-webkit-min-device-pixel-ratio: $ratio), + (min-resolution: (round(96 * $ratio) * 1dpi)) { + @content; + } + +} + +@mixin scut-hide-visually { + + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; + +} + +%scut-hide-visually { + @include scut-hide-visually; +} +@mixin scut-image-replace { + + text-indent: 102%; + white-space: nowrap; + overflow: hidden; + padding: 0; + +} + +%scut-image-replace { + @include scut-image-replace; +} + +// Depends on scut-rem and scut-strip-unit + +@mixin scut-rem-fallback ( + $pixels, + $property: font-size +) { + + $px-vals: null; + @each $val in $pixels { + $val-in-px: scut-strip-unit($val) * 1px; + $px-vals: append($px-vals, $val-in-px); + } + $rem-vals: scut-rem($pixels); + + #{$property}: $px-vals; + #{$property}: $rem-vals; + +} +@mixin scut-reset-border-box { + // Make everything a border-box, because why not? + html { + box-sizing: border-box; + } + *, *:before, *:after { + box-sizing: inherit; + } +} + +@mixin scut-reset-antialias { + // Antialias! + body { + -webkit-font-smoothing: antialiased; + } + *, *:before, *:after { + -webkit-font-smoothing: inherit; + } +} + +@mixin scut-reset-semanticize { + // Make headers and semantic, not presentational. + h1, + h2, + h3, + h4, + h5, + h6 { + font-size: 1em; + font-weight: normal; + margin: 0; + } + b { + font-weight: normal; + } +} + +@mixin scut-reset-pointer { + // Clickable form elements should have a pointer. + label, + select, + option, + button { + cursor: pointer; + } +} + +@mixin scut-reset-form { + fieldset { + border: 0; + margin: 0; + padding: 0; + } + textarea { + resize: vertical; + } +} + +@mixin scut-reset-button { + // Reset default button styles, which are never used. + button, + input[type="button"], + input[type="submit"], + input[type="reset"] { + background: transparent; + border: 0; + color: inherit; + font: inherit; + margin: 0; + padding: 0; + width: auto; + -webkit-appearance: none; + -webkit-font-smoothing: antialiased; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + &::-moz-focus-inner { + padding: 0; + border: 0; + } + } +} + +@mixin scut-reset-paragraph { + // Some paragraph margins just get in the way. + p:first-of-type { + margin-top: 0; + } + p:last-of-type { + margin-bottom: 0; + } +} + +@mixin scut-reset-media { + // You want these elements fluid, probably. + img, + video { + max-width: 100%; + height: auto; + } +} + +@mixin scut-reset-figure { + // Remove default margins. + figure { + margin: 0; + } +} + +// Call them all, minus exclusions! +@mixin scut-reset ($exclude: false) { + @if not(index($exclude, border-box)) { + @include scut-reset-border-box; + } + @if not(index($exclude, antialias)) { + @include scut-reset-antialias; + } + @if not(index($exclude, semanticize)) { + @include scut-reset-semanticize; + } + @if not(index($exclude, pointer)) { + @include scut-reset-pointer; + } + @if not(index($exclude, form)) { + @include scut-reset-form; + } + @if not(index($exclude, button)) { + @include scut-reset-button; + } + @if not(index($exclude, paragraph)) { + @include scut-reset-paragraph; + } + @if not(index($exclude, media)) { + @include scut-reset-media; + } + @if not(index($exclude, figure)) { + @include scut-reset-figure; + } +} + +@mixin scut-selected ( + $active: false +) { + + @if $active { + &:hover, + &:focus, + &:active { + @content; + } + } + @else { + &:hover, + &:focus { + @content; + } + } + +} +@mixin scut-triangle ( + $direction: right, + $size: 0.75em, + $color: inherit +) { + + display: inline-block; + height: 0; + width: 0; + // For improved appearance in some Webkit browsers + -webkit-transform: rotate(360deg); + + // Set up some variables + $width: null; + $height: null; + $border-widths: null; + + @if type-of($size) == list { + $width: nth($size, 1); + $height: nth($size, 2); + } + @else { + $width: $size; + $height: $size; + } + + @if ($direction == up) or ($direction == down) { + // For up and down, width gets two borders but height only one, + // so divide second border-width value by 2 + $border-widths: $height ($width / 2); + } + @else if ($direction == right) or ($direction == left) { + // For right and left, height gets two borders but width only one, + // so divide first border-width value by 2 + $border-widths: ($height / 2) $width; + } + @else { + // For right triangles (the rest), both sides get two borders, + // so divide both by 2 + $border-widths: ($height / 2) ($width / 2); + } + + border-width: $border-widths; + border-style: solid; + + + // STANDARD TRIANGLES + + @if ($direction == up) or ($direction == down) or ($direction == right) or ($direction == left) { + border-color: transparent; + @if $direction == up { + border-bottom-color: $color; + border-top-width: 0; + } + @else if $direction == right { + border-left-color: $color; + border-right-width: 0; + } + @else if $direction == down { + border-top-color: $color; + border-bottom-width: 0; + } + @else if $direction == left { + border-right-color: $color; + border-left-width: 0; + } + } + + + // CORNER TRIANGLES + + @else if ($direction == top-right) or ($direction == top-left) { + border-top-color: $color; + border-bottom-color: transparent; + @if $direction == top-right { + border-left-color: transparent; + border-right-color: $color; + } + @else if $direction == top-left { + border-left-color: $color; + border-right-color: transparent; + } + } + + @else if ($direction == bottom-right) or ($direction == bottom-left) { + border-top-color: transparent; + border-bottom-color: $color; + @if $direction == bottom-right { + border-left-color: transparent; + border-right-color: $color; + } + @else if $direction == bottom-left { + border-left-color: $color; + border-right-color: transparent; + } + } + +} + +%scut-triangle { + @include scut-triangle; +} +@mixin scut-center-absolutely ( + $dimensions +) { + + $width: nth($dimensions, 1); + $height: nth($dimensions, 2); + + position: absolute; + + @if $width != n { + width: $width; + left: 50%; + margin-left: (-$width / 2); + } + + @if $height != n { + height: $height; + top: 50%; + margin-top: (-$height / 2); + } + +} +@mixin scut-center-block ( + $max-width: false +) { + + margin-left: auto; + margin-right: auto; + @if $max-width { + max-width: $max-width; + } + +} + +%scut-center-block { + @include scut-center-block; +} + +@mixin scut-center-transform ( + $axis: false // or x or y +) { + + position: absolute; + + @if $axis != x { + top: 50%; + margin-top: auto; + margin-bottom: auto; + } + + @if $axis != y { + left: 50%; + margin-left: auto; + margin-right: auto; + } + + $translate-val: null; + + @if not($axis) { + $translate-val: translate(-50%, -50%); + } + @else if $axis != x { + $translate-val: translateY(-50%); + } + @else if $axis != y { + $translate-val: translateX(-50%); + } + + -webkit-transform: $translate-val; + -ms-transform: $translate-val; + transform: $translate-val; +} + +%scut-center-transform { + @include scut-center-transform; +} + +%scut-center-transform-x { + @include scut-center-transform(x); +} + +%scut-center-transform-y { + @include scut-center-transform(y); +} + +@mixin scut-fill ( + $width-height: false +) { + + position: absolute; + left: 0; + top: 0; + @if $width-height { + width: 100%; + height: 100%; + } + @else { + right: 0; + bottom: 0; + } + +} + +%scut-fill { + @include scut-fill; +} +@mixin scut-list-custom ( + $content: "\2022", + $marker-width: 0.75em, + $pad: 0, + $no-margin: false +) { + + $content-val: null; + $counter: index($content, count); + @if $counter { + @if length($content) == 3 { + $content-val: counter(scutlistcounter, nth($content, 3))nth($content,2); + } + @else if length($content) == 2 { + $content-val: counter(scutlistcounter)nth($content,2); + } + @else { + $content-val: counter(scutlistcounter); + } + } + @else { + $content-val: $content; + } + + padding-left: $marker-width + $pad; + list-style-type: none; + + @if $no-margin { + margin-top: 0; + margin-bottom: 0; + } + + & > li { + position: relative; + @if $counter { + counter-increment: scutlistcounter; + } + &:before { + content: $content-val; + display: block; + position: absolute; + top: 0; + left: -$marker-width; + width: $marker-width; + @content; + } + } + +} +// Depends on `list-floated`, which depends in turn on `list-unstyled` and `clearfix`. + +@mixin scut-list-divided ( + $divider: "|", + $space: 0.5em, + $dir: left, + $height: false, + $no-margin: true +) { + + @include scut-list-floated($dir: $dir, $no-margin: $no-margin); + + $pseudo: if($dir == left, 'before', 'after'); + + // If an explicit height is passed, + // things are different: All
          • s + // need the pseudo-element (to force height), + // but the first's must be hidden. + + @if $height { + & > li { + height: $height; + } + & > li:#{$pseudo} { + height: $height; + content: $divider; + display: inline-block; + vertical-align: middle; + @content; + } + & > li:first-child:#{$pseudo} { + width: 0; + overflow: hidden; + } + } + + & > li + li:#{$pseudo} { + @if not($height) { + content: $divider; + display: inline-block; + @content; + } + margin-left: $space; + margin-right: $space; + } + +} + +%scut-list-bar { + @include scut-list-divided; +} + +%scut-list-breadcrumb { + @include scut-list-divided("/"); +} +// Depends on `list-unstyled`. + +@mixin scut-list-inline ( + $space: false, + $no-margin: true +) { + + @include scut-list-unstyled($no-margin); + + & > li { + display: inline-block; + } + + @if $space { + & > li + li { + margin-left: $space; + } + } + +} + +%scut-list-inline { + @include scut-list-inline; +} +// Depends on `list-unstyled`. + +@mixin scut-list-punctuated ( + $divider: ", ", + $display: inline, + $no-margin: true +) { + + @include scut-list-unstyled($no-margin); + + & > li { + display: $display; + &:not(:last-child):after { + content: $divider; + } + } + +} + +%scut-list-comma { + @include scut-list-punctuated; +} +@mixin scut-margin ( + $margin +) { + + @if length($margin) == 1 and $margin != n { + margin-top: $margin; + margin-right: $margin; + margin-bottom: $margin; + margin-left: $margin; + } + + @if length($margin) == 2 { + $margin-y: nth($margin, 1); + $margin-x: nth($margin, 2); + @if $margin-y != n { + margin-top: $margin-y; + margin-bottom: $margin-y; + } + @if $margin-x != n { + margin-left: $margin-x; + margin-right: $margin-x; + } + } + + @if length($margin) == 3 { + $margin-y-top: nth($margin, 1); + $margin-x: nth($margin, 2); + $margin-y-bottom: nth($margin, 3); + @if $margin-y-top != n { + margin-top: $margin-y-top; + } + @if $margin-x != n { + margin-right: $margin-x; + margin-left: $margin-x; + } + @if $margin-y-bottom != n { + margin-bottom: $margin-y-bottom; + } + } + + @if length($margin) == 4 { + $margin-top: nth($margin, 1); + $margin-right: nth($margin, 2); + $margin-bottom: nth($margin, 3); + $margin-left: nth($margin, 4); + @if $margin-top != n { + margin-top: $margin-top; + } + @if $margin-right != n { + margin-right: $margin-right; + } + @if $margin-bottom != n { + margin-bottom: $margin-bottom; + } + @if $margin-left != n { + margin-left: $margin-left; + } + } + +} +@mixin scut-padding ( + $padding +) { + + @if length($padding) == 1 and $padding != n { + padding-top: $padding; + padding-right: $padding; + padding-bottom: $padding; + padding-left: $padding; + } + + @if length($padding) == 2 { + $padding-y: nth($padding, 1); + $padding-x: nth($padding, 2); + @if $padding-y != n { + padding-top: $padding-y; + padding-bottom: $padding-y; + } + @if $padding-x != n { + padding-left: $padding-x; + padding-right: $padding-x; + } + } + + @if length($padding) == 3 { + $padding-y-top: nth($padding, 1); + $padding-x: nth($padding, 2); + $padding-y-bottom: nth($padding, 3); + @if $padding-y-top != n { + padding-top: $padding-y-top; + } + @if $padding-x != n { + padding-right: $padding-x; + padding-left: $padding-x; + } + @if $padding-y-bottom != n { + padding-bottom: $padding-y-bottom; + } + } + + @if length($padding) == 4 { + $padding-top: nth($padding, 1); + $padding-right: nth($padding, 2); + $padding-bottom: nth($padding, 3); + $padding-left: nth($padding, 4); + @if $padding-top != n { + padding-top: $padding-top; + } + @if $padding-right != n { + padding-right: $padding-right; + } + @if $padding-bottom != n { + padding-bottom: $padding-bottom; + } + @if $padding-left != n { + padding-left: $padding-left; + } + } +} +// Depends on `positioning-coordinates`. + +@mixin scut-absolute ( + $coordinates: 0 n n 0 +) { + + position: absolute; + @include scut-coords($coordinates); + +} + +%scut-absolute { + @include scut-absolute; +} +// Depends on `positioning-coordinates`. + +@mixin scut-fixed ( + $coordinates: 0 n n 0 +) { + + position: fixed; + @include scut-coords($coordinates); + +} + +%scut-fixed { + @include scut-fixed; +} +// Depends on `positioning-coordinates`. + +@mixin scut-relative ( + $coordinates: n n n n +) { + + position: relative; + @include scut-coords($coordinates); + +} +@mixin scut-ratio-box ( + $ratio: 1/1 +) { + + overflow: hidden; + position: relative; + + // The container's height, as a percentage of the + // container's width, is set by assigning + // padding-top to a pseudo-element. + &:before { + content: ""; + display: block; + height: 0; + padding-top: (1 / $ratio) * 100%; + } + +} + +%scut-ratio-box { + @include scut-ratio-box; +} +@mixin scut-size( + $size +) { + + @if length($size) == 1 { + width: $size; + height: $size; + } + @else if length($size) == 2 { + width: nth($size, 1); + height: nth($size, 2); + } + +} +@mixin scut-sticky-footer-fixed ( + $height, + $wrapper: ".wrapper", + $footer: ".scut-sticky" +) { + + html, + body { + height: 100%; + margin: 0; + padding: 0; + } + + #{$wrapper} { + min-height: 100%; + margin-bottom: -$height; + &:after { + content: ""; + display: block; + } + } + + #{$wrapper}:after, + #{$footer} { + height: $height; + } + +} + +// deprecated +@mixin scut-sticky-footer ( + $height, + $wrapper: ".wrapper", + $footer: ".scut-sticky" +){ + @include scut-sticky-footer-fixed($height, $wrapper, $footer); +} +@mixin scut-sticky-footer-fluid ( + $wrapper: ".wrapper", + $footer: ".scut-sticky" +) { + + html, + body { + height: 100%; + margin: 0; + padding: 0; + } + + #{$wrapper} { + display: table; + height: 100%; + width: 100%; + } + + #{$footer} { + display: table-row; + height: 1px; + } + +} +@mixin scut-vcenter-ib ( + $inner... +) { + + // The inner element is vertically centered + // by middle-aligning it with an inline pseudo-element + // whose height is 100%. + + &:before { + content: ""; + height: 100%; + display: inline-block; + vertical-align: middle; + // A small negative right margin is set + // to account for the default + // word-spacing of inline-block. + margin-right: -0.25em; + } + + $inner: if(length($inner) == 0, ".scut-inner", $inner); + @each $cell-selector in $inner { + $cell-selector: unquote($cell-selector); + & > #{$cell-selector} { + display: inline-block; + vertical-align: middle; + } + } + +} + +%scut-vcenter-ib { + @include scut-vcenter-ib; +} + +@mixin scut-vcenter-lh ( + $height +) { + + height: $height; + line-height: $height; + +} +@mixin scut-vcenter-td ( + $inner... +) { + + display: table; + + $inner: if(length($inner) == 0, ".scut-inner", $inner); + @each $cell-selector in $inner { + $cell-selector: unquote($cell-selector); + & > #{$cell-selector} { + display: table-cell; + vertical-align: middle; + } + } + +} + + +%scut-vcenter-td { + @include scut-vcenter-td; +} + +// Depends on scut-center-transform + +@mixin scut-vcenter-tt () { + @include scut-center-transform(y); +} + +%scut-vcenter-tt { + @include scut-vcenter-tt; +} +// space +$scut-space: "\0020"; +// non-breaking space +$scut-nbsp: "\00a0"; + +// quotation mark +$scut-quot: "\0022"; +// left single curly quote +$scut-lsquo: "\2018"; +// right single curly quote +$scut-rsquo: "\2019"; +// left double curly quote +$scut-ldquo: "\201C"; +// right double curly quote +$scut-rdquo: "\201D"; +// left single angle quote (guillemet) +$scut-lsaquo: "\2039"; +// right single angle quote (guillemet) +$scut-rsaquo: "\203A"; +// left double angle quote (guillemet) +$scut-laquo: "\00ab"; +// right double angle quote (guillemet) +$scut-raquo: "\00bb"; + +// em dash (mutton) +$scut-mdash: "\2014"; +// en dash (nut) +$scut-ndash: "\2013"; +// hyphen +$scut-hyphen: "\2010"; + +// ampersand +$scut-amp: "\0026"; +// greater than +$scut-gt: "\003e"; +// less than +$scut-lt: "\003c"; +// times +$scut-times: "\00D7"; +// big times +$scut-bigtimes: "\2715"; +// checkmark +$scut-checkmark: "\2713"; + +// section sign (double S, hurricane, sectional symbol, the legal doughnut, signum sectionis) +$scut-sect: "\00a7"; +// paragraph symbol (pilcrow) +$scut-para: "\00b6"; + +// middot (interpunct, interpoint) +$scut-middot: "\00b7"; +// o-slash (slashed o) +$scut-oslash: "\00f8"; +// bullet +$scut-bull: "\2022"; +// white bullet +$scut-whibull: "\25E6"; +// horizontal ellipsis +$scut-hellip: "\2026"; +// vertical ellipsis +$scut-vellip: "\22EE"; +// midline horizontal ellipsis +$scut-midhellip: "\22EF"; + +// up-pointing triangle +$scut-utri: "\25b2"; +// down-pointing triangle +$scut-dtri: "\25bc"; +// left-pointing triangle +$scut-ltri: "\25c0"; +// right-pointing triangle +$scut-rtri: "\25b6"; +// up-pointing small triangle +$scut-ustri: "\25b4"; +// down-pointing small triangle +$scut-dstri: "\25be"; +// left-pointing small triangle +$scut-lstri: "\25c2"; +// right-pointing small triangle +$scut-rstri: "\25b8"; +// diamond +$scut-diamond: "\25c6"; +// fisheye +$scut-fisheye: "\25c9"; +// bullseye +$scut-bullseye: "\25ce"; +// circle +$scut-circle: "\25cf"; +// white circle +$scut-whitecircle: "\25cb"; +// square +$scut-square: "\25a0"; +// white square +$scut-whitesquare: "\25a1"; +// small square +$scut-ssquare: "\25aa"; +// small white square +$scut-swhitesquare: "\25ab"; +@function main-src($formats, $file-path, $font-family) { + // Return the list of `src` values, in order, that + // a good `@font-face` will need, including only + // those formats specified in the list `$formats`. + $result: (); + @if index($formats, eot) { + $eot-val: url('#{$file-path}.eot?#iefix') format('embedded-opentype'); + $result: append($result, $eot-val, comma); + } + @if index($formats, woff2) { + $woff2-val: url('#{$file-path}.woff2') format('woff2'); + $result: append($result, $woff2-val, comma); + } + @if index($formats, woff) { + $woff-val: url('#{$file-path}.woff') format('woff'); + $result: append($result, $woff-val, comma); + } + @if index($formats, ttf) { + $ttf-val: url('#{$file-path}.ttf') format('truetype'); + $result: append($result, $ttf-val, comma); + } + @if index($formats, svg) { + $svg-val: url('#{$file-path}.svg##{$font-family}') format('svg'); + $result: append($result, $svg-val, comma); + } + @return $result; +} + +@mixin scut-font-face ( + $font-family, + $file-path, + $weight: normal, + $style: normal, + $formats: eot woff2 woff ttf svg +) { + + @if index('italic' 'oblique', $weight) { + $style: $weight; + $weight: normal; + } + + @font-face { + font-family: $font-family; + font-weight: $weight; + font-style: $style; + + @if index($formats, eot) { + src: url('#{$file-path}.eot'); + } + src: main-src($formats, $file-path, $font-family); + } + +} + +@mixin scut-hanging-indent ( + $indent: 1em +) { + + // padding-left creates the indent, + // while text-indent pulls the first line + // back to the edge. + + padding-left: $indent; + text-indent: -$indent; + +} + +%scut-hanging-indent { + @include scut-hanging-indent; +} +@mixin scut-indented-ps ( + $indent: 1.5em, + $no-first-indent: true +) { + + p { + margin: 0; + text-indent: $indent; + } + + @if $no-first-indent { + p:first-of-type { + text-indent: 0; + } + } + +} + +%scut-indented-ps { + @include scut-indented-ps; +} +@mixin scut-key-val ( + $divider: ":", + $pad: 0.25em, + $indent: 1em, + $spacing: 0, + $pad-left: 0 +) { + + & > dt { + clear: both; + float: left; + &:after { + content: $divider; + margin-right: $pad; + @if $pad-left != 0 { + margin-left: $pad-left; + } + } + } + + & > dd { + margin-left: $indent; + @if $spacing != 0 { + margin-bottom: $spacing; + } + } + +} + +%scut-key-val { + @include scut-key-val; +} +@mixin scut-link-bb ( + $color: inherit, + $style: solid, + $width: 1px +) { + + text-decoration: none; + + border-bottom-width: $width; + border-bottom-style: $style; + @if $color != inherit { + border-bottom-color: $color; + } + +} + +%scut-link-bb { + @include scut-link-bb; +} +// SCUT LINK UNSTYLED +// http://davidtheclark.github.io/scut/#link-unstyled + +@mixin scut-link-unstyled() { + + text-decoration: none; + color: inherit; + +} + +%scut-link-unstyled { + @include scut-link-unstyled(); +} + +@mixin scut-reverse-italics ( + $elements: null +) { + + $element-list: em, cite, i; + font-style: italic; + #{join($element-list, $elements)} { + font-style: normal; + } + +} + +%scut-reverse-italics { + @include scut-reverse-italics; +} + +@mixin scut-side-lined ( + $height: 1px, + $space: 0.5em, + $color: inherit, + $style: solid, + $v-adjust: false, + $double: false +) { + + display: block; + overflow: hidden; + text-align: center; + + &:before, + &:after { + content: ""; + display: inline-block; + vertical-align: middle; + position: relative; + width: 50%; + + border-top-style: $style; + border-top-width: $height; + + @if $color != inherit { + border-top-color: $color; + } + + @if $v-adjust != false { + bottom: $v-adjust; + } + + @if $double != false { + height: $double; + border-bottom-style: $style; + border-bottom-width: $height; + @if $color != inherit { + border-bottom-color: $color; + } + } + } + + &:before { + right: $space; + margin-left: -50%; + } + &:after { + left: $space; + margin-right: -50%; + } + +} + +%scut-side-lined { + @include scut-side-lined; +} +@mixin scut-truncate { + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + +} + +%scut-truncate { + @include scut-truncate; +} \ No newline at end of file diff --git a/web-ui/public/scss/views/_action-bar.scss b/web-ui/public/scss/views/_action-bar.scss new file mode 100644 index 00000000..40e677b0 --- /dev/null +++ b/web-ui/public/scss/views/_action-bar.scss @@ -0,0 +1,159 @@ +#top-pane { + height: auto; + overflow: hidden; + background: $top_pane; + border-top: 1px solid $top_pane; + + #list-actions { + width: 100%; + height: 34px; + margin: 0; + border-top: 1px solid $white; + border-bottom: 2px solid lighten($top_pane, 30%); + background: $white; + clear: both; + overflow: hidden; + padding-left: 10px; + + li { + display: inline-block; + margin: 1px -3px; + vertical-align: top; + + input[type=checkbox] { + @include check-box; + + margin: 7px 13px 7px; + } + + select { + padding: 1px 3px; + margin: 0; + } + + input[type=button] { + margin: 2px; + padding: 4px 10px; + background: $background_light_grey; + color: $dark_grey; + text-transform: uppercase; + font-weight: 400; + font-size: 0.8em; + opacity: 0.7; + border: 1px solid darken($contrast, 10%); + + @include border-radius(1px); + + @include btn-transition; + + &:hover { + opacity: 1; + } + + &[disabled=disabled] { + opacity: 0.5; + cursor: default; + } + } + } + + #pagination-trigger { + cursor: pointer; + margin: 4px 12px 0 5px; + + span { + padding-left: 5px; + } + } + } + + #compose-search-trigger { + padding: 4px; + } + + #actions { + ul { + margin: 0; + + li { + display: inline-block; + margin-right: -5px; + + a { + transition: background-color 150ms ease-out; + background: $top_pane; + color: $white; + font-size: 1.5em; + display: block; + padding: 14px 20px; + margin: 0 1px 0px; + opacity: 0.35; + + &.selected { + background: $top_pane; + opacity: 1; + cursor: default; + } + + &:hover { + opacity: 1; + } + } + } + } + } + + #search-trigger { + padding: 5px; + padding-left: 0; + + input { + margin: 0; + padding: 8px 30px; + color: $navigation_background; + background: white; + border: none; + transition: background-color 150ms ease-out; + + &:hover { + background: darken(white, 2%); + } + + &:focus { + background: darken(white, 5%); + } + } + + form:before { + font-family: "FontAwesome"; + content: "\f002"; + position: absolute; + padding: 0 10px; + top: 15px; + color: $medium_light_grey; + } + } +} + +#refresh-mails-trigger { + i { + margin-top: 3px; + cursor: pointer; + opacity: 0.9; + padding: 4px; + + &:hover { + opacity: 1; + + &:after { + content: "\f021"; + } + + &:before { + content: attr(data-label); + font-size: 0.8em; + padding-right: 5px; + } + } + } +} diff --git a/web-ui/public/scss/views/_close-button.scss b/web-ui/public/scss/views/_close-button.scss new file mode 100644 index 00000000..37171c18 --- /dev/null +++ b/web-ui/public/scss/views/_close-button.scss @@ -0,0 +1,22 @@ +.close-mail-button { + $button-size: 27px; + + margin-right: 3px; + float: left; + background: $lighter_gray; + color: $medium_light_grey; + width: $button-size; + height: $button-size; + padding: 0; + border-radius: 0; + + &:hover, &:focus, &:active { + background-color: darken($lighter_gray, 2); + color: darken($medium_light_grey, 10); + } + + i { + padding: 0; + margin: 0; + } +} diff --git a/web-ui/public/scss/views/_compose-button.scss b/web-ui/public/scss/views/_compose-button.scss new file mode 100644 index 00000000..81e0bb33 --- /dev/null +++ b/web-ui/public/scss/views/_compose-button.scss @@ -0,0 +1,27 @@ +// COMPOSE BUTTON +#compose { + margin-bottom: 5px; + padding-right: 4px; + #compose-trigger { + width: 100%; + display: inline-block; + padding: 5px; + #compose-mails-trigger { + background: $action_buttons; + color: $white; + padding: 10px 30px; + text-align: center; + font-weight: 400; + font-size: 1.2em; + width: 100%; + height: 100%; + margin-bottom: 0px; + @include btn-transition; + &:hover { + background: lighten($action_buttons, 10%); + cursor: pointer; + } + } + } +} + diff --git a/web-ui/public/scss/views/_compose-view.scss b/web-ui/public/scss/views/_compose-view.scss new file mode 100644 index 00000000..9e120357 --- /dev/null +++ b/web-ui/public/scss/views/_compose-view.scss @@ -0,0 +1,451 @@ +.compose-view { + overflow: auto; + + &__buttons { + &-attachment { + cursor: pointer; + margin-left: 18px; + padding-top: 0px; + display: inline; + border: 1px $contrast solid; + background: $background_light_grey; + padding: 7px 4px; + font-size: 0.8em; + + span { + -ms-transform: rotate(224deg); + -webkit-transform: rotate(224deg); + transform: rotate(224deg); + outline: 0; + } + + i.fa-paperclip { + font-size: 1.7em; + } + + &--busy { + color: lighten($recipients_font_color, 10%); + cursor: progress; + } + } + } + + &__attachments { + &-wrapper { + padding: 0; + margin-top: 30px; + } + + &-list { + &-item { + display: block; + position: relative; + margin-bottom: 8px; + padding: 5px; + border: 1px solid $border_light_grey; + border-radius: 2px; + background-color: $contrast; + + &-label { + color: $attachment_text; + text-decoration: none; + + &:hover, &:focus { + color: $attachment_icon; + outline: none; + } + } + + &-icon { + color: #a2a2a2; + float: right; + margin-top: 7px; + cursor: pointer; + } + + &-progress { + width: 0%; + position: absolute; + right: 0; + left: 0; + top: 0; + bottom: 0; + min-height: 100%; + + &-bar { + height: 100%; + background-color: rgba($light_blue, 0.3); + } + } + + } + + &--upload { + display: none; + } + + } + + &-error { + background-color: $background_light_grey; + border-radius: 2px; + border: 1px solid $error; + display: block; + font-size: 0.9rem; + margin-bottom: 20px; + padding: 5px; + width: 100%; + + &-close { + float: left; + margin: 5px 5px 0 0; + } + + & > * { + color: $error; + } + + & > a { + display: inline-block; + text-decoration: underline; + padding: 5px; + } + } + } + +} + +// COMPOSE PANE +#compose-box, #draft-box, #reply-box, #feedback-box { + div.floatlabel { + position: relative; + } + + .input-container { + padding: 1px; + } + + label, span { + color: $recipients_font_color; + padding: 0.5rem; + display: inline-block; + } + + label { + padding: 13px 10px; + } + + span { + padding: 3px; + + &.attachment-size { + color: $attachment_size; + cursor: pointer; + } + } + + label.floatlabel { + padding: 0.4rem !important; + position: absolute; + font-size: 0.6rem; + transition: all 0.1s linear; + opacity: 0; + font-weight: bold; + } + + label.showfloatlabel { + color: $light_blue !important; + top: -0.3rem; + opacity: 1; + } + + input, textarea { + margin: 0; + border: none; + transition: all 0.1s linear; + } + + input.showfloatlabel, textarea.showfloatlabel { + padding-top: 1rem !important; + } + + input#subject, #feedback-subject { + font-size: 1.6875rem; + line-height: 1.4; + border-top: 1px solid $lighter_gray; + } + + #feedback-subject { + color: $dark_grey; + } + + textarea { + border-bottom: 2px solid $lighter_gray; + min-height: 400px; + font-family: inherit; + font-weight: normal; + font-size: 1rem; + line-height: 1.6; + text-rendering: optimizeLegibility; + } + + &.reply-box, &.forward-box { + margin: 0; + + h4 { + font-size: 0.9em; + font-style: italic; + color: $medium_grey; + margin: 2px 0; + clear: both; + cursor: pointer; + + &:hover { + background: $contrast; + } + } + + textarea { + min-height: 200px; + margin: 10px 0; + } + + p { + padding: 5px; + margin: 10px 0; + font-style: italic; + cursor: pointer; + + &:hover { + background: $contrast; + } + } + } + + button.close-mail-button { + margin: 1px; + } + + .buttons-group { + margin-top: 0px; + } + + .recipients-area { + -webkit-appearance: none; + background-color: white; + font-family: inherit; + display: flex; + flex-wrap: wrap; + font-size: 0.898em; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + position: relative; + + .compose-column-label { + width: 5%; + display: inline-block; + } + + .compose-column-recipients { + width: 95%; + display: inline-block; + } + + .recipients-label { + width: 100%; + height: 100%; + } + + .recipients-navigation-handler { + z-index: -1; + position: absolute; + top: -200px; + } + + .twitter-typeahead { + flex: 1 1 50px; + + .tt-dropdown-menu { + background: $dark_white; + + div div { + padding: 8px; + + &:hover { + background: $background_dropdown_grey; + } + } + } + } + + .invalid-format { + border-bottom: 1px dotted $error; + } + + input[type=text] { + vertical-align: top; + height: 35px; + margin-left: 1px; + font-size: 0.9em; + width: 100%; + } + + .fixed-recipient { + display: inline-block; + margin-right: -3px; + flex: none; + position: relative; + + .recipient-value { + &.selected { + border: 1px solid $medium_dark_grey; + } + + &:before { + font-family: FontAwesome; + padding-right: 6px; + font-size: 1.4em; + } + + &.encrypted { + border-bottom-color: $will_be_encrypted; + + &:before { + color: $will_be_encrypted; + content: "\f023 "; + } + } + + &.not-encrypted { + border-bottom-color: $wont_be_encrypted; + + &:before { + color: $wont_be_encrypted; + content: "\f09c"; + } + } + + &.deleting span { + text-decoration: line-through; + } + + & span { + margin: 0px; + padding: 0px 0px 0px 0px; + vertical-align: top; + cursor: pointer; + } + + margin: 3px; + padding: 5px; + background-color: $background_light_grey; + border: 1px solid $border_light_grey; + border-radius: 2px; + } + + .recipient-del { + position: relative; + color: $recipients_font_color; + + &:hover, &:focus { + color: $recipients_font_color; + } + + &:before { + margin-left: 0.4em; + font-weight: bold; + content: "x"; + } + + &.deleteTooltip:hover:after { + position: absolute; + content: attr(data-label); + font-size: 0.5rem; + + @include tooltip(25px, 0px); + } + } + } + + input.recipients-input:focus { + background-color: $dark_white !important; + border-color: $medium_light_grey; + outline: none; + width: 270px; + } + } + + .collapse { + display: block; + position: absolute; + right: 10px; + padding-right: 15px; + padding-left: 15px; + font-family: 'FontAwesome'; + font-weight: bolder; + font-size: larger; + cursor: pointer; + } + + .collapse + input, .collapse + input + * { + display: none; + } + + .collapse + input:checked + * { + display: block; + } +} + +#reply-section { + padding-left: 30px; + + .reply-container { + margin: 10px 0; + padding: 10px; + border: 1px dashed darken($contrast, 10%); + + @include btn-transition; + } + + button { + margin: 0; + } + + #all-recipients { + color: $black; + } + + #all-recipients:focus { + background-color: darken($contrast, 10%); + } + + #reply-button, #reply-all-button, #forward-button { + text-align: center; + font-weight: 100; + font-size: 1.1em; + background: $white; + color: $medium_light_grey; + padding: 25px; + margin: 0; + + @include border-radius(0); + + &:hover { + background: darken($contrast, 5%); + cursor: pointer; + } + } +} + +.buttons-group { + clear: both; + margin: 20px 0 0; + padding: 0; +} + +#draft-save-status { + float: right; + padding: 0.4rem 1.1rem; + color: $lighter_blue; +} diff --git a/web-ui/public/scss/views/_mail-list.scss b/web-ui/public/scss/views/_mail-list.scss new file mode 100644 index 00000000..f5c4c60f --- /dev/null +++ b/web-ui/public/scss/views/_mail-list.scss @@ -0,0 +1,124 @@ +.mail-list-entry { + @include scut-clearfix; + + border-bottom: 1px solid white; + transition: background-color 150ms ease-out; + font-weight: bold; + height: 80px; + position: relative; + + // Workaround: + // Foundation is of the opinion that a 1.6 line height for all lists + // is a totally good idea. Please remove when Foundation is gone + line-height: normal; + + &.status-read { + font-weight: normal; + color: $attachment_text; + + .mail-list-entry__checkbox::after { + display: none; + } + } + + + &.selected { + background: $light_blue; + z-index: 10; // overlay the box-shadow of the right page (z-index: 2) + + &:hover { + background: $light_blue; + } + + a { + color: $white; + } + } + + &:hover { + background: darken($contrast, 5%); + } + + &__checkbox { + margin-right: 5px; + display: block; + float: left; + margin: { + top: 8px; + left: 20px; + } + + &::after { + content: ''; + display: inline-block; + width: 8px; + height: 8px; + -moz-border-radius: 15px; + -webkit-border-radius: 15px; + border-radius: 15px; + background-color: $bullet-blue; + position: absolute; + left: 48px; + top: 13px; + } + + & > input[type=checkbox] { + @include check-box; + } + } + + &__item { + display: block; + color: $dark_grey; + padding: 8px 10px 10px 67px; + height: 100%; + + &-from { + white-space: nowrap; + font-size: 0.8em; + overflow: hidden; + text-overflow: ellipsis; + display: inline-block; + } + + &-date { + font-size: 0.7em; + float: right; + display: inline-block; + } + + &-subject { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + width: 85%; + + &-icon { + color: $light_gray; + } + } + + &-attachment { + width: 14px; + text-align: right; + display: inline-block; + float: right; + color: $light_gray; + } + + &-tags { + @include tags; + + // Workaround: + // Foundation is of the opinion that a 1.6 line height and a 0.6 rem margin-bottom + // for all lists is a totally good idea. Please remove when Foundation is gone + line-height: normal; + margin-bottom: 0; + } + + &:hover, &:focus, &:active { + color: $dark_grey; + } + } +} diff --git a/web-ui/public/scss/views/_message-panel.scss b/web-ui/public/scss/views/_message-panel.scss new file mode 100644 index 00000000..4a0a7a6b --- /dev/null +++ b/web-ui/public/scss/views/_message-panel.scss @@ -0,0 +1,26 @@ +.message-panel { + width: 100%; + margin: 10px auto; + position: fixed; + z-index: 10000; + text-align: center; + + &__growl { + padding: 5px 60px; + + &--success { + background: $warning; + color: darken($warning, 50%); + border: 1px solid darken($warning, 10%); + @include box-shadow(1px 1px 3px darken($warning, 60%)); + } + + &--error { + font-weight: bold; + color: white; + background: $error; + border: 1px solid darken($error, 10%); + @include box-shadow(1px 1px 3px darken($error, 60%)); + } + } +} diff --git a/web-ui/public/scss/views/_navigation.scss b/web-ui/public/scss/views/_navigation.scss new file mode 100644 index 00000000..2c33a791 --- /dev/null +++ b/web-ui/public/scss/views/_navigation.scss @@ -0,0 +1,589 @@ +#logo { + color: $white; +} + +#logout { + color: $white; + cursor: pointer; +} + +#user-settings-box { + position: fixed; + z-index: 10; + + & > div { + position: fixed; + left: 70px; + bottom: 0px; + z-index: 1; + padding: 10px 16px 10px 18px; + background-color: rgba($dark_slate_gray, 0.9); + min-width: 230px; + + &.extra-bottom-space { + bottom: 33px; + } + + header { + border-bottom: 1px solid white; + margin-bottom: 10px; + } + + #user-settings-close { + float: right; + } + + h1, i { + font-size: 1.2em; + color: white; + line-height: 1.2em; + } + + h2 { + font-size: 1.1em; + color: white; + line-height: 1.1em; + display: inline; + margin-left: 5px; + } + + i.fa-user { + margin-right: 10px; + float: left; + } + + i.fa-close { + margin-left: 10px; + float: right; + cursor: pointer; + } + + p { + font-size: 1.1em; + color: $light_orange; + } + } +} + +@keyframes hideshow { + 0% { + fill: lighten($logo_color, 30); + } + + 25% { + opacity: 1; + } + + 100% { + opacity: 0; + } +} + +.logo-part-animation-off { + animation: none; +} + +.logo-part-animation-on { + animation: hideshow 0.6s ease infinite; + opacity: 1; + + &:nth-child(2) { + opacity: 0; + animation-delay: 0.1s; + } + + &:nth-child(3) { + animation-delay: 0.2s; + } + + &:nth-child(4) { + animation-delay: 0.3s; + } + + &:nth-child(5) { + animation-delay: 0.4s; + } + + &:nth-child(6) { + animation-delay: 0.5s; + } +} + +.arrow-box:before { + right: 100%; + top: 65%; + border: 20px solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + border-right-color: rgba($dark_slate_gray, 0.9); + margin-top: -20px; +} + +.side-nav-toggle, .side-nav-toggle-icon { + color: white; + cursor: pointer; + + &:hover, &:focus { + color: white; + } + + background: $navigation_background; + + &.logout { + color: $action_buttons; + } +} + +.side-nav-toggle-icon { + padding: 6px 0px 8px 19px; + display: block; + left: 0; + top: 0; + position: relative; + + .fa-navicon { + font-size: 24px; + &:before { + margin-left: -5px; + } + } +} + +.left-off-canvas-logo { + svg { + width: 162px; + height: 56px; + padding-left: 6px; + padding-top: 2px; + + path, polygon, rect { + fill: $logo_color; + } + } +} + +.collapsed-nav { + width: 50px; + position: absolute; + height: 100vh; + background: $navigation_background; + + ul.shortcuts { + li { + position: relative; + margin-bottom: 5px; + opacity: 0.8; + + &.selected { + background: $contrast; + opacity: 1; + cursor: default; + + a { + color: $navigation_background; + } + } + + @include searching(6px, 26px, $medium_dark_grey, 0.9em); + + a { + display: block; + position: relative; + font-size: 1.4em; + padding: 5px; + color: white; + text-align: center; + + &:hover { + background: darken($contrast, 10%); + color: $navigation_background; + + @include btn-transition; + + &.logout { + color: $black; + background: $action_buttons; + } + } + + &[title]:hover:after { + content: attr(title); + + @include tooltip; + } + } + } + } + + #custom-tags-shortcuts { + li { + border-top: 1px solid $lighter_gray; + } + } + + div.shortcut-label { + font-size: xx-small; + text-transform: uppercase; + text-align: center; + } +} + +.move-right { + ul.shortcuts { + li { + display: none; + } + } +} + +.left-off-canvas-menu { + width: 222px; + -webkit-backface-visibility: hidden; + box-sizing: content-box; + left: 0; + top: 0; + bottom: 0; + position: absolute; + overflow-y: auto; +} + +.left-off-canvas-menu * { + -webkit-backface-visibility: hidden; +} + +.off-canvas-wrap { + -webkit-backface-visibility: hidden; + position: relative; + width: 100%; + overflow: hidden; +} + +.off-canvas-wrap.move-right, .off-canvas-wrap.move-left { + min-height: 100%; + -webkit-overflow-scrolling: touch; +} + +.inner-wrap { + -webkit-backface-visibility: hidden; + width: 100%; +} + +.inner-wrap:before, .inner-wrap:after { + content: " "; + display: table; +} + +.inner-wrap:after { + clear: both; +} + +.off-canvas-wrap.content { + -webkit-ransition: -webkit-transform 500ms ease; + -moz-transition: -moz-transform 500ms ease; + -ms-transition: -ms-transform 500ms ease; + -o-transition: -o-transform 500ms ease; + transition: transform 500ms ease; + + &.move-right { + -webkit-transform: translate3d(10rem, 0, 0); + -moz-transform: translate3d(10rem, 0, 0); + -ms-transform: translate3d(10rem, 0, 0); + -o-transform: translate3d(10rem, 0, 0); + transform: translate3d(10rem, 0, 0); + + #user-settings-box > div { + left: 20px; + } + } +} + +.move-right .exit-off-canvas { + -webkit-backface-visibility: hidden; + transition: background 300ms ease; + cursor: pointer; + display: block; + position: absolute; + background: rgba(255, 255, 255, 0.2); + top: 0; + bottom: 0; + left: 0; + right: 0; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +@media only screen and (min-width: 40.063em) { + .move-right .exit-off-canvas:hover { + background: rgba(255, 255, 255, 0.05); + } +} + +.off-canvas-wrap.move-right.menu { + position: absolute; +} + +.off-canvas-wrap.content { + left: 50px; + padding-right: 50px; +} + +.offcanvas-overlap .left-off-canvas-menu, .offcanvas-overlap .right-off-canvas-menu { + -ms-transform: none; + -webkit-transform: none; + -moz-transform: none; + -o-transform: none; + transform: none; +} + +.offcanvas-overlap .exit-offcanvas-menu { + -webkit-backface-visibility: hidden; + transition: background 300ms ease; + cursor: pointer; + display: block; + position: absolute; + background: rgba(255, 255, 255, 0.2); + top: 0; + bottom: 0; + left: 0; + right: 0; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); +} + +div.side-nav-bottom { + width: 100%; + position: fixed; + bottom: 20px; + background-color: $navigation_background; + + .version { + padding-left: 55px; + padding-bottom: 3px; + } +} + +#left-pane nav { + border-right: 1px solid lighten($navigation_background, 10%); + + ul#default-tag-list, #custom-tag-list { + li { + transition: background-color 150ms ease-out; + padding: 2px 10px; + cursor: pointer; + + &:hover { + background: $light_gray; + color: $navigation_background; + } + + &.selected { + font-weight: bold; + background: $contrast; + color: $navigation_background; + } + } + } + + ul#default-tag-list { + + span.tag-label { + padding-left: 2px; + } + + li { + padding: 5px 10px 5px 18px; + position: relative; + + @include searching(4px, 19px, $dark_grey, 0.7em); + + &:before { + font-size: 1.5em; + font-family: "FontAwesome"; + margin-right: 16px; + font-weight: normal; + position: relative; + top: 2px; + margin-left: -3px; + } + + &:after { + padding-left: 10px; + } + + &:nth-child(1) { + &:before { + content: "\f01c"; + } + } + + &:nth-child(2) { + &:before { + font-family: "icomoon"; + content: "\e900"; + margin-left: -5px; + } + } + + &:nth-child(3) { + &:before { + content: "\f040"; + } + } + + &:nth-child(4) { + &:before { + content: "\f014"; + } + } + + &:nth-child(5) { + &:before { + content: "\f187"; + margin-left: -5px; + } + } + } + } + + ul#custom-tag-list { + visibility: hidden; + opacity: 0; + transition-duration: 500ms; + height: 100%; + max-height: 220px; + overflow: auto; + background-color: lighten($navigation_background, 1); + + li { + white-space: nowrap; + overflow: hidden; + font-size: 0.8em; + padding: 5px 10px 5px 15px; + + &.custom-tag { + text-overflow: ellipsis; + } + + span.tag-label { + padding: 5px 20px 5px 38px; + } + } + + .unread-count, .total-count { + padding: 1px 4px; + position: relative; + } + + } + + ul#custom-tag-list.expanded { + visibility: visible; + opacity: 1; + } + + div.tags-icon { + border-top: 1px solid white; + padding-top: 25px; + margin-bottom: 20px; + + i { + font-size: 1.5em; + font-family: "FontAwesome"; + margin-right: 13px; + font-weight: normal; + position: relative; + top: 2px; + left: 16px; + } + + span.tag-label { + font-size: 0.9rem; + padding-left: 16px; + margin-bottom: 10px; + } + } + + ul#logout, ul#feedback, ul#user-settings-icon { + margin-bottom: 0; + + li { + background-color: $navigation_background; + padding: 5px 10px; + position: relative; + + @include searching(4px, 19px, $dark_grey, 0.7em); + + &:hover { + color: $navigation_background; + } + + div { + padding-left: 7px; + + &:before { + font-size: 1.5em; + font-family: "FontAwesome"; + margin-right: 13px; + font-weight: normal; + position: relative; + top: 2px; + } + } + } + } + + ul { + &#logout li { + color: $action_buttons; + + &:hover { + background-color: $action_buttons; + } + } + + &#user-settings-icon { + li { + color: white; + + &:hover { + background-color: white; + } + } + } + + &#feedback { + margin-bottom: 0; + + li { + color: $light_orange; + + &:hover { + background-color: $light_orange; + } + } + } + } + + h3 { + color: white; + text-transform: uppercase; + font-size: 0.6em; + padding: 5px; + font-weight: 600; + margin: 0 10px; + border-bottom: 1px dotted lighten($navigation_background, 10%); + } +} + +.unread-count { + @extend .mail-count; + + background: $secondary_callout; +} + +.total-count { + @extend .mail-count; + + background: $medium_light_grey; +} diff --git a/web-ui/public/scss/views/_no-mails-available.scss b/web-ui/public/scss/views/_no-mails-available.scss new file mode 100644 index 00000000..bf5d256a --- /dev/null +++ b/web-ui/public/scss/views/_no-mails-available.scss @@ -0,0 +1,3 @@ +.no-mails-available-pane { + @extend .no-content-placeholder; +} diff --git a/web-ui/public/scss/views/_no-message-selected.scss b/web-ui/public/scss/views/_no-message-selected.scss new file mode 100644 index 00000000..0e367bf2 --- /dev/null +++ b/web-ui/public/scss/views/_no-message-selected.scss @@ -0,0 +1,14 @@ +.no-message-selected-pane { + background: $contrast; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + + &__text { + @extend .no-content-placeholder; + + margin-bottom: 40px; // aligns label with "no results for XYZ" + } +} diff --git a/web-ui/public/scss/views/_read-view.scss b/web-ui/public/scss/views/_read-view.scss new file mode 100644 index 00000000..f69d51a5 --- /dev/null +++ b/web-ui/public/scss/views/_read-view.scss @@ -0,0 +1,165 @@ +.mail-read-view { + $component-vertical-spacing: 10px; + $view-top-spacing: 3px; + + // NB! Setting overflow: hidden on an element causes + // a new float context to be created, so elements that + // are floated inside an element that has overflow: hidden + // applied are cleared. + overflow: hidden; + + hr { + margin: 0; + } + + &__header { + @include scut-clearfix; + + font-size: 0.9em; + margin: 0; + margin: $view-top-spacing 0 $component-vertical-spacing 0; + + &-recipients { + display: inline; + margin-bottom: 5px; + line-height: 1.5em; + + &-separator { + margin: 0 10px; + } + + &--highlight-sender { + font-weight: bold; + } + } + + &-date { + display: inline; + float: right; + } + + &-subject { + display: inline; + float: left; + max-width: 80%; + } + + &-actions { + display: inline; + float: right; + max-width: 20%; + background: $white; + white-space: nowrap; + margin-top: $component-vertical-spacing; + + &-button { + color: $medium_light_grey; + background-color: inherit; + display: inline; + border: 1px solid $lighter_gray; + line-height: 2em; + + margin-bottom: 0; + + i { + // workaround: remove padding and margin inserted by font-awesome + margin: 0; + padding: 0; + } + + &:hover, &:active, &:focus { + @include btn-transition; + + background: darken($contrast, 5%); + color: inherit; + } + + &--reply { + padding: 0 20px; + margin-right: -4px; // force buttons together + + } + + &--more { + padding: 0 5px; + } + } + + &-dropdown { + $container-right-padding: 10px; + + background: inherit; + position: absolute; + border: 1px solid $lighter_gray; + right: $container-right-padding; + + &-entry { + box-sizing: border-box; + background: inherit; + padding: 5px 10px; + display: block; + border-bottom: 1px solid $lighter_gray; + + &:last-child { + border-bottom: none; + } + + &:hover { + cursor: pointer; + background: $contrast; + } + } + } + } + + &-tags { + @include tags-editable; + + clear: both; + margin: 0 0 10px; + } + } + + &__body { + margin: $component-vertical-spacing 0; + width: 100%; + border: none; + } + + &__attachments { + margin: $component-vertical-spacing 0; + + &-header { + font-weight: bold; + } + + &-item { + display: block; + margin-bottom: 8px; + padding: 5px; + border: 1px solid $border_light_grey; + border-radius: 2px; + background-color: $background_light_grey; + + &-label { + color: $attachment_text; + text-decoration: none; + + &:hover, &:focus { + i.download-icon { + color: lighten($attachment_icon, 15); + } + + color: $attachment_icon; + outline: none; + } + } + + &-download { + color: #a2a2a2; + float: right; + margin-top: 5px; + } + } + } +} diff --git a/web-ui/public/scss/views/_security-labels.scss b/web-ui/public/scss/views/_security-labels.scss new file mode 100644 index 00000000..ac966ded --- /dev/null +++ b/web-ui/public/scss/views/_security-labels.scss @@ -0,0 +1,67 @@ +.security-status { + margin: 0 0 5px; + + &__label { + display: inline-block; + padding: 2px 6px; + white-space: nowrap; + background: $success; + color: $white; + border-radius: 12px; + + &:before { + font-family: FontAwesome; + } + + &--encrypted { + &:before { + content: "\f023"; + } + + &--with-error { + background: $attention; + &:before { + content: "\f023 \f057"; + } + } + } + + &--not-encrypted { + background: $attention; + + &:before { + content: "\f09c"; + } + } + + &--signed { + &:before { + content: "\f00c"; + } + + &--revoked, &--expired { + background: $attention; + + &:before { + content: "\f05e"; + } + } + + &--not-trusted { + background: $error; + + &:before { + content: "\f05e"; + } + } + } + + &--not-signed { + background: $attention; + + &:before { + content: "\f05e"; + } + } + } +} diff --git a/web-ui/public/templates/compose/attachment_item.hbs b/web-ui/public/templates/compose/attachment_item.hbs new file mode 100644 index 00000000..7a64f6f5 --- /dev/null +++ b/web-ui/public/templates/compose/attachment_item.hbs @@ -0,0 +1,4 @@ +
          • + {{ this.name }} ({{ formatSize this.size}}) + {{#if removable}}{{/if}} +
          • diff --git a/web-ui/public/templates/compose/attachment_upload_item.hbs b/web-ui/public/templates/compose/attachment_upload_item.hbs new file mode 100644 index 00000000..eb6c4ba6 --- /dev/null +++ b/web-ui/public/templates/compose/attachment_upload_item.hbs @@ -0,0 +1,5 @@ +
          • +
            + {{ this.name }} ({{ formatSize this.size}}) + +
          • diff --git a/web-ui/public/templates/compose/attachments_list.hbs b/web-ui/public/templates/compose/attachments_list.hbs new file mode 100644 index 00000000..6f34df9e --- /dev/null +++ b/web-ui/public/templates/compose/attachments_list.hbs @@ -0,0 +1,14 @@ +
            + + + +
            +
              + {{#each attachments }} + {{> attachment_item this }} + {{/each }} +
            +
              +
              + +
              diff --git a/web-ui/public/templates/compose/compose_box.hbs b/web-ui/public/templates/compose/compose_box.hbs new file mode 100644 index 00000000..fcfbeaaf --- /dev/null +++ b/web-ui/public/templates/compose/compose_box.hbs @@ -0,0 +1,32 @@ + + +
              + + {{> recipients }} + + +
              + + +
              +
              + + +
              + + {{> attachments_list }} + +
              + +
              + +
              +
              + +
              diff --git a/web-ui/public/templates/compose/feedback_box.hbs b/web-ui/public/templates/compose/feedback_box.hbs new file mode 100644 index 00000000..346a6192 --- /dev/null +++ b/web-ui/public/templates/compose/feedback_box.hbs @@ -0,0 +1,18 @@ + + +
              +
              + Feedback +
              + +
              + + +
              + +
              + +
              +
              diff --git a/web-ui/public/templates/compose/fixed_recipient.hbs b/web-ui/public/templates/compose/fixed_recipient.hbs new file mode 100644 index 00000000..8b01717c --- /dev/null +++ b/web-ui/public/templates/compose/fixed_recipient.hbs @@ -0,0 +1,8 @@ +
              + +
              + {{ address }} +
              +
              + +
              diff --git a/web-ui/public/templates/compose/inline_box.hbs b/web-ui/public/templates/compose/inline_box.hbs new file mode 100644 index 00000000..c9c114ec --- /dev/null +++ b/web-ui/public/templates/compose/inline_box.hbs @@ -0,0 +1,20 @@ +
              +

              {{subject}}

              + +
              + + +
              + {{t 'to'}}: {{formatRecipients recipients}} + + +{{> recipients }} + +{{> attachments_list }} + +
              + +
              + +
              +
              diff --git a/web-ui/public/templates/compose/no_mails_available.hbs b/web-ui/public/templates/compose/no_mails_available.hbs new file mode 100644 index 00000000..c61152a4 --- /dev/null +++ b/web-ui/public/templates/compose/no_mails_available.hbs @@ -0,0 +1,7 @@ +
              + {{#if forSearch }} + {{t 'no-results-for'}}: '{{ forSearch }}'. + {{else}} + {{t 'no-emails-in'}} '{{t tag}}'. + {{/if}} +
              diff --git a/web-ui/public/templates/compose/no_message_selected.hbs b/web-ui/public/templates/compose/no_message_selected.hbs new file mode 100644 index 00000000..0b9beaf8 --- /dev/null +++ b/web-ui/public/templates/compose/no_message_selected.hbs @@ -0,0 +1,3 @@ +
              +
              {{t 'nothing-selected'}}.
              +
              diff --git a/web-ui/public/templates/compose/recipient_input.hbs b/web-ui/public/templates/compose/recipient_input.hbs new file mode 100644 index 00000000..9416f11f --- /dev/null +++ b/web-ui/public/templates/compose/recipient_input.hbs @@ -0,0 +1 @@ + diff --git a/web-ui/public/templates/compose/recipients.hbs b/web-ui/public/templates/compose/recipients.hbs new file mode 100644 index 00000000..43aced1c --- /dev/null +++ b/web-ui/public/templates/compose/recipients.hbs @@ -0,0 +1,33 @@ + diff --git a/web-ui/public/templates/compose/reply_section.hbs b/web-ui/public/templates/compose/reply_section.hbs new file mode 100644 index 00000000..45203d87 --- /dev/null +++ b/web-ui/public/templates/compose/reply_section.hbs @@ -0,0 +1,6 @@ +
              + + + + +
              diff --git a/web-ui/public/templates/compose/upload_attachment_failed.hbs b/web-ui/public/templates/compose/upload_attachment_failed.hbs new file mode 100644 index 00000000..dbb1437b --- /dev/null +++ b/web-ui/public/templates/compose/upload_attachment_failed.hbs @@ -0,0 +1,6 @@ +
              + + Upload failed. This file exceeds the 1MB limit. + Choose another file + Dismiss +
              diff --git a/web-ui/public/templates/feedback/feedback_trigger.hbs b/web-ui/public/templates/feedback/feedback_trigger.hbs new file mode 100644 index 00000000..7f3f8ef1 --- /dev/null +++ b/web-ui/public/templates/feedback/feedback_trigger.hbs @@ -0,0 +1,8 @@ + diff --git a/web-ui/public/templates/mail_actions/actions_box.hbs b/web-ui/public/templates/mail_actions/actions_box.hbs new file mode 100644 index 00000000..68a8d0bf --- /dev/null +++ b/web-ui/public/templates/mail_actions/actions_box.hbs @@ -0,0 +1,7 @@ +
            • +
            • +
            • +
            • +
            • +
            • +
            • diff --git a/web-ui/public/templates/mail_actions/compose_trigger.hbs b/web-ui/public/templates/mail_actions/compose_trigger.hbs new file mode 100644 index 00000000..06f05fca --- /dev/null +++ b/web-ui/public/templates/mail_actions/compose_trigger.hbs @@ -0,0 +1,3 @@ + diff --git a/web-ui/public/templates/mail_actions/pagination_trigger.hbs b/web-ui/public/templates/mail_actions/pagination_trigger.hbs new file mode 100644 index 00000000..cbd8a089 --- /dev/null +++ b/web-ui/public/templates/mail_actions/pagination_trigger.hbs @@ -0,0 +1,3 @@ + +{{ currentPage }} + diff --git a/web-ui/public/templates/mail_actions/refresh_trigger.hbs b/web-ui/public/templates/mail_actions/refresh_trigger.hbs new file mode 100644 index 00000000..dffc7090 --- /dev/null +++ b/web-ui/public/templates/mail_actions/refresh_trigger.hbs @@ -0,0 +1,3 @@ +
              + +
              diff --git a/web-ui/public/templates/mail_actions/trash_actions_box.hbs b/web-ui/public/templates/mail_actions/trash_actions_box.hbs new file mode 100644 index 00000000..4e0ec332 --- /dev/null +++ b/web-ui/public/templates/mail_actions/trash_actions_box.hbs @@ -0,0 +1,5 @@ +
            • +
            • +
            • +
            • +
            • diff --git a/web-ui/public/templates/mails/draft.hbs b/web-ui/public/templates/mails/draft.hbs new file mode 100644 index 00000000..808ce3ff --- /dev/null +++ b/web-ui/public/templates/mails/draft.hbs @@ -0,0 +1,41 @@ +
              + +
              + + +
              +
              + {{t 'to'}}: + {{#if header.to }} + {{ header.to }} + {{else}} + {{t 'no-recipient'}} + {{/if}} +
              + + {{ formatDate header.date }} +
              +
              +
              + + {{#if header.subject }} + {{header.subject}} + {{else}} + {{t 'no-subject'}} + {{/if}} +
              + + {{#if attachments}} +
              + {{/if}} +
              +
                + {{#each tagsForListView }} +
              • {{ this }}
              • + {{/each }} +
              +
              + + + + diff --git a/web-ui/public/templates/mails/full_view.hbs b/web-ui/public/templates/mails/full_view.hbs new file mode 100644 index 00000000..40bfd4a2 --- /dev/null +++ b/web-ui/public/templates/mails/full_view.hbs @@ -0,0 +1,83 @@ + + +
              +
              + + +
              + {{#if signatureStatus}} + + {{t signatureStatus.label }} + + {{/if}} + {{#if encryptionStatus}} + + {{t encryptionStatus.label }} + + {{/if}} +
              + +
              + + {{#if header.from }} + {{ header.from }} + {{else}} + {{t 'you'}} + {{/if}} + + + {{{formatRecipients header}}} +
              + +
              + {{ formatDate header.date }} +
              + +
              + +
              +

              {{ header.subject }}

              +
              + + + +
                +
              • + +
              • + + {{#each tags }} +
              • {{ this }}
              • + {{/each }} + +
              • + +
              • + +
              • + +
              • +
              +
              + + + + {{#if attachments}} +
              + +
              +

              {{ attachments.length }} attachment(s):

              + +
              + {{/if}} +
              diff --git a/web-ui/public/templates/mails/mail_actions.hbs b/web-ui/public/templates/mails/mail_actions.hbs new file mode 100644 index 00000000..0adfe853 --- /dev/null +++ b/web-ui/public/templates/mails/mail_actions.hbs @@ -0,0 +1,6 @@ + + +
                +
              • {{t 'reply-to-all'}}
              • +
              • {{t 'delete-this-message'}}
              • +
              diff --git a/web-ui/public/templates/mails/sent.hbs b/web-ui/public/templates/mails/sent.hbs new file mode 100644 index 00000000..158b20c8 --- /dev/null +++ b/web-ui/public/templates/mails/sent.hbs @@ -0,0 +1,36 @@ +
              + +
              + +
              +
              + {{t 'to'}}: + {{#if header.to }} + {{ header.to }} + {{else}} + {{t 'no-recipient'}} + {{/if}} +
              + + {{ formatDate header.date }} +
              +
              +
              + {{#if header.subject }} + {{header.subject}} + {{else}} + {{t 'no-subject'}} + {{/if}} +
              + + {{#if attachments}} +
              + {{/if}} +
              +
                + {{#each tagsForListView }} +
              • {{ this }}
              • + {{/each }} +
              +
              + diff --git a/web-ui/public/templates/mails/single.hbs b/web-ui/public/templates/mails/single.hbs new file mode 100644 index 00000000..aaede844 --- /dev/null +++ b/web-ui/public/templates/mails/single.hbs @@ -0,0 +1,28 @@ +
              + +
              + +
              +
              + {{#if header.from }} + {{ header.from }} + {{else}} + {{t "you"}} + {{/if}} +
              + + {{ formatDate header.date }} +
              +
              +
              {{ header.subject }}
              + + {{#if attachments}} +
              + {{/if}} +
              +
                + {{#each tagsForListView }} +
              • {{ this }}
              • + {{/each }} +
              +
              diff --git a/web-ui/public/templates/mails/trash.hbs b/web-ui/public/templates/mails/trash.hbs new file mode 100644 index 00000000..f8947b15 --- /dev/null +++ b/web-ui/public/templates/mails/trash.hbs @@ -0,0 +1,32 @@ +
              + +
              + +
              +
              + {{#if header.from }} + {{ header.from }} + {{else}} + {{t "you"}} + {{/if}} +
              + + {{ formatDate header.date }} +
              +
              +
              + + {{ header.subject }} +
              + + {{#if attachments}} +
              + {{/if}} +
              +
                + {{#each tagsForListView }} +
              • {{ this }}
              • + {{/each }} +
              +
              + diff --git a/web-ui/public/templates/page/logout.hbs b/web-ui/public/templates/page/logout.hbs new file mode 100644 index 00000000..0cc079bc --- /dev/null +++ b/web-ui/public/templates/page/logout.hbs @@ -0,0 +1,9 @@ +
                +
                + +
              • +
                + {{t 'logout'}} +
              • +
                +
              diff --git a/web-ui/public/templates/page/logout_shortcut.hbs b/web-ui/public/templates/page/logout_shortcut.hbs new file mode 100644 index 00000000..043ab0dc --- /dev/null +++ b/web-ui/public/templates/page/logout_shortcut.hbs @@ -0,0 +1,6 @@ +
            • + + +
              {{t 'logout'}}
              +
              +
            • diff --git a/web-ui/public/templates/page/user_settings_box.hbs b/web-ui/public/templates/page/user_settings_box.hbs new file mode 100644 index 00000000..2152b779 --- /dev/null +++ b/web-ui/public/templates/page/user_settings_box.hbs @@ -0,0 +1,10 @@ +
              + + +

              {{t 'user-account'}}

              + +
              +

              {{t 'email-address'}}

              +

              {{ account_email }}

              +

              {{t 'public-key-fingerprint'}}

              +

              {{ formatFingerPrint fingerprint }}

              diff --git a/web-ui/public/templates/page/user_settings_icon.hbs b/web-ui/public/templates/page/user_settings_icon.hbs new file mode 100644 index 00000000..8f2f9215 --- /dev/null +++ b/web-ui/public/templates/page/user_settings_icon.hbs @@ -0,0 +1,8 @@ + diff --git a/web-ui/public/templates/page/version.hbs b/web-ui/public/templates/page/version.hbs new file mode 100644 index 00000000..5f43f78a --- /dev/null +++ b/web-ui/public/templates/page/version.hbs @@ -0,0 +1,2 @@ +{{t 'version'}}: UNKNOWN_VERSION
              + diff --git a/web-ui/public/templates/search/search_trigger.hbs b/web-ui/public/templates/search/search_trigger.hbs new file mode 100644 index 00000000..2261d154 --- /dev/null +++ b/web-ui/public/templates/search/search_trigger.hbs @@ -0,0 +1,3 @@ +
              + +
              diff --git a/web-ui/public/templates/tags/shortcut.hbs b/web-ui/public/templates/tags/shortcut.hbs new file mode 100644 index 00000000..1e82d6a9 --- /dev/null +++ b/web-ui/public/templates/tags/shortcut.hbs @@ -0,0 +1,9 @@ +
            • + + {{#if displayBadge }} + {{ count }} + {{/if}} + +
              {{ tagName }}
              +
              +
            • diff --git a/web-ui/public/templates/tags/tag.hbs b/web-ui/public/templates/tags/tag.hbs new file mode 100644 index 00000000..ca397b9a --- /dev/null +++ b/web-ui/public/templates/tags/tag.hbs @@ -0,0 +1,3 @@ +
            • + {{> tag_inner }} +
            • diff --git a/web-ui/public/templates/tags/tag_inner.hbs b/web-ui/public/templates/tags/tag_inner.hbs new file mode 100644 index 00000000..2e0958cb --- /dev/null +++ b/web-ui/public/templates/tags/tag_inner.hbs @@ -0,0 +1,4 @@ +{{ tagName }} +{{#if displayBadge }} +{{ count }} +{{/if}} diff --git a/web-ui/public/templates/tags/tag_list.hbs b/web-ui/public/templates/tags/tag_list.hbs new file mode 100644 index 00000000..92a73283 --- /dev/null +++ b/web-ui/public/templates/tags/tag_list.hbs @@ -0,0 +1,6 @@ +
                +
                + + {{t 'tags.tags'}} +
                +
                  diff --git a/web-ui/public/templates/user_alerts/message.hbs b/web-ui/public/templates/user_alerts/message.hbs new file mode 100644 index 00000000..abba1f91 --- /dev/null +++ b/web-ui/public/templates/user_alerts/message.hbs @@ -0,0 +1 @@ +{{ message.content }} diff --git a/web-ui/test/test-main.js b/web-ui/test/test-main.js index 4396993f..ce76be9b 100644 --- a/web-ui/test/test-main.js +++ b/web-ui/test/test-main.js @@ -19,34 +19,34 @@ requirejs.config({ baseUrl: '/base', paths: { - 'page': 'app/js/page', - 'js': 'app/js', - 'lib': 'app/js/lib', - 'hbs': 'app/js/generated/hbs', - 'flight': 'app/bower_components/flight', - 'DOMPurify': 'app/bower_components/DOMPurify/dist/purify.min', - 'he': 'app/bower_components/he/he', - 'views': 'app/js/views', - 'helpers': 'app/js/helpers', - 'feedback': 'app/js/feedback', - 'tags': 'app/js/tags', - 'mail_list': 'app/js/mail_list', - 'mail_list_actions': 'app/js/mail_list_actions', - 'user_alerts': 'app/js/user_alerts', - 'mail_view': 'app/js/mail_view', - 'dispatchers': 'app/js/dispatchers', - 'mixins': 'app/js/mixins', - 'services': 'app/js/services', - 'search': 'app/js/search', - 'monkey_patching': 'app/js/monkey_patching', - 'i18next': 'app/bower_components/i18next/i18next', - 'i18nextXHRBackend': 'app/bower_components/i18next-xhr-backend/i18nextXHRBackend', - 'i18nextBrowserLanguageDetector': 'app/bower_components/i18next-browser-languagedetector/i18nextBrowserLanguageDetector', - 'quoted-printable': 'app/bower_components/quoted-printable', - 'utf8': 'app/bower_components/utf8', + 'page': 'public/js/page', + 'js': 'public/js', + 'lib': 'public/js/lib', + 'hbs': 'public/js/generated/hbs', + 'flight': 'public/bower_components/flight', + 'DOMPurify': 'public/bower_components/DOMPurify/dist/purify.min', + 'he': 'public/bower_components/he/he', + 'views': 'public/js/views', + 'helpers': 'public/js/helpers', + 'feedback': 'public/js/feedback', + 'tags': 'public/js/tags', + 'mail_list': 'public/js/mail_list', + 'mail_list_actions': 'public/js/mail_list_actions', + 'user_alerts': 'public/js/user_alerts', + 'mail_view': 'public/js/mail_view', + 'dispatchers': 'public/js/dispatchers', + 'mixins': 'public/js/mixins', + 'services': 'public/js/services', + 'search': 'public/js/search', + 'monkey_patching': 'public/js/monkey_patching', + 'i18next': 'public/bower_components/i18next/i18next', + 'i18nextXHRBackend': 'public/bower_components/i18next-xhr-backend/i18nextXHRBackend', + 'i18nextBrowserLanguageDetector': 'public/bower_components/i18next-browser-languagedetector/i18nextBrowserLanguageDetector', + 'quoted-printable': 'public/bower_components/quoted-printable', + 'utf8': 'public/bower_components/utf8', 'test': 'test', 'features': 'test/features', - 'user_settings': 'app/js/user_settings' + 'user_settings': 'public/js/user_settings' }, deps: tests, @@ -73,7 +73,7 @@ requirejs.config({ .init({ lng: 'en_US', backend: { - loadPath: '/base/app/locales/en_US/translation.json' + loadPath: '/base/public/locales/en_US/translation.json' } }); Handlebars.registerHelper('t', i18n.t); -- cgit v1.2.3