├── .biscottoopts ├── .biscottoref.coffee ├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── .tx └── config ├── Cakefile ├── LICENSE ├── README.md ├── assets └── screen_webmail.png ├── bin └── emails ├── build ├── client │ ├── app │ │ └── locales │ │ │ ├── de.js │ │ │ ├── en.js │ │ │ └── fr.js │ └── 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 │ │ ├── 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 │ │ ├── css │ │ ├── app.css │ │ └── app.css.map │ │ ├── favicon-16x16.png │ │ ├── favicon-194x194.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── fonts │ │ ├── fontawesome-webfont.woff │ │ ├── fontawesome-webfont.woff2 │ │ ├── fontawesome │ │ │ ├── FontAwesome.otf │ │ │ ├── FontAwesome.ttf │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── maven-pro-light-300.otf │ │ │ └── signika-regular.ttf │ │ ├── fonts.css │ │ ├── glyphicons │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ └── glyphicons-halflings-regular.woff │ │ ├── mavenpro-bold.woff │ │ ├── mavenpro-bold.woff2 │ │ ├── mavenpro-regular.woff │ │ ├── mavenpro-regular.woff2 │ │ ├── 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 │ │ └── sourcesanspro │ │ │ ├── LICENSE.txt │ │ │ ├── SourceSansPro-Regular.eot │ │ │ ├── SourceSansPro-Regular.otf │ │ │ ├── SourceSansPro-Regular.otf.woff │ │ │ └── SourceSansPro-Regular.ttf │ │ ├── humans.txt │ │ ├── images │ │ ├── spinner-white.svg │ │ └── spinner.svg │ │ ├── js │ │ ├── app.js │ │ ├── app.js.map │ │ ├── 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 │ │ ├── vendor.js │ │ └── vendor.js.map │ │ ├── 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 │ └── reporter.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_remove_by_account.js │ ├── message_remove_by_mailbox.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.jade │ ├── reindexing.jade │ └── test.jade ├── client ├── .gitignore ├── app │ ├── actions │ │ ├── account_action_creator.coffee │ │ ├── contact_action_creator.coffee │ │ ├── layout_action_creator.coffee │ │ ├── message_action_creator.coffee │ │ ├── search_action_creator.coffee │ │ └── settings_action_creator.coffee │ ├── app_dispatcher.coffee │ ├── 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 │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── fontawesome-webfont.woff2 │ │ │ ├── fonts.css │ │ │ ├── mavenpro-bold.woff │ │ │ ├── mavenpro-bold.woff2 │ │ │ ├── mavenpro-regular.woff │ │ │ ├── mavenpro-regular.woff2 │ │ │ ├── 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 │ │ ├── images │ │ │ ├── spinner-white.svg │ │ │ └── spinner.svg │ │ ├── js │ │ │ └── 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 │ ├── 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_picker.coffee │ │ ├── mailbox_list.coffee │ │ ├── mails_input.coffee │ │ ├── menu.coffee │ │ ├── message-list-body.coffee │ │ ├── message-list-item.coffee │ │ ├── message-list.coffee │ │ ├── message.coffee │ │ ├── message_footer.coffee │ │ ├── message_header.coffee │ │ ├── modal.coffee │ │ ├── panel.coffee │ │ ├── participant.coffee │ │ ├── popup_message_attachments.coffee │ │ ├── popup_message_details.coffee │ │ ├── search-form.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 │ │ ├── flux │ │ │ ├── dispatcher │ │ │ │ └── dispatcher.coffee │ │ │ ├── invariant.js │ │ │ └── store │ │ │ │ └── store.coffee │ │ └── panel_router.coffee │ ├── locales │ │ ├── de.json │ │ ├── en.json │ │ └── fr.json │ ├── mixins │ │ ├── participant_mixin.coffee │ │ ├── router_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 │ │ ├── _toasts.styl │ │ ├── _toolbar.styl │ │ └── application.styl │ └── utils │ │ ├── activity_utils.coffee │ │ ├── api_utils.coffee │ │ ├── colorhash.coffee │ │ ├── dom_utils.coffee │ │ ├── file_utils.coffee │ │ ├── intent_manager.coffee │ │ ├── message_utils.coffee │ │ ├── plugin_utils.coffee │ │ ├── socketio_utils.coffee │ │ ├── translators │ │ └── account_translator.coffee │ │ └── xhr_utils.coffee ├── config.coffee ├── package.json ├── 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 │ ├── assets │ └── fonts │ │ ├── fontawesome │ │ ├── FontAwesome.otf │ │ ├── FontAwesome.ttf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ ├── maven-pro-light-300.otf │ │ └── signika-regular.ttf │ │ ├── glyphicons │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ │ └── sourcesanspro │ │ ├── LICENSE.txt │ │ ├── SourceSansPro-Regular.eot │ │ ├── SourceSansPro-Regular.otf │ │ ├── SourceSansPro-Regular.otf.woff │ │ └── SourceSansPro-Regular.ttf │ ├── plugins │ ├── README.md │ ├── gallery │ │ ├── README.md │ │ ├── baguetteBox.min.css │ │ ├── baguetteBox.min.js │ │ └── init.js │ ├── keyboard │ │ ├── LICENSE │ │ ├── css │ │ │ └── mailkeys.css │ │ ├── init.js │ │ └── js │ │ │ └── mousetrap.min.js │ ├── medium-editor │ │ ├── README.md │ │ ├── init.js │ │ ├── scripts │ │ │ └── medium-editor.js │ │ └── styles │ │ │ ├── medium-default.min.css │ │ │ └── medium-editor.min.css │ ├── minislate │ │ ├── LICENSE │ │ ├── css │ │ │ ├── fonts │ │ │ │ ├── FontAwesome.otf │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ └── fontawesome-webfont.woff │ │ │ └── minislate-full.css │ │ ├── init.js │ │ └── js │ │ │ └── minislate.js │ ├── sample │ │ └── init.js │ └── vcard │ │ ├── LICENSE │ │ ├── init.js │ │ └── js │ │ └── vcardjs-0.3.min.js │ ├── scripts │ ├── Immutable.js │ ├── aria-tips.js │ ├── backbone.js │ ├── bootstrap-3.1.1.min.js │ ├── datePicker │ │ └── datepicker.js │ ├── events.js │ ├── he.js │ ├── jquery.js │ ├── json-patch-duplex.js │ ├── markdown.js │ ├── moment.js │ ├── polyfills.js │ ├── polyglot.js │ ├── react-onclickoutside.js │ ├── react-with-addons.js │ ├── revalidator.js │ ├── socket.io.js │ ├── superagent.js │ ├── talkerjs-1.0.1.js │ ├── to-markdown.js │ └── underscore.js │ └── styles │ ├── aria-tips.css │ ├── bootstrap │ ├── bootstrap-theme.min.css │ └── bootstrap.min.css │ └── helpers.css ├── coffeelint.json ├── 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_remove_by_account.coffee │ ├── message_remove_by_mailbox.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 ├── 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 /.biscottoopts: -------------------------------------------------------------------------------- 1 | --name "Cozy Mails" 2 | --readme README.md 3 | --title "Cozy Mails Documentation" 4 | --output-dir doc 5 | -- 6 | .biscottoref.coffee 7 | server 8 | LICENSE -------------------------------------------------------------------------------- /.biscottoref.coffee: -------------------------------------------------------------------------------- 1 | # Public: node 's Buffer object 2 | class Buffer -------------------------------------------------------------------------------- /.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 | client/bower_components 11 | client/public 12 | doc 13 | last.html 14 | last.png 15 | lib-cov 16 | logs 17 | node_modules 18 | npm-debug.log 19 | pids 20 | results 21 | tests/fixtures/messages_generated.json 22 | tests/fixtures/messages_loaded.json 23 | tests/fixtures/samples/* 24 | sandbox.coffee 25 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /client 2 | /server 3 | doc 4 | node_modules 5 | tests 6 | .travis.yml 7 | .git* 8 | Cakefile 9 | coffeelint.json 10 | server.coffee 11 | .biscottoopts 12 | db 13 | attachments 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | matrix: 3 | fast_finish: true 4 | allow_failures: 5 | - node_js: "0.12" 6 | node_js: 7 | - "0.10" 8 | - "0.12" 9 | services: 10 | - couchdb 11 | env: 12 | global: 13 | - NODE_ENV=test 14 | - DEBUG_LEVEL=0 15 | before_install: 16 | - git clone git://github.com/mycozycloud/cozy-data-system.git 17 | - cd cozy-data-system 18 | - npm install forever coffee-script -g 19 | - npm install #data-system 20 | - pwd 21 | - NAME=data-system TOKEN=token forever start -o forever-ds.log build/server.js 22 | - ps aux | grep server.js 23 | - sudo netstat -plunt 24 | - sleep 5 25 | - cat forever-ds.log 26 | - curl http://localhost:9101/ 27 | - coffee commands.coffee test-install emails 28 | - cd .. 29 | - export NAME=emails 30 | - export TOKEN=apptoken 31 | - mkdir log 32 | - npm install # do it now, DovecotTesting needs it 33 | 34 | script: 35 | - npm run test:build 36 | - npm run fixtures 37 | - forever start -o casper-server.log build/server.js 38 | - sleep 30 39 | - npm run test:client 40 | 41 | 42 | after_failure: 43 | - cat cozy-data-system/forever-ds.log 44 | - cat cozy-data-system/log/test.log 45 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /Cakefile: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | {exec} = require 'child_process' 3 | logger = require('printit') 4 | date: false 5 | prefix: 'cake' 6 | 7 | task 'tests', (opts) -> 8 | console.log "DEPRECATED : use npm run test:server" 9 | 10 | # convert JSON lang files to JS 11 | buildJsInLocales = -> 12 | path = require 'path' 13 | for file in fs.readdirSync './client/app/locales/' 14 | filename = './client/app/locales/' + file 15 | template = fs.readFileSync filename, 'utf8' 16 | exported = "module.exports = #{template};\n" 17 | name = file.replace '.json', '.js' 18 | fs.writeFileSync "./build/client/app/locales/#{name}", exported 19 | # add locales at the end of app.js 20 | exec "rm -rf build/client/app/locales/*.json" 21 | 22 | task 'build', 'Build CoffeeScript to Javascript', -> 23 | logger.options.prefix = 'cake:build' 24 | logger.info "Start compilation..." 25 | command = "coffee -cb --output build/server server && " + \ 26 | "coffee -cb --output build/ server.coffee && " + \ 27 | "mkdir -p build/server/views && " + \ 28 | "cp server/views/* build/server/views/ && " + \ 29 | "rm -rf build/client && mkdir build/client && " + \ 30 | "cd client/ && brunch build --production && cd .. && " + \ 31 | "mkdir -p build/client/app/locales/ && " + \ 32 | "rm -rf build/client/app/locales/* && " + \ 33 | "cp -R client/public build/client/ && " + \ 34 | "rm -rf client/app/locales/*.coffee" 35 | 36 | exec command, (err, stdout, stderr) -> 37 | if err 38 | logger.error "An error has occurred while compiling:\n" + err 39 | process.exit 1 40 | else 41 | buildJsInLocales() 42 | logger.info "Compilation succeeded." 43 | process.exit 0 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 Cozy Cloud (contact@cozycloud.cc)) 2 | 3 | This project is free software released under the MIT/X11 license: 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Cozy](http://cozy.io) Emails 2 | 3 | Modern Email Client Based on Node.js and React.js. 4 | 5 | ![screenshot](https://cozy.io/assets/press/screenshots/emails.png) 6 | 7 | ## Install 8 | 9 | We assume that Node.js >= 0.10 is already installed on your computer/server. 10 | Then install Emails via NPM. 11 | 12 | npm install emails -g 13 | 14 | 15 | ## Run 16 | 17 | Run Emails with the command line, it will listen on the 9125 port: 18 | 19 | emails 20 | 21 | ## Next steps 22 | 23 | Here are the main features on which we want to focus after: 24 | 25 | * Quick filters 26 | * Search 27 | * Encryption 28 | 29 | ## Contribution 30 | 31 | 32 | For more details about contribution and bug reports, see the upstream repository: 33 | [Cozy Emails](https://github.com/cozy/cozy-emails) 34 | 35 | ## Icons 36 | 37 | By [Fontawesome](http://fortawesome.github.io/Font-Awesome/). 38 | Main icon by [Elegant Themes](http://www.elegantthemes.com/blog/freebie-of-the-week/beautiful-flat-icons-for-free). 39 | 40 | 41 | ## License 42 | 43 | Webmail is developed by Cozy Cloud and distributed under the MIT license. 44 | 45 | 46 | ## Community 47 | 48 | You can reach the Cozy Community by: 49 | 50 | * Chatting with us on IRC #cozycloud on irc.freenode.net 51 | * Posting on our [Forum](https://groups.google.com/forum/?fromgroups#!forum/cozy-cloud) 52 | * Posting issues on the [Github repos](https://github.com/mycozycloud/) 53 | * Mentioning us on [Twitter](http://twitter.com/mycozycloud) 54 | -------------------------------------------------------------------------------- /assets/screen_webmail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/assets/screen_webmail.png -------------------------------------------------------------------------------- /bin/emails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path-extra'); 4 | var fs = require('fs'); 5 | 6 | if(process.env.POUCHDB_NAME === undefined) { 7 | var defaultDir = path.join(path.homedir(), '.emails'); 8 | if(!fs.existsSync(defaultDir)) { 9 | fs.mkdirSync(defaultDir); 10 | } 11 | process.chdir(defaultDir); 12 | } 13 | 14 | var root = path.join(__dirname, '..', 'build'); 15 | var start = require(path.join(root, 'server')).start; 16 | var port = process.env.PORT || 9125; 17 | 18 | start({root: root, port: port, dbName: 'db'}, function() {}); 19 | -------------------------------------------------------------------------------- /build/client/public/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/android-chrome-144x144.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/android-chrome-36x36.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/android-chrome-48x48.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/android-chrome-72x72.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/android-chrome-96x96.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/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-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/favicon-16x16.png -------------------------------------------------------------------------------- /build/client/public/favicon-194x194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/favicon-194x194.png -------------------------------------------------------------------------------- /build/client/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/favicon-32x32.png -------------------------------------------------------------------------------- /build/client/public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/favicon-96x96.png -------------------------------------------------------------------------------- /build/client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/favicon.ico -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/fontawesome/FontAwesome.otf -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/fontawesome/FontAwesome.ttf -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/fontawesome/fontawesome-webfont.eot -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/fontawesome/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/fontawesome/fontawesome-webfont.woff -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome/maven-pro-light-300.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/fontawesome/maven-pro-light-300.otf -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome/signika-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/fontawesome/signika-regular.ttf -------------------------------------------------------------------------------- /build/client/public/fonts/glyphicons/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/glyphicons/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /build/client/public/fonts/glyphicons/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/glyphicons/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /build/client/public/fonts/glyphicons/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/glyphicons/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/mavenpro-bold.woff -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/mavenpro-bold.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/mavenpro-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/mavenpro-regular.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro-bold-italic.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro-bold-italic.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro-bold.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro-bold.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro-italic.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro-italic.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro-regular.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro/SourceSansPro-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro/SourceSansPro-Regular.eot -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro/SourceSansPro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro/SourceSansPro-Regular.otf -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro/SourceSansPro-Regular.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro/SourceSansPro-Regular.otf.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro/SourceSansPro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/fonts/sourcesanspro/SourceSansPro-Regular.ttf -------------------------------------------------------------------------------- /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/images/spinner-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/client/public/images/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/client/public/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/mstile-144x144.png -------------------------------------------------------------------------------- /build/client/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/mstile-150x150.png -------------------------------------------------------------------------------- /build/client/public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/mstile-310x150.png -------------------------------------------------------------------------------- /build/client/public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/mstile-310x310.png -------------------------------------------------------------------------------- /build/client/public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/build/client/public/mstile-70x70.png -------------------------------------------------------------------------------- /build/server.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.0 2 | var americano, application; 3 | 4 | americano = require('americano'); 5 | 6 | application = module.exports.start = function(options, callback) { 7 | if (options == null) { 8 | options = {}; 9 | } 10 | options.name = 'webmail'; 11 | if (options.root == null) { 12 | options.root = __dirname; 13 | } 14 | if (options.port == null) { 15 | options.port = process.env.PORT || 9125; 16 | } 17 | if (options.host == null) { 18 | options.host = process.env.HOST || '0.0.0.0'; 19 | } 20 | options.dbName = process.env.POUCHDB_NAME || 'db'; 21 | if (callback == null) { 22 | callback = function() {}; 23 | } 24 | return americano.start(options, function(err, app, server) { 25 | return callback(null, app, server); 26 | }); 27 | }; 28 | 29 | if (!module.parent) { 30 | application(); 31 | } 32 | -------------------------------------------------------------------------------- /build/server/config.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.0 2 | var americano, config, cozydb, errorHandler, log, path; 3 | 4 | path = require('path'); 5 | 6 | americano = require('americano'); 7 | 8 | log = require('./utils/logging')({ 9 | prefix: 'config' 10 | }); 11 | 12 | cozydb = require('cozy-db-pouchdb'); 13 | 14 | errorHandler = require('./utils/errors').errorHandler; 15 | 16 | config = { 17 | common: { 18 | set: { 19 | 'view engine': 'jade', 20 | 'views': path.resolve(__dirname, 'views') 21 | }, 22 | use: [ 23 | americano.bodyParser(), americano.methodOverride(), americano["static"](__dirname + '/../client/public', { 24 | maxAge: 86400000 25 | }) 26 | ], 27 | useAfter: [errorHandler], 28 | afterStart: function(app, server) { 29 | var ApplicationStartup, Scheduler, SocketHandler, proc; 30 | SocketHandler = require('./utils/socket_handler'); 31 | SocketHandler.setup(app, server); 32 | Scheduler = require('./processes/_scheduler'); 33 | ApplicationStartup = require('./processes/application_startup'); 34 | proc = new ApplicationStartup(); 35 | return Scheduler.schedule(proc, function(err) { 36 | return log.info("Initialization complete"); 37 | }); 38 | } 39 | }, 40 | development: [americano.logger('dev')], 41 | production: [americano.logger('short')], 42 | plugins: ['cozy-db-pouchdb'] 43 | }; 44 | 45 | module.exports = config; 46 | -------------------------------------------------------------------------------- /build/server/controllers/contacts.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.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, value, _ref, _results; 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.8.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.8.0 2 | var Account, async, cozydb; 3 | 4 | async = require('async'); 5 | 6 | Account = require('../models/account'); 7 | 8 | cozydb = require('cozy-db-pouchdb'); 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.8.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.8.0 2 | exports.MSGBYPAGE = 30; 3 | 4 | exports.LIMIT_DESTROY = 200; 5 | 6 | exports.LIMIT_UPDATE = 30; 7 | 8 | exports.CONCURRENT_DESTROY = 1; 9 | 10 | exports.FETCH_AT_ONCE = 10000; 11 | 12 | exports.LIMIT_BY_BOX = 1000; 13 | 14 | exports.REFRESH_INTERVAL = 300000; 15 | -------------------------------------------------------------------------------- /build/server/utils/localization.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.0 2 | var Polyglot, cozydb, drainWaiting, translator, waiting; 3 | 4 | cozydb = require('cozy-db-pouchdb'); 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, phrases, polyglot; 32 | phrases = (function() { 33 | try { 34 | return require("../../client/app/locales/" + locale); 35 | } catch (_error) { 36 | e = _error; 37 | return require("../../client/app/locales/en"); 38 | } 39 | })(); 40 | try { 41 | polyglot = new Polyglot({ 42 | locale: locale, 43 | phrases: phrases 44 | }); 45 | } catch (_error) { 46 | e = _error; 47 | return drainWaiting(e, function() {}); 48 | } 49 | translator = polyglot.t.bind(polyglot); 50 | return drainWaiting(null, translator); 51 | }); 52 | -------------------------------------------------------------------------------- /build/server/utils/logging.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.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 = 0; 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, newDate, _i, _len; 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 = _i = 0, _len = arguments.length; _i < _len; i = ++_i) { 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 | return console.log.apply(console, args); 49 | }; 50 | }; 51 | return api = { 52 | debug: logger(0), 53 | info: logger(1), 54 | warn: logger(2), 55 | error: logger(3) 56 | }; 57 | }; 58 | 59 | module.exports.getLasts = function() { 60 | return lastLogs.slice(index + 1, +MAX_INDEX + 1 || 9e9).join("\n") + "\n" + lastLogs.slice(0, +index + 1 || 9e9).join("\n"); 61 | }; 62 | -------------------------------------------------------------------------------- /build/server/utils/safeloop.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.8.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.8.0 2 | var Bufferer, stream, stream_to_buffer_array, 3 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 4 | __hasProp = {}.hasOwnProperty, 5 | __extends = 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; }; 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(_super) { 24 | __extends(Bufferer, _super); 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.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="css/app.css") 10 | 11 | body 12 | script!= imports 13 | script(src="js/vendor.js") 14 | script(src="js/app.js") 15 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # Numerous always-ignore extensions 2 | *.diff 3 | *.err 4 | *.orig 5 | *.log 6 | *.rej 7 | *.swo 8 | *.swp 9 | *.vi 10 | *~ 11 | *.sass-cache 12 | 13 | # OS or Editor folders 14 | .DS_Store 15 | .cache 16 | .project 17 | .settings 18 | .tmproj 19 | nbproject 20 | Thumbs.db 21 | 22 | # NPM packages folder. 23 | node_modules/ 24 | 25 | # Brunch folder for temporary files. 26 | tmp/ 27 | -------------------------------------------------------------------------------- /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/search_action_creator.coffee: -------------------------------------------------------------------------------- 1 | AppDispatcher = require '../app_dispatcher' 2 | {ActionTypes} = require '../constants/app_constants' 3 | 4 | module.exports = SearchActionCreator = 5 | 6 | setQuery: (query) -> 7 | AppDispatcher.handleViewAction 8 | type: ActionTypes.SET_SEARCH_QUERY 9 | value: query 10 | 11 | receiveRawSearchResults: (results) -> 12 | 13 | # first clear the previous results 14 | SearchActionCreator.clearSearch false 15 | 16 | AppDispatcher.handleViewAction 17 | type: ActionTypes.RECEIVE_RAW_SEARCH_RESULTS 18 | value: results 19 | 20 | clearSearch: (clearQuery = true) -> 21 | if clearQuery then SearchActionCreator.setQuery "" 22 | 23 | AppDispatcher.handleViewAction 24 | type: ActionTypes.CLEAR_SEARCH_RESULTS 25 | value: null 26 | 27 | -------------------------------------------------------------------------------- /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/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/android-chrome-144x144.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/android-chrome-36x36.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/android-chrome-48x48.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/android-chrome-72x72.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/android-chrome-96x96.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /client/app/assets/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #8bee8c 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /client/app/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/favicon-16x16.png -------------------------------------------------------------------------------- /client/app/assets/favicon-194x194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/favicon-194x194.png -------------------------------------------------------------------------------- /client/app/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/favicon-32x32.png -------------------------------------------------------------------------------- /client/app/assets/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/favicon-96x96.png -------------------------------------------------------------------------------- /client/app/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/favicon.ico -------------------------------------------------------------------------------- /client/app/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/mavenpro-bold.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/mavenpro-bold.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/mavenpro-regular.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/mavenpro-regular.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/sourcesanspro-bold-italic.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/sourcesanspro-bold-italic.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/sourcesanspro-bold.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/sourcesanspro-bold.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/sourcesanspro-italic.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/sourcesanspro-italic.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/sourcesanspro-regular.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/fonts/sourcesanspro-regular.woff2 -------------------------------------------------------------------------------- /client/app/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/app/assets/images/spinner-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/app/assets/images/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/assets/js/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/app/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/app/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/app/assets/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/mstile-144x144.png -------------------------------------------------------------------------------- /client/app/assets/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/mstile-150x150.png -------------------------------------------------------------------------------- /client/app/assets/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/mstile-310x150.png -------------------------------------------------------------------------------- /client/app/assets/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/mstile-310x310.png -------------------------------------------------------------------------------- /client/app/assets/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/app/assets/mstile-70x70.png -------------------------------------------------------------------------------- /client/app/components/account_config_delete.coffee: -------------------------------------------------------------------------------- 1 | { div } = React.DOM 2 | 3 | AccountActionCreator = require '../actions/account_action_creator' 4 | LayoutActionCreator = require '../actions/layout_action_creator' 5 | 6 | {FieldSet, FormButtons} = require './basic_components' 7 | 8 | module.exports = AccountConfigDelete = React.createClass 9 | displayName: 'AccountConfigDelete' 10 | 11 | 12 | getInitialState: -> 13 | state = {} 14 | state.deleting = false 15 | return state 16 | 17 | render: -> 18 | div null, 19 | FieldSet text: t 'account danger zone' 20 | 21 | FormButtons 22 | buttons: [ 23 | class: 'btn-remove' 24 | contrast: false 25 | default: true 26 | danger: true 27 | onClick: @onRemove 28 | spinner: @state.deleting 29 | icon: 'trash' 30 | text: t "account remove" 31 | ] 32 | 33 | # Ask for confirmation before running remove operation. 34 | onRemove: (event) -> 35 | event?.preventDefault() 36 | 37 | label = @props.selectedAccount.get 'label' 38 | modal = 39 | title : t 'app confirm delete' 40 | subtitle : t 'account remove confirm', {label: label} 41 | closeModal : -> 42 | LayoutActionCreator.hideModal() 43 | closeLabel : t 'app cancel' 44 | actionLabel : t 'app confirm' 45 | action : => 46 | LayoutActionCreator.hideModal() 47 | @setState deleting: true 48 | AccountActionCreator.remove @props.selectedAccount.get('id') 49 | 50 | LayoutActionCreator.displayModal modal 51 | -------------------------------------------------------------------------------- /client/app/components/alert.coffee: -------------------------------------------------------------------------------- 1 | {div, button, span, strong} = React.DOM 2 | {AlertLevel} = require '../constants/app_constants' 3 | LayoutActionCreator = require '../actions/layout_action_creator' 4 | 5 | 6 | module.exports = React.createClass 7 | displayName: 'Alert' 8 | 9 | 10 | # Render an alert message box with color and style corresponding to given 11 | # level of alert. 12 | render: -> 13 | 14 | alert = @props.alert 15 | if alert.level? 16 | levels = {} 17 | levels[AlertLevel.SUCCESS] = 'alert-success' 18 | levels[AlertLevel.INFO] = 'alert-info' 19 | levels[AlertLevel.WARNING] = 'alert-warning' 20 | levels[AlertLevel.ERROR] = 'alert-danger' 21 | 22 | div className: 'row row-alert', 23 | if alert.level? 24 | div 25 | ref: 'alert' 26 | className: "alert #{levels[alert.level]} alert-dismissible", 27 | role: "alert", 28 | button 29 | type: "button", 30 | className: "close", 31 | onClick: @hide, 32 | span 'aria-hidden': "true", "×" 33 | span className: "sr-only", t "app alert close" 34 | strong null, alert.message 35 | 36 | 37 | # Hide current alert. 38 | hide: -> 39 | LayoutActionCreator.alertHide() 40 | 41 | 42 | # Hide after 10 seconds. 43 | autohide: -> 44 | if false and @props.alert.level is AlertLevel.SUCCESS 45 | setTimeout => 46 | @refs.alert.getDOMNode().classList.add 'autoclose' 47 | , 1000 48 | setTimeout @hide, 10000 49 | 50 | 51 | componentDidMount: -> 52 | @autohide() 53 | 54 | 55 | componentDidUpdate: -> 56 | @autohide() 57 | 58 | -------------------------------------------------------------------------------- /client/app/components/message_footer.coffee: -------------------------------------------------------------------------------- 1 | {div, span, ul, li, a, i} = React.DOM 2 | MessageUtils = require '../utils/message_utils' 3 | 4 | AttachmentPreview = require './attachement_preview' 5 | 6 | 7 | module.exports = React.createClass 8 | displayName: 'MessageFooter' 9 | 10 | propTypes: 11 | message: React.PropTypes.object.isRequired 12 | 13 | 14 | render: -> 15 | div className: 'attachments', 16 | @renderAttachments() 17 | 18 | 19 | renderAttachments: -> 20 | attachments = @props.message.get('attachments')?.toJS() or [] 21 | return unless attachments.length 22 | 23 | resources =_.groupBy attachments, (file) -> 24 | if MessageUtils.getAttachmentType(file.contentType) is 'image' 25 | 'preview' 26 | else 27 | 'binary' 28 | ul className: null, 29 | if resources.preview 30 | for file in resources.preview 31 | AttachmentPreview 32 | ref: 'attachmentPreview' 33 | file: file 34 | key: file.checksum 35 | preview: true 36 | previewLink: true 37 | if resources.binary 38 | for file in resources.binary 39 | AttachmentPreview 40 | ref: 'attachmentPreview' 41 | file: file 42 | key: file.checksum 43 | preview: false 44 | previewLink: true 45 | -------------------------------------------------------------------------------- /client/app/components/participant.coffee: -------------------------------------------------------------------------------- 1 | {span, a, i} = React.DOM 2 | MessageUtils = require '../utils/message_utils' 3 | 4 | Participant = React.createClass 5 | displayName: 'Participant' 6 | 7 | render: -> 8 | if not @props.address? 9 | span null 10 | else 11 | span 12 | className: 'address-item' 13 | 'data-toggle': "tooltip" 14 | ref: 'participant' 15 | title: @props.address.address, 16 | key: @props.key, 17 | MessageUtils.displayAddress @props.address 18 | 19 | _initTooltip: -> 20 | if @props.tooltip and @refs.participant? 21 | MessageUtils.tooltip @refs.participant.getDOMNode(), @props.address, @props.onAdd 22 | 23 | componentDidMount: -> 24 | @_initTooltip() 25 | 26 | componentDidUpdate: -> 27 | @_initTooltip() 28 | 29 | Participants = React.createClass 30 | displayName: 'Participants' 31 | 32 | render: -> 33 | span className: 'address-list', 34 | if @props.participants 35 | for address, key in @props.participants 36 | span key: key, className: null, 37 | Participant 38 | key: key, 39 | address: address, 40 | onAdd: @props.onAdd, 41 | tooltip: @props.tooltip 42 | if key < ( @props.participants.length - 1) 43 | span null, ', ' 44 | 45 | module.exports = Participants 46 | -------------------------------------------------------------------------------- /client/app/components/popup_message_attachments.coffee: -------------------------------------------------------------------------------- 1 | {div, ul, i} = React.DOM 2 | {Tooltips} = require '../constants/app_constants' 3 | 4 | AttachmentPreview = require './attachement_preview' 5 | 6 | 7 | module.exports = React.createClass 8 | displayName: 'MessageAttachmentsPopup' 9 | 10 | mixins: [ 11 | OnClickOutside 12 | ] 13 | 14 | 15 | getInitialState: -> 16 | showAttachements: false 17 | 18 | 19 | toggleAttachments: -> 20 | @setState showAttachements: not @state.showAttachements 21 | 22 | 23 | handleClickOutside: -> 24 | @setState showAttachements: false 25 | 26 | 27 | render: -> 28 | attachments = @props.message.get('attachments')?.toJS() or [] 29 | 30 | div 31 | className: 'attachments' 32 | 'aria-expanded': @state.showAttachements 33 | onClick: (event) -> event.stopPropagation() 34 | i 35 | className: 'btn fa fa-paperclip' 36 | onClick: @toggleAttachments 37 | 'aria-describedby': Tooltips.OPEN_ATTACHMENTS 38 | 'data-tooltip-direction': 'left' 39 | 40 | div className: 'popup', 'aria-hidden': not @state.showAttachements, 41 | ul className: null, 42 | for file in attachments 43 | AttachmentPreview 44 | ref: 'attachmentPreview' 45 | file: file 46 | key: file.checksum 47 | preview: false 48 | -------------------------------------------------------------------------------- /client/app/components/search-form.coffee: -------------------------------------------------------------------------------- 1 | {div, input, span} = React.DOM 2 | classer = React.addons.classSet 3 | 4 | SearchActionCreator = require '../actions/search_action_creator' 5 | 6 | ENTER_KEY = 13 7 | 8 | RouterMixin = require '../mixins/router_mixin' 9 | 10 | module.exports = React.createClass 11 | displayName: 'SearchForm' 12 | 13 | mixins: [RouterMixin] 14 | 15 | render: -> 16 | div className: 'form-group pull-left', 17 | div className: 'input-group', 18 | input 19 | className: 'form-control', 20 | type: 'text', 21 | placeholder: t('app search'), 22 | onKeyPress: @onKeyPress, 23 | ref: 'searchInput', 24 | defaultValue: @props.query 25 | div 26 | className: 'input-group-addon btn btn-cozy', 27 | onClick: @onSubmit, 28 | span className: 'fa fa-search' 29 | 30 | onSubmit: -> 31 | query = encodeURIComponent @refs.searchInput.getDOMNode().value.trim() 32 | 33 | # only submit query if it's longer than 3 characters 34 | # @TODO: validate and give feedback to the user 35 | if query.length > 3 36 | @redirect 37 | direction: 'first' 38 | action: 'search' 39 | parameters: [query] 40 | 41 | onKeyPress: (evt) -> 42 | if evt.charCode is ENTER_KEY 43 | @onSubmit() 44 | evt.preventDefault() 45 | return false 46 | else 47 | query = @refs.searchInput.getDOMNode().value 48 | SearchActionCreator.setQuery query 49 | -------------------------------------------------------------------------------- /client/app/components/toolbox_move.coffee: -------------------------------------------------------------------------------- 1 | {div, ul, li, span, i, p, a, button} = React.DOM 2 | {MenuHeader, MenuItem} = require './basic_components' 3 | 4 | 5 | module.exports = ToolboxMove = React.createClass 6 | displayName: 'ToolboxMove' 7 | 8 | 9 | shouldComponentUpdate: (nextProps, nextState) -> 10 | return not(_.isEqual(nextState, @state)) or 11 | not(_.isEqual(nextProps, @props)) 12 | 13 | render: -> 14 | direction = if @props.direction is 'right' then 'right' else 'left' 15 | 16 | div className: 'menu-move btn-group btn-group-sm', 17 | button 18 | className: 'btn btn-default dropdown-toggle fa fa-folder-open' 19 | type: 'button' 20 | 'data-toggle': 'dropdown' 21 | ' ' 22 | span className: 'caret' 23 | ul 24 | className: "dropdown-menu dropdown-menu-#{direction}" 25 | role: 'menu', 26 | MenuHeader null, t 'mail action move' 27 | @renderMailboxes() 28 | 29 | 30 | renderMailboxes: -> 31 | for id, mbox of @props.mailboxes when id isnt @props.selectedMailboxID 32 | do (id) => # bind id to each mailbox 33 | MenuItem 34 | key: id 35 | className: "pusher pusher-#{mbox.depth}" 36 | onClick: => @props.onMove id 37 | mbox.label 38 | -------------------------------------------------------------------------------- /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 | AppDispatcher = require '../../../app_dispatcher' 2 | 3 | 4 | module.exports = class Store extends EventEmitter 5 | 6 | uniqID: null 7 | 8 | # this variable will be shared with all subclasses so we store the items 9 | # by subclass we don't use `@constructor.name` because it breaks when 10 | # mangled 11 | _nextUniqID = 0 12 | _handlers = {} 13 | _addHandlers = (type, callback) -> 14 | 15 | _handlers[@uniqID] = {} unless _handlers[@uniqID]? 16 | _handlers[@uniqID][type] = callback 17 | 18 | # Registers the store's callbacks to the dispatcher 19 | _processBinding = -> 20 | @dispatchToken = AppDispatcher.register (payload) => 21 | {type, value} = payload.action 22 | if (callback = _handlers[@uniqID][type])? 23 | callback.call @, value 24 | 25 | 26 | constructor: -> 27 | super() 28 | # set a uniq ID 29 | @uniqID = _nextUniqID++ 30 | @__bindHandlers _addHandlers.bind @ 31 | _processBinding.call @ 32 | 33 | # Must be overriden by each store 34 | __bindHandlers: (handle) -> 35 | if __DEV__ 36 | message = "The store #{@constructor.name} must define a " + \ 37 | "`__bindHandlers` method" 38 | throw new Error message 39 | 40 | -------------------------------------------------------------------------------- /client/app/mixins/participant_mixin.coffee: -------------------------------------------------------------------------------- 1 | ### 2 | Participant mixin. 3 | ### 4 | {span, a, i} = React.DOM 5 | 6 | ContactStore = require '../stores/contact_store' 7 | ContactLabel = require '../components/contact_label' 8 | 9 | 10 | module.exports = 11 | formatUsers: (users) -> 12 | return unless users? 13 | 14 | if _.isArray users 15 | items = [] 16 | for user in users 17 | items.push ContactLabel 18 | contact: user 19 | tooltip: true 20 | 21 | items.push ", " if user isnt _.last users 22 | return items 23 | else 24 | return ContactLabel 25 | contact: users 26 | tooltip: true 27 | -------------------------------------------------------------------------------- /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/store_watch_mixin.coffee: -------------------------------------------------------------------------------- 1 | module.exports = StoreWatchMixin = (stores) -> 2 | 3 | 4 | # Update state when linked store emit changes. 5 | componentDidMount: -> 6 | stores.forEach (store) => 7 | store.on 'change', @_setStateFromStores 8 | 9 | 10 | # Stop listening to the linked stores when the component is unmounted. 11 | componentWillUnmount: -> 12 | stores.forEach (store) => 13 | store.removeListener 'change', @_setStateFromStores 14 | 15 | 16 | # Build initial state from store values. 17 | getInitialState: -> 18 | return @getStateFromStores() 19 | 20 | 21 | # Update state with store values. 22 | _setStateFromStores: -> 23 | if @isMounted() 24 | @setState @getStateFromStores() 25 | 26 | -------------------------------------------------------------------------------- /client/app/mixins/tooltip_refresher_mixin.coffee: -------------------------------------------------------------------------------- 1 | module.exports = TooltipRefresherMixin = 2 | 3 | 4 | # AriaTips must bind the elements declared as tooltip to their 5 | # respective tooltip when the component is mounted (DOM elements exist). 6 | componentDidMount: -> 7 | AriaTips.bind() 8 | 9 | 10 | # AriaTips must bind the elements declared as tooltip to their 11 | # respective tooltip, each time the application component (the root) 12 | # is updated to make sure new tooltips are also bound. 13 | componentDidUpdate: -> 14 | AriaTips.bind() 15 | -------------------------------------------------------------------------------- /client/app/stores/refreshes_store.coffee: -------------------------------------------------------------------------------- 1 | Store = require '../libs/flux/store/store' 2 | 3 | {ActionTypes} = require '../constants/app_constants' 4 | 5 | 6 | refreshesToImmutable = (refreshes) -> 7 | Immutable.Sequence refreshes 8 | # sets objectID as index 9 | .mapKeys (_, refresh) -> return refresh.objectID 10 | .map (refresh) -> Immutable.fromJS refresh 11 | .toOrderedMap() 12 | 13 | 14 | class RefreshesStore extends Store 15 | 16 | ### 17 | Initialization. 18 | Defines private variables here. 19 | ### 20 | _refreshes = refreshesToImmutable window.refreshes or [] 21 | 22 | 23 | ### 24 | Defines here the action handlers. 25 | ### 26 | __bindHandlers: (handle) -> 27 | 28 | 29 | handle ActionTypes.RECEIVE_REFRESH_STATUS, (refreshes) -> 30 | _refreshes = refreshesToImmutable refreshes 31 | 32 | handle ActionTypes.RECEIVE_REFRESH_UPDATE, (refresh) -> 33 | refresh = Immutable.Map refresh 34 | id = refresh.get('objectID') 35 | _refreshes = _refreshes.set(id, refresh).toOrderedMap() 36 | @emit 'change' 37 | 38 | handle ActionTypes.RECEIVE_REFRESH_DELETE, (refreshID) -> 39 | _refreshes = _refreshes.filter (refresh) -> 40 | refresh.get('id') isnt refreshID 41 | .toOrderedMap() 42 | @emit 'change' 43 | 44 | handle ActionTypes.RECEIVE_REFRESH_NOTIF, (data) -> 45 | @emit 'notify', t('notif new title'), body: data.message 46 | 47 | 48 | getRefreshing: -> 49 | return _refreshes 50 | 51 | 52 | module.exports = new RefreshesStore() 53 | 54 | -------------------------------------------------------------------------------- /client/app/stores/search_store.coffee: -------------------------------------------------------------------------------- 1 | Store = require '../libs/flux/store/store' 2 | 3 | {ActionTypes} = require '../constants/app_constants' 4 | 5 | 6 | class SearchStore extends Store 7 | 8 | ### 9 | Initialization. 10 | Defines private variables here. 11 | ### 12 | 13 | _query = "" 14 | 15 | # search results are a list of message 16 | _results = Immutable.OrderedMap.empty() 17 | 18 | ### 19 | Defines here the action handlers. 20 | ### 21 | __bindHandlers: (handle) -> 22 | 23 | handle ActionTypes.RECEIVE_RAW_SEARCH_RESULTS, (rawResults) -> 24 | if rawResults? 25 | _results = _results.withMutations (map) -> 26 | rawResults.forEach (rawResult) -> 27 | message = Immutable.Map rawResult 28 | map.set message.get('id'), message 29 | else 30 | _results = Immutable.OrderedMap.empty() 31 | 32 | @emit 'change' 33 | 34 | handle ActionTypes.CLEAR_SEARCH_RESULTS, -> 35 | _results = Immutable.OrderedMap.empty() 36 | @emit 'change' 37 | 38 | handle ActionTypes.SET_SEARCH_QUERY, (query) -> 39 | _query = query 40 | @emit 'change' 41 | 42 | 43 | ### 44 | Public API 45 | ### 46 | getResults: -> 47 | return _results 48 | 49 | getQuery: -> 50 | return _query 51 | 52 | 53 | module.exports = new SearchStore() 54 | -------------------------------------------------------------------------------- /client/app/stores/settings_store.coffee: -------------------------------------------------------------------------------- 1 | Store = require '../libs/flux/store/store' 2 | 3 | {ActionTypes} = require '../constants/app_constants' 4 | 5 | class SettingsStore extends Store 6 | 7 | ### 8 | Initialization. 9 | Defines private variables here. 10 | ### 11 | _settings = Immutable.Map window.settings 12 | 13 | ### 14 | Defines here the action handlers. 15 | ### 16 | __bindHandlers: (handle) -> 17 | 18 | handle ActionTypes.SETTINGS_UPDATED, (settings) -> 19 | _settings = Immutable.Map settings 20 | @emit 'change' 21 | 22 | 23 | ### 24 | Public API 25 | ### 26 | get: (settingName = null) -> 27 | if settingName? 28 | return _settings.get settingName 29 | else 30 | return _settings 31 | 32 | 33 | module.exports = new SettingsStore() 34 | -------------------------------------------------------------------------------- /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 | 15 | /* 16 | Slide 17 | */ 18 | @keyframes slide { 19 | from { 20 | right 100% 21 | } 22 | to { 23 | right -100px 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /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 | // gray and dark gray 15 | lightcolor = #EEE 16 | mediumcolor = #BDBDBD 17 | mediumcolor-contrast = #428bca 18 | darkcolor = #444 19 | 20 | // special cozy colors 21 | homeheadercolor = #363a46 22 | bluegreen = #34A6BB 23 | lightorange = #FFC47F 24 | lightblue = #CBE4FB 25 | lightred = #F4F4F4 26 | grey = #666 27 | lightgrey = #EEE 28 | 29 | 30 | // App palette 31 | // ----------------------------------------------------------------------------- 32 | base-background-color = #f5f5f5 33 | 34 | 35 | // Defintions 36 | // ----------------------------------------------------------------------------- 37 | .app 38 | background-color base-background-color 39 | 40 | 41 | .app [role=menubar] [role=menuitem] 42 | color #777 43 | 44 | &:hover 45 | color basecolor 46 | -------------------------------------------------------------------------------- /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 #f2f2f2 14 | border-radius 3px 15 | white-space no-wrap 16 | 17 | a 18 | cursor pointer 19 | padding 0 20 | color #333 21 | 22 | .mime 23 | padding 0 0.4em 0 0 24 | font-size 1.2em 25 | color #444 26 | 27 | .delete 28 | padding 0 0 0 0.4em 29 | cursor pointer 30 | 31 | .file-size 32 | margin-left 0.5em 33 | color #999 34 | 35 | .file-actions 36 | margin-left 0.5em 37 | border-left 1px solid #ccc 38 | 39 | a 40 | padding: 0 .5em 41 | 42 | a, 43 | i 44 | color #999 45 | 46 | &:hover 47 | color #333 48 | text-decoration none 49 | 50 | .attachment-name 51 | color darkcolor 52 | 53 | .file-detail 54 | color #999 55 | font-size 0.9em 56 | 57 | .dropzone 58 | width 20em 59 | padding 0.5em 0 60 | border 2px #ddd dashed 61 | cursor pointer 62 | color #bbb 63 | 64 | .dropzone.target 65 | border-color basecolor 66 | 67 | 68 | i 69 | padding 0.5em 70 | 71 | .file-wrapper 72 | display none 73 | -------------------------------------------------------------------------------- /client/app/styles/_responsive.styl: -------------------------------------------------------------------------------- 1 | @media (max-width 768px) 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/_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 #bbb 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 #777 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 | -------------------------------------------------------------------------------- /client/app/utils/intent_manager.coffee: -------------------------------------------------------------------------------- 1 | TIMEOUT = 3000 # 3 s 2 | 3 | 4 | module.exports = class IntentManager 5 | 6 | 7 | constructor : -> 8 | @talker = new Talker window.parent, '*' 9 | 10 | 11 | send : (nameSpace, intent, timeout) -> 12 | @talker.timeout = if timeout then timeout else TIMEOUT 13 | @talker.send 'nameSpace', intent 14 | 15 | -------------------------------------------------------------------------------- /client/app/utils/socketio_utils.coffee: -------------------------------------------------------------------------------- 1 | AppDispatcher = require '../app_dispatcher' 2 | {ActionTypes} = require '../constants/app_constants' 3 | url = window.location.origin 4 | pathToSocketIO = "#{window.location.pathname}socket.io" 5 | socket = io.connect url, 6 | path: pathToSocketIO 7 | reconnectionDelayMax: 60000 8 | reconectionDelay: 2000 9 | reconnectionAttempts: 3 10 | 11 | dispatchAs = (action) -> (content) -> 12 | AppDispatcher.handleServerAction 13 | type: action 14 | value: content 15 | 16 | scope = {} 17 | setServerScope = -> 18 | socket.emit 'change_scope', scope 19 | 20 | 21 | # socket.on 'refresh.status', dispatchAs ActionTypes.RECEIVE_REFRESH_STATUS 22 | # socket.on 'refresh.create', dispatchAs ActionTypes.RECEIVE_REFRESH_UPDATE 23 | # socket.on 'refresh.update', dispatchAs ActionTypes.RECEIVE_REFRESH_UPDATE 24 | # socket.on 'refresh.delete', dispatchAs ActionTypes.RECEIVE_REFRESH_DELETE 25 | 26 | socket.on 'message.create', dispatchAs ActionTypes.RECEIVE_RAW_MESSAGE_REALTIME 27 | socket.on 'message.update', dispatchAs ActionTypes.RECEIVE_RAW_MESSAGE 28 | socket.on 'message.delete', dispatchAs ActionTypes.RECEIVE_MESSAGE_DELETE 29 | 30 | socket.on 'mailbox.update', dispatchAs ActionTypes.RECEIVE_MAILBOX_UPDATE 31 | 32 | socket.on 'connect', -> 33 | setServerScope() 34 | socket.on 'reconnect', -> 35 | setServerScope() 36 | 37 | socket.on 'refresh.notify', dispatchAs ActionTypes.RECEIVE_REFRESH_NOTIF 38 | 39 | exports.changeRealtimeScope = (boxid, date) -> 40 | scope = 41 | mailboxID: boxid 42 | before: date 43 | setServerScope() 44 | -------------------------------------------------------------------------------- /client/config.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | glob = require 'glob' 3 | 4 | exports.config = 5 | files: 6 | javascripts: 7 | joinTo: 8 | 'js/app.js': /^app/ 9 | 'js/vendor.js': /^vendor/ 10 | order: 11 | # Files in `vendor` directories are compiled before other files 12 | # even if they aren't specified in order. 13 | before: [ 14 | 'vendor/scripts/polyfills.js' 15 | 'vendor/scripts/events.js' 16 | 'vendor/scripts/react-with-addons.js' 17 | 'vendor/scripts/jquery.js' 18 | 'vendor/scripts/underscore.js' 19 | 'vendor/scripts/backbone.js' 20 | 'vendor/scripts/superagent.js' 21 | 'vendor/scripts/bootstrap-3.1.1.min.js' 22 | 'vendor/scripts/moment.js' 23 | 'vendor/scripts/polyglot.js' 24 | 'vendor/scripts/json-patch-duplex.min.js' 25 | 'vendor/scripts/revalidator.js' 26 | ].concat(glob.sync 'vendor/plugins/**/*.js') 27 | 28 | stylesheets: 29 | joinTo: 'css/app.css' 30 | order: 31 | before: [] 32 | after: ['vendor/stylesheets/helpers.css'] 33 | .concat(glob.sync 'vendor/plugins/**/*.css') 34 | 35 | plugins: 36 | cleancss: 37 | keepSpecialComments: 0 38 | removeEmpty: true 39 | 40 | digest: 41 | referenceFiles: /\.jade$/ 42 | 43 | overrides: 44 | production: 45 | # re-enable when uglifyjs will handle properly in source maps 46 | # with sourcesContent attribute 47 | #optimize: true 48 | sourceMaps: true 49 | paths: 50 | public: path.resolve __dirname, '../build/client/public' 51 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "coffee-script-brunch": "1.8.0", 4 | "css-brunch": "1.7.0", 5 | "digest-brunch": "1.2.2", 6 | "javascript-brunch": "1.7.1", 7 | "stylus-brunch": "1.8.0", 8 | "backbone": "1.1.2", 9 | "bootstrap": "3.2.0", 10 | "clean-css-brunch": "1.7.1", 11 | "immutable": "2.0.15", 12 | "jquery": "2.1.1", 13 | "moment": "2.8.1", 14 | "nib": "1.0.3", 15 | "node-polyglot": "0.3.0", 16 | "pretty-hrtime": "0.2.1", 17 | "react": "0.11.1", 18 | "superagent": "0.18.2", 19 | "underscore": "1.6.0", 20 | "watchify": "1.0.1", 21 | "node-event-emitter": "0.0.1", 22 | "glob": "4.0.6", 23 | "json-brunch": "^1.5.4" 24 | 25 | }, 26 | "devDependencies": { 27 | "coffeeify": "0.7.0", 28 | "vinyl-source-stream": "0.1.1", 29 | "vinyl-buffer": "0.0.0", 30 | "coffee-script": "1.7.1", 31 | "chai": "1.9.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /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/fonts/fontawesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/fontawesome/FontAwesome.otf -------------------------------------------------------------------------------- /client/vendor/assets/fonts/fontawesome/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/fontawesome/FontAwesome.ttf -------------------------------------------------------------------------------- /client/vendor/assets/fonts/fontawesome/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/fontawesome/fontawesome-webfont.eot -------------------------------------------------------------------------------- /client/vendor/assets/fonts/fontawesome/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/fontawesome/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /client/vendor/assets/fonts/fontawesome/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/fontawesome/fontawesome-webfont.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/fontawesome/maven-pro-light-300.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/fontawesome/maven-pro-light-300.otf -------------------------------------------------------------------------------- /client/vendor/assets/fonts/fontawesome/signika-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/fontawesome/signika-regular.ttf -------------------------------------------------------------------------------- /client/vendor/assets/fonts/glyphicons/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/glyphicons/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /client/vendor/assets/fonts/glyphicons/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/glyphicons/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /client/vendor/assets/fonts/glyphicons/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/glyphicons/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro/SourceSansPro-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/sourcesanspro/SourceSansPro-Regular.eot -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro/SourceSansPro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/sourcesanspro/SourceSansPro-Regular.otf -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro/SourceSansPro-Regular.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/sourcesanspro/SourceSansPro-Regular.otf.woff -------------------------------------------------------------------------------- /client/vendor/assets/fonts/sourcesanspro/SourceSansPro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/assets/fonts/sourcesanspro/SourceSansPro-Regular.ttf -------------------------------------------------------------------------------- /client/vendor/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/vendor/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/vendor/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/vendor/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 | .plugin-help { 43 | padding-top: .2em; 44 | font-size: 1.5em; 45 | } 46 | kbd { 47 | line-height: 1.5em; 48 | margin: 0 1em 0 0; 49 | } 50 | -------------------------------------------------------------------------------- /client/vendor/plugins/medium-editor/init.js: -------------------------------------------------------------------------------- 1 | //jshint browser: true 2 | if (typeof window.plugins !== "object") { 3 | window.plugins = {}; 4 | } 5 | window.plugins.mediumeditor = { 6 | name: "medium-editor", 7 | type: "editor", 8 | active: false, 9 | onAdd: { 10 | condition: function (node) { 11 | "use strict"; 12 | return node.querySelector('.rt-editor') !== null; 13 | }, 14 | action: function (node) { 15 | /* eslint no-unused-vars: 0 */ 16 | "use strict"; 17 | var editorNode = node.querySelector('.rt-editor'), 18 | medium, 19 | options; 20 | options = { 21 | imageDragging: false, // We handle image drag'n'drop ourself 22 | cleanPastedHTML: true, 23 | static: true, 24 | targetBlank: true, 25 | toolbar: { 26 | buttons: ['bold', 'italic', 'underline', 'anchor', 'h2', 'h3'] 27 | } 28 | } 29 | if (!editorNode.classList.contains('medium-editor')) { 30 | medium = new window.MediumEditor(editorNode, options); 31 | editorNode.classList.add('medium-editor'); 32 | } 33 | } 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /client/vendor/plugins/medium-editor/styles/medium-default.min.css: -------------------------------------------------------------------------------- 1 | .medium-toolbar-arrow-under:after{border-color:#242424 transparent transparent;top:50px}.medium-toolbar-arrow-over:before{border-color:transparent transparent #242424;top:-8px}.medium-editor-toolbar{background:-webkit-linear-gradient(top,#242424,rgba(36,36,36,.75));background:linear-gradient(to bottom,#242424,rgba(36,36,36,.75));border:1px solid #000;border-radius:5px;box-shadow:0 0 3px #000}.medium-editor-toolbar li button{background:-webkit-linear-gradient(top,#242424,rgba(36,36,36,.89));background:linear-gradient(to bottom,#242424,rgba(36,36,36,.89));border:0;border-right:1px solid #000;border-left:1px solid #333;border-left:1px solid rgba(255,255,255,.1);box-shadow:0 2px 2px rgba(0,0,0,.3);color:#fff;height:50px;min-width:50px;-webkit-transition:background-color .2s ease-in;transition:background-color .2s ease-in}.medium-editor-toolbar li button:hover{background-color:#000;color:#ff0}.medium-editor-toolbar li .medium-editor-button-first{border-bottom-left-radius:5px;border-top-left-radius:5px}.medium-editor-toolbar li .medium-editor-button-last{border-bottom-right-radius:5px;border-top-right-radius:5px}.medium-editor-toolbar li .medium-editor-button-active{background:-webkit-linear-gradient(top,#242424,rgba(0,0,0,.89));background:linear-gradient(to bottom,#242424,rgba(0,0,0,.89));color:#fff}.medium-editor-toolbar-form{background:#242424;border-radius:5px;color:#999}.medium-editor-toolbar-form .medium-editor-toolbar-input{background:#242424;box-sizing:border-box;color:#ccc;height:50px}.medium-editor-toolbar-form a{color:#fff}.medium-editor-toolbar-anchor-preview{background:#242424;border-radius:5px;color:#fff}.medium-editor-placeholder:after{color:#b3b3b1} -------------------------------------------------------------------------------- /client/vendor/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/vendor/plugins/minislate/css/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/plugins/minislate/css/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /client/vendor/plugins/minislate/css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/plugins/minislate/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /client/vendor/plugins/minislate/css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/plugins/minislate/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /client/vendor/plugins/minislate/css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy-labs/emails/f8ad59150579195d08d13984e75d376b7f3b538d/client/vendor/plugins/minislate/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /client/vendor/plugins/minislate/init.js: -------------------------------------------------------------------------------- 1 | //jshint browser: true 2 | if (typeof window.plugins !== "object") { 3 | window.plugins = {}; 4 | } 5 | window.plugins.minislate = { 6 | name: "MiniSlate", 7 | type: "editor", 8 | active: true, 9 | onAdd: { 10 | condition: function (node) { 11 | "use strict"; 12 | return node.querySelector('.rt-editor') !== null; 13 | }, 14 | action: function (node) { 15 | // jshint unused: false 16 | "use strict"; 17 | var editorNode = node.querySelector('.rt-editor'), 18 | editor = new window.Minislate.simpleEditor([editorNode]); 19 | } 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /client/vendor/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/vendor/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/vendor/scripts/polyfills.js: -------------------------------------------------------------------------------- 1 | var isFunction = function(o) { 2 | return typeof o == 'function'; 3 | }; 4 | 5 | 6 | var bind, 7 | slice = [].slice, 8 | proto = Function.prototype, 9 | featureMap; 10 | 11 | featureMap = { 12 | 'function-bind': 'bind' 13 | }; 14 | 15 | function has(feature) { 16 | var prop = featureMap[feature]; 17 | return isFunction(proto[prop]); 18 | } 19 | 20 | // check for missing features 21 | if (!has('function-bind')) { 22 | // adapted from Mozilla Developer Network example at 23 | // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind 24 | bind = function bind(obj) { 25 | var args = slice.call(arguments, 1), 26 | self = this, 27 | nop = function() { 28 | }, 29 | bound = function() { 30 | return self.apply(this instanceof nop ? this : (obj || {}), args.concat(slice.call(arguments))); 31 | }; 32 | nop.prototype = this.prototype || {}; // Firefox cries sometimes if prototype is undefined 33 | bound.prototype = new nop(); 34 | return bound; 35 | }; 36 | proto.bind = bind; 37 | } 38 | 39 | if (!isFunction(window.CustomEvent)) { 40 | (function () { 41 | function CustomEvent ( event, params ) { 42 | params = params || { bubbles: false, cancelable: false, detail: undefined }; 43 | var evt = document.createEvent( 'CustomEvent' ); 44 | evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); 45 | return evt; 46 | }; 47 | 48 | CustomEvent.prototype = window.Event.prototype; 49 | 50 | window.CustomEvent = CustomEvent; 51 | })(); 52 | } 53 | -------------------------------------------------------------------------------- /client/vendor/styles/helpers.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 | } -------------------------------------------------------------------------------- /server.coffee: -------------------------------------------------------------------------------- 1 | americano = require 'americano' 2 | 3 | application = module.exports.start = (options, callback) -> 4 | options ?= {} 5 | options.name = 'emails' 6 | options.root ?= __dirname 7 | options.port ?= process.env.PORT or 9125 8 | options.host ?= process.env.HOST or '0.0.0.0' 9 | options.dbName = process.env.POUCHDB_NAME or 'db' 10 | 11 | callback ?= -> 12 | 13 | americano.start options, (err, app, server) -> 14 | callback null, app, server 15 | 16 | if not module.parent 17 | application() 18 | 19 | -------------------------------------------------------------------------------- /server/config.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | americano = require 'americano' 3 | log = require('./utils/logging')(prefix: 'config') 4 | cozydb = require 'cozy-db-pouchdb' 5 | {errorHandler} = require './utils/errors' 6 | 7 | config = 8 | common: 9 | set: 10 | 'view engine': 'jade' 11 | 'views': path.resolve __dirname, 'views' 12 | use: [ 13 | americano.bodyParser() 14 | americano.methodOverride() 15 | americano.static __dirname + '/../client/public', 16 | maxAge: 86400000 17 | ] 18 | 19 | useAfter: [ 20 | errorHandler 21 | ] 22 | 23 | afterStart: (app, server) -> 24 | SocketHandler = require './utils/socket_handler' 25 | SocketHandler.setup app, server 26 | 27 | Scheduler = require './processes/_scheduler' 28 | ApplicationStartup = require './processes/application_startup' 29 | proc = new ApplicationStartup() 30 | Scheduler.schedule proc, (err) -> 31 | log.info "Initialization complete" 32 | 33 | development: [ 34 | americano.logger 'dev' 35 | ] 36 | 37 | production: [ 38 | americano.logger 'short' 39 | ] 40 | 41 | plugins: [ 42 | 'cozy-db-pouchdb' 43 | ] 44 | 45 | module.exports = config 46 | 47 | -------------------------------------------------------------------------------- /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 'cozy-db-pouchdb' 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 'cozy-db-pouchdb' 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/mailbox_refresh.coffee: -------------------------------------------------------------------------------- 1 | 2 | Process = require './_base' 3 | log = require('../utils/logging')(prefix: 'process/refreshpick') 4 | ramStore = require '../models/store_account_and_boxes' 5 | MailboxRefreshFast = require './mailbox_refresh_fast' 6 | MailboxRefreshDeep = require './mailbox_refresh_deep' 7 | 8 | 9 | module.exports = class MailboxRefresh extends Process 10 | 11 | code: 'mailbox-refresh' 12 | 13 | getProgress = => 14 | @actualRefresh?.getProgress() or 0 15 | 16 | initialize: (options, callback) -> 17 | 18 | @mailbox = mailbox = options.mailbox 19 | account = ramStore.getAccount mailbox.accountID 20 | @shouldNotif = false 21 | return callback null unless account 22 | 23 | log.debug "refreshing box" 24 | if account.supportRFC4551 and mailbox.lastHighestModSeq 25 | @refreshFast (err) => 26 | if err and err is MailboxRefreshFast.algorithmFailure 27 | log.warn "refreshFast fail, trying deep" 28 | @refreshDeep callback 29 | else if err 30 | callback err 31 | else 32 | callback null 33 | 34 | else 35 | if not account.supportRFC4551 36 | log.debug "account doesnt support RFC4551" 37 | else if not mailbox.lastHighestModSeq 38 | log.debug "no highestmodseq, first refresh ?" 39 | @options.storeHighestModSeq = true 40 | 41 | @refreshDeep callback 42 | 43 | 44 | refreshFast: (callback) => 45 | @actualRefresh = new MailboxRefreshFast @options 46 | @actualRefresh.run (err) => 47 | @shouldNotif = @actualRefresh.shouldNotif 48 | callback err 49 | 50 | refreshDeep: (callback) => 51 | @actualRefresh = new MailboxRefreshDeep @options 52 | @actualRefresh.run (err) => 53 | @shouldNotif = @actualRefresh.shouldNotif 54 | callback err 55 | -------------------------------------------------------------------------------- /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 | # loads 200 ids in memory at once 5 | exports.LIMIT_DESTROY = 200 6 | # loads 30 messages in memory at once 7 | exports.LIMIT_UPDATE = 30 8 | # send 5 request 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 | # accounts refreshs in ms, 5 minutes 15 | exports.REFRESH_INTERVAL = 300000 16 | -------------------------------------------------------------------------------- /server/utils/localization.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozy-db-pouchdb' 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 | 25 | 26 | -------------------------------------------------------------------------------- /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 = 0 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 | console.log.apply console, args 52 | 53 | return api = 54 | debug: logger 0 55 | info: logger 1 56 | warn: logger 2 57 | error: logger 3 58 | 59 | module.exports.getLasts = -> 60 | return lastLogs[index+1..MAX_INDEX].join("\n") + "\n" + 61 | lastLogs[0..index].join("\n") 62 | -------------------------------------------------------------------------------- /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="css/app.css") 10 | 11 | body 12 | script!= imports 13 | script(src="js/vendor.js") 14 | script(src="js/app.js") 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.not.equal -1 11 | done() 12 | 13 | it "Wait reindexing", (done) -> 14 | @timeout 30000 15 | do attempt = -> 16 | client.get '/', (err, res, body) -> 17 | if -1 is body?.indexOf(REINDEXING_MSG) then done() 18 | else setTimeout attempt, 1000 19 | 20 | 21 | -------------------------------------------------------------------------------- /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/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/helpers.coffee: -------------------------------------------------------------------------------- 1 | 2 | # See cozy-fixtures documentation for testing on 3 | # https://github.com/jsilvestre/cozy-fixtures#automatic-tests 4 | fixtures = require 'cozy-fixtures' 5 | {exec} = require 'child_process' 6 | DovecotTesting = require 'dovecot-testing' 7 | Imap = require '../server/imap/connection' 8 | _ = require 'lodash' 9 | 10 | module.exports = helpers = {} 11 | 12 | helpers.app = null 13 | 14 | helpers.getImapServerRawConnection = (done, operation) -> 15 | 16 | next = _.once done 17 | imap = new Imap 18 | user: "testuser" 19 | password: "applesauce" 20 | host: DovecotTesting.serverIP() 21 | port: 993 22 | tls: true 23 | tlsOptions: rejectUnauthorized: false 24 | imap.on 'ready', operation.bind imap 25 | imap.on 'error', (err) -> next err 26 | imap.on 'close', (err) -> 27 | if err then next err else next null 28 | imap.connect() 29 | 30 | helpers.waitAllTaskComplete = (done) -> 31 | lastFinished = false 32 | checkIfDone = -> 33 | client.get "/refreshes", (err, res, body) -> 34 | console.log "WAITING TASKS", body 35 | finished = not body.some (task) -> not task.finished 36 | if finished and lastFinished then return done() 37 | 38 | lastFinished = finished 39 | setTimeout checkIfDone, 1000 40 | 41 | setTimeout checkIfDone, 1000 42 | 43 | helpers.startApp = (appPath, host, port) -> (done) -> 44 | @timeout 20000 45 | 46 | app = require appPath + 'server' 47 | 48 | options = {host, port, name:'emails-tests'} 49 | app options, (err, app, server) => 50 | return done err if err 51 | @app = app 52 | @app.server = server 53 | done() 54 | 55 | helpers.stopApp = (done) -> 56 | @timeout 10000 57 | if @app then @app.server.close done 58 | else done null 59 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------