├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── .tx └── config ├── LICENSE ├── README.md ├── build ├── client │ ├── app │ │ └── locales │ │ │ ├── ca.json │ │ │ ├── de.json │ │ │ ├── en.json │ │ │ ├── eo.json │ │ │ ├── es.json │ │ │ ├── fr.json │ │ │ ├── it.json │ │ │ ├── ja.json │ │ │ ├── ko.json │ │ │ ├── nl_NL.json │ │ │ ├── pl.json │ │ │ ├── pt_BR.json │ │ │ ├── ro.json │ │ │ ├── ru.json │ │ │ └── zh_CN.json │ └── public │ │ ├── android-chrome-144x144.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-36x36.png │ │ ├── android-chrome-48x48.png │ │ ├── android-chrome-72x72.png │ │ ├── android-chrome-96x96.png │ │ ├── app.440b8540fcdaa8e9d0e9.css │ │ ├── app.440b8540fcdaa8e9d0e9.css.map │ │ ├── app.440b8540fcdaa8e9d0e9.js │ │ ├── app.440b8540fcdaa8e9d0e9.js.map │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-144x144.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-57x57.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-precomposed.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-194x194.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── fonts │ │ ├── adobeblank.woff │ │ ├── fontawesome-webfont.woff │ │ ├── fontawesome-webfont.woff2 │ │ ├── fonts.css │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ ├── glyphicons-halflings-regular.woff2 │ │ ├── mavenpro-bold.woff │ │ ├── mavenpro-bold.woff2 │ │ ├── mavenpro-regular.woff │ │ ├── mavenpro-regular.woff2 │ │ ├── sourcecodepro-bold.woff │ │ ├── sourcecodepro-regular.woff │ │ ├── sourcesanspro-bold-italic.woff │ │ ├── sourcesanspro-bold-italic.woff2 │ │ ├── sourcesanspro-bold.woff │ │ ├── sourcesanspro-bold.woff2 │ │ ├── sourcesanspro-italic.woff │ │ ├── sourcesanspro-italic.woff2 │ │ ├── sourcesanspro-regular.woff │ │ └── sourcesanspro-regular.woff2 │ │ ├── humans.txt │ │ ├── img │ │ ├── glyphicons-halflings-regular.89889688147bd7575d6327160d64e760.svg │ │ ├── spinner-blue.2b4f85ea90a7b17a71f2f681c56c8c0d.svg │ │ ├── spinner-white.3de41a78b243405e7bd3cedc342d116d.svg │ │ └── spinner.cfaa045c92ac91f6e52c69673fedba41.svg │ │ ├── lang │ │ ├── ar.js │ │ ├── cs.js │ │ ├── da.js │ │ ├── de.js │ │ ├── en-US.js │ │ ├── en.js │ │ ├── eo.js │ │ ├── es-MX.js │ │ ├── es.js │ │ ├── et.js │ │ ├── fi.js │ │ ├── fr.js │ │ ├── he.js │ │ ├── hu.js │ │ ├── id.js │ │ ├── it.js │ │ ├── kr.js │ │ ├── lt.js │ │ ├── lv.js │ │ ├── nl.js │ │ ├── no.js │ │ ├── pl.js │ │ ├── pt-br.js │ │ ├── pt.js │ │ ├── ru.js │ │ ├── se.js │ │ ├── si.js │ │ ├── sk.js │ │ ├── th.js │ │ ├── tr.js │ │ └── zh.js │ │ ├── mail_stylesheet.css │ │ ├── manifest.json │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ └── mstile-70x70.png ├── server.js ├── server │ ├── config.js │ ├── controllers │ │ ├── accounts.js │ │ ├── activity.js │ │ ├── contacts.js │ │ ├── index.js │ │ ├── mailboxes.js │ │ ├── messages.js │ │ ├── providers.js │ │ ├── routes.js │ │ ├── settings.js │ │ └── test.js │ ├── imap │ │ ├── account2config.js │ │ ├── connection.js │ │ └── pool.js │ ├── models │ │ ├── account.js │ │ ├── contact.js │ │ ├── mailbox.js │ │ ├── message.js │ │ ├── model-events.js │ │ ├── requests.js │ │ ├── settings.js │ │ └── store_account_and_boxes.js │ ├── patchs │ │ ├── conversation.js │ │ └── ignored.js │ ├── processes │ │ ├── _base.js │ │ ├── _scheduler.js │ │ ├── application_startup.js │ │ ├── mailbox_refresh.js │ │ ├── mailbox_refresh_deep.js │ │ ├── mailbox_refresh_fast.js │ │ ├── mailbox_refresh_list.js │ │ ├── message_move.js │ │ ├── message_remove_by_account.js │ │ ├── message_remove_by_mailbox.js │ │ ├── message_save_or_send.js │ │ ├── orphan_removal.js │ │ └── recover_change_uidvalidity.js │ ├── utils │ │ ├── constants.js │ │ ├── errors.js │ │ ├── jwz_tools.js │ │ ├── localization.js │ │ ├── logging.js │ │ ├── notifications.js │ │ ├── safeloop.js │ │ ├── socket_handler.js │ │ └── stream_to_array.js │ └── views │ │ ├── index.js │ │ ├── reindexing.js │ │ └── test.js └── webpack-assets.json ├── client ├── app │ ├── actions │ │ ├── account_action_creator.coffee │ │ ├── contact_action_creator.coffee │ │ ├── layout_action_creator.coffee │ │ ├── message_action_creator.coffee │ │ └── settings_action_creator.coffee │ ├── app_dispatcher.coffee │ ├── assets │ │ └── images │ │ │ ├── spinner-blue.svg │ │ │ ├── spinner-white.svg │ │ │ └── spinner.svg │ ├── components │ │ ├── account_config.coffee │ │ ├── account_config_delete.coffee │ │ ├── account_config_input.coffee │ │ ├── account_config_item.coffee │ │ ├── account_config_mailboxes.coffee │ │ ├── account_config_main.coffee │ │ ├── account_config_signature.coffee │ │ ├── account_picker.coffee │ │ ├── alert.coffee │ │ ├── application.coffee │ │ ├── attachement_preview.coffee │ │ ├── basic_components.coffee │ │ ├── compose.coffee │ │ ├── compose_editor.coffee │ │ ├── compose_toolbox.coffee │ │ ├── contact_label.coffee │ │ ├── conversation.coffee │ │ ├── date_range_picker.coffee │ │ ├── file_item.coffee │ │ ├── file_picker.coffee │ │ ├── mailbox_picker.coffee │ │ ├── mails_input.coffee │ │ ├── menu.coffee │ │ ├── menu_mailbox_item.coffee │ │ ├── message-content.coffee │ │ ├── message-list-body.coffee │ │ ├── message-list-item.coffee │ │ ├── message-list-loader.coffee │ │ ├── message-list.coffee │ │ ├── message.coffee │ │ ├── message_footer.coffee │ │ ├── message_header.coffee │ │ ├── modal.coffee │ │ ├── panel.coffee │ │ ├── participant.coffee │ │ ├── participants.coffee │ │ ├── popup_message_attachments.coffee │ │ ├── search_bar.coffee │ │ ├── search_input.coffee │ │ ├── search_result.coffee │ │ ├── settings.coffee │ │ ├── toast.coffee │ │ ├── toast_container.coffee │ │ ├── toolbar_conversation.coffee │ │ ├── toolbar_message.coffee │ │ ├── toolbar_messageslist.coffee │ │ ├── toolbar_messageslist_actions.coffee │ │ ├── toolbar_messageslist_filters.coffee │ │ ├── toolbar_messageslist_search.coffee │ │ ├── toolbox_actions.coffee │ │ ├── toolbox_move.coffee │ │ └── tooltips-manager.coffee │ ├── constants │ │ └── app_constants.coffee │ ├── initialize.coffee │ ├── libs │ │ ├── cached_transform.coffee │ │ ├── flux │ │ │ ├── dispatcher │ │ │ │ └── dispatcher.coffee │ │ │ ├── invariant.js │ │ │ └── store │ │ │ │ └── store.coffee │ │ ├── panel_router.coffee │ │ └── prop_types.coffee │ ├── locales │ │ └── en.json │ ├── mixins │ │ ├── participant_mixin.coffee │ │ ├── router_mixin.coffee │ │ ├── selection_manager_mixin.coffee │ │ ├── should_update_mixin.coffee │ │ ├── store_watch_mixin.coffee │ │ └── tooltip_refresher_mixin.coffee │ ├── router.coffee │ ├── stores │ │ ├── account_store.coffee │ │ ├── contact_store.coffee │ │ ├── layout_store.coffee │ │ ├── message_store.coffee │ │ ├── refreshes_store.coffee │ │ ├── search_store.coffee │ │ └── settings_store.coffee │ ├── styles │ │ ├── _account-config.styl │ │ ├── _animations.styl │ │ ├── _base.styl │ │ ├── _colors.styl │ │ ├── _compose.styl │ │ ├── _conversation.styl │ │ ├── _cozy.styl │ │ ├── _date-range-picker.styl │ │ ├── _file-picker.styl │ │ ├── _layout.styl │ │ ├── _menu.styl │ │ ├── _message-list.styl │ │ ├── _responsive.styl │ │ ├── _search-bar.styl │ │ ├── _toasts.styl │ │ ├── _toolbar.styl │ │ └── application.styl │ └── utils │ │ ├── activity_utils.coffee │ │ ├── api_utils.coffee │ │ ├── colorhash.coffee │ │ ├── discovery_to_fields.coffee │ │ ├── dom_utils.coffee │ │ ├── file_utils.coffee │ │ ├── intent_manager.coffee │ │ ├── message_utils.coffee │ │ ├── misc.coffee │ │ ├── plugin_utils.coffee │ │ ├── socketio_utils.coffee │ │ ├── translators │ │ └── account_translator.coffee │ │ └── xhr_utils.coffee ├── package.json ├── plugins │ ├── README.md │ ├── gallery │ │ ├── README.md │ │ ├── baguetteBox.css │ │ ├── baguetteBox.js │ │ └── init.js │ ├── keyboard │ │ ├── LICENSE │ │ ├── css │ │ │ └── mailkeys.css │ │ ├── init.js │ │ └── js │ │ │ └── mousetrap.js │ ├── medium-editor │ │ ├── README.md │ │ ├── init.js.disabled │ │ ├── scripts │ │ │ └── medium-editor.js │ │ └── styles │ │ │ ├── default.css │ │ │ └── medium-editor.css │ ├── minislate │ │ ├── LICENSE │ │ ├── css │ │ │ └── minislate.css │ │ ├── init.js │ │ └── js │ │ │ └── minislate.js │ ├── plugins.loader │ ├── sample │ │ └── init.js │ └── vcard │ │ ├── LICENSE │ │ ├── init.js │ │ └── js │ │ └── vcardjs-0.3.js ├── test │ ├── layout_store.spec.js │ └── utils │ │ └── specs_dispatcher.js ├── tests │ ├── casper │ │ ├── common.coffee │ │ ├── full │ │ │ ├── accounts.coffee │ │ │ ├── compose.coffee │ │ │ ├── convactions.coffee │ │ │ ├── convdisplay.coffee │ │ │ ├── conversation.coffee │ │ │ ├── draft.coffee │ │ │ ├── filters.coffee │ │ │ ├── keyboard.coffee │ │ │ ├── list.coffee │ │ │ ├── mailactions.coffee │ │ │ ├── oauth.coffee │ │ │ └── select.coffee │ │ └── unit │ │ │ ├── activity.coffee │ │ │ └── filepicker.coffee │ ├── output │ │ └── .gitignore │ └── router_test.coffee ├── vendor │ ├── aria-tips │ │ ├── aria-tips.css │ │ └── aria-tips.js │ ├── assets │ │ ├── android-chrome-144x144.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-36x36.png │ │ ├── android-chrome-48x48.png │ │ ├── android-chrome-72x72.png │ │ ├── android-chrome-96x96.png │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-144x144.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-57x57.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon-precomposed.png │ │ ├── apple-touch-icon.png │ │ ├── browserconfig.xml │ │ ├── favicon-16x16.png │ │ ├── favicon-194x194.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── fonts │ │ │ ├── adobeblank.woff │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── fontawesome-webfont.woff2 │ │ │ ├── fonts.css │ │ │ ├── mavenpro-bold.woff │ │ │ ├── mavenpro-bold.woff2 │ │ │ ├── mavenpro-regular.woff │ │ │ ├── mavenpro-regular.woff2 │ │ │ ├── sourcecodepro-bold.woff │ │ │ ├── sourcecodepro-regular.woff │ │ │ ├── sourcesanspro-bold-italic.woff │ │ │ ├── sourcesanspro-bold-italic.woff2 │ │ │ ├── sourcesanspro-bold.woff │ │ │ ├── sourcesanspro-bold.woff2 │ │ │ ├── sourcesanspro-italic.woff │ │ │ ├── sourcesanspro-italic.woff2 │ │ │ ├── sourcesanspro-regular.woff │ │ │ └── sourcesanspro-regular.woff2 │ │ ├── humans.txt │ │ ├── mail_stylesheet.css │ │ ├── manifest.json │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ └── mstile-70x70.png │ ├── datePicker │ │ ├── assets │ │ │ └── lang │ │ │ │ ├── ar.js │ │ │ │ ├── cs.js │ │ │ │ ├── da.js │ │ │ │ ├── de.js │ │ │ │ ├── en-US.js │ │ │ │ ├── en.js │ │ │ │ ├── eo.js │ │ │ │ ├── es-MX.js │ │ │ │ ├── es.js │ │ │ │ ├── et.js │ │ │ │ ├── fi.js │ │ │ │ ├── fr.js │ │ │ │ ├── he.js │ │ │ │ ├── hu.js │ │ │ │ ├── id.js │ │ │ │ ├── it.js │ │ │ │ ├── kr.js │ │ │ │ ├── lt.js │ │ │ │ ├── lv.js │ │ │ │ ├── nl.js │ │ │ │ ├── no.js │ │ │ │ ├── pl.js │ │ │ │ ├── pt-br.js │ │ │ │ ├── pt.js │ │ │ │ ├── ru.js │ │ │ │ ├── se.js │ │ │ │ ├── si.js │ │ │ │ ├── sk.js │ │ │ │ ├── th.js │ │ │ │ ├── tr.js │ │ │ │ └── zh.js │ │ └── datepicker.js │ ├── print-helper.css │ └── talkerjs-1.0.1.js └── webpack.config.js ├── coffeelint.json ├── doc ├── architecture-scheme.png ├── architecture-scheme.txt ├── legacy.md ├── routes.md └── specs-v2-frontend.md ├── package.json ├── server.coffee ├── server ├── config.coffee ├── controllers │ ├── accounts.coffee │ ├── activity.coffee │ ├── contacts.coffee │ ├── index.coffee │ ├── mailboxes.coffee │ ├── messages.coffee │ ├── providers.coffee │ ├── routes.coffee │ ├── settings.coffee │ └── test.coffee ├── imap │ ├── account2config.coffee │ ├── connection.coffee │ └── pool.coffee ├── models │ ├── account.coffee │ ├── contact.coffee │ ├── mailbox.coffee │ ├── message.coffee │ ├── model-events.coffee │ ├── requests.coffee │ ├── settings.coffee │ └── store_account_and_boxes.coffee ├── patchs │ ├── conversation.coffee │ └── ignored.coffee ├── processes │ ├── _base.coffee │ ├── _scheduler.coffee │ ├── application_startup.coffee │ ├── mailbox_refresh.coffee │ ├── mailbox_refresh_deep.coffee │ ├── mailbox_refresh_fast.coffee │ ├── mailbox_refresh_list.coffee │ ├── message_move.coffee │ ├── message_remove_by_account.coffee │ ├── message_remove_by_mailbox.coffee │ ├── message_save_or_send.coffee │ ├── orphan_removal.coffee │ └── recover_change_uidvalidity.coffee ├── utils │ ├── constants.coffee │ ├── errors.coffee │ ├── jwz_tools.coffee │ ├── localization.coffee │ ├── logging.coffee │ ├── notifications.coffee │ ├── safeloop.coffee │ ├── socket_handler.coffee │ └── stream_to_array.coffee └── views │ ├── index.jade │ ├── reindexing.jade │ └── test.jade └── tests ├── 00_index.coffee ├── 01_account_creation.coffee ├── 02_account_synchro.coffee ├── 03_mailbox_operations.coffee ├── 04_message_operations.coffee ├── 05_mailbox_deletion.coffee ├── 06_settings.coffee ├── 07_activities.coffee ├── 08_conversations.coffee ├── client-units ├── account_store.coffee └── helpers.coffee ├── fixtures ├── accounts.json ├── contacts.json ├── fixtures.json ├── generator.coffee ├── loader.coffee ├── mailboxes.json ├── messages.json └── samples │ ├── Test attachments.eml │ ├── attach1 │ └── conv.elm ├── helpers.coffee ├── index.coffee ├── smtp-testing └── index.coffee └── units └── mailbox_flattening.coffee /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | # editorconfig is a unified configuration file that all editors can take 3 | # into account 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | indent_style = space 12 | indent_size = 4 13 | 14 | [*.jade] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.csv 2 | *.dat 3 | *.gz 4 | *.log 5 | *.out 6 | *.pid 7 | *.seed 8 | *.swp 9 | *.transifex.coffee 10 | 11 | .node-version 12 | 13 | client/public 14 | 15 | client/app/locales 16 | !client/app/locales/en.json 17 | 18 | last.html 19 | last.png 20 | 21 | lib-cov 22 | 23 | logs 24 | 25 | node_modules 26 | npm-debug.log 27 | 28 | pids 29 | 30 | results 31 | 32 | tests/fixtures/messages_generated.json 33 | tests/fixtures/messages_loaded.json 34 | tests/fixtures/samples/* 35 | 36 | sandbox.coffee 37 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /client 2 | /server 3 | /tests 4 | server.coffee 5 | 6 | doc 7 | 8 | node_modules 9 | 10 | coffeelint.json 11 | .editorconfig 12 | .git* 13 | .travis.yml 14 | .tx 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | matrix: 3 | fast_finish: true 4 | allow_failures: 5 | - node_js: "5" 6 | node_js: 7 | - "0.10" 8 | - "0.12" 9 | - "4" 10 | - "5" 11 | services: 12 | - couchdb 13 | env: 14 | global: 15 | - NODE_ENV=test 16 | - DEBUG_LEVEL=0 17 | - NAME=emails 18 | - TOKEN=apptoken 19 | - CXX=g++-4.8 20 | addons: 21 | apt: 22 | sources: 23 | - ubuntu-toolchain-r-test 24 | packages: 25 | - gcc-4.8 26 | - g++-4.8 27 | 28 | before_install: 29 | - travis_retry npm install npm@latest-2 -g 30 | - travis_retry npm install forever coffee-script -g 31 | - travis_retry pip install --user transifex-client 32 | - travis_retry git clone git://github.com/cozy/cozy-data-system.git 33 | - cd cozy-data-system 34 | - travis_retry npm install #data-system 35 | - env NAME=data-system TOKEN=token forever start -o forever-ds.log build/server.js 36 | - sleep 5 37 | - coffee commands.coffee test-install emails 38 | - cd .. 39 | - mkdir log 40 | - travis_retry npm install # do it now, DovecotTesting needs it 41 | 42 | script: 43 | - npm run build 44 | - npm run fixtures 45 | - npm run test:build 46 | 47 | after_failure: 48 | - cat cozy-data-system/forever-ds.log 49 | - cat cozy-data-system/log/test.log 50 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [cozy-emails.enjson] 5 | file_filter = client/app/locales/.json 6 | source_file = client/app/locales/en.json 7 | source_lang = en 8 | type = KEYVALUEJSON 9 | -------------------------------------------------------------------------------- /build/client/public/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/android-chrome-144x144.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/android-chrome-36x36.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/android-chrome-48x48.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/android-chrome-72x72.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/android-chrome-96x96.png -------------------------------------------------------------------------------- /build/client/public/app.440b8540fcdaa8e9d0e9.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"app.440b8540fcdaa8e9d0e9.css","sourceRoot":""} -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/apple-touch-icon.png -------------------------------------------------------------------------------- /build/client/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #8bee8c 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /build/client/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/favicon-16x16.png -------------------------------------------------------------------------------- /build/client/public/favicon-194x194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/favicon-194x194.png -------------------------------------------------------------------------------- /build/client/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/favicon-32x32.png -------------------------------------------------------------------------------- /build/client/public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/favicon-96x96.png -------------------------------------------------------------------------------- /build/client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/favicon.ico -------------------------------------------------------------------------------- /build/client/public/fonts/adobeblank.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/adobeblank.woff -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /build/client/public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /build/client/public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/mavenpro-bold.woff -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/mavenpro-bold.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/mavenpro-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/mavenpro-regular.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcecodepro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/sourcecodepro-bold.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcecodepro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/sourcecodepro-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/sourcesanspro-bold-italic.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/sourcesanspro-bold-italic.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/sourcesanspro-bold.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/sourcesanspro-bold.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/sourcesanspro-italic.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/sourcesanspro-italic.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/sourcesanspro-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/fonts/sourcesanspro-regular.woff2 -------------------------------------------------------------------------------- /build/client/public/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3 15 | jQuery, Modernizr, 16 | Backbone, Jade, 17 | Stylus, Brunch, 18 | CoffeeScript 19 | -------------------------------------------------------------------------------- /build/client/public/img/spinner-blue.2b4f85ea90a7b17a71f2f681c56c8c0d.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /build/client/public/img/spinner-white.3de41a78b243405e7bd3cedc342d116d.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /build/client/public/img/spinner.cfaa045c92ac91f6e52c69673fedba41.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /build/client/public/lang/cs.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Leden", "\u00DAnor", "B\u0159ezen", "Duben", "Kv\u011Bten", "\u010Cerven", "\u010Cervenec", "Srpen", "Z\u00E1\u0159\u00ED", "\u0158\u00EDjen", "Listopad", "Prosinec"], 3 | monthAbbrs:["Led", "\u00DAno", "B\u0159e", "Dub", "Kv\u011B", "\u010Crv", "\u010Cvc", "Srp", "Z\u00E1\u0159", "\u0158\u00EDj", "Lis", "Pro"], 4 | fullDays:["Pond\u011Bl\u00ED", "\u00DAter\u00FD", "St\u0159eda", "\u010Ctvrtek", "P\u00E1tek", "Sobota", "Ned\u011Ble"], 5 | dayAbbrs:["Po", "\u00DAt", "St", "\u010Ct", "P\u00E1", "So", "Ne"], 6 | titles:["P\u0159edchoz\u00ED m\u011Bs\u00EDc", "N\u00E1sleduj\u00EDc\u00ED m\u011Bsic", "P\u0159edchoz\u00ED rok", "N\u00E1sleduj\u00EDc\u00ED rok", "Dnes", "Uka\u017E kalendar", "td", "T\u00FDden [[%0%]] of [[%1%]]", "T\u00FDden", "Vyber datum", "Klikni a pot\u00E1hni pro p\u0159esun", "Uka\u017E \u201C[[%0%]]\u201D prvn\u00ED", "Jdi na dne\u0161n\u00FD datum", "Nepovolen\u00FD datum"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /build/client/public/lang/da.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "L\u00F8rdag", "S\u00F8ndag"], 5 | dayAbbrs:["Man", "Tir", "Ons", "Tor", "Fre", "L\u00F8r", "S\u00F8n"], 6 | titles:["Forrige m\u00E5ned", "N\u00E6ste m\u00E5ned", "Forrig \u00E5r", "N\u00E6ste \u00E5r", "I dag", "Vis kalender", "uge", "Uge [[%0%]] af [[%1%]]", "Uge", "V\u00E6lg en dato", "Klik \u0026 Tr\u00E6k for at flytte", "Vis \u201C[[%0%]]\u201D F\u00F8rst", "G\u00E5 til dags dato", "Deaktiveret dato"] 7 | }; 8 | try { 9 | if("datePickerController" in window) { 10 | datePickerController.loadLanguage(); 11 | }; 12 | } catch(err) {}; 13 | -------------------------------------------------------------------------------- /build/client/public/lang/de.js: -------------------------------------------------------------------------------- 1 | /* 2009-03-13 12:38:00 MEZ created by Nils Schreiber (n dot schreiber at gmx dot de) */ 2 | var fdLocale = { 3 | fullMonths:["Januar", "Februar", "M\u00E4rz", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"], 4 | monthAbbrs:["Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"], 5 | fullDays:["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"], 6 | dayAbbrs:["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"], 7 | titles:["vorheriger Monat", "n\u00E4chster Monat", "vorheriges Jahr", "n\u00E4chstes Jahr", "Heute", "Kalender anzeigen", "KW", "Woche [[%0%]] von [[%1%]]", "Woche", "W\u00E4hlen Sie ein Datum", "Klicken \u0026 Ziehen zum Verschieben", "Zeige [[%0%]] zuerst", "Zu Heute wechseln", "Datum deaktivieren"]}; 8 | try { 9 | if("datePickerController" in window) { 10 | datePickerController.loadLanguage(); 11 | }; 12 | } catch(err) {}; 13 | -------------------------------------------------------------------------------- /build/client/public/lang/en-US.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["January","February","March","April","May","June","July","August","September","October","November","December"], 3 | monthAbbrs:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"], 4 | fullDays: ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"], 5 | dayAbbrs: ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"], 6 | titles: ["Previous month","Next month","Previous year","Next year", "Today", "Open Calendar", "wk", "Week [[%0%]] of [[%1%]]", "Week", "Select a date", "Click \u0026 Drag to move", "Display \u201C[[%0%]]\u201D first", "Go to Today\u2019s date", "Disabled date:"], 7 | firstDayOfWeek:6 8 | }; 9 | try { 10 | if("datePickerController" in window) { 11 | datePickerController.loadLanguage(); 12 | }; 13 | } catch(err) {}; 14 | -------------------------------------------------------------------------------- /build/client/public/lang/en.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["January","February","March","April","May","June","July","August","September","October","November","December"], 3 | monthAbbrs:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"], 4 | fullDays: ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"], 5 | dayAbbrs: ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"], 6 | titles: ["Previous month","Next month","Previous year","Next year", "Today", "Open Calendar", "wk", "Week [[%0%]] of [[%1%]]", "Week", "Select a date", "Click \u0026 Drag to move", "Display \u201C[[%0%]]\u201D first", "Go to Today\u2019s date", "Disabled date:"], 7 | firstDayOfWeek:0 8 | }; 9 | try { 10 | if("datePickerController" in window) { 11 | datePickerController.loadLanguage(); 12 | }; 13 | } catch(err) {}; 14 | -------------------------------------------------------------------------------- /build/client/public/lang/eo.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | firstDayOfWeek:6, 3 | fullMonths:["Januaro", "Februaro", "Marto", "Aprilo", "Majo", "Junio", "Julio", "A\u016Dgusto", "Septembro", "Oktobro", "Novembro", "Decembro"], 4 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "A\u016Dg", "Sep", "Okt", "Nov", "Dec"], 5 | fullDays:["Lundo", "Mardo", "Merkredo", "\u0134a\u016Ddo", "Vendredo", "Sabato", "Diman\u0109o"], 6 | dayAbbrs:["Lun", "Mar", "Mer", "\u0134a\u016D", "Ven", "Sab", "Dim"], 7 | titles:["Anta\u016Da Monato", "Sekva Monato", "Anta\u016Da Jaro", "Sekva Jaro", "Hodia\u016D", "Montri Kalendaron", "sem", "Semajno [[%0%]] el [[%1%]]", "Semajno", "Elekti Daton", "Tiri por movi", "Montri \u0022[[%0%]]\u0022 unue", "Iri al la hodiaua dato", "Malhavebla Dato\u003A"] 8 | }; 9 | try { 10 | if("datePickerController" in window) { 11 | datePickerController.loadLanguage(); 12 | }; 13 | } catch(err) {}; 14 | try { datePickerController.loadLanguage(); } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/es-MX.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"], 3 | monthAbbrs:["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"], 4 | fullDays:["Lunes", "Martes", "Mi\u00E9rcoles", "Jueves", "Viernes", "S\u00E1bado", "Domingo"], 5 | dayAbbrs:["Lun", "Mar", "Mi\u00E9", "Jue", "Vie", "Sab", "Dom"], 6 | titles:["Mes Anterior", "Siguente Mes", "A\u00F1o Anterior", "Siguiente A\u00F1o", "Hoy", "Mostrar Calendario", "sm", "Semana [[%0%]] de [[%1%]]", "Semana", "Seleccione una fecha", "Arrastrar para mover", "Mostrar [[%0%]] primero", "Ir a Hoy", "Fecha deshabilitada\u003A"]}; 7 | try { if("datePickerController" in window) { datePickerController.loadLanguage(); }; } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/es.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"], 3 | monthAbbrs:["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"], 4 | fullDays:["Lunes", "Martes", "Mi\u00E9rcoles", "Jueves", "Viernes", "S\u00E1bado", "Domingo"], 5 | dayAbbrs:["Lun", "Mar", "Mi\u00E9", "Jue", "Vie", "S\u00E1b", "Dom"], 6 | titles:["Mes Anterior", "Mes Siguiente", "A\u00F1o Anterior", "A\u00F1o Siguiente", "Hoy", "Mostrar Calendario", "sem", "Semana[[%0%]] de [[%1%]]", "Semana", "Seleccione una Fecha", "Haga clic y arrastre para mover", "Mostrar \u0022[[%0%]]\u0022 primero", "Ir al d\u00EDa de hoy", "Deshabilitar Fecha"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /build/client/public/lang/et.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Jaanuar", "Veebruar", "M\u00E4rts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember"], 3 | monthAbbrs:["Jan", "Veb", "M\u00E4r", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Det"], 4 | fullDays:["Esmasp\u00E4ev", "Teisip\u00E4ev", "Kolmap\u00E4ev", "Neljap\u00E4ev", "Reede", "Laup\u00E4ev", "P\u00FChap\u00E4ev"], 5 | dayAbbrs:["M", "T", "K", "N", "R", "L", "P"], 6 | titles:["eelmine kuu", "j\u00E4rgmine kuu", "eelmine aasta", "j\u00E4rgmine aasta", "t\u00E4na", "n\u00E4ita kalendrit", "nd", "n\u00E4dal [[%0%]] - [[%1%]]", "n\u00E4dal", "vali kuup\u00E4ev", "kliki ja lohista", "n\u00E4ita \u201C[[%0%]]\u201D esimest", "mine t\u00E4nasele", "keelatud kuup\u00E4ev\u003A"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /build/client/public/lang/fi.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Tammikuu", "Helmikuu", "Maaliskuu", "Huhtikuu", "Toukokuu", "Kes\u00E4kuu", "Hein\u00E4kuu", "Elokuu", "Syyskuu", "Lokakuu", "Marraskuu", "Joulukuu"], 3 | monthAbbrs:["Tam", "Hel", "Maa", "Huh", "Tou", "Kes", "Hei", "Elo", "Syy", "Lok", "Mar", "Jou"], 4 | fullDays:["Maanantai", "Tiistai", "Keskiviikko", "Torstai", "Perjantai", "Lauantai", "Sunnuntai"], 5 | dayAbbrs:["Ma", "Ti", "Ke", "To", "Pe", "La", "Su"], 6 | titles:["Edellinen kuukausi", "Seuraava kuukausi", "Edellinen vuosi", "Seuraava vuosi", "T\u00E4n\u00E4\u00E4n", "N\u00E4yt\u00E4 kalenteri", "vko", "Viikko [[%0%]] \u002F [[%1%]]", "Viikko", "Valitse p\u00E4iv\u00E4ys", "Valitse ja raahaa liikuttaaksesi", "N\u00E4yt\u00E4 \u201C[[%0%]]\u201D ensin", "Siirry t\u00E4h\u00E4n p\u00E4iv\u00E4\u00E4n", "Ei valittava p\u00E4iv\u00E4ys"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /build/client/public/lang/he.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["\u05D9\u05E0\u05D5\u05D0\u05E8", "\u05E4\u05D1\u05E8\u05D5\u05D0\u05E8", "\u05DE\u05E8\u05E5", "\u05D0\u05E4\u05E8\u05D9\u05DC", "\u05DE\u05D0\u05D9", "\u05D9\u05D5\u05E0\u05D9", "\u05D9\u05D5\u05DC\u05D9", "\u05D0\u05D5\u05D2\u05D5\u05E1\u05D8", "\u05E1\u05E4\u05D8\u05DE\u05D1\u05E8", "\u05D0\u05D5\u05E7\u05D8\u05D5\u05D1\u05E8", "\u05E0\u05D5\u05D1\u05DE\u05D1\u05E8", "\u05D3\u05E6\u05DE\u05D1\u05E8"], 3 | monthAbbrs:["\u05D9\u05E0\u0027", "\u05E4\u05D1\u0027", "\u05DE\u05E8\u0027", "\u05D0\u05E4\u0027", "\u05DE\u0027", "\u05D9\u05D5\u05E0\u0027", "\u05D9\u05D5\u05DC\u0027", "\u05D0\u05D5\u05D2\u0027", "\u05E1\u05E4\u05D8\u0027", "\u05D0\u05D5\u05E7\u05D8\u0027", "\u05E0\u05D5\u05D1\u0027", "\u05D3\u05E6\u0027"], 4 | fullDays:["\u05E9\u05E0\u05D9", "\u05E9\u05DC\u05D9\u05E9\u05D9", "\u05E8\u05D1\u05D9\u05E2\u05D9", "\u05D7\u05DE\u05D9\u05E9\u05D9", "\u05E9\u05D9\u05E9\u05D9", "\u05E9\u05D1\u05EA", "\u05E8\u05D0\u05E9\u05D5\u05DF"], 5 | dayAbbrs:["\u05E9\u05E0\u0027", "\u05E9\u05DC\u0027", "\u05E8\u05D1\u0027", "\u05D7\u05DE\u0027", "\u05E9\u05D9\u0027", "\u05E9\u0027", "\u05E8\u05D0\u05E9\u0027"], 6 | titles:["\u05D7\u05D5\u05D3\u05E9 \u05E7\u05D5\u05D3\u05DD", "\u05D7\u05D5\u05D3\u05E9 \u05D4\u05D1\u05D0", "\u05E9\u05E0\u05D4 \u05E7\u05D5\u05D3\u05DE\u05EA", "\u05E9\u05E0\u05D4 \u05D4\u05D1\u05D0\u05D4", "\u05D4\u05D9\u05D5\u05DD", "\u05D4\u05E6\u05D2 \u05DC\u05D5\u05D7 \u05E9\u05E0\u05D4", "\u05E9\u05D1", "\u05E9\u05D1\u05D5\u05E2[[%0%]] \u05DE\u05EA\u05D5\u05DA [[%1%]]", "Week", "\u05D1\u05D7\u05E8 \u05EA\u05D0\u05E8\u05D9\u05DA", "\u05D4\u05E7\u05DC\u05E7 \u05D5\u05D2\u05E8\u05D5\u05E8 \u05DC\u05D4\u05E2\u05D1\u05E8\u05D4", "\u05D4\u05E6\u05D2 \u0022[[%0%]]\u0022 \u05E8\u05D0\u05E9\u05D9\u05EA", "\u05E2\u05D1\u05D5\u05E8 \u05DC\u05EA\u05D0\u05E8\u05D9\u05DA \u05E0\u05D5\u05DB\u05D7\u05D9", "\u05E0\u05D8\u05E8\u05DC \u05EA\u05D0\u05E8\u05D9\u05DA\u003A"], 7 | rtl:1}; 8 | try { 9 | if("datePickerController" in window) { 10 | datePickerController.loadLanguage(); 11 | }; 12 | } catch(err) {}; 13 | -------------------------------------------------------------------------------- /build/client/public/lang/hu.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["janu\u00E1r", "febru\u00E1r", "m\u00E1rcius", "\u00E1prilis", "m\u00E1jus", "j\u00FAnius", "j\u00FAlius", "augusztus", "szeptember", "okt\u00F3ber", "november", "december"], 3 | monthAbbrs:["jan", "feb", "m\u00E1r", "\u00E1pr", "m\u00E1j", "j\u00FAn", "j\u00FAl", "aug", "szept", "okt", "nov", "dec"], 4 | fullDays:["h\u00E9tf\u0151", "kedd", "szerda", "cs\u00FCt\u00F6rt\u00F6k", "p\u00E9ntek", "szombat", "vas\u00E1rnap"], 5 | dayAbbrs:["h", "k", "sze", "cs", "p", "szo", "v"], 6 | titles:["el\u0151z\u0151 h\u00F3nap", "k\u00F6vetkez\u0151 h\u00F3nap", "el\u0151z\u0151 \u00E9v", "k\u00F6vetkez\u0151 \u00E9v", "ma", "napt\u00E1r megjelen\u00EDt\u00E9se", "h\u00E9t", "[[%1%]]\u002E [[%0%]]\u002E hete", "h\u00E9t", "v\u00E1lasszon d\u00E1tumot", "\u00E1thelyez\u00E9s kattintson \u00E9s h\u00FAzza", "el\u0151sz\u00F6r \u0022[[%0%]]\u0022 mutat\u00E1sa", "ugr\u00E1s a mai naphoz", "letiltott d\u00E1tum"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /build/client/public/lang/id.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januari", "Febuari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ags", "Sep", "Okt", "Nov", "Des"], 4 | fullDays:["Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"], 5 | dayAbbrs:["Sen", "Sel", "Rab", "Kam", "Jum", "Sab", "Min"], 6 | titles:["Bulan Lalu", "Bulan Depan", "Tahun Lalu", "Tahun Depan", "Hari Ini", "Kalender", "mg", "Minggu [[%0%]] dari [[%1%]]", "Minggu", "Pilih Tanggal", "Klik \u0026 drag untuk geser", "Tampilkan \u201C[[%0%]]\u201D diawal", "Ke tanggal hari ini", "Non\u002Daktifkan Kalender"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /build/client/public/lang/it.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"], 3 | monthAbbrs:["Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic"], 4 | fullDays:["Luned\u00EC", "Marted\u00EC", "Mercoled\u00EC", "Gioved\u00EC", "Venerd\u00EC", "Sabato", "Domenica"], 5 | dayAbbrs:["Lun", "Mar", "Mer", "Gio", "Ven", "Sab", "Dom"], 6 | titles:["Mese precedente", "Mese successivo", "Anno precedente", "Anno successivo", "Oggi", "Mostra Calendario", "sett", "Settimana [[%0%]] di [[%1%]]", "Settimana", "Seleziona una data", "Clicca \u0026 trascina per spostare", "Mostra prima \u201C[[%0%]]\u201D", "Vai alla data Odierna", "Data disabilitata"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/kr.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["1\uC6D4", "2\uC6D4", "3\uC6D4", "4\uC6D4", "5\uC6D4", "6\uC6D4", "7\uC6D4", "8\uC6D4", "9\uC6D4", "10\uC6D4", "11\uC6D4", "12\uC6D4"], 3 | monthAbbrs:["1\uC6D4", "2\uC6D4", "3\uC6D4", "4\uC6D4", "5\uC6D4", "6\uC6D4", "7\uC6D4", "8\uC6D4", "9\uC6D4", "10\uC6D4", "11\uC6D4", "12\uC6D4"], 4 | fullDays:["\uC6D4\uC694\uC77C", "\uD654\uC694\uC77C", "\uC218\uC694\uC77C", "\uBAA9\uC694\uC77C", "\uAE08\uC694\uC77C", "\uD1A0\uC694\uC77C", "\uC77C\uC694\uC77C"], 5 | dayAbbrs:["\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0", "\uC77C"], 6 | titles:["\uC9C0\uB09C \uB2EC", "\uB2E4\uC74C \uB2EC", "\uC9C0\uB09C \uC8FC", "\uB2E4\uC74C \uC8FC", "\uC624\uB298", "\uB2EC\uB825 \uBCF4\uAE30", "\uC8FC", "[[%1%]]\uC8FC\uC911 [[%0%]]\uC8FC\uCC28", "\uC8FC", "\uB0A0\uC9DC \uC120\uD0DD", "\uB04C\uC5B4\uC11C \uC62E\uAE30\uAE30", "\u0022[[%0%]]\u0022 \uC6B0\uC120\uD45C\uC2DC", "\uC624\uB298\uB0A0\uC9DC\uB85C", "\uC120\uD0DD\uBD88\uAC00 \uB0A0\uC9DC"]};$ 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /build/client/public/lang/lt.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Sausis", "Vasaris", "Kovas", "Balandis", "Gegu\u017E\u0117", "Bir\u017Eelis", "Liepa", "Rugpj\u016Btis", "Rugs\u0117jis", "Spalis", "Lapkritis", "Gruodis"], 3 | monthAbbrs:["Sau", "Vas", "Kov", "Bal", "Geg", "Bir", "Lie", "Rugp", "Rugs", "Spa", "Lap", "Gruod"], 4 | fullDays:["Pirmadienis", "Antradienis", "Tre\u010Diadienis", "Ketvirtadienis", "Penktadienis", "\u0160e\u0161tadienis", "Sekmadienis"], 5 | dayAbbrs:["P", "A", "T", "K", "Pn", "\u0160", "S"], 6 | titles:["Buv\u0119s m\u0117nuo", "Kitas m\u0117nuo", "Buv\u0119 metai", "Kiti metai", "\u0160iandien", "Rodyti kalendori\u0173", "sav\u002E", "Savait\u0117 [[%0%]] i\u0161 [[%1%]]", "Savait\u0117", "Pasirinkite dat\u0105", "Paspauskite nor\u0117dami perkleti", "Pirmiausia rodyti \u201C[[%0%]]\u201D ", "\u0160iandienos data", "Negalima data"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/lv.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Janv\u0101ris", "Febru\u0101ris", "Marts", "Apr\u012Blis", "Maijs", "J\u016Bnijs", "J\u016Blijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["Pirmdiena", "Otrdiena", "Tre\u0161diena", "Ceturdiena", "Piektdiena", "Sestdiena", "Sv\u0113tdiena"], 5 | dayAbbrs:["P", "O", "T", "C", "P", "S", "Sv"], 6 | titles:["Iepriek\u0161\u0113jais m\u0113nesis", "N\u0101kamais m\u0113nesis", "Iepriek\u0161\u0113jais gads", "N\u0101kamais gads", "\u0160odiena", "R\u0101d\u012Bt kalend\u0101ru", "ned", "Ned\u0113\u013Ca [[%0%]] no [[%1%]]", "Ned\u0113\u013Ca", "Izv\u0113laties datumu", "Spiest un vilk lai p\u0101rb\u012Bd\u012Btu", "Par\u0101d\u012Bt \u0022[[%0%]]\u0022 pirmos", "Iet uz \u0161odienas datumu", "Inval\u012Bdu diena"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/nl.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"], 3 | monthAbbrs:["Jan", "Feb", "Mrt", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag", "Zondag"], 5 | dayAbbrs:["Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"], 6 | titles:["Vorige maand", "Volgende maand", "Vorig jaar", "Volgend jaar", "Vandaag", "Toon kalender", "wk", "Week [[%0%]] van [[%1%]]", "Week", "Kies een datum", "Klik en versleep", "Zet \u201C[[%0%]]\u201D vooraan", "Ga naar vandaag", "Geblokkeerde datum"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/no.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des"], 4 | fullDays:["Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "L\u00F8rdag", "S\u00F8ndag"], 5 | dayAbbrs:["Man", "Tir", "Ons", "Tor", "Fre", "L\u00F8r", "S\u00F8n"], 6 | titles:["Forrige m\u00E5ned", "Neste m\u00E5ned", "Forrige \u00E5r", "Neste \u00E5r", "I dag", "Vis kalender", "uk", "Uke [[%0%]] av [[%1%]]", "Uke", "Velg dato", "Klikk og dra for \u00E5 flytte", "Vis [[%0%]] f\u00F8rst", "G\u00E5 til dagens dato", "Deaktivert dato"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/pl.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Stycze\u0144", "Luty", "Marzec", "Kwiecie\u0144", "Maj", "Czerwiec", "Lipiec", "Sierpie\u0144", "Wrzesie\u0144", "Pa\u017Adziernik", "Listopad", "Grudzie\u0144"], 3 | monthAbbrs:["Sty", "Lut", "Mar", "Kwi", "Maj", "Cze", "Lip", "Sie", "Wrz", "Pa\u017A", "Lis", "Gru"], 4 | fullDays:["Poniedzia\u0142ek", "Wtorek", "\u015Aroda", "Czwartek", "Pi\u0105tek", "Sobota", "Niedziela"], 5 | dayAbbrs:["Pon", "Wto", "\u015Aro", "Czw", "Pi\u0105", "Sob", "Nie"], 6 | titles:["Poprzedni miesi\u0105c", "Nast\u0119pny miesi\u0105c", "Poprzedni rok", "Nast\u0119pny rok", "Dzi\u015B", "Poka\u017C kalendarz", "Tyd", "Tydzie\u0144 [[%0%]] z [[%1%]]", "Tydzie\u0144", "Wybierz dat\u0119", "Przeci\u0105gnij i upu\u015B\u0107", "Wy\u015Bwietla \u201C[[%0%]]\u201D Jako pierwszy", "Zaznacza dzie\u0144 dzisiejszy", "Data wy\u0142\u0105czona"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/pt-br.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Janeiro", "Fevereiro", "Mar\u00E7o", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"], 3 | monthAbbrs:["Jan", "Fev", "Mar", "Abr", "Maio", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"], 4 | fullDays:["Segunda\u002DFeira", "Ter\u00E7a\u002DFeira", "Quarta\u002DFeira", "Quinta\u002DFeira", "Sexta\u002DFeira", "S\u00E1bado", "Domingo"], 5 | dayAbbrs:["Seg", "Ter", "Qua", "Qui", "Sex", "Sab", "Dom"], 6 | titles:["M\u00EAs Anterior", "Pr\u00F3ximo M\u00EAs", "Ano Anterior", "Pr\u00F3ximo Ano", "Hoje", "Exibir Calend\u00E1rio", "Sem", "Semana [[%0%]] de [[%1%]]", "Semana", "Selecione uma Data", "Clique e Arraste para Mover", "Exibir \u0022[[%0%]]\u0022 primeiro", "Ir para Data de Hoje", "Data Desabilitada"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/pt.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Janeiro", "Fevereiro", "Mar\u00E7o", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"], 3 | monthAbbrs:["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"], 4 | fullDays:["Segunda", "Ter\u00E7a", "Quarta", "Quinta", "Sexta", "S\u00E1bado", "Domingo"], 5 | dayAbbrs:["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 6 | titles:["M\u00EAs Anterior", "M\u00EAs Seguinte", "Ano Anterior", "Ano Seguinte", "Hoje", "Mostrar Calend\u00E1rio", "sem", "Semana [[%0%]] of [[%1%]]", "Semana", "Seleccionar data", "Arrastar", "Mostrar \u201C[[%0%]]\u201D first", "Ir para data actual", "Desabilitar data"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/se.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["M\u00E5ndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "L\u00F6rdag", "S\u00F6ndag"], 5 | dayAbbrs:["M\u00E5n", "Tis", "Ons", "Tors", "Fre", "L\u00F6r", "S\u00F6n"], 6 | titles:["F\u00F6rra m\u00E5naden", "N\u00E4sta m\u00E5nad", "F\u00F6rra \u00E5ret", "N\u00E4sta \u00E5r", "Idag", "Visa kalender", "Vecka", "Vecka [[%0%]] av [[%1%]]", "Vecka", "V\u00E4lj ett datum", "Klicka och Drag", "Visa \u0022[[%0%]]\u0022 f\u00F6rsta", "Dagens datum", "Inaktiverat datum"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/si.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["Ponedeljek", "Torek", "Sreda", "\u010Cetrtek", "Petek", "Sobota", "Nedelja"], 5 | dayAbbrs:["Pon", "Tor", "Sre", "\u010Cet", "Pet", "Sob", "Ned"], 6 | titles:["Prej\u0161nji mesec", "Naslednji mesec", "Prej\u0161nje leto", "Naslednje leto", "Danes", "Poka\u017Ei koledar", "td", "Teden [[%0%]] od [[%1%]]", "Teden", "Izberi datum", "Vleci in spusti za premik", "Prika\u017Ei najprej \u201C[[%0%]]\u201D", "Pojdi na dana\u0161nji datum", "Neveljaven datum"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/sk.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Janu\u00E1r", "Febru\u00E1r", "Marec", "Apr\u00EDl", "M\u00E1j", "J\u00FAn", "J\u00FAl", "August", "September", "Okt\u00F3ber", "November", "December"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "M\u00E1j", "J\u00FAn", "J\u00FAl", "Aug", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["Pondelok", "Utorok", "Streda", "\u0160tvrtok", "Piatok", "Sobota", "Nede\u013Ea"], 5 | dayAbbrs:["Pon", "Uto", "Str", "\u0160tv", "Pia", "Sob", "Ned"], 6 | titles:["Minul\u00FD mesiac", "\u010Eal\u0161\u00ED mesiac", "Minul\u00FD rok", "\u010Eal\u0161\u00ED rok", "Dnes", "Uk\u00E1za\u0165 kalend\u00E1r", "t\u00FD\u017E", "T\u00FD\u017Ede\u0148 [[%0%]] z [[%1%]]", "T\u00FD\u017Ede\u0148", "Vyberte d\u00E1tum", "Kliknite a potiahnite pre presum", "Uk\u00E1\u017E \u0022[[%0%]]\u0022 prv\u00FD", "Dnes", "Nepovolen\u00FD d\u00E1tum\u003A"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/tr.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Ocak", "\u015Eubat", "Mart", "Nisan", "May\u0131s", "Haziran", "Temmuz", "A\u011Fustos", "Eyl\u00FCl", "Ekim", "Kas\u0131m", "Aral\u0131k"], 3 | monthAbbrs:["Oca", "\u015Eub", "Mar", "Nis", "May", "Haz", "Tem", "A\u011Fu", "Eyl", "Eki", "Kas", "Ara"], 4 | fullDays:["Pazartesi", "Sal\u0131", "\u00C7ar\u015Famba", "Per\u015Fembe", "Cuma", "Cumartesi", "Pazar"], 5 | dayAbbrs:["Pzt", "Sal", "\u00C7ar", "Per", "Cum", "Cmt", "Paz"], 6 | titles:["\u00D6nceki Ay", "Sonraki Ay", "\u00D6nceki Y\u0131l", "Sonraki Y\u0131l", "Bug\u00FCn", "Takvimi G\u00F6ster", "hafta", "[[%1%]]\u0027de [[%0%]] \u002Ehafta", "Hafta", "G\u00FCn Se\u00E7", "T\u0131kla ve S\u00FCr\u00FCkle", "G\u00F6ster \u0022[[%0%]]\u0022 \u00F6nce", "Bug\u00FCne git", "Tarih kapal\u0131"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/lang/zh.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["\u4E00\u6708", "\u4E8C\u6708", "\u4E09\u6708", "\u56DB\u6708", "\u4E94\u6708", "\u516D\u6708", "\u4E03\u6708", "\u516B\u6708", "\u4E5D\u6708", "\u5341\u6708", "\u5341\u4E00\u6708", "\u5341\u4E8C\u6708"], 3 | monthAbbrs:["\u4E00\u6708", "\u4E8C\u6708", "\u4E09\u6708", "\u56DB\u6708", "\u4E94\u6708", "\u516D\u6708", "\u4E03\u6708", "\u516B\u6708", "\u4E5D\u6708", "\u5341\u6708", "\u5341\u4E00\u6708", "\u5341\u4E8C\u6708"], 4 | fullDays:["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"], 5 | dayAbbrs:["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"], 6 | titles:["\u524D\u4E00\u4E2A\u6708", "\u540E\u4E00\u4E2A\u6708", "\u4E0A\u4E00\u5E74", "\u4E0B\u4E00\u5E74", "\u4ECA\u5929", "\u663E\u793A\u65E5\u5386", "\u5468", "[[%1%]]\u7B2C[[%0%]]\u5468", "\u5468", "\u9009\u62E9\u65E5\u671F", "\u70B9\u51FB\u0026\u62D6\u52A8", "\u6700\u5148\u663E\u793A\u201C[[%0%]]\u201D", "\u8DF3\u8F6C\u5230\u4ECA\u5929", "\u5C4F\u853D\u65E5\u671F"]}; 7 | try { datePickerController.loadLanguage(); } catch(err) {}; -------------------------------------------------------------------------------- /build/client/public/mail_stylesheet.css: -------------------------------------------------------------------------------- 1 | body { 2 | visibility: visible !important; 3 | font-family: 'Source Sans Pro', sans-serif; 4 | } 5 | table { 6 | max-width: 100%; 7 | } 8 | img { 9 | max-width: 100%; 10 | } 11 | blockquote { 12 | margin-left: .5em; 13 | padding-left: .5em; 14 | border-left: 2px solid blue; 15 | color: blue; 16 | } 17 | blockquote blockquote { border-color: red !important; color: red; } 18 | blockquote blockquote blockquote { border-color: green !important; color: green; } 19 | blockquote blockquote blockquote blockquote { border-color: magenta !important; color: magenta; } 20 | blockquote blockquote blockquote blockquote blockquote { border-color: blue !important; color: blue; } 21 | 22 | .textOnly { 23 | font-family: 'Source Sans Pro', sans-serif; 24 | white-space: pre-line; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /build/client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cozy Emails", 3 | "icons": [ 4 | { 5 | "src": "\/apps\/emails\/android-chrome-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/apps\/emails\/android-chrome-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/apps\/emails\/android-chrome-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/apps\/emails\/android-chrome-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/apps\/emails\/android-chrome-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/apps\/emails\/android-chrome-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /build/client/public/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/mstile-144x144.png -------------------------------------------------------------------------------- /build/client/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/mstile-150x150.png -------------------------------------------------------------------------------- /build/client/public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/mstile-310x150.png -------------------------------------------------------------------------------- /build/client/public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/mstile-310x310.png -------------------------------------------------------------------------------- /build/client/public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/build/client/public/mstile-70x70.png -------------------------------------------------------------------------------- /build/server.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var americano, application; 3 | 4 | americano = require('americano'); 5 | 6 | module.exports = application = function(options, callback) { 7 | if (options == null) { 8 | options = {}; 9 | } 10 | if (callback == null) { 11 | callback = function() {}; 12 | } 13 | options.name = 'cozy-emails'; 14 | if (options.root == null) { 15 | options.root = __dirname; 16 | } 17 | if (options.port == null) { 18 | options.port = process.env.PORT || 9125; 19 | } 20 | if (options.host == null) { 21 | options.host = process.env.HOST || '127.0.0.1'; 22 | } 23 | return americano.start(options, callback); 24 | }; 25 | 26 | if (!module.parent) { 27 | application(); 28 | } 29 | -------------------------------------------------------------------------------- /build/server/config.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var americano, config, cozydb, errorHandler, fs, log, path, useBuildView, viewsDir; 3 | 4 | fs = require('fs'); 5 | 6 | path = require('path'); 7 | 8 | americano = require('americano'); 9 | 10 | cozydb = require('cozydb'); 11 | 12 | log = require('./utils/logging')({ 13 | prefix: 'config' 14 | }); 15 | 16 | errorHandler = require('./utils/errors').errorHandler; 17 | 18 | viewsDir = path.resolve(__dirname, 'views'); 19 | 20 | useBuildView = fs.existsSync(path.resolve(viewsDir, 'index.js')); 21 | 22 | config = { 23 | common: { 24 | set: { 25 | 'view engine': useBuildView ? 'js' : 'jade', 26 | 'views': viewsDir 27 | }, 28 | engine: { 29 | js: function(path, locals, callback) { 30 | return callback(null, require(path)(locals)); 31 | } 32 | }, 33 | use: [ 34 | americano.bodyParser(), americano.methodOverride(), americano["static"](__dirname + '/../client/public', { 35 | maxAge: 86400000 36 | }) 37 | ], 38 | useAfter: [errorHandler], 39 | afterStart: function(app, server) { 40 | var ApplicationStartup, Scheduler, SocketHandler, assets, error, proc; 41 | Scheduler = require('./processes/_scheduler'); 42 | SocketHandler = require('./utils/socket_handler'); 43 | ApplicationStartup = require('./processes/application_startup'); 44 | SocketHandler.setup(app, server); 45 | try { 46 | assets = require('../webpack-assets.json').main; 47 | } catch (error) { 48 | assets = { 49 | js: 'app.js', 50 | css: 'app.css' 51 | }; 52 | } 53 | app.locals.assets = assets; 54 | proc = new ApplicationStartup(); 55 | return Scheduler.schedule(proc, function(err) { 56 | return log.info("Initialization complete"); 57 | }); 58 | } 59 | }, 60 | development: [americano.logger('dev')], 61 | production: [americano.logger('short')], 62 | plugins: ['cozydb'] 63 | }; 64 | 65 | module.exports = config; 66 | -------------------------------------------------------------------------------- /build/server/controllers/contacts.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Contact; 3 | 4 | Contact = require('../models/contact'); 5 | 6 | module.exports.list = function(req, res, next) { 7 | return Contact.list(function(err, contacts) { 8 | if (err) { 9 | return next(err); 10 | } 11 | return res.send(contacts); 12 | }); 13 | }; 14 | 15 | module.exports.picture = function(req, res, next) { 16 | var filename, id, stream; 17 | id = req.params.contactID; 18 | filename = 'picture'; 19 | stream = Contact.getFile(id, filename, function(err) { 20 | return next(err); 21 | }); 22 | stream.pipefilter = function(dsResponse) { 23 | var header, ref, results, value; 24 | ref = dsResponse.headers; 25 | results = []; 26 | for (header in ref) { 27 | value = ref[header]; 28 | results.push(res.setHeader(header, value)); 29 | } 30 | return results; 31 | }; 32 | req.on('close', function() { 33 | return stream.abort(); 34 | }); 35 | res.on('close', function() { 36 | return stream.abort(); 37 | }); 38 | return stream.pipe(res); 39 | }; 40 | -------------------------------------------------------------------------------- /build/server/controllers/settings.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Settings; 3 | 4 | Settings = require('../models/settings'); 5 | 6 | module.exports = { 7 | get: function(req, res, next) { 8 | return Settings.get(function(err, settings) { 9 | if (err) { 10 | return next(err); 11 | } 12 | return res.send(settings); 13 | }); 14 | }, 15 | change: function(req, res, next) { 16 | return Settings.set(req.body, function(err, updated) { 17 | if (err) { 18 | return next(err); 19 | } 20 | return res.send(updated); 21 | }); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /build/server/controllers/test.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Account, async, cozydb; 3 | 4 | async = require('async'); 5 | 6 | Account = require('../models/account'); 7 | 8 | cozydb = require('cozydb'); 9 | 10 | module.exports.main = function(req, res, next) { 11 | return async.series([ 12 | function(cb) { 13 | return cozydb.api.getCozyLocale(cb); 14 | }, function(cb) { 15 | return Account.request('all', cb); 16 | } 17 | ], function(err, results) { 18 | var accounts, locale; 19 | if (err != null) { 20 | console.log(err); 21 | return res.render('test.jade', { 22 | imports: "console.log(\"" + err + "\")\nwindow.locale = \"en\";\nwindow.accounts = {};" 23 | }); 24 | } else { 25 | locale = results[0], accounts = results[1]; 26 | return res.render('test.jade', { 27 | imports: "window.locale = \"" + locale + "\";\nwindow.accounts = " + (JSON.stringify(accounts)) + ";" 28 | }); 29 | } 30 | }); 31 | }; 32 | -------------------------------------------------------------------------------- /build/server/models/model-events.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var EventEmitter, _; 3 | 4 | EventEmitter = require('events').EventEmitter; 5 | 6 | _ = require('lodash'); 7 | 8 | module.exports.wrapModel = function(Model) { 9 | var _oldCreate, _oldDestroy, _oldUpdateAttributes; 10 | Model.ee = new EventEmitter(); 11 | Model.on = function() { 12 | return Model.ee.on.apply(Model.ee, arguments); 13 | }; 14 | _oldCreate = Model.create; 15 | Model.create = function(data, callback) { 16 | return _oldCreate.call(Model, data, function(err, created) { 17 | if (!err) { 18 | Model.ee.emit('create', created); 19 | } 20 | return callback(err, created); 21 | }); 22 | }; 23 | _oldUpdateAttributes = Model.prototype.updateAttributes; 24 | Model.prototype.updateAttributes = function(data, callback) { 25 | var old; 26 | old = _.cloneDeep(this.toObject()); 27 | return _oldUpdateAttributes.call(this, data, (function(_this) { 28 | return function(err, updated) { 29 | if (!err) { 30 | Model.ee.emit('update', _this, old); 31 | } 32 | return callback(err, updated); 33 | }; 34 | })(this)); 35 | }; 36 | _oldDestroy = Model.prototype.destroy; 37 | Model.prototype.destroy = function(callback) { 38 | var id, old; 39 | old = this.toObject(); 40 | id = old.id; 41 | return _oldDestroy.call(this, function(err) { 42 | if (!err) { 43 | Model.ee.emit('delete', id, old); 44 | } 45 | return callback(err); 46 | }); 47 | }; 48 | return Model; 49 | }; 50 | -------------------------------------------------------------------------------- /build/server/utils/constants.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | exports.MSGBYPAGE = 30; 3 | 4 | exports.LIMIT_DESTROY = 200; 5 | 6 | exports.LIMIT_UPDATE = 50; 7 | 8 | exports.CONCURRENT_DESTROY = 1; 9 | 10 | exports.FETCH_AT_ONCE = 10000; 11 | 12 | exports.LIMIT_BY_BOX = 1000; 13 | 14 | exports.RFC6154 = { 15 | draftMailbox: '\\Drafts', 16 | sentMailbox: '\\Sent', 17 | trashMailbox: '\\Trash', 18 | allMailbox: '\\All', 19 | junkMailbox: '\\Junk', 20 | flaggedMailbox: '\\Flagged' 21 | }; 22 | -------------------------------------------------------------------------------- /build/server/utils/localization.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Polyglot, cozydb, drainWaiting, translator, waiting; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | Polyglot = require('node-polyglot'); 7 | 8 | waiting = []; 9 | 10 | translator = null; 11 | 12 | drainWaiting = function(err, translator) { 13 | var callback, i, len, results; 14 | results = []; 15 | for (i = 0, len = waiting.length; i < len; i++) { 16 | callback = waiting[i]; 17 | results.push(callback(err, translator)); 18 | } 19 | return results; 20 | }; 21 | 22 | exports.getPolyglot = function(callback) { 23 | if (translator) { 24 | return callback(null, translator); 25 | } else { 26 | return waiting.push(callback); 27 | } 28 | }; 29 | 30 | cozydb.api.getCozyLocale(function(err, locale) { 31 | var e, error, phrases, polyglot; 32 | phrases = (function() { 33 | var error; 34 | try { 35 | return require("../../client/app/locales/" + locale); 36 | } catch (error) { 37 | e = error; 38 | return require("../../client/app/locales/en"); 39 | } 40 | })(); 41 | try { 42 | polyglot = new Polyglot({ 43 | locale: locale, 44 | phrases: phrases 45 | }); 46 | } catch (error) { 47 | e = error; 48 | return drainWaiting(e, function() {}); 49 | } 50 | translator = polyglot.t.bind(polyglot); 51 | return drainWaiting(null, translator); 52 | }); 53 | -------------------------------------------------------------------------------- /build/server/utils/logging.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var COLORS, LOG_LEVEL, MAX_INDEX, addToLastLogs, index, lastDate, lastLogs, pad, util; 3 | 4 | util = require('util'); 5 | 6 | COLORS = ['\x1B[32mDBUG\x1B[39m', '\x1B[34mINFO\x1B[39m', '\x1B[33mWARN\x1B[39m', '\x1B[31mEROR\x1B[39m']; 7 | 8 | LOG_LEVEL = 1; 9 | 10 | lastLogs = new Array(15); 11 | 12 | lastDate = +new Date(); 13 | 14 | index = -1; 15 | 16 | MAX_INDEX = 15; 17 | 18 | addToLastLogs = function() { 19 | index = (index + 1) % MAX_INDEX; 20 | return lastLogs[index] = util.format.apply(this, arguments); 21 | }; 22 | 23 | pad = function(nb) { 24 | return ((nb + 10000) + "").substring(1); 25 | }; 26 | 27 | module.exports = function(options) { 28 | var api, logger, prefix; 29 | prefix = typeof options === 'string' ? options : options.prefix; 30 | logger = function(level) { 31 | return function() { 32 | var arg, args, delta, i, j, len, newDate; 33 | newDate = +new Date(); 34 | delta = newDate - lastDate; 35 | lastDate = newDate; 36 | args = new Array(arguments.length + 3); 37 | args[0] = COLORS[level]; 38 | args[1] = "+" + ((delta + 10000) + "").substring(1); 39 | args[2] = prefix; 40 | for (i = j = 0, len = arguments.length; j < len; i = ++j) { 41 | arg = arguments[i]; 42 | args[i + 3] = arg; 43 | } 44 | addToLastLogs.apply(null, args); 45 | if (level < LOG_LEVEL) { 46 | return null; 47 | } 48 | if (level === 3) { 49 | return console.error.apply(console, args); 50 | } else { 51 | return console.log.apply(console, args); 52 | } 53 | }; 54 | }; 55 | return api = { 56 | debug: logger(0), 57 | info: logger(1), 58 | warn: logger(2), 59 | error: logger(3) 60 | }; 61 | }; 62 | 63 | module.exports.getLasts = function() { 64 | return lastLogs.slice(index + 1, +MAX_INDEX + 1 || 9e9).join("\n") + "\n" + lastLogs.slice(0, +index + 1 || 9e9).join("\n"); 65 | }; 66 | -------------------------------------------------------------------------------- /build/server/utils/notifications.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var NotificationsHelper, SocketHandler, emailsAppRessource, localization, log, logError, notificationsHelper; 3 | 4 | NotificationsHelper = require('cozy-notifications-helper'); 5 | 6 | notificationsHelper = new NotificationsHelper('emails'); 7 | 8 | localization = require('./localization'); 9 | 10 | SocketHandler = require('./socket_handler'); 11 | 12 | log = require('./logging')({ 13 | prefix: 'notifications' 14 | }); 15 | 16 | emailsAppRessource = { 17 | app: 'emails', 18 | url: '/' 19 | }; 20 | 21 | logError = function(err) { 22 | if (err) { 23 | return log.error("fail to create notif", err); 24 | } 25 | }; 26 | 27 | exports.accountFirstImportComplete = function(account) { 28 | return localization.getPolyglot(function(err, t) { 29 | var text; 30 | text = t('notif complete', { 31 | account: account.label 32 | }); 33 | return notificationsHelper.createTemporary({ 34 | resource: emailsAppRessource, 35 | text: text 36 | }, logError); 37 | }); 38 | }; 39 | 40 | exports.accountRefreshed = function(account) { 41 | return localization.getPolyglot(function(err, t) { 42 | return account.totalUnread(function(err, totalUnread) { 43 | var accountID, data, message, ref; 44 | ref = "notif-unread-" + account.id; 45 | if (totalUnread === 0) { 46 | return notificationsHelper.destroy(ref); 47 | } else { 48 | message = t('notif new', { 49 | smart_count: totalUnread, 50 | account: account.label 51 | }); 52 | accountID = account.id; 53 | data = { 54 | message: message, 55 | totalUnread: totalUnread, 56 | accountID: accountID 57 | }; 58 | SocketHandler.notify('refresh.notify', data, logError); 59 | return notificationsHelper.createOrUpdatePersistent(ref, { 60 | resource: { 61 | app: 'emails', 62 | url: "/#account/" + accountID 63 | }, 64 | text: message 65 | }, logError); 66 | } 67 | }); 68 | }); 69 | }; 70 | -------------------------------------------------------------------------------- /build/server/utils/safeloop.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var async; 3 | 4 | async = require('async'); 5 | 6 | module.exports = function(items, iterator, done) { 7 | var errors; 8 | errors = []; 9 | return async.eachSeries(items, function(item, next) { 10 | var wasImmediate; 11 | wasImmediate = true; 12 | iterator(item, function(err) { 13 | if (err) { 14 | errors.push(err); 15 | } 16 | if (wasImmediate) { 17 | return setImmediate(next); 18 | } else { 19 | return next(null); 20 | } 21 | }); 22 | return wasImmediate = false; 23 | }, function(err) { 24 | if (err) { 25 | return done(err); 26 | } 27 | return done(errors); 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /build/server/utils/stream_to_array.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Bufferer, stream, stream_to_buffer_array, 3 | bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 4 | extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, 5 | hasProp = {}.hasOwnProperty; 6 | 7 | stream = require('stream'); 8 | 9 | module.exports = stream_to_buffer_array = function(stream, cb) { 10 | var parts; 11 | parts = []; 12 | stream.on('error', function(err) { 13 | return cb(err); 14 | }); 15 | stream.on('data', function(d) { 16 | return parts.push(d); 17 | }); 18 | return stream.on('end', function() { 19 | return cb(null, parts); 20 | }); 21 | }; 22 | 23 | module.exports.Bufferer = Bufferer = (function(superClass) { 24 | extend(Bufferer, superClass); 25 | 26 | function Bufferer(onDone) { 27 | this.end = bind(this.end, this); 28 | this._write = bind(this._write, this); 29 | Bufferer.__super__.constructor.apply(this, arguments); 30 | this.chunks = []; 31 | this.onDone = onDone; 32 | } 33 | 34 | Bufferer.prototype._write = function(chunk, enc, next) { 35 | this.chunks.push(chunk); 36 | return next(); 37 | }; 38 | 39 | Bufferer.prototype.end = function() { 40 | return this.onDone(null, Buffer.concat(this.chunks)); 41 | }; 42 | 43 | return Bufferer; 44 | 45 | })(stream.Writable); 46 | -------------------------------------------------------------------------------- /build/server/views/test.js: -------------------------------------------------------------------------------- 1 | var jade = require('pug-runtime'); module.exports = function template(locals) { 2 | var buf = []; 3 | var jade_mixins = {}; 4 | var jade_interp; 5 | ;var locals_for_with = (locals || {});(function (assets, imports) { 6 | buf.push("Cozy Emails");}.call(this,"assets" in locals_for_with?locals_for_with.assets:typeof assets!=="undefined"?assets:undefined,"imports" in locals_for_with?locals_for_with.imports:typeof imports!=="undefined"?imports:undefined));;return buf.join(""); 7 | } -------------------------------------------------------------------------------- /build/webpack-assets.json: -------------------------------------------------------------------------------- 1 | {"main":{"js":"app.440b8540fcdaa8e9d0e9.js","css":"app.440b8540fcdaa8e9d0e9.css"}} -------------------------------------------------------------------------------- /client/app/actions/contact_action_creator.coffee: -------------------------------------------------------------------------------- 1 | AppDispatcher = require '../app_dispatcher' 2 | {ActionTypes} = require '../constants/app_constants' 3 | Activity = require '../utils/activity_utils' 4 | LayoutActionCreator = require '../actions/layout_action_creator' 5 | 6 | module.exports = ContactActionCreator = 7 | 8 | 9 | searchContact: (query) -> 10 | options = 11 | name: 'search' 12 | data: 13 | type: 'contact' 14 | query: query 15 | 16 | activity = new Activity options 17 | 18 | activity.onsuccess = -> 19 | AppDispatcher.handleViewAction 20 | type: ActionTypes.RECEIVE_RAW_CONTACT_RESULTS 21 | value: @result 22 | 23 | activity.onerror = -> 24 | console.log "KO", @error, @name 25 | 26 | 27 | searchContactLocal: (query) -> 28 | AppDispatcher.handleViewAction 29 | type: ActionTypes.CONTACT_LOCAL_SEARCH 30 | value: query 31 | 32 | 33 | createContact: (contact, callback) -> 34 | options = 35 | name: 'create' 36 | data: 37 | type: 'contact' 38 | contact: contact 39 | 40 | activity = new Activity options 41 | 42 | activity.onsuccess = (err, res) -> 43 | AppDispatcher.handleViewAction 44 | type: ActionTypes.RECEIVE_RAW_CONTACT_RESULTS 45 | value: @result 46 | 47 | msg = t('contact create success', 48 | {contact: contact.name or contact.address}) 49 | LayoutActionCreator.notify msg, autoclose: true 50 | callback?() 51 | 52 | activity.onerror = -> 53 | console.log @name 54 | msg = t('contact create error', {error: @name}) 55 | LayoutActionCreator.alertError msg, autoclose: true 56 | callback?() 57 | 58 | -------------------------------------------------------------------------------- /client/app/actions/settings_action_creator.coffee: -------------------------------------------------------------------------------- 1 | XHRUtils = require '../utils/xhr_utils' 2 | AppDispatcher = require '../app_dispatcher' 3 | {ActionTypes} = require '../constants/app_constants' 4 | 5 | SettingsStore = require '../stores/settings_store' 6 | LayoutActionCreator = require './layout_action_creator' 7 | 8 | module.exports = SettingsActionCreator = 9 | 10 | edit: (inputValues) -> 11 | XHRUtils.changeSettings inputValues, (err, values) -> 12 | if err 13 | LayoutActionCreator.alertError t('settings save error') + err 14 | 15 | else 16 | AppDispatcher.handleViewAction 17 | type: ActionTypes.SETTINGS_UPDATED 18 | value: values 19 | 20 | -------------------------------------------------------------------------------- /client/app/app_dispatcher.coffee: -------------------------------------------------------------------------------- 1 | Dispatcher = require './libs/flux/dispatcher/dispatcher' 2 | {PayloadSources} = require './constants/app_constants' 3 | 4 | ### 5 | Custom dispatcher class to add semantic method. 6 | ### 7 | class AppDispatcher extends Dispatcher 8 | 9 | handleViewAction: (action) -> 10 | window.cozyMails.logAction action 11 | payload = 12 | source: PayloadSources.VIEW_ACTION 13 | action: action 14 | 15 | @dispatch payload 16 | 17 | # create and dispatch a DOM event for plugins 18 | window.cozyMails.customEvent PayloadSources.VIEW_ACTION, action 19 | 20 | handleServerAction: (action) -> 21 | window.cozyMails.logAction action 22 | payload = 23 | source: PayloadSources.SERVER_ACTION 24 | action: action 25 | 26 | @dispatch payload 27 | 28 | # create and dispatch a DOM event for plugins 29 | window.cozyMails.customEvent PayloadSources.SERVER_ACTION, action 30 | 31 | 32 | module.exports = new AppDispatcher() 33 | -------------------------------------------------------------------------------- /client/app/assets/images/spinner-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /client/app/assets/images/spinner-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /client/app/assets/images/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /client/app/components/account_config_delete.coffee: -------------------------------------------------------------------------------- 1 | React = require 'react' 2 | 3 | { div } = React.DOM 4 | 5 | AccountActionCreator = require '../actions/account_action_creator' 6 | LayoutActionCreator = require '../actions/layout_action_creator' 7 | 8 | {FieldSet, FormButtons, FormButton} = require('./basic_components').factories 9 | 10 | module.exports = AccountConfigDelete = React.createClass 11 | displayName: 'AccountConfigDelete' 12 | 13 | 14 | getInitialState: -> 15 | state = {} 16 | state.deleting = false 17 | return state 18 | 19 | render: -> 20 | FieldSet text: t('account danger zone'), 21 | FormButtons null, 22 | FormButton 23 | className: 'btn-remove' 24 | default: true 25 | danger: true 26 | onClick: @onRemove 27 | spinner: @state.deleting 28 | icon: 'trash' 29 | text: t "account remove" 30 | 31 | # Ask for confirmation before running remove operation. 32 | onRemove: (event) -> 33 | event?.preventDefault() 34 | 35 | label = @props.editedAccount.get 'label' 36 | LayoutActionCreator.displayModal 37 | title : t 'app confirm delete' 38 | subtitle : t 'account remove confirm', {label: label} 39 | closeLabel : t 'app cancel' 40 | actionLabel : t 'app confirm' 41 | action : => 42 | LayoutActionCreator.hideModal() 43 | @setState deleting: true 44 | AccountActionCreator.remove @props.editedAccount.get('id') 45 | -------------------------------------------------------------------------------- /client/app/components/alert.coffee: -------------------------------------------------------------------------------- 1 | React = require 'react' 2 | 3 | {div, button, span, strong} = React.DOM 4 | {AlertLevel} = require '../constants/app_constants' 5 | LayoutActionCreator = require '../actions/layout_action_creator' 6 | 7 | 8 | module.exports = React.createClass 9 | displayName: 'Alert' 10 | 11 | 12 | # Render an alert message box with color and style corresponding to given 13 | # level of alert. 14 | render: -> 15 | 16 | alert = @props.alert 17 | if alert.level? 18 | levels = {} 19 | levels[AlertLevel.SUCCESS] = 'alert-success' 20 | levels[AlertLevel.INFO] = 'alert-info' 21 | levels[AlertLevel.WARNING] = 'alert-warning' 22 | levels[AlertLevel.ERROR] = 'alert-danger' 23 | 24 | div className: 'row row-alert', 25 | if alert.level? 26 | div 27 | ref: 'alert' 28 | className: "alert #{levels[alert.level]} alert-dismissible", 29 | role: "alert", 30 | button 31 | type: "button", 32 | className: "close", 33 | onClick: @hide, 34 | span 'aria-hidden': "true", "×" 35 | span className: "sr-only", t "app alert close" 36 | strong null, alert.message 37 | 38 | 39 | # Hide current alert. 40 | hide: -> 41 | LayoutActionCreator.alertHide() 42 | 43 | 44 | # Hide after 10 seconds. 45 | autohide: -> 46 | if false and @props.alert.level is AlertLevel.SUCCESS 47 | setTimeout => 48 | @refs.alert.classList.add 'autoclose' 49 | , 1000 50 | setTimeout @hide, 10000 51 | 52 | 53 | componentDidMount: -> 54 | @autohide() 55 | 56 | 57 | componentDidUpdate: -> 58 | @autohide() 59 | -------------------------------------------------------------------------------- /client/app/components/mailbox_picker.coffee: -------------------------------------------------------------------------------- 1 | React = require 'react' 2 | 3 | {div, ul, li, span, a, button} = React.DOM 4 | 5 | Immutable = require 'immutable' 6 | 7 | {Dropdown} = require('./basic_components').factories 8 | PropTypes = require '../libs/prop_types' 9 | RouterMixin = require '../mixins/router_mixin' 10 | cachedTransform = require '../libs/cached_transform' 11 | ShouldComponentUpdate = require '../mixins/should_update_mixin' 12 | 13 | 14 | module.exports = React.createClass 15 | displayName: 'MailboxPicker' 16 | 17 | mixins: [RouterMixin, ShouldComponentUpdate.UnderscoreEqualitySlow] 18 | 19 | propTypes: 20 | allowUndefined: React.PropTypes.bool 21 | mailboxes: PropTypes.mapOfMailbox 22 | valueLink: PropTypes.valueLink(PropTypes.string).isRequired 23 | 24 | getDefaultOptions: -> allowUndefined: true 25 | 26 | makeOptions: -> 27 | cachedTransform @, 'mailboxesOptions', @props.mailboxes, => 28 | @props.mailboxes 29 | .map (box) -> 30 | pusher = new Array(box.get('depth') + 1).join '--' 31 | "#{pusher}#{box.get 'label'}" 32 | .toJS() 33 | 34 | render: -> 35 | return div null unless @props.mailboxes?.size 36 | 37 | Dropdown 38 | valueLink: @props.valueLink 39 | className: 'btn-group btn-group-sm pull-left' 40 | btnClassName: 'btn' 41 | options: @makeOptions() 42 | defaultLabel: t 'mailbox pick one' 43 | undefinedLabel: t 'mailbox pick null' 44 | allowUndefined: @props.allowUndefined 45 | undefinedValue: null 46 | -------------------------------------------------------------------------------- /client/app/components/message-list-loader.coffee: -------------------------------------------------------------------------------- 1 | React = require 'react' 2 | 3 | {div, p, strong} = React.DOM 4 | {Spinner} = require('./basic_components').factories 5 | 6 | 7 | module.exports = MessageListLoader = React.createClass 8 | displayName: 'MessageListLoader' 9 | 10 | render: -> 11 | div className: 'mailbox-loading', 12 | Spinner(color: 'blue') 13 | strong null, t('emails are fetching') 14 | p null, t('thanks for patience') 15 | -------------------------------------------------------------------------------- /client/app/components/message_footer.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore' 2 | React = require 'react' 3 | 4 | {div, span, ul, li, a, i} = React.DOM 5 | 6 | MessageUtils = require '../utils/message_utils' 7 | 8 | AttachmentPreview = React.createFactory require './attachement_preview' 9 | 10 | 11 | module.exports = React.createClass 12 | displayName: 'MessageFooter' 13 | 14 | propTypes: 15 | message: React.PropTypes.object.isRequired 16 | 17 | 18 | render: -> 19 | div className: 'attachments', 20 | @renderAttachments() 21 | 22 | 23 | renderAttachments: -> 24 | attachments = @props.message.get('attachments')?.toJS() or [] 25 | return unless attachments.length 26 | 27 | resources =_.groupBy attachments, (file) -> 28 | if MessageUtils.getAttachmentType(file.contentType) is 'image' 29 | 'preview' 30 | else 31 | 'binary' 32 | ul className: null, 33 | if resources.preview 34 | for file in resources.preview 35 | AttachmentPreview 36 | ref: 'attachmentPreview' 37 | file: file 38 | key: file.checksum 39 | preview: true 40 | previewLink: true 41 | if resources.binary 42 | for file in resources.binary 43 | AttachmentPreview 44 | ref: 'attachmentPreview' 45 | file: file 46 | key: file.checksum 47 | preview: false 48 | previewLink: true 49 | -------------------------------------------------------------------------------- /client/app/components/participant.coffee: -------------------------------------------------------------------------------- 1 | React = require 'react' 2 | 3 | {span} = React.DOM 4 | MessageUtils = require '../utils/message_utils' 5 | 6 | 7 | module.exports = Participant = React.createClass 8 | displayName: 'Participant' 9 | 10 | render: -> 11 | name = MessageUtils.displayAddress @props.address 12 | 13 | if not @props.address? 14 | span null 15 | else 16 | span 17 | className: 'address-item' 18 | 'data-toggle': "tooltip" 19 | ref: 'participant' 20 | title: @props.address.address 21 | key: @props.key 22 | 23 | MessageUtils.highlightSearch(name)... 24 | 25 | _initTooltip: -> 26 | if @props.tooltip and @refs.participant? 27 | MessageUtils.tooltip @refs.participant, @props.address, @props.onAdd 28 | 29 | componentDidMount: -> 30 | @_initTooltip() 31 | 32 | componentDidUpdate: -> 33 | @_initTooltip() 34 | -------------------------------------------------------------------------------- /client/app/components/participants.coffee: -------------------------------------------------------------------------------- 1 | React = require 'react' 2 | 3 | {span} = React.DOM 4 | 5 | Participant = React.createFactory require './participant' 6 | 7 | 8 | module.exports = Participants = React.createClass 9 | displayName: 'Participants' 10 | 11 | render: -> 12 | span className: 'address-list', 13 | if @props.participants 14 | for address, key in @props.participants 15 | span key: key, 16 | Participant 17 | key: key, 18 | address: address, 19 | onAdd: @props.onAdd, 20 | tooltip: @props.tooltip 21 | if key < ( @props.participants.length - 1) 22 | span null, ', ' 23 | -------------------------------------------------------------------------------- /client/app/components/popup_message_attachments.coffee: -------------------------------------------------------------------------------- 1 | React = require 'react' 2 | 3 | {div, ul, i} = React.DOM 4 | {Tooltips} = require '../constants/app_constants' 5 | 6 | AttachmentPreview = React.createFactory require './attachement_preview' 7 | 8 | 9 | module.exports = React.createClass 10 | displayName: 'MessageAttachmentsPopup' 11 | 12 | mixins: [ 13 | require 'react-onclickoutside' 14 | ] 15 | 16 | 17 | getInitialState: -> 18 | showAttachements: false 19 | 20 | 21 | toggleAttachments: -> 22 | @setState showAttachements: not @state.showAttachements 23 | 24 | 25 | handleClickOutside: -> 26 | @setState showAttachements: false 27 | 28 | 29 | render: -> 30 | attachments = @props.message.get('attachments')?.toJS() or [] 31 | 32 | div 33 | className: 'attachments' 34 | 'aria-expanded': @state.showAttachements 35 | onClick: (event) -> event.stopPropagation() 36 | i 37 | className: 'btn fa fa-paperclip' 38 | onClick: @toggleAttachments 39 | 'aria-describedby': Tooltips.OPEN_ATTACHMENTS 40 | 'data-tooltip-direction': 'left' 41 | 42 | div className: 'popup', 'aria-hidden': not @state.showAttachements, 43 | ul className: null, 44 | for file in attachments 45 | AttachmentPreview 46 | ref: 'attachmentPreview' 47 | file: file 48 | key: file.checksum 49 | preview: false 50 | -------------------------------------------------------------------------------- /client/app/components/search_input.coffee: -------------------------------------------------------------------------------- 1 | React = require 'react' 2 | 3 | {div, input, button} = React.DOM 4 | 5 | module.exports = React.createClass 6 | displayName: 'SearchInput' 7 | 8 | getInitialState: -> 9 | value: @props.value 10 | 11 | componentWillReceiveProps: (nextProps) -> 12 | @setState value: nextProps.value 13 | 14 | onChange: (event) -> 15 | @setState value: event.target.value 16 | 17 | onKeyUp: (event) -> 18 | @setState value: event.target.value 19 | if event.key is "Enter" 20 | event?.preventDefault() 21 | event?.stopPropagation() 22 | @props.onSubmit event.target.value 23 | @onResetClick() if event.key is "Escape" 24 | 25 | onCheckClick: (event) -> 26 | event?.preventDefault?() 27 | event?.stopPropagation?() 28 | @props.onSubmit @state.value 29 | 30 | onResetClick: (event) -> 31 | @setState value: '' 32 | event?.preventDefault?() 33 | event?.stopPropagation?() 34 | @props.onSubmit '' 35 | 36 | render: -> 37 | div role: 'search', 38 | input 39 | type: 'text' 40 | placeholder: @props.placeholder 41 | value: @state.value 42 | onChange: @onChange 43 | onKeyUp: @onKeyUp 44 | name: 'searchterm' 45 | 46 | unless @state.value.length is 0 47 | div className: 'btn-group', 48 | button 49 | className: 'btn fa fa-check' 50 | onClick: @onCheckClick 51 | 52 | button 53 | className: 'btn fa fa-close' 54 | onClick: @onResetClick 55 | -------------------------------------------------------------------------------- /client/app/components/toolbar_messageslist_search.coffee: -------------------------------------------------------------------------------- 1 | React = require 'react' 2 | 3 | {div, i, button, input, form} = React.DOM 4 | 5 | {Dropdown} = require('./basic_components').factories 6 | SearchInput = React.createFactory require './search_input' 7 | 8 | {MessageFilter, Tooltips} = require '../constants/app_constants' 9 | 10 | filters = 11 | from: t "list filter from" 12 | dest: t "list filter dest" 13 | 14 | 15 | module.exports = SearchToolbarMessagesList = React.createClass 16 | displayName: 'SearchToolbarMessagesList' 17 | 18 | propTypes: 19 | accountID: React.PropTypes.string.isRequired 20 | mailboxID: React.PropTypes.string.isRequired 21 | 22 | getInitialState: -> 23 | type: 'from' 24 | value: '' 25 | 26 | prevent: (e) -> e.preventDefault() 27 | 28 | onTypeChange: (filter) -> 29 | @setState type: filter, value: '' 30 | 31 | onValueChange: (newvalue) -> 32 | @props.onFilterChange 33 | type: @state.type 34 | value: newvalue 35 | 36 | render: -> 37 | form 38 | role: 'group' 39 | className: 'search' 40 | onSubmit: @prevent 41 | 42 | Dropdown 43 | options: filters 44 | valueLink: 45 | value: @state.type 46 | requestChange: @onTypeChange 47 | 48 | SearchInput 49 | value: @state.value 50 | placeholder: t 'filters search placeholder' 51 | onSubmit: @onValueChange 52 | -------------------------------------------------------------------------------- /client/app/components/toolbox_move.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore' 2 | React = require 'react' 3 | 4 | {div, ul, li, span, i, p, a, button} = React.DOM 5 | 6 | {MenuHeader, MenuItem} = require('./basic_components').factories 7 | 8 | 9 | module.exports = ToolboxMove = React.createClass 10 | displayName: 'ToolboxMove' 11 | 12 | 13 | shouldComponentUpdate: (nextProps, nextState) -> 14 | return not(_.isEqual(nextState, @state)) or 15 | not(_.isEqual(nextProps, @props)) 16 | 17 | render: -> 18 | direction = if @props.direction is 'right' then 'right' else 'left' 19 | 20 | div className: 'menu-move btn-group btn-group-sm', 21 | button 22 | className: 'btn btn-default dropdown-toggle fa fa-folder-open' 23 | type: 'button' 24 | 'data-toggle': 'dropdown' 25 | ' ' 26 | span className: 'caret' 27 | ul 28 | className: "dropdown-menu dropdown-menu-#{direction}" 29 | role: 'menu', 30 | MenuHeader null, t 'mail action move' 31 | @renderMailboxes() 32 | 33 | 34 | renderMailboxes: -> 35 | @props.mailboxes 36 | .filter (box, id) => id isnt @props.selectedMailboxID 37 | .map (mbox, id) => 38 | MenuItem 39 | key: id 40 | className: "pusher pusher-#{mbox.get('depth')}" 41 | onClick: @props.onMove 42 | onClickValue: id 43 | mbox.get('label') 44 | .toArray() 45 | -------------------------------------------------------------------------------- /client/app/libs/cached_transform.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (store, key, args..., transform) -> 2 | store.__cachedTransforms ?= {} 3 | cached = store.__cachedTransforms[key] 4 | sameArgs = true 5 | if cached 6 | sameArgs = false for arg, i in cached.args when args[i] isnt arg 7 | return cached.result if sameArgs 8 | 9 | result = transform() 10 | store.__cachedTransforms[key] = {args, result} 11 | return result 12 | -------------------------------------------------------------------------------- /client/app/libs/flux/invariant.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | * 9 | * @providesModule invariant 10 | */ 11 | 12 | "use strict"; 13 | 14 | /** 15 | * Use invariant() to assert state which your program assumes to be true. 16 | * 17 | * Provide sprintf-style format (only %s is supported) and arguments 18 | * to provide information about what broke and what you were 19 | * expecting. 20 | * 21 | * The invariant message will be stripped in production, but the invariant 22 | * will remain to ensure logic does not differ in production. 23 | */ 24 | 25 | var invariant = function(condition, format, a, b, c, d, e, f) { 26 | if (__DEV__) { 27 | if (format === undefined) { 28 | throw new Error('invariant requires an error message argument'); 29 | } 30 | } 31 | 32 | if (!condition) { 33 | var error; 34 | if (format === undefined) { 35 | error = new Error( 36 | 'Minified exception occurred; use the non-minified dev environment ' + 37 | 'for the full error message and additional helpful warnings.' 38 | ); 39 | } else { 40 | var args = [a, b, c, d, e, f]; 41 | var argIndex = 0; 42 | error = new Error( 43 | 'Invariant Violation: ' + 44 | format.replace(/%s/g, function() { return args[argIndex++]; }) 45 | ); 46 | } 47 | 48 | error.framesToPop = 1; // we don't care about invariant's own frame 49 | throw error; 50 | } 51 | }; 52 | 53 | module.exports = invariant; -------------------------------------------------------------------------------- /client/app/libs/flux/store/store.coffee: -------------------------------------------------------------------------------- 1 | EventEmitter = require 'node-event-emitter' 2 | 3 | AppDispatcher = require '../../../app_dispatcher' 4 | 5 | 6 | module.exports = class Store extends EventEmitter 7 | 8 | uniqID: null 9 | 10 | # this variable will be shared with all subclasses so we store the items 11 | # by subclass we don't use `@constructor.name` because it breaks when 12 | # mangled 13 | _nextUniqID = 0 14 | _handlers = {} 15 | _addHandlers = (type, callback) -> 16 | 17 | _handlers[@uniqID] = {} unless _handlers[@uniqID]? 18 | _handlers[@uniqID][type] = callback 19 | 20 | # Registers the store's callbacks to the dispatcher 21 | _processBinding = -> 22 | @dispatchToken = AppDispatcher.register (payload) => 23 | {type, value} = payload.action 24 | if (callback = _handlers[@uniqID][type])? 25 | callback.call @, value 26 | 27 | 28 | constructor: -> 29 | super() 30 | # set a uniq ID 31 | @uniqID = _nextUniqID++ 32 | @__bindHandlers _addHandlers.bind @ 33 | _processBinding.call @ 34 | 35 | # Must be overriden by each store 36 | __bindHandlers: (handle) -> 37 | if __DEV__ 38 | message = "The store #{@constructor.name} must define a " + \ 39 | "`__bindHandlers` method" 40 | throw new Error message 41 | -------------------------------------------------------------------------------- /client/app/libs/prop_types.coffee: -------------------------------------------------------------------------------- 1 | React = require 'react' 2 | Immutable = require 'immutable' 3 | 4 | PropTypes = exports 5 | PropTypes[key] = value for key, value of React.PropTypes 6 | 7 | PropTypes.valueLink = (type) -> 8 | PropTypes.shape( 9 | value: type 10 | requestChange: React.PropTypes.func.isRequired 11 | ) 12 | 13 | PropTypes.immutableMapStringTo = (valueType) -> 14 | (props, name, cname, loc) -> 15 | err = PropTypes.instanceOf(Immutable.Map).isRequired.apply @, arguments 16 | return err if err 17 | 18 | values = props[name].values() 19 | for v, i of values 20 | err = valueType(values, i, cname, loc) 21 | return err if err 22 | 23 | return null 24 | 25 | PropTypes.Mailbox = PropTypes.shape 26 | id: PropTypes.string 27 | label: PropTypes.string 28 | depth: PropTypes.number 29 | weight: PropTypes.number 30 | attribs: PropTypes.arrayOf PropTypes.string 31 | 32 | PropTypes.mapOfMailbox = PropTypes.immutableMapStringTo(PropTypes.Mailbox) 33 | 34 | PropTypes.Account = PropTypes.shape 35 | label : PropTypes.string 36 | name : PropTypes.string 37 | login : PropTypes.string 38 | password : PropTypes.string 39 | imapServer : PropTypes.string 40 | imapPort : PropTypes.string 41 | smtpServer : PropTypes.string 42 | smtpPort : PropTypes.string 43 | smtpMethod : PropTypes.string 44 | draftMailbox : PropTypes.string 45 | sentMailbox : PropTypes.string 46 | trashMailbox : PropTypes.string 47 | -------------------------------------------------------------------------------- /client/app/mixins/participant_mixin.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Participant mixin. 3 | ### 4 | _ = require 'underscore' 5 | React = require 'react' 6 | 7 | {span, a, i} = React.DOM 8 | 9 | ContactLabel = React.createFactory require '../components/contact_label' 10 | 11 | ContactStore = require '../stores/contact_store' 12 | 13 | 14 | module.exports = 15 | formatUsers: (users) -> 16 | return unless users? 17 | 18 | if _.isArray users 19 | items = [] 20 | for user in users 21 | items.push ContactLabel 22 | contact: user 23 | tooltip: true 24 | 25 | items.push ", " if user isnt _.last users 26 | return items 27 | else 28 | return ContactLabel 29 | contact: users 30 | tooltip: true 31 | -------------------------------------------------------------------------------- /client/app/mixins/router_mixin.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Router mixin. 3 | Aliases `buildUrl` and `buildClosePanelUrl` 4 | ### 5 | 6 | router = window.router 7 | 8 | module.exports = 9 | 10 | 11 | buildUrl: (options) -> 12 | router.buildUrl.call router, options 13 | 14 | 15 | buildClosePanelUrl: (direction) -> 16 | router.buildClosePanelUrl.call router, direction 17 | 18 | 19 | # Builds the URL (based on options) and redirect to it. 20 | # If `options` is a string, it will be considered as the target URL. 21 | redirect: (options) -> 22 | url = if typeof options is "string" then options else @buildUrl options 23 | router.navigate url, true 24 | 25 | -------------------------------------------------------------------------------- /client/app/mixins/selection_manager_mixin.coffee: -------------------------------------------------------------------------------- 1 | Immutable = require 'immutable' 2 | 3 | NEED_GETSELECTABLES = """ 4 | Components using selection_mananager should provide 5 | a getSelectables method 6 | """ 7 | 8 | module.exports = 9 | 10 | getInitialState: -> 11 | selected: Immutable.Set() 12 | allSelected: false 13 | 14 | componentWillReceiveProps: (props, state) -> 15 | throw new Error(NEED_GETSELECTABLES) unless @getSelectables 16 | 17 | # remove selected messages that are not in view anymore 18 | @setState 19 | allSelected: false 20 | selected: @state.selected.intersect @getSelectables props, state 21 | 22 | hasSelected: -> 23 | @state.selected.size > 0 24 | 25 | allSelected: -> 26 | @state.allSelected 27 | 28 | setNoneSelected: -> 29 | @setState 30 | allSelected: false, 31 | selected: Immutable.Set() 32 | 33 | setAllSelected: -> 34 | @setState 35 | allSelected: true, 36 | selected: Immutable.Set @getSelectables().toArray() 37 | 38 | addToSelected: (key) -> 39 | selected = @state.selected.add key 40 | allLength = @getSelectablesLength?() or @getSelectables().size 41 | @setState 42 | allSelected: selected.size is allLength 43 | selected: selected 44 | 45 | removeFromSelected: (key) -> 46 | @setState 47 | allSelected: false 48 | selected: @state.selected.remove key 49 | 50 | getSelected: -> 51 | @state.selected.toObject() 52 | -------------------------------------------------------------------------------- /client/app/mixins/should_update_mixin.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore' 2 | 3 | shallowEqual = (A, B, log) -> 4 | return true if A is B 5 | for own key, value of A when B[key] isnt value 6 | console.log "DIFF ON #{key}" if log 7 | return false 8 | for own key of B when B[key]? and not A[key]? 9 | console.log "LOST #{key}" if log 10 | return false 11 | return true 12 | 13 | 14 | module.exports = 15 | 16 | ImmutableEquality: 17 | shouldComponentUpdate: (nextProps, nextState) -> 18 | shallowEqual(@props, nextProps) and shallowEqual(@state, nextState) 19 | 20 | UnderscoreEqualitySlow: 21 | shouldComponentUpdate: (nextProps, nextState) -> 22 | isNextState = _.isEqual nextState, @state 23 | isNextProps = _.isEqual nextProps, @props 24 | return not (isNextState and isNextProps) 25 | 26 | Logging: 27 | shouldComponentUpdate: (nextProps, nextState) -> 28 | not shallowEqual(nextProps, @props, true) or 29 | not shallowEqual(nextState, @state, true) 30 | -------------------------------------------------------------------------------- /client/app/mixins/store_watch_mixin.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'underscore' 2 | 3 | module.exports = StoreWatchMixin = (stores) -> 4 | 5 | # Update state when linked store emit changes. 6 | componentDidMount: -> 7 | stores.forEach (store) => 8 | store.addListener 'change', @_setStateFromStores 9 | 10 | 11 | # Stop listening to the linked stores when the component is unmounted. 12 | componentWillUnmount: -> 13 | stores.forEach (store) => 14 | store.removeListener 'change', @_setStateFromStores 15 | 16 | # Build initial state from store values. 17 | getInitialState: -> 18 | return @getStateFromStores() 19 | 20 | 21 | # Update state with store values. 22 | _setStateFromStores: -> 23 | return unless @isMounted() 24 | 25 | _difference = (obj0, obj1) -> 26 | result = {} 27 | _.filter obj0, (value, key) -> 28 | unless value is obj1[key] 29 | result[key] = value 30 | result 31 | 32 | nextState = @getStateFromStores() 33 | changes = _difference nextState, @state 34 | unless _.isEmpty changes 35 | @setState nextState 36 | -------------------------------------------------------------------------------- /client/app/mixins/tooltip_refresher_mixin.coffee: -------------------------------------------------------------------------------- 1 | require '../../vendor/aria-tips/aria-tips.css' 2 | AriaTips = require '../../vendor/aria-tips/aria-tips' 3 | 4 | 5 | module.exports = TooltipRefresherMixin = 6 | 7 | 8 | # AriaTips must bind the elements declared as tooltip to their 9 | # respective tooltip when the component is mounted (DOM elements exist). 10 | componentDidMount: -> 11 | AriaTips.bind() 12 | 13 | 14 | # AriaTips must bind the elements declared as tooltip to their 15 | # respective tooltip, each time the application component (the root) 16 | # is updated to make sure new tooltips are also bound. 17 | componentDidUpdate: -> 18 | # AriaTips.bind() 19 | -------------------------------------------------------------------------------- /client/app/stores/refreshes_store.coffee: -------------------------------------------------------------------------------- 1 | Immutable = require 'immutable' 2 | 3 | Store = require '../libs/flux/store/store' 4 | 5 | {ActionTypes} = require '../constants/app_constants' 6 | 7 | 8 | refreshesToImmutable = (refreshes) -> 9 | Immutable.Iterable refreshes 10 | # sets objectID as index 11 | .toKeyedSeq() 12 | .mapKeys (_, refresh) -> return refresh.objectID 13 | .map (refresh) -> Immutable.fromJS refresh 14 | .toOrderedMap() 15 | 16 | 17 | class RefreshesStore extends Store 18 | 19 | ### 20 | Initialization. 21 | Defines private variables here. 22 | ### 23 | _refreshes = refreshesToImmutable window.refreshes or [] 24 | 25 | 26 | ### 27 | Defines here the action handlers. 28 | ### 29 | __bindHandlers: (handle) -> 30 | 31 | 32 | handle ActionTypes.RECEIVE_REFRESH_STATUS, (refreshes) -> 33 | _refreshes = refreshesToImmutable refreshes 34 | 35 | handle ActionTypes.RECEIVE_REFRESH_UPDATE, (refresh) -> 36 | refresh = Immutable.Map refresh 37 | id = refresh.get('objectID') 38 | _refreshes = _refreshes.set(id, refresh).toOrderedMap() 39 | @emit 'change' 40 | 41 | handle ActionTypes.RECEIVE_REFRESH_DELETE, (refreshID) -> 42 | _refreshes = _refreshes.filter (refresh) -> 43 | refresh.get('id') isnt refreshID 44 | .toOrderedMap() 45 | @emit 'change' 46 | 47 | handle ActionTypes.RECEIVE_REFRESH_NOTIF, (data) -> 48 | window.cozyMails.notify t('notif new title'), body: data.message 49 | 50 | 51 | getRefreshing: -> 52 | return _refreshes 53 | 54 | 55 | module.exports = new RefreshesStore() 56 | -------------------------------------------------------------------------------- /client/app/stores/settings_store.coffee: -------------------------------------------------------------------------------- 1 | Immutable = require 'immutable' 2 | 3 | Store = require '../libs/flux/store/store' 4 | 5 | {ActionTypes} = require '../constants/app_constants' 6 | 7 | class SettingsStore extends Store 8 | 9 | ### 10 | Initialization. 11 | Defines private variables here. 12 | ### 13 | _settings = Immutable.Map window.settings 14 | 15 | ### 16 | Defines here the action handlers. 17 | ### 18 | __bindHandlers: (handle) -> 19 | 20 | handle ActionTypes.SETTINGS_UPDATED, (settings) -> 21 | _settings = Immutable.Map settings 22 | @emit 'change' 23 | 24 | 25 | ### 26 | Public API 27 | ### 28 | get: (settingName = null) -> 29 | if settingName? 30 | return _settings.get settingName 31 | else 32 | return _settings 33 | 34 | 35 | module.exports = new SettingsStore() 36 | -------------------------------------------------------------------------------- /client/app/styles/_animations.styl: -------------------------------------------------------------------------------- 1 | animationDuration = .3s 2 | 3 | /** 4 | * Generic animations 5 | */ 6 | @keyframes fadeIn 7 | from 8 | opacity 0 9 | 10 | to 11 | opacity 1 12 | 13 | /** 14 | * Slide 15 | */ 16 | @keyframes slide 17 | from 18 | right 100% 19 | 20 | to 21 | right -100px 22 | 23 | 24 | /** 25 | * Rotation 26 | */ 27 | @keyframes spin 28 | from 29 | transform rotate(0deg) 30 | 31 | to 32 | transform rotate(359deg) 33 | 34 | 35 | .spin-animate 36 | animation 800ms linear infinite spin 37 | -------------------------------------------------------------------------------- /client/app/styles/_colors.styl: -------------------------------------------------------------------------------- 1 | // "Cozy" colors palette 2 | // ----------------------------------------------------------------------------- 3 | // blue 4 | basecolor = #34A6FF 5 | basecolor-alpha = rgba(49, 156, 256, 0.5) 6 | 7 | // green 8 | contrastcolor = #5C5 9 | 10 | // orange 11 | actioncolor = #fe8800 12 | actioncolor-alpha = rgba(254,128,0,0.5) 13 | 14 | //rainbow 15 | blue = #33A6FF 16 | red = #FF3713 17 | green = #16D943 18 | 19 | // gray and dark gray 20 | lightcolor = grey-01 21 | mediumcolor = grey-04 22 | mediumcolor-contrast = grey-07 23 | darkcolor = grey-08 24 | 25 | // special cozy colors 26 | homeheadercolor = #363a46 27 | bluegreen = #34A6BB 28 | lightorange = #FFC47F 29 | lightblue = #CBE4FB 30 | lightred = #F4F4F4 31 | grey = #666 32 | lightgrey = #EEE 33 | 34 | // 50 (well, in fact, mostly 8) shades of grey 35 | grey-01 = #EAEEF2 36 | grey-02 = #DDE6EF 37 | grey-03 = #C8D5DF 38 | grey-04 = #ACB8C5 39 | grey-05 = #92A0B2 40 | grey-06 = #748192 41 | grey-07 = #4F5B69 42 | grey-08 = #32363F 43 | 44 | 45 | // Defintions 46 | // ----------------------------------------------------------------------------- 47 | .app 48 | background-color grey-01 49 | 50 | 51 | .app [role=menubar] [role=menuitem] 52 | color darkcolor 53 | 54 | &:hover 55 | color basecolor 56 | -------------------------------------------------------------------------------- /client/app/styles/_date-range-picker.styl: -------------------------------------------------------------------------------- 1 | .date-range-picker 2 | .fd-hidden-input 3 | display none 4 | 5 | .fd-screen-reader 6 | position absolute 7 | left -999em 8 | top auto 9 | width 1px 10 | height 1px 11 | overflow hidden 12 | outline 0 none 13 | 14 | 15 | &.open .dropdown-menu 16 | display flex 17 | 18 | .dropdown-menu 19 | display none 20 | left 50% 21 | transform translateX(-50%) 22 | padding 1em .5em 23 | flex-direction column 24 | align-items center 25 | 26 | 27 | .presets 28 | .date-picker 29 | margin 0 .5em 30 | 31 | 32 | .presets 33 | display flex 34 | justify-content space-around 35 | 36 | li 37 | margin-bottom 1em 38 | white-space nowrap 39 | 40 | button 41 | border none !important 42 | text-align left 43 | 44 | &:hover 45 | color actioncolor 46 | 47 | 48 | .date-picker 49 | width auto !important 50 | height auto !important 51 | margin .5em 52 | padding .5em 53 | border 1px solid rgba(0, 0, 0, 0.15) 54 | border-radius 4px 55 | 56 | .date-picker-day-header 57 | display none 58 | 59 | th 60 | td 61 | min-width 2em 62 | min-height 2em 63 | padding .3125em 64 | text-align center 65 | vertical-align center 66 | color darkcolor 67 | 68 | &.month-out 69 | opacity .35 70 | 71 | &:not(.date-picker-title) 72 | span 73 | display block 74 | 75 | &:hover 76 | color actioncolor 77 | 78 | &[aria-selected=true] 79 | background-color basecolor 80 | color white 81 | border-radius 4px 82 | -------------------------------------------------------------------------------- /client/app/styles/_file-picker.styl: -------------------------------------------------------------------------------- 1 | .file-picker 2 | .files 3 | overflow auto 4 | display flex 5 | flex-wrap wrap 6 | 7 | .file-item 8 | display flex 9 | flex none 10 | align-items center 11 | margin .25em 0.5em 0.25em 0 12 | padding 0.6em 0.8em 13 | background-color lighten(grey-01, 50%) 14 | border-radius 3px 15 | white-space no-wrap 16 | 17 | a 18 | cursor pointer 19 | padding 0 20 | color grey-08 21 | 22 | .mime 23 | padding 0 0.4em 0 0 24 | font-size 1.25em 25 | color grey-07 26 | 27 | .delete 28 | padding 0 0 0 0.4em 29 | cursor pointer 30 | 31 | .file-size 32 | margin-left 0.5em 33 | color grey-05 34 | 35 | .file-actions 36 | margin-left 0.5em 37 | border-left 1px solid grey-03 38 | 39 | a 40 | padding: 0 .5em 41 | 42 | a, 43 | i 44 | color grey-05 45 | 46 | &:hover 47 | color grey-08 48 | text-decoration none 49 | 50 | .attachment-name 51 | color darkcolor 52 | 53 | .file-detail 54 | color grey-05 55 | font-size 0.9em 56 | 57 | .dropzone 58 | width 20em 59 | padding 0.5em 0 60 | border 2px grey-03 dashed 61 | cursor pointer 62 | color grey-04 63 | 64 | i 65 | padding 0.5em 66 | 67 | .dropzone.target 68 | border-color basecolor 69 | 70 | .dropzone-wrapper 71 | position relative 72 | 73 | .dropzone-mask 74 | position absolute 75 | bottom 0 76 | width 100% 77 | height 100% 78 | border 2px solid transparent 79 | z-index 10 80 | 81 | 82 | 83 | .file-wrapper 84 | display none 85 | -------------------------------------------------------------------------------- /client/app/styles/_responsive.styl: -------------------------------------------------------------------------------- 1 | @media (max-width (768/16)em) 2 | 3 | // Message list 4 | #page-content 5 | #panels 6 | .panel 7 | padding 0 8 | 9 | .participants 10 | max-width 100px 11 | min-width 100px 12 | margin-left 0 13 | margin-right 15px 14 | 15 | .message 16 | padding 5px 17 | 18 | 19 | .avatar-wrapper 20 | .flags 21 | .hour 22 | display none 23 | 24 | .mailbox-config 25 | display none 26 | 27 | .message-list-option 28 | 29 | .btn.btn-default 30 | font-size 1.6em 31 | 32 | // Mailbox menu 33 | 34 | #menu 35 | .settings-action 36 | .new-account-action 37 | .compose-action 38 | display none 39 | 40 | .message-list-actions 41 | 42 | .toggle-menu-button 43 | display inline-block 44 | font-size 1.1em 45 | 46 | 47 | // Conversations 48 | 49 | .message-title 50 | padding 10px 51 | 52 | .conversation .thread .message .messageToolbox 53 | display none 54 | 55 | .conversation .thread .message .content-action 56 | padding 10px 20px 57 | 58 | .conversation .thread .message .header.compact 59 | padding 0px 20px 60 | -------------------------------------------------------------------------------- /client/app/styles/_search-bar.styl: -------------------------------------------------------------------------------- 1 | .search-bar 2 | display flex 3 | align-items center 4 | 5 | 6 | .search-bar 7 | border 1px solid grey-04 8 | border-radius .1875em 9 | 10 | i 11 | margin-left .35em 12 | align-self center 13 | 14 | 15 | .account-picker 16 | margin 0 .35em 17 | cursor pointer 18 | 19 | 20 | [role="search"] 21 | flex 1 22 | align-self stretch 23 | display flex 24 | border-left 1px solid grey-04 25 | 26 | 27 | input 28 | flex 1 1 0% 29 | height auto 30 | padding-left .35em 31 | border none 32 | border-radius 0 .1875em .1875em 0 33 | background-color grey-01 34 | 35 | &:focus 36 | background-color white 37 | outline none 38 | box-shadow 0 0 0 1px basecolor 39 | 40 | 41 | .btn-group 42 | align-self center 43 | 44 | 45 | .btn 46 | color mediumcolor 47 | padding 0 .5em 48 | 49 | &:hover 50 | color actioncolor 51 | -------------------------------------------------------------------------------- /client/app/styles/_toolbar.styl: -------------------------------------------------------------------------------- 1 | /* 2 | Toolbar style 3 | 4 | -- toolbar are components that handle actions for their parent context 5 | */ 6 | .toolbar 7 | white-space nowrap 8 | 9 | &:hover 10 | cursor default 11 | 12 | .btn 13 | border none 14 | color grey-03 15 | text-shadow none 16 | box-shadow none 17 | transition color 0.2s ease-out 18 | 19 | &:focus 20 | background-color transparent 21 | 22 | &:hover .btn 23 | color grey-06 24 | 25 | &:hover 26 | color basecolor 27 | background-color transparent 28 | 29 | .fa-compress 30 | transform none 31 | 32 | 33 | .dropdown-menu 34 | for num in (1..10) 35 | .pusher-{num} 36 | padding-left num * 20px + 20px 37 | -------------------------------------------------------------------------------- /client/app/utils/activity_utils.coffee: -------------------------------------------------------------------------------- 1 | XHRUtils = require '../utils/xhr_utils' 2 | 3 | ActivityUtils = (options) -> 4 | 5 | activity = {} 6 | 7 | XHRUtils.activityCreate options, (error, res) -> 8 | if error 9 | activity.onerror.call(error) 10 | else 11 | activity.onsuccess.call(res) 12 | 13 | return activity 14 | 15 | module.exports = ActivityUtils 16 | -------------------------------------------------------------------------------- /client/app/utils/colorhash.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | ColorHash 3 | 4 | This file exports a simple method that return an hex color from a given string. 5 | A same string will always returns the same color. 6 | ### 7 | 8 | 9 | hue2rgb = (p,q,t) -> 10 | if t < 0 then t += 1 11 | if t > 1 then t -= 1 12 | if t < 1 / 6 then return p + (q - p) * 6 * t 13 | if t < 1 / 2 then return q 14 | if t < 2 / 3 then return p + (q - p) * (2 / 3 - t) * 6 15 | return p 16 | 17 | 18 | hslToRgb = (h, s, l) -> 19 | if s is 0 then r = g = b = l 20 | else 21 | q = if l < 0.5 then l * (1 + s) else l + s - l * s 22 | p = 2 * l - q 23 | r = hue2rgb p, q, h + 1 / 3 24 | g = hue2rgb p, q, h 25 | b = hue2rgb p, q, h - 1 / 3 26 | 27 | color = ((1 << 24) + (r*255 << 16) + (g * 255 << 8) + parseInt(b * 255)) 28 | return "##{color.toString(16).slice 1}" 29 | 30 | 31 | module.exports = (tag) -> 32 | hash = 0 33 | 34 | for i in [0..tag.length-1] 35 | hash = (tag.charCodeAt(i) + (hash << 5) - hash) 36 | 37 | h = (hash% 100) / 100 38 | s = (hash% 1000) / 1000 39 | l = 0.5 + 0.2 * (hash % 2) / 2 40 | colour = hslToRgb h, s, l 41 | 42 | return colour 43 | 44 | -------------------------------------------------------------------------------- /client/app/utils/dom_utils.coffee: -------------------------------------------------------------------------------- 1 | 2 | module.exports = DomUtils = 3 | 4 | # Check if an element is inside visible viewport 5 | # @params DOMElement node 6 | isVisible: (node) -> 7 | # Get the element bounding client rect and check if it's inside 8 | # visible viewport 9 | rect = node.getBoundingClientRect() 10 | height = window.innerHeight or document.documentElement.clientHeight 11 | width = window.innerWidth or document.documentElement.clientWidth 12 | if height is 0 or width is 0 13 | # when iframe is in background, height and width are 0 14 | # so prevent to always return true when application is not 15 | # in foreground 16 | return false 17 | else 18 | return rect.bottom <= ( height + 0 ) and rect.top >= 0 19 | 20 | 21 | -------------------------------------------------------------------------------- /client/app/utils/file_utils.coffee: -------------------------------------------------------------------------------- 1 | 2 | 3 | module.exports = FileUtils = 4 | 5 | 6 | dataURItoBlob: (dataURI) -> 7 | if (dataURI.split(',')[0].indexOf('base64') >= 0) 8 | byteString = atob(dataURI.split(',')[1]) 9 | 10 | else 11 | byteString = window.unescape(dataURI.split(',')[1]) 12 | 13 | res = 14 | mime: dataURI.split(',')[0].split(':')[1].split(';')[0], 15 | blob: new Uint8Array(byteString.length) 16 | 17 | for i in [0..byteString.length] 18 | res.blob[i] = byteString.charCodeAt(i) 19 | 20 | return res 21 | 22 | 23 | fileToDataURI: (file, cb) -> 24 | fileReader = new FileReader() 25 | fileReader.readAsDataURL file 26 | 27 | fileReader.onload = -> 28 | cb fileReader.result 29 | 30 | 31 | getFileURL: (file) -> 32 | if file.rawFileObject and not file.url 33 | return URL.createObjectURL file.rawFileObject 34 | file.url 35 | -------------------------------------------------------------------------------- /client/app/utils/intent_manager.coffee: -------------------------------------------------------------------------------- 1 | # Catch Talker.js top level var using the webpack `exports-loader` 2 | Talker = require 'exports?Talker!../../vendor/talkerjs-1.0.1.js' 3 | 4 | TIMEOUT = 3000 # 3 s 5 | 6 | 7 | module.exports = class IntentManager 8 | 9 | 10 | constructor : -> 11 | @talker = new Talker window.parent, '*' 12 | 13 | 14 | send : (nameSpace, intent, timeout) -> 15 | @talker.timeout = if timeout then timeout else TIMEOUT 16 | @talker.send 'nameSpace', intent 17 | -------------------------------------------------------------------------------- /client/app/utils/misc.coffee: -------------------------------------------------------------------------------- 1 | exports.getSortFunction = (criteria, order) -> 2 | return sortFunction = (message1, message2) -> 3 | val1 = message1.get criteria 4 | val2 = message2.get criteria 5 | if val1 > val2 then return -1 * order 6 | else if val1 < val2 then return 1 * order 7 | else return 0 8 | 9 | exports.reverseDateSort = exports.getSortFunction 'date', -1 10 | -------------------------------------------------------------------------------- /client/app/utils/socketio_utils.coffee: -------------------------------------------------------------------------------- 1 | AppDispatcher = require '../app_dispatcher' 2 | {ActionTypes} = require '../constants/app_constants' 3 | 4 | socket = require('socket.io-client').connect window.location.origin, 5 | path: "#{window.location.pathname}socket.io" 6 | reconnectionDelayMax: 60000 7 | reconectionDelay: 2000 8 | reconnectionAttempts: 3 9 | 10 | dispatchAs = (action) -> (content) -> 11 | AppDispatcher.handleServerAction 12 | type: action 13 | value: content 14 | 15 | scope = {} 16 | setServerScope = -> 17 | socket.emit 'change_scope', scope 18 | 19 | 20 | # socket.on 'refresh.status', dispatchAs ActionTypes.RECEIVE_REFRESH_STATUS 21 | # socket.on 'refresh.create', dispatchAs ActionTypes.RECEIVE_REFRESH_UPDATE 22 | # socket.on 'refresh.update', dispatchAs ActionTypes.RECEIVE_REFRESH_UPDATE 23 | # socket.on 'refresh.delete', dispatchAs ActionTypes.RECEIVE_REFRESH_DELETE 24 | 25 | socket.on 'message.create', dispatchAs ActionTypes.RECEIVE_RAW_MESSAGE_REALTIME 26 | socket.on 'message.update', dispatchAs ActionTypes.RECEIVE_RAW_MESSAGE_REALTIME 27 | socket.on 'message.delete', dispatchAs ActionTypes.RECEIVE_MESSAGE_DELETE 28 | 29 | socket.on 'mailbox.update', dispatchAs ActionTypes.RECEIVE_MAILBOX_UPDATE 30 | 31 | socket.on 'connect', -> 32 | setServerScope() 33 | socket.on 'reconnect', -> 34 | setServerScope() 35 | 36 | socket.on 'refresh.notify', dispatchAs ActionTypes.RECEIVE_REFRESH_NOTIF 37 | 38 | exports.changeRealtimeScope = (boxid, date) -> 39 | scope = 40 | mailboxID: boxid 41 | before: date 42 | setServerScope() 43 | -------------------------------------------------------------------------------- /client/app/utils/translators/account_translator.coffee: -------------------------------------------------------------------------------- 1 | Immutable = require 'immutable' 2 | 3 | {MailboxFlags} = require '../../constants/app_constants' 4 | 5 | module.exports = AccountTranslator = 6 | 7 | # Creates an immutable account from a raw account object 8 | toImmutable: (raw) -> 9 | 10 | # Used to sort mailboxes 11 | last = {} 12 | weight1 = 900 13 | weight2 = 400 14 | 15 | # Creates Immutable OrderedMap of mailboxes 16 | mailboxes = Immutable.Iterable raw.mailboxes 17 | .toKeyedSeq() 18 | .mapKeys (_, box) -> box.id 19 | .map (box) -> 20 | 21 | box.depth = box.tree.length - 1 22 | 23 | # fake weight for sort 24 | if box.depth is 0 25 | label = box.label.toLowerCase() 26 | if label is 'inbox' 27 | box.weight = 1000 28 | else if (box.attribs.length > 0 or 29 | /draft/.test(label) or 30 | /sent/.test(label) or 31 | /trash/.test(label)) 32 | box.weight = weight1 33 | weight1 -= 5 34 | else 35 | box.weight = weight2 36 | weight2 -= 5 37 | last[box.depth] = box.weight 38 | else 39 | box.weight = last[box.depth - 1] - 0.1 40 | last[box.depth] = box.weight 41 | 42 | return Immutable.Map box 43 | 44 | .toOrderedMap() 45 | 46 | delete raw.totalUnread 47 | 48 | raw.mailboxes = mailboxes 49 | return Immutable.Map raw 50 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "backbone": "1.2.3", 4 | "classnames": "2.2.3", 5 | "immutable": "3.7.6", 6 | "jquery": "2.2.1", 7 | "markdown": "0.5.0", 8 | "moment": "2.11.2", 9 | "node-event-emitter": "0.0.1", 10 | "node-polyglot": "1.0.0", 11 | "rc-animate": "2.0.3", 12 | "react": "0.14.7", 13 | "react-addons-linked-state-mixin": "0.14.7", 14 | "react-addons-perf": "0.14.7", 15 | "react-dom": "0.14.7", 16 | "react-onclickoutside": "4.5.0", 17 | "socket.io-client": "1.4.5", 18 | "superagent": "1.7.2", 19 | "to-markdown": "2.0.1", 20 | "underscore": "1.8.3" 21 | }, 22 | "devDependencies": { 23 | "assets-webpack-plugin": "3.3.0", 24 | "autoprefixer": "6.3.3", 25 | "bootstrap": "3.3.6", 26 | "browser-sync": "2.11.1", 27 | "browser-sync-webpack-plugin": "1.0.1", 28 | "coffee-loader": "0.7.2", 29 | "coffee-script": "1.10.0", 30 | "copy-webpack-plugin": "1.1.1", 31 | "css-loader": "0.23.1", 32 | "css-mqpacker": "4.0.0", 33 | "exports-loader": "0.6.3", 34 | "extract-text-webpack-plugin": "1.0.1", 35 | "file-loader": "0.8.5", 36 | "glob-loader": "0.2.0", 37 | "imports-loader": "0.6.5", 38 | "jade": "1.11.0", 39 | "jade-loader": "0.8.0", 40 | "json-loader": "0.5.4", 41 | "postcss-loader": "0.8.1", 42 | "style-loader": "0.13.0", 43 | "stylus-loader": "1.5.1", 44 | "url-loader": "0.5.7" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /client/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | Each plugin folder should contain a `_init.js` or `_init.coffee` file that register the plugin. 4 | 5 | Registering the plugin only require to add an object to `window.plugins` with the following fields: 6 | 7 | - name: string, mandatory 8 | - active: boolean, mandatory 9 | - type: only one plugin of each type can be active at the same time 10 | - onAdd: functions called everytime a node is added to the DOM 11 | - onActivate: called when plugin is activated 12 | - onDeactivate: called when plugin is deActivated 13 | 14 | Only one plugin of each type can be active at the same time. So to prevent conflicts, conflicting plugin must use the same type. For example, all available rich text editors use `type: "editor"`, so when one is choosen, the others are deactivated 15 | -------------------------------------------------------------------------------- /client/plugins/gallery/README.md: -------------------------------------------------------------------------------- 1 | `baguetteBox.js` is copyright (c) 2014 [feimosi](https://github.com/feimosi/) and released under the [MIT License](http://opensource.org/licenses/MIT). 2 | -------------------------------------------------------------------------------- /client/plugins/keyboard/LICENSE: -------------------------------------------------------------------------------- 1 | This plugin uses Mousetrap by Craig Campbell released under Apache 2.0 license 2 | See http://craig.is/killing/mice 3 | 4 | -------------------------------------------------------------------------------- /client/plugins/keyboard/css/mailkeys.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | height: 100%; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | .mailkeys-container { 7 | position: absolute; 8 | top: 0; 9 | left: 0; 10 | height: 100%; 11 | width: 100%; 12 | text-align: center; 13 | } 14 | .mailkeys-container:before { 15 | content: ''; 16 | display: inline-block; 17 | height: 100%; 18 | vertical-align: middle; 19 | margin-right: -0.25em; /* Adjusts for spacing */ 20 | } 21 | .mailkeys-help { 22 | display: inline-block; 23 | vertical-align: middle; 24 | min-width: 50%; 25 | max-width: 80%; 26 | min-height: 50%; 27 | max-height: 80%; 28 | background-color: white; 29 | border-radius: 2em; 30 | text-align: left; 31 | padding: 2em; 32 | box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); 33 | overflow: auto; 34 | } 35 | .mailkeys-help dt { 36 | float: left; 37 | min-width: 7em; 38 | } 39 | .mailkeys-help dd { 40 | line-height: 1.7em; 41 | } 42 | .mailkeys-help dd:last-child { 43 | padding-bottom: 2em; 44 | } 45 | .plugin-help { 46 | padding-top: .2em; 47 | font-size: 1.5em; 48 | } 49 | kbd { 50 | line-height: 1.5em; 51 | margin: 0 1em 0 0; 52 | } 53 | -------------------------------------------------------------------------------- /client/plugins/medium-editor/init.js.disabled: -------------------------------------------------------------------------------- 1 | //jshint browser: true 2 | require('./styles/medium-editor.css'); 3 | require('./styles/default.css'); 4 | require('./scripts/medium-editor.js'); 5 | 6 | 7 | if (typeof window.plugins !== "object") { 8 | window.plugins = {}; 9 | } 10 | window.plugins.mediumeditor = { 11 | name: "medium-editor", 12 | type: "editor", 13 | active: false, 14 | onAdd: { 15 | condition: function (node) { 16 | "use strict"; 17 | return node.querySelector('.rt-editor') !== null; 18 | }, 19 | action: function (node) { 20 | /* eslint no-unused-vars: 0 */ 21 | "use strict"; 22 | var editorNode = node.querySelector('.rt-editor'), 23 | medium, 24 | options; 25 | options = { 26 | imageDragging: false, // We handle image drag'n'drop ourself 27 | cleanPastedHTML: true, 28 | static: true, 29 | targetBlank: true, 30 | toolbar: { 31 | buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'h3'] 32 | } 33 | } 34 | if (!editorNode.classList.contains('medium-editor')) { 35 | medium = new window.MediumEditor(editorNode, options); 36 | editorNode.classList.add('medium-editor'); 37 | } 38 | } 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /client/plugins/minislate/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2014 Olivier Meunier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 4 | associated documentation files (the “Software”), to deal in the Software without restriction, 5 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 6 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 7 | furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial 10 | portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 13 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES 15 | OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /client/plugins/minislate/init.js: -------------------------------------------------------------------------------- 1 | //jshint browser: true 2 | require('./css/minislate.css'); 3 | 4 | var Minislate = require('./js/minislate.js'); 5 | 6 | 7 | if (typeof window.plugins !== "object") { 8 | window.plugins = {}; 9 | } 10 | window.plugins.minislate = { 11 | name: "MiniSlate", 12 | type: "editor", 13 | active: true, 14 | onAdd: { 15 | condition: function (node) { 16 | "use strict"; 17 | return node.querySelector('.rt-editor') !== null; 18 | }, 19 | action: function (node) { 20 | // jshint unused: false 21 | "use strict"; 22 | var editorNode = node.querySelector('.rt-editor'), 23 | editor = new Minislate.simpleEditor([editorNode]); 24 | } 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /client/plugins/plugins.loader: -------------------------------------------------------------------------------- 1 | ./**/init.js 2 | -------------------------------------------------------------------------------- /client/plugins/sample/init.js: -------------------------------------------------------------------------------- 1 | //jshint browser: true, strict: false 2 | if (typeof window.plugins !== "object") { 3 | window.plugins = {}; 4 | } 5 | window.plugins.sample = { 6 | name: "Sample JS", 7 | active: false, 8 | onAdd: { 9 | /** 10 | * Should return true if plugin applies on added subtree 11 | * 12 | * @param {DOMNode} root node of added subtree 13 | */ 14 | condition: function (node) { 15 | //return node.querySelector('iframe.content') !== null; 16 | return false; 17 | }, 18 | /** 19 | * Perform action on added subtree 20 | * 21 | * @param {DOMNode} root node of added subtree 22 | */ 23 | action: function (node) { 24 | console.log('Add', node); 25 | } 26 | }, 27 | onDelete: { 28 | /** 29 | * Should return true if plugin applies on added subtree 30 | * 31 | * @param {DOMNode} root node of added subtree 32 | */ 33 | condition: function (node) { 34 | //return node.querySelector('iframe.content') !== null; 35 | return false; 36 | }, 37 | /** 38 | * Perform action on added subtree 39 | * 40 | * @param {DOMNode} root node of added subtree 41 | */ 42 | action: function (node) { 43 | console.log('Del', node); 44 | } 45 | }, 46 | /** 47 | * Called when plugin is activated 48 | */ 49 | onActivate: function () { 50 | //console.log('Plugin sample activated'); 51 | }, 52 | /** 53 | * Called when plugin is deactivated 54 | */ 55 | onDeactivate: function () { 56 | //console.log('Plugin sample deactivated'); 57 | }, 58 | listeners: { 59 | 'VIEW_ACTION': function (params) { 60 | //console.log('Got View action', params.detail); 61 | } 62 | } 63 | }; 64 | -------------------------------------------------------------------------------- /client/plugins/vcard/LICENSE: -------------------------------------------------------------------------------- 1 | This plugin use the vCardJS library by Niklas Cathor (https://github.com/nilclass/vcardjs) 2 | provided under the following license 3 | 4 | 5 | Copyright (C) 2012 Niklas Cathor (http://github.com/nilclass) 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is furnished 12 | to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /client/test/layout_store.spec.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert 2 | var mockery = require('mockery') 3 | var sinon = require('sinon') 4 | 5 | var SpecDispatcher = require('./utils/specs_dispatcher') 6 | 7 | var Dispositions = require('../app/constants/app_constants').Dispositions 8 | var ActionTypes = require('../app/constants/app_constants').ActionTypes 9 | 10 | 11 | describe('LayoutStore spec', function() { 12 | 13 | var sandbox, layoutStore, dispatcher 14 | 15 | before(function () { 16 | dispatcher = new SpecDispatcher() 17 | sandbox = sinon.sandbox.create() 18 | mockery.enable({ 19 | warnOnUnregistered: false, 20 | useCleanCache: true 21 | }) 22 | 23 | mockery.registerMock('../app_dispatcher', dispatcher) 24 | mockery.registerMock('../../../app_dispatcher', dispatcher) 25 | mockery.registerMock('./account_store', {}) 26 | mockery.registerAllowables([ 27 | 'node-event-emitter', 28 | '../constants/app_constants', 29 | '../libs/flux/store/store', 30 | '../app/stores/layout_store' 31 | ]) 32 | 33 | global.window = { innerWidth: 1600 } 34 | layoutStore = require('../app/stores/layout_store') 35 | }) 36 | 37 | after(function () { 38 | mockery.disable() 39 | sandbox.restore() 40 | }) 41 | 42 | describe('Disposition', function () { 43 | 44 | it('Default: DISPOSITION is COLUMN', function () { 45 | assert.equal(layoutStore.getDisposition(), Dispositions.COL) 46 | }) 47 | 48 | it('Switch to ROW: DISPOSITION is ROW', function (done) { 49 | layoutStore.on('change', function () { 50 | assert.equal(layoutStore.getDisposition(), Dispositions.ROW) 51 | done() 52 | }) 53 | 54 | dispatcher.dispatch({ 55 | type: ActionTypes.SET_DISPOSITION, 56 | value: Dispositions.ROW 57 | }) 58 | }) 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /client/test/utils/specs_dispatcher.js: -------------------------------------------------------------------------------- 1 | var TestDispatcher = function TestDispatcher() { 2 | this._callbacks = [] 3 | } 4 | 5 | TestDispatcher.prototype.register = function (callback) { 6 | this._callbacks.push(callback) 7 | }; 8 | 9 | 10 | TestDispatcher.prototype.dispatch = function (payload) { 11 | this._callbacks.forEach(function (callback) { 12 | callback.call(this, {action: payload}) 13 | }); 14 | }; 15 | 16 | module.exports = TestDispatcher 17 | -------------------------------------------------------------------------------- /client/tests/casper/full/oauth.coffee: -------------------------------------------------------------------------------- 1 | if global? 2 | require = patchRequire global.require 3 | else 4 | require = patchRequire this.require 5 | require.globals.casper = casper 6 | init = require(fs.workingDirectory + "/client/tests/casper/common").init 7 | utils = require "utils.js" 8 | 9 | casper.test.begin 'Test accounts with OAuth', (test) -> 10 | init casper 11 | 12 | casper.start casper.cozy.startUrl + '#account/gmail-ID/config/account', -> 13 | 14 | casper.then -> 15 | # Ensure that no tast is displayed 16 | casper.waitWhileSelector '.toast' 17 | 18 | casper.then -> 19 | test.assertDoesntExist '#mailbox-password', 'Password field hidden' 20 | test.assertDoesntExist '#mailbox-imapServer', 'IMAP server hidden' 21 | test.assertDoesntExist '#mailbox-smtpServer', 'SMTP server hidden' 22 | casper.click 'button.action-save' 23 | casper.waitForSelector '.toast', -> 24 | test.assertEquals casper.fetchText('.toast .message').trim(), 'Account updated', 'Account update ok' 25 | casper.waitWhileSelector '.toast' 26 | 27 | casper.run -> 28 | test.done() 29 | 30 | casper.test.begin 'Test accounts without OAuth', (test) -> 31 | init casper 32 | 33 | casper.start casper.cozy.startUrl + '#account/dovecot-ID/config/account', -> 34 | 35 | casper.then -> 36 | test.assertExist '#mailbox-password', 'Password field shown' 37 | test.assertExist '#mailbox-imapServer', 'IMAP server shown' 38 | test.assertExist '#mailbox-smtpServer', 'SMTP server shown' 39 | casper.click 'button.action-save' 40 | casper.waitForSelector '.toast', -> 41 | test.assertEquals casper.fetchText('.toast .message').trim(), 'Account updated', 'Account update ok' 42 | casper.waitWhileSelector '.toast' 43 | 44 | casper.run -> 45 | test.done() 46 | -------------------------------------------------------------------------------- /client/tests/output/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /client/tests/router_test.coffee: -------------------------------------------------------------------------------- 1 | should = require('chai').Should 2 | 3 | router = require '../app/router' 4 | 5 | describe '#Router', -> 6 | 7 | describe 'Pattern loading', -> 8 | 9 | describe 'When a pattern is declared', -> 10 | it 'it should load the pattern itself' 11 | it 'it should load the extend pattern' 12 | 13 | describe 'Subrouting', -> 14 | describe 'Single route with no parameter' 15 | describe 'Single route with one parameter' 16 | describe 'Single route with multiple parameters' 17 | 18 | describe 'Dual routes with no parameter vs no parameter' 19 | describe 'Dual routes with one parameter vs no parameter' 20 | describe 'Dual routes with multiple parameters vs no parameter' 21 | 22 | describe 'Dual routes with no parameter vs one parameter' 23 | describe 'Dual routes with one parameter vs one parameter' 24 | describe 'Dual routes with multiple parameters vs one parameter' 25 | 26 | describe 'Dual routes with no parameter vs multiple parameters' 27 | describe 'Dual routes with one parameter vs multiple parameters' 28 | describe 'Dual routes with multiple parameters vs multiple parameters' 29 | 30 | -------------------------------------------------------------------------------- /client/vendor/assets/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/android-chrome-144x144.png -------------------------------------------------------------------------------- /client/vendor/assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /client/vendor/assets/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/android-chrome-36x36.png -------------------------------------------------------------------------------- /client/vendor/assets/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/android-chrome-48x48.png -------------------------------------------------------------------------------- /client/vendor/assets/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/android-chrome-72x72.png -------------------------------------------------------------------------------- /client/vendor/assets/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/android-chrome-96x96.png -------------------------------------------------------------------------------- /client/vendor/assets/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /client/vendor/assets/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /client/vendor/assets/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /client/vendor/assets/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /client/vendor/assets/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /client/vendor/assets/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /client/vendor/assets/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /client/vendor/assets/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /client/vendor/assets/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /client/vendor/assets/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /client/vendor/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /client/vendor/assets/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #8bee8c 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /client/vendor/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/favicon-16x16.png -------------------------------------------------------------------------------- /client/vendor/assets/favicon-194x194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/favicon-194x194.png -------------------------------------------------------------------------------- /client/vendor/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/favicon-32x32.png -------------------------------------------------------------------------------- /client/vendor/assets/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/favicon-96x96.png -------------------------------------------------------------------------------- /client/vendor/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/favicon.ico -------------------------------------------------------------------------------- /client/vendor/assets/fonts/adobeblank.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/adobeblank.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /client/vendor/assets/fonts/mavenpro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/mavenpro-bold.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/mavenpro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/mavenpro-bold.woff2 -------------------------------------------------------------------------------- /client/vendor/assets/fonts/mavenpro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/mavenpro-regular.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/mavenpro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/mavenpro-regular.woff2 -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcecodepro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/sourcecodepro-bold.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcecodepro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/sourcecodepro-regular.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/sourcesanspro-bold-italic.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/sourcesanspro-bold-italic.woff2 -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/sourcesanspro-bold.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/sourcesanspro-bold.woff2 -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/sourcesanspro-italic.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/sourcesanspro-italic.woff2 -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/sourcesanspro-regular.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/fonts/sourcesanspro-regular.woff2 -------------------------------------------------------------------------------- /client/vendor/assets/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3 15 | jQuery, Modernizr, 16 | Backbone, Jade, 17 | Stylus, Brunch, 18 | CoffeeScript 19 | -------------------------------------------------------------------------------- /client/vendor/assets/mail_stylesheet.css: -------------------------------------------------------------------------------- 1 | body { 2 | visibility: visible !important; 3 | font-family: 'Source Sans Pro', sans-serif; 4 | } 5 | table { 6 | max-width: 100%; 7 | } 8 | img { 9 | max-width: 100%; 10 | } 11 | blockquote { 12 | margin-left: .5em; 13 | padding-left: .5em; 14 | border-left: 2px solid blue; 15 | color: blue; 16 | } 17 | blockquote blockquote { border-color: red !important; color: red; } 18 | blockquote blockquote blockquote { border-color: green !important; color: green; } 19 | blockquote blockquote blockquote blockquote { border-color: magenta !important; color: magenta; } 20 | blockquote blockquote blockquote blockquote blockquote { border-color: blue !important; color: blue; } 21 | 22 | .textOnly { 23 | font-family: 'Source Sans Pro', sans-serif; 24 | white-space: pre-line; 25 | } 26 | 27 | -------------------------------------------------------------------------------- /client/vendor/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cozy Emails", 3 | "icons": [ 4 | { 5 | "src": "\/apps\/emails\/android-chrome-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/apps\/emails\/android-chrome-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/apps\/emails\/android-chrome-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/apps\/emails\/android-chrome-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/apps\/emails\/android-chrome-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/apps\/emails\/android-chrome-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /client/vendor/assets/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/mstile-144x144.png -------------------------------------------------------------------------------- /client/vendor/assets/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/mstile-150x150.png -------------------------------------------------------------------------------- /client/vendor/assets/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/mstile-310x150.png -------------------------------------------------------------------------------- /client/vendor/assets/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/mstile-310x310.png -------------------------------------------------------------------------------- /client/vendor/assets/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/client/vendor/assets/mstile-70x70.png -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/cs.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Leden", "\u00DAnor", "B\u0159ezen", "Duben", "Kv\u011Bten", "\u010Cerven", "\u010Cervenec", "Srpen", "Z\u00E1\u0159\u00ED", "\u0158\u00EDjen", "Listopad", "Prosinec"], 3 | monthAbbrs:["Led", "\u00DAno", "B\u0159e", "Dub", "Kv\u011B", "\u010Crv", "\u010Cvc", "Srp", "Z\u00E1\u0159", "\u0158\u00EDj", "Lis", "Pro"], 4 | fullDays:["Pond\u011Bl\u00ED", "\u00DAter\u00FD", "St\u0159eda", "\u010Ctvrtek", "P\u00E1tek", "Sobota", "Ned\u011Ble"], 5 | dayAbbrs:["Po", "\u00DAt", "St", "\u010Ct", "P\u00E1", "So", "Ne"], 6 | titles:["P\u0159edchoz\u00ED m\u011Bs\u00EDc", "N\u00E1sleduj\u00EDc\u00ED m\u011Bsic", "P\u0159edchoz\u00ED rok", "N\u00E1sleduj\u00EDc\u00ED rok", "Dnes", "Uka\u017E kalendar", "td", "T\u00FDden [[%0%]] of [[%1%]]", "T\u00FDden", "Vyber datum", "Klikni a pot\u00E1hni pro p\u0159esun", "Uka\u017E \u201C[[%0%]]\u201D prvn\u00ED", "Jdi na dne\u0161n\u00FD datum", "Nepovolen\u00FD datum"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/da.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januar", "Februar", "Marts", "April", "Maj", "Juni", "Juli", "August", "September", "Oktober", "November", "December"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "L\u00F8rdag", "S\u00F8ndag"], 5 | dayAbbrs:["Man", "Tir", "Ons", "Tor", "Fre", "L\u00F8r", "S\u00F8n"], 6 | titles:["Forrige m\u00E5ned", "N\u00E6ste m\u00E5ned", "Forrig \u00E5r", "N\u00E6ste \u00E5r", "I dag", "Vis kalender", "uge", "Uge [[%0%]] af [[%1%]]", "Uge", "V\u00E6lg en dato", "Klik \u0026 Tr\u00E6k for at flytte", "Vis \u201C[[%0%]]\u201D F\u00F8rst", "G\u00E5 til dags dato", "Deaktiveret dato"] 7 | }; 8 | try { 9 | if("datePickerController" in window) { 10 | datePickerController.loadLanguage(); 11 | }; 12 | } catch(err) {}; 13 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/de.js: -------------------------------------------------------------------------------- 1 | /* 2009-03-13 12:38:00 MEZ created by Nils Schreiber (n dot schreiber at gmx dot de) */ 2 | var fdLocale = { 3 | fullMonths:["Januar", "Februar", "M\u00E4rz", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"], 4 | monthAbbrs:["Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"], 5 | fullDays:["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"], 6 | dayAbbrs:["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"], 7 | titles:["vorheriger Monat", "n\u00E4chster Monat", "vorheriges Jahr", "n\u00E4chstes Jahr", "Heute", "Kalender anzeigen", "KW", "Woche [[%0%]] von [[%1%]]", "Woche", "W\u00E4hlen Sie ein Datum", "Klicken \u0026 Ziehen zum Verschieben", "Zeige [[%0%]] zuerst", "Zu Heute wechseln", "Datum deaktivieren"]}; 8 | try { 9 | if("datePickerController" in window) { 10 | datePickerController.loadLanguage(); 11 | }; 12 | } catch(err) {}; 13 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/en-US.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["January","February","March","April","May","June","July","August","September","October","November","December"], 3 | monthAbbrs:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"], 4 | fullDays: ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"], 5 | dayAbbrs: ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"], 6 | titles: ["Previous month","Next month","Previous year","Next year", "Today", "Open Calendar", "wk", "Week [[%0%]] of [[%1%]]", "Week", "Select a date", "Click \u0026 Drag to move", "Display \u201C[[%0%]]\u201D first", "Go to Today\u2019s date", "Disabled date:"], 7 | firstDayOfWeek:6 8 | }; 9 | try { 10 | if("datePickerController" in window) { 11 | datePickerController.loadLanguage(); 12 | }; 13 | } catch(err) {}; 14 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/en.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["January","February","March","April","May","June","July","August","September","October","November","December"], 3 | monthAbbrs:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"], 4 | fullDays: ["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"], 5 | dayAbbrs: ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"], 6 | titles: ["Previous month","Next month","Previous year","Next year", "Today", "Open Calendar", "wk", "Week [[%0%]] of [[%1%]]", "Week", "Select a date", "Click \u0026 Drag to move", "Display \u201C[[%0%]]\u201D first", "Go to Today\u2019s date", "Disabled date:"], 7 | firstDayOfWeek:0 8 | }; 9 | try { 10 | if("datePickerController" in window) { 11 | datePickerController.loadLanguage(); 12 | }; 13 | } catch(err) {}; 14 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/eo.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | firstDayOfWeek:6, 3 | fullMonths:["Januaro", "Februaro", "Marto", "Aprilo", "Majo", "Junio", "Julio", "A\u016Dgusto", "Septembro", "Oktobro", "Novembro", "Decembro"], 4 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "A\u016Dg", "Sep", "Okt", "Nov", "Dec"], 5 | fullDays:["Lundo", "Mardo", "Merkredo", "\u0134a\u016Ddo", "Vendredo", "Sabato", "Diman\u0109o"], 6 | dayAbbrs:["Lun", "Mar", "Mer", "\u0134a\u016D", "Ven", "Sab", "Dim"], 7 | titles:["Anta\u016Da Monato", "Sekva Monato", "Anta\u016Da Jaro", "Sekva Jaro", "Hodia\u016D", "Montri Kalendaron", "sem", "Semajno [[%0%]] el [[%1%]]", "Semajno", "Elekti Daton", "Tiri por movi", "Montri \u0022[[%0%]]\u0022 unue", "Iri al la hodiaua dato", "Malhavebla Dato\u003A"] 8 | }; 9 | try { 10 | if("datePickerController" in window) { 11 | datePickerController.loadLanguage(); 12 | }; 13 | } catch(err) {}; 14 | try { datePickerController.loadLanguage(); } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/es-MX.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"], 3 | monthAbbrs:["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"], 4 | fullDays:["Lunes", "Martes", "Mi\u00E9rcoles", "Jueves", "Viernes", "S\u00E1bado", "Domingo"], 5 | dayAbbrs:["Lun", "Mar", "Mi\u00E9", "Jue", "Vie", "Sab", "Dom"], 6 | titles:["Mes Anterior", "Siguente Mes", "A\u00F1o Anterior", "Siguiente A\u00F1o", "Hoy", "Mostrar Calendario", "sm", "Semana [[%0%]] de [[%1%]]", "Semana", "Seleccione una fecha", "Arrastrar para mover", "Mostrar [[%0%]] primero", "Ir a Hoy", "Fecha deshabilitada\u003A"]}; 7 | try { if("datePickerController" in window) { datePickerController.loadLanguage(); }; } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/es.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"], 3 | monthAbbrs:["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"], 4 | fullDays:["Lunes", "Martes", "Mi\u00E9rcoles", "Jueves", "Viernes", "S\u00E1bado", "Domingo"], 5 | dayAbbrs:["Lun", "Mar", "Mi\u00E9", "Jue", "Vie", "S\u00E1b", "Dom"], 6 | titles:["Mes Anterior", "Mes Siguiente", "A\u00F1o Anterior", "A\u00F1o Siguiente", "Hoy", "Mostrar Calendario", "sem", "Semana[[%0%]] de [[%1%]]", "Semana", "Seleccione una Fecha", "Haga clic y arrastre para mover", "Mostrar \u0022[[%0%]]\u0022 primero", "Ir al d\u00EDa de hoy", "Deshabilitar Fecha"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/et.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Jaanuar", "Veebruar", "M\u00E4rts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember"], 3 | monthAbbrs:["Jan", "Veb", "M\u00E4r", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Det"], 4 | fullDays:["Esmasp\u00E4ev", "Teisip\u00E4ev", "Kolmap\u00E4ev", "Neljap\u00E4ev", "Reede", "Laup\u00E4ev", "P\u00FChap\u00E4ev"], 5 | dayAbbrs:["M", "T", "K", "N", "R", "L", "P"], 6 | titles:["eelmine kuu", "j\u00E4rgmine kuu", "eelmine aasta", "j\u00E4rgmine aasta", "t\u00E4na", "n\u00E4ita kalendrit", "nd", "n\u00E4dal [[%0%]] - [[%1%]]", "n\u00E4dal", "vali kuup\u00E4ev", "kliki ja lohista", "n\u00E4ita \u201C[[%0%]]\u201D esimest", "mine t\u00E4nasele", "keelatud kuup\u00E4ev\u003A"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/fi.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Tammikuu", "Helmikuu", "Maaliskuu", "Huhtikuu", "Toukokuu", "Kes\u00E4kuu", "Hein\u00E4kuu", "Elokuu", "Syyskuu", "Lokakuu", "Marraskuu", "Joulukuu"], 3 | monthAbbrs:["Tam", "Hel", "Maa", "Huh", "Tou", "Kes", "Hei", "Elo", "Syy", "Lok", "Mar", "Jou"], 4 | fullDays:["Maanantai", "Tiistai", "Keskiviikko", "Torstai", "Perjantai", "Lauantai", "Sunnuntai"], 5 | dayAbbrs:["Ma", "Ti", "Ke", "To", "Pe", "La", "Su"], 6 | titles:["Edellinen kuukausi", "Seuraava kuukausi", "Edellinen vuosi", "Seuraava vuosi", "T\u00E4n\u00E4\u00E4n", "N\u00E4yt\u00E4 kalenteri", "vko", "Viikko [[%0%]] \u002F [[%1%]]", "Viikko", "Valitse p\u00E4iv\u00E4ys", "Valitse ja raahaa liikuttaaksesi", "N\u00E4yt\u00E4 \u201C[[%0%]]\u201D ensin", "Siirry t\u00E4h\u00E4n p\u00E4iv\u00E4\u00E4n", "Ei valittava p\u00E4iv\u00E4ys"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/he.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["\u05D9\u05E0\u05D5\u05D0\u05E8", "\u05E4\u05D1\u05E8\u05D5\u05D0\u05E8", "\u05DE\u05E8\u05E5", "\u05D0\u05E4\u05E8\u05D9\u05DC", "\u05DE\u05D0\u05D9", "\u05D9\u05D5\u05E0\u05D9", "\u05D9\u05D5\u05DC\u05D9", "\u05D0\u05D5\u05D2\u05D5\u05E1\u05D8", "\u05E1\u05E4\u05D8\u05DE\u05D1\u05E8", "\u05D0\u05D5\u05E7\u05D8\u05D5\u05D1\u05E8", "\u05E0\u05D5\u05D1\u05DE\u05D1\u05E8", "\u05D3\u05E6\u05DE\u05D1\u05E8"], 3 | monthAbbrs:["\u05D9\u05E0\u0027", "\u05E4\u05D1\u0027", "\u05DE\u05E8\u0027", "\u05D0\u05E4\u0027", "\u05DE\u0027", "\u05D9\u05D5\u05E0\u0027", "\u05D9\u05D5\u05DC\u0027", "\u05D0\u05D5\u05D2\u0027", "\u05E1\u05E4\u05D8\u0027", "\u05D0\u05D5\u05E7\u05D8\u0027", "\u05E0\u05D5\u05D1\u0027", "\u05D3\u05E6\u0027"], 4 | fullDays:["\u05E9\u05E0\u05D9", "\u05E9\u05DC\u05D9\u05E9\u05D9", "\u05E8\u05D1\u05D9\u05E2\u05D9", "\u05D7\u05DE\u05D9\u05E9\u05D9", "\u05E9\u05D9\u05E9\u05D9", "\u05E9\u05D1\u05EA", "\u05E8\u05D0\u05E9\u05D5\u05DF"], 5 | dayAbbrs:["\u05E9\u05E0\u0027", "\u05E9\u05DC\u0027", "\u05E8\u05D1\u0027", "\u05D7\u05DE\u0027", "\u05E9\u05D9\u0027", "\u05E9\u0027", "\u05E8\u05D0\u05E9\u0027"], 6 | titles:["\u05D7\u05D5\u05D3\u05E9 \u05E7\u05D5\u05D3\u05DD", "\u05D7\u05D5\u05D3\u05E9 \u05D4\u05D1\u05D0", "\u05E9\u05E0\u05D4 \u05E7\u05D5\u05D3\u05DE\u05EA", "\u05E9\u05E0\u05D4 \u05D4\u05D1\u05D0\u05D4", "\u05D4\u05D9\u05D5\u05DD", "\u05D4\u05E6\u05D2 \u05DC\u05D5\u05D7 \u05E9\u05E0\u05D4", "\u05E9\u05D1", "\u05E9\u05D1\u05D5\u05E2[[%0%]] \u05DE\u05EA\u05D5\u05DA [[%1%]]", "Week", "\u05D1\u05D7\u05E8 \u05EA\u05D0\u05E8\u05D9\u05DA", "\u05D4\u05E7\u05DC\u05E7 \u05D5\u05D2\u05E8\u05D5\u05E8 \u05DC\u05D4\u05E2\u05D1\u05E8\u05D4", "\u05D4\u05E6\u05D2 \u0022[[%0%]]\u0022 \u05E8\u05D0\u05E9\u05D9\u05EA", "\u05E2\u05D1\u05D5\u05E8 \u05DC\u05EA\u05D0\u05E8\u05D9\u05DA \u05E0\u05D5\u05DB\u05D7\u05D9", "\u05E0\u05D8\u05E8\u05DC \u05EA\u05D0\u05E8\u05D9\u05DA\u003A"], 7 | rtl:1}; 8 | try { 9 | if("datePickerController" in window) { 10 | datePickerController.loadLanguage(); 11 | }; 12 | } catch(err) {}; 13 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/hu.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["janu\u00E1r", "febru\u00E1r", "m\u00E1rcius", "\u00E1prilis", "m\u00E1jus", "j\u00FAnius", "j\u00FAlius", "augusztus", "szeptember", "okt\u00F3ber", "november", "december"], 3 | monthAbbrs:["jan", "feb", "m\u00E1r", "\u00E1pr", "m\u00E1j", "j\u00FAn", "j\u00FAl", "aug", "szept", "okt", "nov", "dec"], 4 | fullDays:["h\u00E9tf\u0151", "kedd", "szerda", "cs\u00FCt\u00F6rt\u00F6k", "p\u00E9ntek", "szombat", "vas\u00E1rnap"], 5 | dayAbbrs:["h", "k", "sze", "cs", "p", "szo", "v"], 6 | titles:["el\u0151z\u0151 h\u00F3nap", "k\u00F6vetkez\u0151 h\u00F3nap", "el\u0151z\u0151 \u00E9v", "k\u00F6vetkez\u0151 \u00E9v", "ma", "napt\u00E1r megjelen\u00EDt\u00E9se", "h\u00E9t", "[[%1%]]\u002E [[%0%]]\u002E hete", "h\u00E9t", "v\u00E1lasszon d\u00E1tumot", "\u00E1thelyez\u00E9s kattintson \u00E9s h\u00FAzza", "el\u0151sz\u00F6r \u0022[[%0%]]\u0022 mutat\u00E1sa", "ugr\u00E1s a mai naphoz", "letiltott d\u00E1tum"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/id.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januari", "Febuari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Ags", "Sep", "Okt", "Nov", "Des"], 4 | fullDays:["Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"], 5 | dayAbbrs:["Sen", "Sel", "Rab", "Kam", "Jum", "Sab", "Min"], 6 | titles:["Bulan Lalu", "Bulan Depan", "Tahun Lalu", "Tahun Depan", "Hari Ini", "Kalender", "mg", "Minggu [[%0%]] dari [[%1%]]", "Minggu", "Pilih Tanggal", "Klik \u0026 drag untuk geser", "Tampilkan \u201C[[%0%]]\u201D diawal", "Ke tanggal hari ini", "Non\u002Daktifkan Kalender"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/it.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"], 3 | monthAbbrs:["Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic"], 4 | fullDays:["Luned\u00EC", "Marted\u00EC", "Mercoled\u00EC", "Gioved\u00EC", "Venerd\u00EC", "Sabato", "Domenica"], 5 | dayAbbrs:["Lun", "Mar", "Mer", "Gio", "Ven", "Sab", "Dom"], 6 | titles:["Mese precedente", "Mese successivo", "Anno precedente", "Anno successivo", "Oggi", "Mostra Calendario", "sett", "Settimana [[%0%]] di [[%1%]]", "Settimana", "Seleziona una data", "Clicca \u0026 trascina per spostare", "Mostra prima \u201C[[%0%]]\u201D", "Vai alla data Odierna", "Data disabilitata"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/kr.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["1\uC6D4", "2\uC6D4", "3\uC6D4", "4\uC6D4", "5\uC6D4", "6\uC6D4", "7\uC6D4", "8\uC6D4", "9\uC6D4", "10\uC6D4", "11\uC6D4", "12\uC6D4"], 3 | monthAbbrs:["1\uC6D4", "2\uC6D4", "3\uC6D4", "4\uC6D4", "5\uC6D4", "6\uC6D4", "7\uC6D4", "8\uC6D4", "9\uC6D4", "10\uC6D4", "11\uC6D4", "12\uC6D4"], 4 | fullDays:["\uC6D4\uC694\uC77C", "\uD654\uC694\uC77C", "\uC218\uC694\uC77C", "\uBAA9\uC694\uC77C", "\uAE08\uC694\uC77C", "\uD1A0\uC694\uC77C", "\uC77C\uC694\uC77C"], 5 | dayAbbrs:["\uC6D4", "\uD654", "\uC218", "\uBAA9", "\uAE08", "\uD1A0", "\uC77C"], 6 | titles:["\uC9C0\uB09C \uB2EC", "\uB2E4\uC74C \uB2EC", "\uC9C0\uB09C \uC8FC", "\uB2E4\uC74C \uC8FC", "\uC624\uB298", "\uB2EC\uB825 \uBCF4\uAE30", "\uC8FC", "[[%1%]]\uC8FC\uC911 [[%0%]]\uC8FC\uCC28", "\uC8FC", "\uB0A0\uC9DC \uC120\uD0DD", "\uB04C\uC5B4\uC11C \uC62E\uAE30\uAE30", "\u0022[[%0%]]\u0022 \uC6B0\uC120\uD45C\uC2DC", "\uC624\uB298\uB0A0\uC9DC\uB85C", "\uC120\uD0DD\uBD88\uAC00 \uB0A0\uC9DC"]};$ 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; 12 | -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/lt.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Sausis", "Vasaris", "Kovas", "Balandis", "Gegu\u017E\u0117", "Bir\u017Eelis", "Liepa", "Rugpj\u016Btis", "Rugs\u0117jis", "Spalis", "Lapkritis", "Gruodis"], 3 | monthAbbrs:["Sau", "Vas", "Kov", "Bal", "Geg", "Bir", "Lie", "Rugp", "Rugs", "Spa", "Lap", "Gruod"], 4 | fullDays:["Pirmadienis", "Antradienis", "Tre\u010Diadienis", "Ketvirtadienis", "Penktadienis", "\u0160e\u0161tadienis", "Sekmadienis"], 5 | dayAbbrs:["P", "A", "T", "K", "Pn", "\u0160", "S"], 6 | titles:["Buv\u0119s m\u0117nuo", "Kitas m\u0117nuo", "Buv\u0119 metai", "Kiti metai", "\u0160iandien", "Rodyti kalendori\u0173", "sav\u002E", "Savait\u0117 [[%0%]] i\u0161 [[%1%]]", "Savait\u0117", "Pasirinkite dat\u0105", "Paspauskite nor\u0117dami perkleti", "Pirmiausia rodyti \u201C[[%0%]]\u201D ", "\u0160iandienos data", "Negalima data"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/lv.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Janv\u0101ris", "Febru\u0101ris", "Marts", "Apr\u012Blis", "Maijs", "J\u016Bnijs", "J\u016Blijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["Pirmdiena", "Otrdiena", "Tre\u0161diena", "Ceturdiena", "Piektdiena", "Sestdiena", "Sv\u0113tdiena"], 5 | dayAbbrs:["P", "O", "T", "C", "P", "S", "Sv"], 6 | titles:["Iepriek\u0161\u0113jais m\u0113nesis", "N\u0101kamais m\u0113nesis", "Iepriek\u0161\u0113jais gads", "N\u0101kamais gads", "\u0160odiena", "R\u0101d\u012Bt kalend\u0101ru", "ned", "Ned\u0113\u013Ca [[%0%]] no [[%1%]]", "Ned\u0113\u013Ca", "Izv\u0113laties datumu", "Spiest un vilk lai p\u0101rb\u012Bd\u012Btu", "Par\u0101d\u012Bt \u0022[[%0%]]\u0022 pirmos", "Iet uz \u0161odienas datumu", "Inval\u012Bdu diena"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/nl.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"], 3 | monthAbbrs:["Jan", "Feb", "Mrt", "Apr", "Mei", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrijdag", "Zaterdag", "Zondag"], 5 | dayAbbrs:["Ma", "Di", "Wo", "Do", "Vr", "Za", "Zo"], 6 | titles:["Vorige maand", "Volgende maand", "Vorig jaar", "Volgend jaar", "Vandaag", "Toon kalender", "wk", "Week [[%0%]] van [[%1%]]", "Week", "Kies een datum", "Klik en versleep", "Zet \u201C[[%0%]]\u201D vooraan", "Ga naar vandaag", "Geblokkeerde datum"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/no.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Des"], 4 | fullDays:["Mandag", "Tirsdag", "Onsdag", "Torsdag", "Fredag", "L\u00F8rdag", "S\u00F8ndag"], 5 | dayAbbrs:["Man", "Tir", "Ons", "Tor", "Fre", "L\u00F8r", "S\u00F8n"], 6 | titles:["Forrige m\u00E5ned", "Neste m\u00E5ned", "Forrige \u00E5r", "Neste \u00E5r", "I dag", "Vis kalender", "uk", "Uke [[%0%]] av [[%1%]]", "Uke", "Velg dato", "Klikk og dra for \u00E5 flytte", "Vis [[%0%]] f\u00F8rst", "G\u00E5 til dagens dato", "Deaktivert dato"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/pl.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Stycze\u0144", "Luty", "Marzec", "Kwiecie\u0144", "Maj", "Czerwiec", "Lipiec", "Sierpie\u0144", "Wrzesie\u0144", "Pa\u017Adziernik", "Listopad", "Grudzie\u0144"], 3 | monthAbbrs:["Sty", "Lut", "Mar", "Kwi", "Maj", "Cze", "Lip", "Sie", "Wrz", "Pa\u017A", "Lis", "Gru"], 4 | fullDays:["Poniedzia\u0142ek", "Wtorek", "\u015Aroda", "Czwartek", "Pi\u0105tek", "Sobota", "Niedziela"], 5 | dayAbbrs:["Pon", "Wto", "\u015Aro", "Czw", "Pi\u0105", "Sob", "Nie"], 6 | titles:["Poprzedni miesi\u0105c", "Nast\u0119pny miesi\u0105c", "Poprzedni rok", "Nast\u0119pny rok", "Dzi\u015B", "Poka\u017C kalendarz", "Tyd", "Tydzie\u0144 [[%0%]] z [[%1%]]", "Tydzie\u0144", "Wybierz dat\u0119", "Przeci\u0105gnij i upu\u015B\u0107", "Wy\u015Bwietla \u201C[[%0%]]\u201D Jako pierwszy", "Zaznacza dzie\u0144 dzisiejszy", "Data wy\u0142\u0105czona"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/pt-br.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Janeiro", "Fevereiro", "Mar\u00E7o", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"], 3 | monthAbbrs:["Jan", "Fev", "Mar", "Abr", "Maio", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"], 4 | fullDays:["Segunda\u002DFeira", "Ter\u00E7a\u002DFeira", "Quarta\u002DFeira", "Quinta\u002DFeira", "Sexta\u002DFeira", "S\u00E1bado", "Domingo"], 5 | dayAbbrs:["Seg", "Ter", "Qua", "Qui", "Sex", "Sab", "Dom"], 6 | titles:["M\u00EAs Anterior", "Pr\u00F3ximo M\u00EAs", "Ano Anterior", "Pr\u00F3ximo Ano", "Hoje", "Exibir Calend\u00E1rio", "Sem", "Semana [[%0%]] de [[%1%]]", "Semana", "Selecione uma Data", "Clique e Arraste para Mover", "Exibir \u0022[[%0%]]\u0022 primeiro", "Ir para Data de Hoje", "Data Desabilitada"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/pt.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Janeiro", "Fevereiro", "Mar\u00E7o", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"], 3 | monthAbbrs:["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"], 4 | fullDays:["Segunda", "Ter\u00E7a", "Quarta", "Quinta", "Sexta", "S\u00E1bado", "Domingo"], 5 | dayAbbrs:["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 6 | titles:["M\u00EAs Anterior", "M\u00EAs Seguinte", "Ano Anterior", "Ano Seguinte", "Hoje", "Mostrar Calend\u00E1rio", "sem", "Semana [[%0%]] of [[%1%]]", "Semana", "Seleccionar data", "Arrastar", "Mostrar \u201C[[%0%]]\u201D first", "Ir para data actual", "Desabilitar data"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/se.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["M\u00E5ndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "L\u00F6rdag", "S\u00F6ndag"], 5 | dayAbbrs:["M\u00E5n", "Tis", "Ons", "Tors", "Fre", "L\u00F6r", "S\u00F6n"], 6 | titles:["F\u00F6rra m\u00E5naden", "N\u00E4sta m\u00E5nad", "F\u00F6rra \u00E5ret", "N\u00E4sta \u00E5r", "Idag", "Visa kalender", "Vecka", "Vecka [[%0%]] av [[%1%]]", "Vecka", "V\u00E4lj ett datum", "Klicka och Drag", "Visa \u0022[[%0%]]\u0022 f\u00F6rsta", "Dagens datum", "Inaktiverat datum"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/si.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "Maj", "Jun", "Jul", "Avg", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["Ponedeljek", "Torek", "Sreda", "\u010Cetrtek", "Petek", "Sobota", "Nedelja"], 5 | dayAbbrs:["Pon", "Tor", "Sre", "\u010Cet", "Pet", "Sob", "Ned"], 6 | titles:["Prej\u0161nji mesec", "Naslednji mesec", "Prej\u0161nje leto", "Naslednje leto", "Danes", "Poka\u017Ei koledar", "td", "Teden [[%0%]] od [[%1%]]", "Teden", "Izberi datum", "Vleci in spusti za premik", "Prika\u017Ei najprej \u201C[[%0%]]\u201D", "Pojdi na dana\u0161nji datum", "Neveljaven datum"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/sk.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Janu\u00E1r", "Febru\u00E1r", "Marec", "Apr\u00EDl", "M\u00E1j", "J\u00FAn", "J\u00FAl", "August", "September", "Okt\u00F3ber", "November", "December"], 3 | monthAbbrs:["Jan", "Feb", "Mar", "Apr", "M\u00E1j", "J\u00FAn", "J\u00FAl", "Aug", "Sep", "Okt", "Nov", "Dec"], 4 | fullDays:["Pondelok", "Utorok", "Streda", "\u0160tvrtok", "Piatok", "Sobota", "Nede\u013Ea"], 5 | dayAbbrs:["Pon", "Uto", "Str", "\u0160tv", "Pia", "Sob", "Ned"], 6 | titles:["Minul\u00FD mesiac", "\u010Eal\u0161\u00ED mesiac", "Minul\u00FD rok", "\u010Eal\u0161\u00ED rok", "Dnes", "Uk\u00E1za\u0165 kalend\u00E1r", "t\u00FD\u017E", "T\u00FD\u017Ede\u0148 [[%0%]] z [[%1%]]", "T\u00FD\u017Ede\u0148", "Vyberte d\u00E1tum", "Kliknite a potiahnite pre presum", "Uk\u00E1\u017E \u0022[[%0%]]\u0022 prv\u00FD", "Dnes", "Nepovolen\u00FD d\u00E1tum\u003A"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/tr.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["Ocak", "\u015Eubat", "Mart", "Nisan", "May\u0131s", "Haziran", "Temmuz", "A\u011Fustos", "Eyl\u00FCl", "Ekim", "Kas\u0131m", "Aral\u0131k"], 3 | monthAbbrs:["Oca", "\u015Eub", "Mar", "Nis", "May", "Haz", "Tem", "A\u011Fu", "Eyl", "Eki", "Kas", "Ara"], 4 | fullDays:["Pazartesi", "Sal\u0131", "\u00C7ar\u015Famba", "Per\u015Fembe", "Cuma", "Cumartesi", "Pazar"], 5 | dayAbbrs:["Pzt", "Sal", "\u00C7ar", "Per", "Cum", "Cmt", "Paz"], 6 | titles:["\u00D6nceki Ay", "Sonraki Ay", "\u00D6nceki Y\u0131l", "Sonraki Y\u0131l", "Bug\u00FCn", "Takvimi G\u00F6ster", "hafta", "[[%1%]]\u0027de [[%0%]] \u002Ehafta", "Hafta", "G\u00FCn Se\u00E7", "T\u0131kla ve S\u00FCr\u00FCkle", "G\u00F6ster \u0022[[%0%]]\u0022 \u00F6nce", "Bug\u00FCne git", "Tarih kapal\u0131"]}; 7 | try { 8 | if("datePickerController" in window) { 9 | datePickerController.loadLanguage(); 10 | }; 11 | } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/datePicker/assets/lang/zh.js: -------------------------------------------------------------------------------- 1 | var fdLocale = { 2 | fullMonths:["\u4E00\u6708", "\u4E8C\u6708", "\u4E09\u6708", "\u56DB\u6708", "\u4E94\u6708", "\u516D\u6708", "\u4E03\u6708", "\u516B\u6708", "\u4E5D\u6708", "\u5341\u6708", "\u5341\u4E00\u6708", "\u5341\u4E8C\u6708"], 3 | monthAbbrs:["\u4E00\u6708", "\u4E8C\u6708", "\u4E09\u6708", "\u56DB\u6708", "\u4E94\u6708", "\u516D\u6708", "\u4E03\u6708", "\u516B\u6708", "\u4E5D\u6708", "\u5341\u6708", "\u5341\u4E00\u6708", "\u5341\u4E8C\u6708"], 4 | fullDays:["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"], 5 | dayAbbrs:["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"], 6 | titles:["\u524D\u4E00\u4E2A\u6708", "\u540E\u4E00\u4E2A\u6708", "\u4E0A\u4E00\u5E74", "\u4E0B\u4E00\u5E74", "\u4ECA\u5929", "\u663E\u793A\u65E5\u5386", "\u5468", "[[%1%]]\u7B2C[[%0%]]\u5468", "\u5468", "\u9009\u62E9\u65E5\u671F", "\u70B9\u51FB\u0026\u62D6\u52A8", "\u6700\u5148\u663E\u793A\u201C[[%0%]]\u201D", "\u8DF3\u8F6C\u5230\u4ECA\u5929", "\u5C4F\u853D\u65E5\u671F"]}; 7 | try { datePickerController.loadLanguage(); } catch(err) {}; -------------------------------------------------------------------------------- /client/vendor/print-helper.css: -------------------------------------------------------------------------------- 1 | @media print { 2 | * { background: transparent !important; color: black !important; box-shadow:none !important; text-shadow: none !important; filter:none !important; -ms-filter: none !important; } 3 | a, a:visited { text-decoration: underline; } 4 | a[href]:after { content: " (" attr(href) ")"; } 5 | abbr[title]:after { content: " (" attr(title) ")"; } 6 | .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } 7 | pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } 8 | thead { display: table-header-group; } 9 | tr, img { page-break-inside: avoid; } 10 | img { max-width: 100% !important; } 11 | @page { margin: 0.5cm; } 12 | p, h2, h3 { orphans: 3; widows: 3; } 13 | h2, h3 { page-break-after: avoid; } 14 | } 15 | -------------------------------------------------------------------------------- /coffeelint.json: -------------------------------------------------------------------------------- 1 | { 2 | "indentation" : { 3 | "value" : 4, 4 | "level" : "error" 5 | }, 6 | "no_empty_param_list": { 7 | "level": "warn" 8 | }, 9 | "cyclomatic_complexity": { 10 | "level": "warn" 11 | }, 12 | "max_line_length": { 13 | "value": 80, 14 | "level": "error" 15 | }, 16 | "prefer_english_operator": { 17 | "level": "warn" 18 | } 19 | } -------------------------------------------------------------------------------- /doc/architecture-scheme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-emails/e31b4e724e6310a3e0451ab1703857287aef1f6f/doc/architecture-scheme.png -------------------------------------------------------------------------------- /server.coffee: -------------------------------------------------------------------------------- 1 | americano = require 'americano' 2 | 3 | 4 | module.exports = application = (options = {}, callback = ->) -> 5 | options.name = 'cozy-emails' 6 | options.root ?= __dirname 7 | options.port ?= process.env.PORT or 9125 8 | options.host ?= process.env.HOST or '127.0.0.1' 9 | 10 | americano.start options, callback 11 | 12 | 13 | if not module.parent 14 | application() 15 | -------------------------------------------------------------------------------- /server/config.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | path = require 'path' 3 | americano = require 'americano' 4 | cozydb = require 'cozydb' 5 | 6 | log = require('./utils/logging')(prefix: 'config') 7 | {errorHandler} = require './utils/errors' 8 | 9 | viewsDir = path.resolve __dirname, 'views' 10 | useBuildView = fs.existsSync path.resolve viewsDir, 'index.js' 11 | 12 | 13 | config = 14 | common: 15 | set: 16 | 'view engine': if useBuildView then 'js' else 'jade' 17 | 'views': viewsDir 18 | 19 | engine: 20 | js: (path, locals, callback) -> 21 | callback null, require(path)(locals) 22 | 23 | use: [ 24 | americano.bodyParser() 25 | americano.methodOverride() 26 | americano.static __dirname + '/../client/public', 27 | maxAge: 86400000 28 | ] 29 | 30 | useAfter: [ 31 | errorHandler 32 | ] 33 | 34 | afterStart: (app, server) -> 35 | Scheduler = require './processes/_scheduler' 36 | SocketHandler = require './utils/socket_handler' 37 | ApplicationStartup = require './processes/application_startup' 38 | 39 | SocketHandler.setup app, server 40 | 41 | # Try to get assets definitions from root (only valid in build, not 42 | # used in watch mode) 43 | try 44 | assets = require('../webpack-assets.json').main 45 | catch 46 | assets = 47 | js: 'app.js' 48 | css: 'app.css' 49 | app.locals.assets = assets 50 | 51 | proc = new ApplicationStartup() 52 | Scheduler.schedule proc, (err) -> 53 | log.info "Initialization complete" 54 | 55 | development: [ 56 | americano.logger 'dev' 57 | ] 58 | 59 | production: [ 60 | americano.logger 'short' 61 | ] 62 | 63 | plugins: [ 64 | 'cozydb' 65 | ] 66 | 67 | module.exports = config 68 | -------------------------------------------------------------------------------- /server/controllers/contacts.coffee: -------------------------------------------------------------------------------- 1 | Contact = require '../models/contact' 2 | 3 | module.exports.list = (req, res, next) -> 4 | Contact.list (err, contacts) -> 5 | return next err if err 6 | res.send contacts 7 | 8 | module.exports.picture = (req, res, next) -> 9 | id = req.params.contactID 10 | filename = 'picture' 11 | stream = Contact.getFile id, filename, (err) -> next err 12 | stream.pipefilter = (dsResponse) -> 13 | res.setHeader(header, value) for header, value of dsResponse.headers 14 | req.on 'close', -> stream.abort() 15 | res.on 'close', -> stream.abort() 16 | stream.pipe res 17 | -------------------------------------------------------------------------------- /server/controllers/providers.coffee: -------------------------------------------------------------------------------- 1 | https = require 'https' 2 | DOMParser = require('xmldom').DOMParser 3 | 4 | # fetch account config for a domain 5 | # from prams.domain 6 | module.exports.get = (req, res, next) -> 7 | url = "https://autoconfig.thunderbird.net/v1.1/" + req.params.domain 8 | req = https.get url, (response) -> 9 | if response.statusCode isnt 200 10 | res.status(response.statusCode).send('') 11 | else 12 | body = '' 13 | response.on 'data', (data) -> 14 | body += data 15 | response.on 'end', -> 16 | doc = new DOMParser().parseFromString body 17 | providers = doc.getElementsByTagName 'emailProvider' 18 | infos = [] 19 | getValue = (node, tag) -> 20 | nodes = node.getElementsByTagName tag 21 | if nodes.length > 0 22 | return nodes[0].childNodes[0].nodeValue 23 | parseServer = (node) -> 24 | server = 25 | type: node.getAttribute 'type' 26 | hostname: getValue node, 'hostname' 27 | port: getValue node, 'port' 28 | socketType: getValue node, 'socketType' 29 | infos.push server 30 | getServers = (provider) -> 31 | servers = provider.getElementsByTagName 'incomingServer' 32 | parseServer server for server in servers 33 | servers = provider.getElementsByTagName 'outgoingServer' 34 | parseServer server for server in servers 35 | res.send infos 36 | getServers provider for provider in providers 37 | 38 | req.on 'error', (e) -> 39 | res.status(500).send 40 | error: "Error getting provider infos : " + e.message 41 | -------------------------------------------------------------------------------- /server/controllers/settings.coffee: -------------------------------------------------------------------------------- 1 | Settings = require '../models/settings' 2 | 3 | module.exports = 4 | 5 | get: (req, res, next) -> 6 | Settings.get (err, settings) -> 7 | return next err if err 8 | res.send settings 9 | 10 | change: (req, res, next) -> 11 | Settings.set req.body, (err, updated) -> 12 | return next err if err 13 | res.send updated -------------------------------------------------------------------------------- /server/controllers/test.coffee: -------------------------------------------------------------------------------- 1 | async = require 'async' 2 | Account = require '../models/account' 3 | cozydb = require 'cozydb' 4 | 5 | module.exports.main = (req, res, next) -> 6 | async.series [ 7 | (cb) -> cozydb.api.getCozyLocale cb 8 | (cb) -> Account.request 'all', cb 9 | ], (err, results) -> 10 | 11 | if err? 12 | # for now we handle error case loosely 13 | console.log err 14 | res.render 'test.jade', imports: """ 15 | console.log("#{err}") 16 | window.locale = "en"; 17 | window.accounts = {}; 18 | """ 19 | else 20 | [locale, accounts] = results 21 | res.render 'test.jade', imports: """ 22 | window.locale = "#{locale}"; 23 | window.accounts = #{JSON.stringify accounts}; 24 | """ 25 | -------------------------------------------------------------------------------- /server/models/model-events.coffee: -------------------------------------------------------------------------------- 1 | 2 | {EventEmitter} = require 'events' 3 | _ = require 'lodash' 4 | 5 | # using DS events imply one more query for each update 6 | # instead we monkeypatch cozydb 7 | module.exports.wrapModel = (Model) -> 8 | 9 | Model.ee = new EventEmitter() 10 | 11 | Model.on = -> Model.ee.on.apply Model.ee, arguments 12 | 13 | _oldCreate = Model.create 14 | Model.create = (data, callback) -> 15 | _oldCreate.call Model, data, (err, created) -> 16 | Model.ee.emit 'create', created unless err 17 | callback err, created 18 | 19 | _oldUpdateAttributes = Model::updateAttributes 20 | Model::updateAttributes = (data, callback) -> 21 | old = _.cloneDeep @toObject() 22 | _oldUpdateAttributes.call this, data, (err, updated) => 23 | Model.ee.emit 'update', this, old unless err 24 | callback err, updated 25 | 26 | _oldDestroy = Model::destroy 27 | Model::destroy = (callback) -> 28 | old = @toObject() 29 | id = old.id 30 | _oldDestroy.call this, (err) -> 31 | Model.ee.emit 'delete', id, old unless err 32 | callback err 33 | 34 | return Model 35 | -------------------------------------------------------------------------------- /server/patchs/conversation.coffee: -------------------------------------------------------------------------------- 1 | Message = require '../models/message' 2 | async = require 'async' 3 | ramStore = require '../models/store_account_and_boxes' 4 | log = require('../utils/logging')(prefix: 'patch:conversation') 5 | 6 | PATCH_BATCH_SIZE = 10000 7 | 8 | exports.patchAllAccounts = (callback) -> 9 | accounts = ramStore.getAllAccounts() 10 | async.eachSeries accounts, exports.patchOneAccount, callback 11 | 12 | 13 | exports.patchOneAccount = (account, callback) -> 14 | log.debug "applyPatchConversation" 15 | status = {skip: 0} 16 | async.whilst (-> not status.complete), 17 | (cb) -> applyPatchConversationStep account, status, cb 18 | , callback 19 | 20 | applyPatchConversationStep = (account, status, next) -> 21 | Message.rawRequest 'conversationPatching', 22 | reduce: true 23 | group_level: 2 24 | startkey: [account.id] 25 | endkey: [account.id, {}] 26 | limit: PATCH_BATCH_SIZE 27 | skip: status.skip 28 | , (err, rows) -> 29 | return next err if err 30 | if rows.length is 0 31 | status.complete = true 32 | return next null 33 | 34 | # rows without value are correct conversations 35 | problems = rows.filter (row) -> row.value isnt null 36 | .map (row) -> row.key 37 | 38 | log.debug "conversationPatchingStep", status.skip, 39 | rows.length, problems.length 40 | 41 | if problems.length is 0 42 | status.skip += PATCH_BATCH_SIZE 43 | next null 44 | else 45 | async.eachSeries problems, patchConversationOne, (err) -> 46 | return next err if err 47 | status.skip += PATCH_BATCH_SIZE 48 | next null 49 | 50 | patchConversationOne = (key, callback) -> 51 | Message.rawRequest 'conversationPatching', 52 | reduce: false 53 | key: key 54 | , (err, rows) -> 55 | return callback err if err 56 | Message.pickConversationID rows, callback 57 | -------------------------------------------------------------------------------- /server/processes/_base.coffee: -------------------------------------------------------------------------------- 1 | _ = require 'lodash' 2 | uuid = require 'uuid' 3 | Logger = require('../utils/logging') 4 | log = Logger('imap:reporter') 5 | {EventEmitter} = require('events') 6 | 7 | module.exports = class Process 8 | 9 | constructor: (options) -> 10 | @errors = [] 11 | @options = options 12 | @id = @code + uuid.v4() 13 | log.debug "constructor process #{@id}" 14 | 15 | initialize: -> 16 | throw new Error 'initialize should be overriden by process subclass' 17 | 18 | summary: -> 19 | { 20 | @id, 21 | @finished, 22 | done: @getProgress(), 23 | total: 1, 24 | @errors, 25 | @box, 26 | @account, 27 | @code, 28 | @objectID, 29 | @firstImport 30 | } 31 | 32 | clone: -> 33 | return new @constructor(@options) 34 | 35 | getProgress: -> 36 | 0.5 37 | 38 | abort: (callback) -> 39 | @aborted = true 40 | 41 | addCallback: (callback) -> 42 | @callbacks ?= [] 43 | @callbacks.push callback 44 | 45 | run: (callback) -> 46 | log.debug "run process #{@id}" 47 | @addCallback callback 48 | @initialize @options, (err, arg1, arg2, arg3, arg4) => 49 | cb? err, arg1, arg2, arg3, arg4 for cb in @callbacks 50 | 51 | onError: (err) -> 52 | @errors.push Logger.getLasts() + "\n" + err.stack 53 | log.error "reporter err", err.stack 54 | -------------------------------------------------------------------------------- /server/processes/application_startup.coffee: -------------------------------------------------------------------------------- 1 | Process = require './_base' 2 | OrphanRemoval = require './orphan_removal' 3 | patchIgnored = require '../patchs/ignored' 4 | log = require('../utils/logging')(prefix: 'process:application_startup') 5 | ramStore = require '../models/store_account_and_boxes' 6 | Message = require '../models/message' 7 | Mailbox = require '../models/mailbox' 8 | safeLoop = require '../utils/safeloop' 9 | cozydb = require 'cozydb' 10 | async = require 'async' 11 | MailboxRefreshList = require '../processes/mailbox_refresh_list' 12 | 13 | 14 | module.exports = class ApplicationStartup extends Process 15 | 16 | code: 'application-startup' 17 | 18 | initialize: (options, callback) -> 19 | async.series [ 20 | @forceCozyDBReindexing 21 | ramStore.initialize 22 | @initializeNewAccounts 23 | patchIgnored.patchAllAccounts 24 | @removeOrphans 25 | ], callback 26 | 27 | forceCozyDBReindexing: (callback) -> 28 | log.debug "cozydbReindexing" 29 | cozydb.forceReindexing callback 30 | 31 | initializeNewAccounts: (callback) -> 32 | log.debug "initializeNewAccounts" 33 | safeLoop ramStore.getUninitializedAccount(), (account, next) -> 34 | refreshList = new MailboxRefreshList {account} 35 | refreshList.run (err) => 36 | return next err if err 37 | boxes = ramStore.getMailboxesByAccount @id 38 | changes = Mailbox.scanBoxesForSpecialUse boxes 39 | changes.initialized = true 40 | account.updateAttributes changes, next 41 | , (errors) -> 42 | log.error 'failed to init account', err for err in errors or [] 43 | callback null 44 | 45 | removeOrphans: (callback) -> 46 | proc = new OrphanRemoval() 47 | proc.run callback 48 | -------------------------------------------------------------------------------- /server/processes/message_remove_by_account.coffee: -------------------------------------------------------------------------------- 1 | Process = require './_base' 2 | {MAX_RETRIES, CONCURRENT_DESTROY, LIMIT_DESTROY} = require '../utils/constants' 3 | ERRORMSG = "DS has crashed ? waiting 4s before try again" 4 | Message = require '../models/message' 5 | async = require 'async' 6 | log = require('../utils/logging')('process:removebyaccount') 7 | 8 | 9 | 10 | module.exports = class RemoveAllMessagesFromAccount extends Process 11 | 12 | code: 'delete-messages-from-account' 13 | 14 | initialize: (options, callback)-> 15 | @accountID = options.accountID 16 | @retries = MAX_RETRIES 17 | 18 | async.doWhilst @step, @notFinished, callback 19 | 20 | notFinished: => 21 | @batch and @batch.length > 0 22 | 23 | step: (callback) => 24 | @fetchMessages (err) => 25 | return callback err if err 26 | return callback null if @batch.length is 0 27 | @destroyMessages (err) => 28 | if err and @retries > 0 29 | log.warn ERRORMSG, err 30 | @retries-- 31 | setTimeout callback, 4000 32 | 33 | else if err 34 | callback err 35 | 36 | else 37 | # we are not done, loop again, resetting the retries 38 | @retries = MAX_RETRIES 39 | callback null 40 | 41 | fetchMessages: (callback) => 42 | Message.rawRequest 'dedupRequest', 43 | limit: LIMIT_DESTROY 44 | startkey: [@accountID] 45 | endkey: [@accountID, {}] 46 | 47 | , (err, rows) => 48 | return callback err if err 49 | @batch = rows or [] 50 | callback null 51 | 52 | 53 | destroyMessages: (callback) => 54 | async.eachLimit @batch, CONCURRENT_DESTROY, (row, cb) -> 55 | Message.destroy(row.id, cb) 56 | , callback 57 | -------------------------------------------------------------------------------- /server/utils/constants.coffee: -------------------------------------------------------------------------------- 1 | 2 | exports.MSGBYPAGE = 30 3 | # safeDestroy parameters (to be tweaked) 4 | # number of ids loaded in memory at once 5 | exports.LIMIT_DESTROY = 200 6 | # number of messages loaded in memory at once 7 | exports.LIMIT_UPDATE = 50 8 | # number of request sent to the DS in parallel 9 | exports.CONCURRENT_DESTROY = 1 10 | # number of IMAP & cozy UID&flags in memory when comparing boxes 11 | exports.FETCH_AT_ONCE = 10000 12 | # maximum number of messages to fetch at once for each box 13 | exports.LIMIT_BY_BOX = 1000 14 | 15 | exports.RFC6154 = 16 | draftMailbox: '\\Drafts' 17 | sentMailbox: '\\Sent' 18 | trashMailbox: '\\Trash' 19 | allMailbox: '\\All' 20 | junkMailbox: '\\Junk' 21 | flaggedMailbox: '\\Flagged' 22 | -------------------------------------------------------------------------------- /server/utils/localization.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | Polyglot = require 'node-polyglot' 3 | 4 | 5 | waiting = [] 6 | translator = null 7 | 8 | drainWaiting = (err, translator) -> 9 | callback err, translator for callback in waiting 10 | 11 | exports.getPolyglot = (callback) -> 12 | if translator 13 | return callback null, translator 14 | else 15 | waiting.push callback 16 | 17 | cozydb.api.getCozyLocale (err, locale) -> 18 | phrases = try require "../../client/app/locales/#{locale}" 19 | catch e then require "../../client/app/locales/en" 20 | try polyglot = new Polyglot {locale, phrases} 21 | catch e then return drainWaiting e, -> 22 | translator = polyglot.t.bind polyglot 23 | drainWaiting null, translator 24 | -------------------------------------------------------------------------------- /server/utils/logging.coffee: -------------------------------------------------------------------------------- 1 | util = require 'util' 2 | 3 | COLORS = [ 4 | '\x1B[32mDBUG\x1B[39m' 5 | '\x1B[34mINFO\x1B[39m' 6 | '\x1B[33mWARN\x1B[39m' 7 | '\x1B[31mEROR\x1B[39m' 8 | ] 9 | 10 | # LOG_LEVEL = if process.env.DEBUG_LEVEL? then parseInt process.env.DEBUG_LEVEL 11 | # else if process.env.NODE_ENV is 'test' then 3 12 | # else if process.env.NODE_ENV is 'production' then 1 13 | # else 0 14 | 15 | LOG_LEVEL = 1 16 | 17 | lastLogs = new Array(15) 18 | lastDate = +new Date() 19 | index = -1 20 | MAX_INDEX = 15 21 | 22 | addToLastLogs = -> 23 | index = (index + 1) % MAX_INDEX 24 | lastLogs[index] = util.format.apply this, arguments 25 | 26 | pad = (nb) -> 27 | ((nb + 10000) + "").substring 1 28 | 29 | module.exports = (options) -> 30 | 31 | prefix = if typeof options is 'string' then options 32 | else options.prefix 33 | 34 | logger = (level) -> -> 35 | 36 | newDate = +new Date() 37 | delta = newDate - lastDate 38 | lastDate = newDate 39 | 40 | args = new Array arguments.length + 3 41 | args[0] = COLORS[level] 42 | args[1] = "+" + ((delta + 10000) + "").substring 1 43 | args[2] = prefix 44 | for arg, i in arguments 45 | args[i+3] = arg 46 | 47 | 48 | addToLastLogs.apply null, args 49 | return null if level < LOG_LEVEL 50 | 51 | if level is 3 52 | console.error.apply console, args 53 | else 54 | console.log.apply console, args 55 | 56 | return api = 57 | debug: logger 0 58 | info: logger 1 59 | warn: logger 2 60 | error: logger 3 61 | 62 | module.exports.getLasts = -> 63 | return lastLogs[index+1..MAX_INDEX].join("\n") + "\n" + 64 | lastLogs[0..index].join("\n") 65 | -------------------------------------------------------------------------------- /server/utils/notifications.coffee: -------------------------------------------------------------------------------- 1 | NotificationsHelper = require 'cozy-notifications-helper' 2 | notificationsHelper = new NotificationsHelper 'emails' 3 | localization = require './localization' 4 | SocketHandler = require './socket_handler' 5 | log = require('./logging')(prefix: 'notifications') 6 | 7 | emailsAppRessource = app: 'emails', url: '/' 8 | logError = (err) -> 9 | log.error "fail to create notif", err if err 10 | 11 | exports.accountFirstImportComplete = (account) -> 12 | localization.getPolyglot (err, t) -> 13 | 14 | text = t 'notif complete', 15 | account: account.label 16 | 17 | notificationsHelper.createTemporary 18 | resource: emailsAppRessource 19 | text: text 20 | , logError 21 | 22 | exports.accountRefreshed = (account) -> 23 | localization.getPolyglot (err, t) -> 24 | account.totalUnread (err, totalUnread) -> 25 | ref = "notif-unread-#{account.id}" 26 | if totalUnread is 0 27 | notificationsHelper.destroy ref 28 | else 29 | message = t 'notif new', 30 | smart_count: totalUnread 31 | account: account.label 32 | 33 | accountID = account.id 34 | 35 | data = {message, totalUnread, accountID} 36 | 37 | SocketHandler.notify 'refresh.notify', data, logError 38 | 39 | notificationsHelper.createOrUpdatePersistent ref, 40 | resource: 41 | app: 'emails', 42 | url: "/#account/#{accountID}" 43 | text: message 44 | , logError 45 | -------------------------------------------------------------------------------- /server/utils/safeloop.coffee: -------------------------------------------------------------------------------- 1 | async = require 'async' 2 | 3 | module.exports = (items, iterator, done) -> 4 | errors = [] 5 | async.eachSeries items, (item, next) -> 6 | wasImmediate = true 7 | iterator item, (err) -> 8 | errors.push err if err 9 | # loop anyway 10 | if wasImmediate then setImmediate next 11 | else next null 12 | wasImmediate = false 13 | , (err) -> 14 | return done err if err # this should never happens 15 | done errors 16 | -------------------------------------------------------------------------------- /server/utils/stream_to_array.coffee: -------------------------------------------------------------------------------- 1 | stream = require 'stream' 2 | 3 | 4 | module.exports = stream_to_buffer_array = (stream, cb) -> 5 | parts = [] 6 | stream.on 'error', (err) -> cb err 7 | stream.on 'data', (d) -> parts.push d 8 | stream.on 'end', -> cb null, parts 9 | 10 | module.exports.Bufferer = class Bufferer extends stream.Writable 11 | 12 | constructor: (onDone) -> 13 | super 14 | @chunks = [] 15 | @onDone = onDone 16 | 17 | _write: (chunk, enc, next) => 18 | @chunks.push(chunk) 19 | next() 20 | 21 | end: => 22 | @onDone null, Buffer.concat @chunks 23 | -------------------------------------------------------------------------------- /server/views/test.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | title Cozy Emails 5 | meta(charset="utf-8") 6 | meta(http-equiv="X-UA-Compatible",content="IE=edge,chrome=1") 7 | meta(name="viewport", content="width=device-width") 8 | 9 | link(rel="stylesheet", href="#{assets? assets.css : 'app.css'}") 10 | 11 | script!= imports 12 | script(src="#{assets? assets.js : 'app.js'}" defer=true) 13 | 14 | body 15 | -------------------------------------------------------------------------------- /tests/00_index.coffee: -------------------------------------------------------------------------------- 1 | REINDEXING_MSG = "This page will refresh in a minute." 2 | 3 | 4 | describe 'Index page', -> 5 | 6 | it "When I get the index", (done) -> 7 | @timeout 6000 8 | client.get '/', (err, res, body) -> 9 | res.statusCode.should.equal 200 10 | body.indexOf(REINDEXING_MSG).should.equal -1 11 | done() 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/01_account_creation.coffee: -------------------------------------------------------------------------------- 1 | describe 'Account creation', -> 2 | 3 | it "When I post a new account to /accounts", (done) -> 4 | @timeout 12000 5 | account = store.accountDefinition 6 | client.post '/account', account, (err, res, body) => 7 | res.statusCode.should.equal 200 8 | body.should.have.property('mailboxes').with.lengthOf(4) 9 | 10 | expectedBoxes = 11 | 'INBOX': 'inboxID' 12 | 'Sent': 'sentBoxID' 13 | 'Test Folder': 'testBoxID' 14 | 'Flagged Email': 'flaggedBoxID' 15 | 16 | for box in body.mailboxes 17 | if key = expectedBoxes[box.label] 18 | store[key] = box.id 19 | 20 | body.sentMailbox .should.equal store.sentBoxID 21 | body.inboxMailbox .should.equal store.inboxID 22 | body.flaggedMailbox .should.equal store.flaggedBoxID 23 | 24 | body.should.have.property('favorites').with.lengthOf 4 25 | 26 | store.accountID = body.id 27 | done() 28 | 29 | it "When I update an account", (done) -> 30 | changes = store.accountDefinition 31 | changes.label = "Ze Dovecot" 32 | client.put "/account/#{store.accountID}", changes, (err, res, body) => 33 | res.statusCode.should.equal 200 34 | body.should.have.property 'label', 'Ze Dovecot' 35 | done() 36 | 37 | it "Wait for mails fetching", (done) -> 38 | @timeout 30000 39 | helpers.waitAllTaskComplete done 40 | 41 | it "When I query the /refreshes, they are all finished", (done) -> 42 | client.get "/refreshes", (err, res, body) => 43 | # body.should.have.length 12 44 | # 12 = 1 diff (limited) + 1 diff-apply-fetch + 1 diff (not limited but nothing) / mailbox 45 | unfinishedTask = body.some (task) -> not task.finished 46 | unfinishedTask.should.be.false 47 | done() 48 | -------------------------------------------------------------------------------- /tests/05_mailbox_deletion.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | 3 | describe "mailbox deletion", -> 4 | 5 | it "When I delete the draft box", (done) -> 6 | @timeout 4000 7 | 8 | client.del "/mailbox/#{store.draftBoxID}", (err, res, body) => 9 | res.statusCode.should.equal 200 10 | body.should.have.property('mailboxes').with.lengthOf(5) 11 | for box in body.mailboxes 12 | box.id.should.not.equal store.draftBoxID 13 | done() 14 | 15 | it "Wait a sec", (done) -> 16 | @timeout 4000 17 | setTimeout done, 3000 18 | 19 | it "And its message have been cleaned up", (done) -> 20 | client.get "/mailbox/#{store.draftBoxID}", (err, res, body) -> 21 | res.statusCode.should.equal 200 22 | body.messages.should.have.lengthOf 0 23 | done() 24 | 25 | it "And it has been removed from the account favorites", (done) -> 26 | 27 | Account = require appPath + 'server/models/account' 28 | Account.find store.accountID, (err, account) -> 29 | return done err if err 30 | account.favorites.should.not.containEql store.draftBoxID 31 | should.not.exist account.draftMailbox 32 | done null -------------------------------------------------------------------------------- /tests/client-units/helpers.coffee: -------------------------------------------------------------------------------- 1 | exports.initGlobals = -> 2 | global.Immutable = require 'immutable' 3 | global.EventEmitter = require('events').EventEmitter 4 | global.t = (x) -> "translated #{x}" 5 | global.__DEV__ = true 6 | global.window = cozyMails: 7 | logAction: -> 8 | customEvent: -> 9 | 10 | exports.setWindowVariable = (windowVariable) -> 11 | global.window[k] = v for k, v of windowVariable 12 | 13 | requireNoCache = (modulePath) -> 14 | absPath = require('path').resolve __dirname, modulePath 15 | modulePathResolved = require.resolve absPath 16 | delete require.cache[modulePathResolved] 17 | return require modulePathResolved 18 | 19 | exports.getCleanStore = (which) -> 20 | Dispatcher = requireNoCache '../../client/app/app_dispatcher' 21 | Store = requireNoCache "../../client/app/stores/#{which}" 22 | dispatch = (type, value) -> Dispatcher.handleViewAction {type, value} 23 | return {dispatch, Store} 24 | -------------------------------------------------------------------------------- /tests/fixtures/accounts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "_id": "gmail-ID", 4 | "docType": "Account", 5 | "label": "Gmail", 6 | "login": "some.guy@cozytest.cc", 7 | "password": "", 8 | "smtpServer": "smtp.gmail.com", 9 | "smtpPort": 465, 10 | "imapServer": "imap.gmail.com", 11 | "imapPort": 993, 12 | "accountType": "TEST", 13 | "oauthProvider": "GMAIL", 14 | "favorites": [ 15 | "gmail-ID-folder1", 16 | "e7332094-e043-0156-0b5c-790219161c7a", 17 | "5fd7b96a-6bc1-d1ec-747e-3a238386013c", 18 | "6a4803cf-fe3d-6f56-287e-45198f8c2ab6", 19 | "gmail-ID-folder6", 20 | "ef0d8561-80f7-3f7d-841a-58a5c170a250", 21 | "d77dacc9-e794-ac80-ea9f-39ce38a10871", 22 | "e72b1432-a1b6-100f-363d-6ac228a67cd4", 23 | "0d886c94-be51-fc71-790d-7f4ff5da2af7", 24 | "gmail-ID-folder2", 25 | "gmail-ID-folder3" 26 | ] 27 | }, 28 | { 29 | "_id": "dovecot-ID", 30 | "docType": "Account", 31 | "label": "DoveCot", 32 | "login": "me@cozytest.cc", 33 | "password": "applesauce", 34 | "smtpServer": "172.0.0.1", 35 | "smtpPort": 0, 36 | "imapServer": "127.0.0.1", 37 | "imapPort": 993, 38 | "accountType": "TEST", 39 | "draftMailbox": "dovecot-ID-draft", 40 | "sentMailbox": "dovecot-ID-folder2", 41 | "trashMailbox": "dovecot-ID-trash", 42 | "initialized": true, 43 | "favorites": [ 44 | "dovecot-ID-folder2", 45 | "f5cbd722-c3f9-4f6e-73d0-c75ddf65a2f1", 46 | "0b3a2b31-7acb-dbab-d57f-3050ae2c78c5", 47 | "dovecot-ID-draft", 48 | "dovecot-ID-trash", 49 | "dovecot-ID-folder1" 50 | ] 51 | } 52 | ] 53 | -------------------------------------------------------------------------------- /tests/fixtures/contacts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "_id": "fake-contact-alice", 4 | "fn": "Alice", 5 | "datapoints": [ 6 | { 7 | "name": "email", 8 | "value": "alice@casper.cozy" 9 | } 10 | ], 11 | "docType": "Contact" 12 | }, 13 | { 14 | "_id": "fake-contact-bob", 15 | "fn": "Bob", 16 | "datapoints": [ 17 | { 18 | "name": "email", 19 | "value": "bob@casper.cozy" 20 | } 21 | ], 22 | "docType": "Contact" 23 | } 24 | ] 25 | -------------------------------------------------------------------------------- /tests/fixtures/fixtures.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /tests/smtp-testing/index.coffee: -------------------------------------------------------------------------------- 1 | smtp = require 'simplesmtp' 2 | 3 | module.exports = SMTPTesting = {} 4 | 5 | SMTPTesting.mailStore = [] 6 | SMTPTesting.lastConnection = {} 7 | queueID = 0 8 | 9 | # to be overidden 10 | SMTPTesting.onSecondMessage = (env, callback) -> 11 | callback null 12 | 13 | SMTPTesting.init = (port, done) -> 14 | 15 | smtpServer = smtp.createServer 16 | debug: false 17 | disableDNSValidation: true 18 | authMethods: ['LOGIN'] 19 | requireAuthentication: true 20 | 21 | smtpServer.on 'startData', (env) -> env.body = '' 22 | smtpServer.on 'data', (env, chunk) -> env.body += chunk 23 | smtpServer.on 'authorizeUser', (connection, username, password, callback) -> 24 | SMTPTesting.lastConnection = {username, password} 25 | callback null, true 26 | 27 | smtpServer.on 'dataReady', (envelope, callback) -> 28 | SMTPTesting.mailStore.push envelope 29 | if queueID is 0 30 | # just say ok 31 | callback null, "ABC" + queueID++ 32 | 33 | else 34 | SMTPTesting.onSecondMessage envelope, -> 35 | callback null, "ABC" + queueID++ 36 | 37 | smtpServer.listen parseInt(port), done 38 | 39 | unless module.parent 40 | port = process.argv[2] or 587 41 | SMTPTesting.init port, -> console.log arguments 42 | --------------------------------------------------------------------------------