├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── .tx └── config ├── Cakefile ├── LICENSE ├── README.md ├── build ├── client │ ├── app │ │ └── locales │ │ │ ├── ar.json │ │ │ ├── cs.json │ │ │ ├── de.json │ │ │ ├── en.json │ │ │ ├── eo.json │ │ │ ├── es.json │ │ │ ├── fr.json │ │ │ ├── id.json │ │ │ ├── it.json │ │ │ ├── ja.json │ │ │ ├── ko.json │ │ │ ├── nl.json │ │ │ ├── pl.json │ │ │ ├── pt_BR.json │ │ │ ├── ro.json │ │ │ ├── ru.json │ │ │ ├── sk.json │ │ │ ├── sq.json │ │ │ └── zh_CN.json │ └── public │ │ ├── 404_en.js │ │ ├── 404_fr.js │ │ ├── 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 │ │ ├── crossdomain.xml │ │ ├── event_public_en.js │ │ ├── event_public_fr.js │ │ ├── favicon-16x16.png │ │ ├── favicon-194x194.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── fonts │ │ ├── adobeblank.woff │ │ ├── fontawesome-webfont.woff │ │ ├── fontawesome-webfont.woff2 │ │ ├── fonts.css │ │ ├── mavenpro-bold.woff │ │ ├── mavenpro-bold.woff2 │ │ ├── mavenpro-regular.woff │ │ ├── mavenpro-regular.woff2 │ │ ├── sourcecodepro-bold.woff │ │ ├── sourcecodepro-regular.woff │ │ ├── sourcesanspro-bold-italic.woff │ │ ├── sourcesanspro-bold-italic.woff2 │ │ ├── sourcesanspro-bold.woff │ │ ├── sourcesanspro-bold.woff2 │ │ ├── sourcesanspro-italic.woff │ │ ├── sourcesanspro-italic.woff2 │ │ ├── sourcesanspro-regular.woff │ │ └── sourcesanspro-regular.woff2 │ │ ├── humans.txt │ │ ├── icons │ │ └── main_icon.png │ │ ├── img │ │ ├── background.jpg │ │ ├── color-picker.png │ │ ├── defaultpicture.png │ │ ├── glyphicons-halflings-white.png │ │ ├── glyphicons-halflings.png │ │ ├── spinner-white.svg │ │ └── spinner.svg │ │ ├── index.js │ │ ├── javascripts │ │ ├── app-d7f251e7.js │ │ ├── app.js.map │ │ ├── modernizr-2.6.1.js │ │ ├── vendor-d0a31592.js │ │ └── vendor.js.map │ │ ├── manifest.json │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ ├── mstile-70x70.png │ │ ├── robots.txt │ │ └── stylesheets │ │ ├── app-92bfc9dc.css │ │ └── app.css.map ├── server.js └── server │ ├── config.js │ ├── controllers │ ├── contacts.js │ ├── events.js │ ├── ical.js │ ├── index.js │ ├── routes.js │ ├── settings.js │ ├── sharings.js │ └── tags.js │ ├── helpers │ ├── client.js │ └── utils.js │ ├── libs │ └── localization_manager.js │ ├── mails │ ├── assets │ │ └── cozy-logo.png │ ├── en │ │ ├── mail_delete.jade │ │ ├── mail_invitation.jade │ │ └── mail_update.jade │ ├── fr │ │ ├── mail_delete.jade │ │ ├── mail_invitation.jade │ │ └── mail_update.jade │ └── mail_handler.js │ ├── models │ ├── alarm.js │ ├── contact.js │ ├── event.js │ ├── requests.js │ ├── settings.js │ ├── sharing.js │ ├── tag.js │ ├── user.js │ └── webdavaccount.js │ └── share │ └── share_handler.js ├── client ├── .gitignore ├── app │ ├── application.coffee │ ├── assets │ │ ├── 404_en.jade │ │ ├── 404_fr.jade │ │ ├── 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 │ │ ├── crossdomain.xml │ │ ├── event_public_en.jade │ │ ├── event_public_fr.jade │ │ ├── favicon-16x16.png │ │ ├── favicon-194x194.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── favicon.ico │ │ ├── fonts │ │ │ ├── adobeblank.woff │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── fontawesome-webfont.woff2 │ │ │ ├── fonts.css │ │ │ ├── mavenpro-bold.woff │ │ │ ├── mavenpro-bold.woff2 │ │ │ ├── mavenpro-regular.woff │ │ │ ├── mavenpro-regular.woff2 │ │ │ ├── sourcecodepro-bold.woff │ │ │ ├── sourcecodepro-regular.woff │ │ │ ├── sourcesanspro-bold-italic.woff │ │ │ ├── sourcesanspro-bold-italic.woff2 │ │ │ ├── sourcesanspro-bold.woff │ │ │ ├── sourcesanspro-bold.woff2 │ │ │ ├── sourcesanspro-italic.woff │ │ │ ├── sourcesanspro-italic.woff2 │ │ │ ├── sourcesanspro-regular.woff │ │ │ └── sourcesanspro-regular.woff2 │ │ ├── humans.txt │ │ ├── icons │ │ │ └── main_icon.png │ │ ├── img │ │ │ ├── background.jpg │ │ │ ├── color-picker.png │ │ │ ├── defaultpicture.png │ │ │ ├── glyphicons-halflings-white.png │ │ │ ├── glyphicons-halflings.png │ │ │ ├── spinner-white.svg │ │ │ └── spinner.svg │ │ ├── index.jade │ │ ├── javascripts │ │ │ └── modernizr-2.6.1.js │ │ ├── manifest.json │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ ├── mstile-70x70.png │ │ └── robots.txt │ ├── collections │ │ ├── calendars.coffee │ │ ├── contacts.coffee │ │ ├── daybuckets.coffee │ │ ├── events.coffee │ │ ├── realevents.coffee │ │ ├── realeventsgenerator.coffee │ │ ├── scheduleitems.coffee │ │ ├── sharings.coffee │ │ └── tags.coffee │ ├── format.coffee │ ├── helpers.coffee │ ├── helpers │ │ ├── color-set.coffee │ │ └── timezone.coffee │ ├── initialize.coffee │ ├── lib │ │ ├── base_view.coffee │ │ ├── modal.coffee │ │ ├── popover_screen_view.coffee │ │ ├── popover_view.coffee │ │ ├── popup_view.coffee │ │ ├── random.coffee │ │ ├── request.coffee │ │ ├── socket_listener.coffee │ │ ├── today_checker.coffee │ │ ├── view.coffee │ │ └── view_collection.coffee │ ├── locales │ │ ├── ar.json │ │ ├── cs.json │ │ ├── de.json │ │ ├── en.json │ │ ├── eo.json │ │ ├── es.json │ │ ├── fr.json │ │ ├── id.json │ │ ├── it.json │ │ ├── ja.json │ │ ├── ko.json │ │ ├── pl.json │ │ ├── pt_BR.json │ │ ├── ro.json │ │ ├── ru.json │ │ ├── sk.json │ │ └── zh_CN.json │ ├── models │ │ ├── calendar.coffee │ │ ├── contact.coffee │ │ ├── event.coffee │ │ ├── realevent.coffee │ │ ├── scheduleitem.coffee │ │ ├── settings.coffee │ │ ├── sharing.coffee │ │ └── tag.coffee │ ├── router.coffee │ └── views │ │ ├── calendar_header.coffee │ │ ├── calendar_view.coffee │ │ ├── collection_counter.coffee │ │ ├── event_popover.coffee │ │ ├── event_popover_screen.coffee │ │ ├── import_event_list.coffee │ │ ├── import_event_view.coffee │ │ ├── import_view.coffee │ │ ├── list_view.coffee │ │ ├── list_view_bucket.coffee │ │ ├── list_view_item.coffee │ │ ├── menu.coffee │ │ ├── menu_item.coffee │ │ ├── pending_event_sharings_button.coffee │ │ ├── pending_event_sharings_button_item.coffee │ │ ├── popover_screens │ │ ├── alert.coffee │ │ ├── confirm.coffee │ │ ├── delete.coffee │ │ ├── details.coffee │ │ ├── duplicate.coffee │ │ ├── guests.coffee │ │ ├── main.coffee │ │ └── repeat.coffee │ │ ├── settings_modal.coffee │ │ ├── styles │ │ ├── _base.styl │ │ ├── _calendar.styl │ │ ├── _colors.styl │ │ ├── _cozy.styl │ │ ├── _datepicker.styl │ │ ├── _dialog.styl │ │ ├── _drawer.styl │ │ ├── _layout.styl │ │ ├── _menu.styl │ │ ├── _mobile.styl │ │ ├── _pending_event_sharings_button.styl │ │ ├── _popover.styl │ │ ├── _responsive.styl │ │ ├── _states.styl │ │ ├── _ui.styl │ │ ├── application.styl │ │ └── mixins │ │ │ └── _layout.styl │ │ ├── tags.coffee │ │ ├── templates │ │ ├── calendar_header.jade │ │ ├── calendarview.jade │ │ ├── collection_counter.jade │ │ ├── import_event.jade │ │ ├── import_view.jade │ │ ├── list_view.jade │ │ ├── list_view_bucket.jade │ │ ├── list_view_item.jade │ │ ├── menu.jade │ │ ├── menu_item.jade │ │ ├── pending_event_sharings_button.jade │ │ ├── pending_event_sharings_button_item.jade │ │ ├── popover.jade │ │ ├── popover_screens │ │ │ ├── alert.jade │ │ │ ├── alert_row.jade │ │ │ ├── confirm.jade │ │ │ ├── confirm_title.jade │ │ │ ├── delete.jade │ │ │ ├── delete_title.jade │ │ │ ├── details.jade │ │ │ ├── duplicate.jade │ │ │ ├── duplicate_title.jade │ │ │ ├── generic_title.jade │ │ │ ├── guest_row.jade │ │ │ ├── guests.jade │ │ │ ├── main.jade │ │ │ ├── main_title.jade │ │ │ └── repeat.jade │ │ └── settings_modal.jade │ │ ├── toggle.coffee │ │ └── widgets │ │ └── combobox.coffee ├── brunch-config.coffee ├── package.json └── vendor │ ├── scripts │ ├── ColorHash.js │ ├── async.js │ ├── backbone-1.1.2.js │ ├── backbone.mediator.js │ ├── bootstrap-datetimepicker.js │ ├── bootstrap-timepicker.js │ ├── bootstrap.js │ ├── cozy-realtime.js │ ├── fullcalendar.min.js │ ├── jquery-2.1.1.js │ ├── jquery-ui-1.10.3.custom.js │ ├── lang │ │ ├── bootstrap-datetimepicker.de.js │ │ ├── bootstrap-datetimepicker.es.js │ │ ├── bootstrap-datetimepicker.fr.js │ │ ├── bootstrap-datetimepicker.ko.js │ │ ├── bootstrap-datetimepicker.pt.js │ │ ├── bootstrap-datetimepicker.ro.js │ │ ├── fullcalendar-de.js │ │ ├── fullcalendar-es.js │ │ ├── fullcalendar-fr.js │ │ ├── fullcalendar-ko.js │ │ ├── fullcalendar-pt.js │ │ ├── fullcalendar-ro.js │ │ ├── moment-locale-de.js │ │ ├── moment-locale-es.js │ │ ├── moment-locale-fr.js │ │ ├── moment-locale-ko.js │ │ ├── moment-locale-pt.js │ │ └── moment-locale-ro.js │ ├── moment-timezone-with-data.js │ ├── moment.js │ ├── polyglot.js │ ├── rrule-nlp.js │ ├── rrule.js │ ├── socketio.js │ ├── spin.js │ ├── tag-it.js │ └── underscore-1.4.4.js │ └── styles │ ├── bootstrap-datetimepicker.css │ ├── bootstrap-timepicker.css │ ├── bootstrap.css │ ├── fullcalendar.min.css │ ├── helpers.css │ ├── jquery-ui-1.10.3.custom.css │ ├── jquery.tagit.css │ └── normalize.css ├── coffeelint.json ├── package.json ├── server.coffee ├── server ├── config.coffee ├── controllers │ ├── contacts.coffee │ ├── events.coffee │ ├── ical.coffee │ ├── index.coffee │ ├── routes.coffee │ ├── settings.coffee │ ├── sharings.coffee │ └── tags.coffee ├── helpers │ ├── client.coffee │ └── utils.coffee ├── libs │ └── localization_manager.coffee ├── mails │ ├── assets │ │ └── cozy-logo.png │ ├── en │ │ ├── mail_delete.jade │ │ ├── mail_invitation.jade │ │ └── mail_update.jade │ ├── fr │ │ ├── mail_delete.jade │ │ ├── mail_invitation.jade │ │ └── mail_update.jade │ └── mail_handler.coffee ├── models │ ├── alarm.coffee │ ├── contact.coffee │ ├── event.coffee │ ├── requests.coffee │ ├── settings.coffee │ ├── sharing.coffee │ ├── tag.coffee │ ├── user.coffee │ └── webdavaccount.coffee └── share │ └── share_handler.coffee └── test ├── apple.ics ├── calendar.ics ├── empty_desc.ics ├── event_model_test.coffee ├── event_test.coffee ├── fixtures ├── events_generated.json └── generator.coffee ├── google.ics ├── helpers.coffee ├── ical_test.coffee ├── index_test.coffee ├── settings_model_test.coffee └── settings_test.coffee /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | # editorconfig is a unified configuration file that all editors can take 3 | # into account 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | indent_style = space 12 | indent_size = 4 13 | 14 | [*.jade] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | lib-cov 3 | *.seed 4 | *.csv 5 | *.log 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | 11 | pids 12 | logs 13 | results 14 | client/public 15 | 16 | deploy_config.coffee 17 | 18 | node_modules 19 | npm-debug.log 20 | *.transifex.coffee 21 | test/fixtures/events_generated.json 22 | 23 | locales 24 | !locales/en.json 25 | !build/client/app/locales 26 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | lib-cov 3 | *.seed 4 | *.csv 5 | *.log 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | 11 | client 12 | !build/client 13 | server 14 | !build/server 15 | test 16 | server.coffee 17 | .editorconfig 18 | .tx 19 | .travis.yml 20 | Cakefile 21 | coffeelint.json 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | matrix: 3 | fast_finish: true 4 | allow_failures: 5 | - node_js: "6" 6 | node_js: 7 | - "4" 8 | - "6" 9 | services: 10 | - couchdb 11 | env: 12 | global: 13 | - NODE_ENV=test 14 | - NAME=agenda 15 | - TOKEN=apptoken 16 | before_install: 17 | - npm install forever coffee-script -g 18 | - git clone git://github.com/cozy/cozy-data-system.git 19 | - cd cozy-data-system 20 | - npm install # data-system 21 | - NAME=data-system TOKEN=token forever start -o forever-ds.log build/server.js 22 | - sleep 5 23 | - coffee commands.coffee test-install agenda 24 | - cd .. 25 | script: 26 | - npm run build 27 | - npm run test 28 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [cozy-calendar.enjson] 5 | file_filter = client/app/locales/.json 6 | source_file = client/app/locales/en.json 7 | source_lang = en 8 | type = KEYVALUEJSON 9 | -------------------------------------------------------------------------------- /build/client/public/404_en.js: -------------------------------------------------------------------------------- 1 | var jade = require('pug-runtime'); module.exports = function template(locals) { 2 | var buf = []; 3 | var jade_mixins = {}; 4 | var jade_interp; 5 | 6 | buf.push("404 - Event not found

This event is unaccessible

Either it does not exist

or

You are not invited ;)

");;return buf.join(""); 7 | } -------------------------------------------------------------------------------- /build/client/public/404_fr.js: -------------------------------------------------------------------------------- 1 | var jade = require('pug-runtime'); module.exports = function template(locals) { 2 | var buf = []; 3 | var jade_mixins = {}; 4 | var jade_interp; 5 | 6 | buf.push("404 - Événement non trouvé

Cet événement n'est pas accessible

Il n'existe pas

ou

Vous n'y êtes pas convié ;)

");;return buf.join(""); 7 | } -------------------------------------------------------------------------------- /build/client/public/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/android-chrome-144x144.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/android-chrome-36x36.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/android-chrome-48x48.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/android-chrome-72x72.png -------------------------------------------------------------------------------- /build/client/public/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/android-chrome-96x96.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /build/client/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/apple-touch-icon.png -------------------------------------------------------------------------------- /build/client/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #ec8e73 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /build/client/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /build/client/public/event_public_en.js: -------------------------------------------------------------------------------- 1 | var jade = require('pug-runtime'); module.exports = function template(locals) { 2 | var buf = []; 3 | var jade_mixins = {}; 4 | var jade_interp; 5 | ;var locals_for_with = (locals || {});(function (date, event, file, key, visitor) { 6 | buf.push("Cozy Calendar - Upcoming Event

" + (jade.escape((jade_interp = event.description) == null ? '' : jade_interp)) + "

On " + (jade.escape((jade_interp = date) == null ? '' : jade_interp)) + ""); 7 | if ( event.place != null && event.place.length != 0) 8 | { 9 | buf.push("at (" + (jade.escape((jade_interp = event.place) == null ? '' : jade_interp)) + ")"); 10 | } 11 | buf.push("

"); 12 | if ( visitor.status == 'NEEDS-ACTION') 13 | { 14 | buf.push("

Going

Decline

"); 15 | } 16 | else if ( visitor.status == 'ACCEPTED') 17 | { 18 | buf.push("

You are attending this event.

Download event


Changed your mind ?

Decline"); 19 | } 20 | else 21 | { 22 | buf.push("

You are not attending this event.


Changed your mind ?

Going"); 23 | } 24 | buf.push("
");}.call(this,"date" in locals_for_with?locals_for_with.date:typeof date!=="undefined"?date:undefined,"event" in locals_for_with?locals_for_with.event:typeof event!=="undefined"?event:undefined,"file" in locals_for_with?locals_for_with.file:typeof file!=="undefined"?file:undefined,"key" in locals_for_with?locals_for_with.key:typeof key!=="undefined"?key:undefined,"visitor" in locals_for_with?locals_for_with.visitor:typeof visitor!=="undefined"?visitor:undefined));;return buf.join(""); 25 | } -------------------------------------------------------------------------------- /build/client/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/favicon-16x16.png -------------------------------------------------------------------------------- /build/client/public/favicon-194x194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/favicon-194x194.png -------------------------------------------------------------------------------- /build/client/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/favicon-32x32.png -------------------------------------------------------------------------------- /build/client/public/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/favicon-96x96.png -------------------------------------------------------------------------------- /build/client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/favicon.ico -------------------------------------------------------------------------------- /build/client/public/fonts/adobeblank.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/adobeblank.woff -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /build/client/public/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/mavenpro-bold.woff -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/mavenpro-bold.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/mavenpro-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/mavenpro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/mavenpro-regular.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcecodepro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/sourcecodepro-bold.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcecodepro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/sourcecodepro-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/sourcesanspro-bold-italic.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/sourcesanspro-bold-italic.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/sourcesanspro-bold.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/sourcesanspro-bold.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/sourcesanspro-italic.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/sourcesanspro-italic.woff2 -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/sourcesanspro-regular.woff -------------------------------------------------------------------------------- /build/client/public/fonts/sourcesanspro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/fonts/sourcesanspro-regular.woff2 -------------------------------------------------------------------------------- /build/client/public/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3 15 | jQuery, Modernizr, 16 | Lodash, Backbone, 17 | Handlebars, Coffeescript, 18 | Stylus, Brunch 19 | -------------------------------------------------------------------------------- /build/client/public/icons/main_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/icons/main_icon.png -------------------------------------------------------------------------------- /build/client/public/img/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/img/background.jpg -------------------------------------------------------------------------------- /build/client/public/img/color-picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/img/color-picker.png -------------------------------------------------------------------------------- /build/client/public/img/defaultpicture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/img/defaultpicture.png -------------------------------------------------------------------------------- /build/client/public/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /build/client/public/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /build/client/public/img/spinner-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/client/public/img/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /build/client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cozy Calendar", 3 | "icons": [ 4 | { 5 | "src": "\/apps\/calendar\/android-chrome-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/apps\/calendar\/android-chrome-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/apps\/calendar\/android-chrome-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/apps\/calendar\/android-chrome-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/apps\/calendar\/android-chrome-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/apps\/calendar\/android-chrome-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /build/client/public/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/mstile-144x144.png -------------------------------------------------------------------------------- /build/client/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/mstile-150x150.png -------------------------------------------------------------------------------- /build/client/public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/mstile-310x150.png -------------------------------------------------------------------------------- /build/client/public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/mstile-310x310.png -------------------------------------------------------------------------------- /build/client/public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/client/public/mstile-70x70.png -------------------------------------------------------------------------------- /build/client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /build/server.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var port, start; 3 | 4 | start = function(port, callback) { 5 | return require('americano').start({ 6 | name: 'Calendar', 7 | port: port, 8 | host: process.env.HOST || "0.0.0.0", 9 | root: __dirname 10 | }, function(err, app, server) { 11 | var Realtimer, User, cozydb, localization, realtime; 12 | cozydb = require('cozydb'); 13 | User = require('./server/models/user'); 14 | localization = require('./server/libs/localization_manager'); 15 | Realtimer = require('cozy-realtime-adapter'); 16 | realtime = Realtimer(server, ['event.*', 'contact.*', 'sharing.*']); 17 | realtime.on('user.*', function() { 18 | return User.updateUser(); 19 | }); 20 | realtime.on('cozyinstance.*', function() { 21 | return cozydb.api.getCozyInstance(function(err, instance) { 22 | var locale; 23 | locale = (instance != null ? instance.locale : void 0) || null; 24 | return localization.updateLocale(locale); 25 | }); 26 | }); 27 | return User.updateUser(function(err) { 28 | return localization.initialize(function() { 29 | var Alarm, Event; 30 | Event = require('./server/models/event'); 31 | Alarm = require('./server/models/alarm'); 32 | return Event.migrateAll(function() { 33 | return Alarm.migrateAll(function() { 34 | return Event.initializeData(function(err2, event) { 35 | return callback(err, app, server); 36 | }); 37 | }); 38 | }); 39 | }); 40 | }); 41 | }); 42 | }; 43 | 44 | if (!module.parent) { 45 | port = process.env.PORT || 9113; 46 | start(port, function(err) { 47 | if (err) { 48 | console.log("Initialization failed, not starting"); 49 | console.log(err.stack); 50 | return process.exit(1); 51 | } 52 | }); 53 | } else { 54 | module.exports = start; 55 | } 56 | -------------------------------------------------------------------------------- /build/server/config.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var americano, expressValidator, fs, path, publicPath, publicStatic, staticMiddleware, useBuildView, viewsDir; 3 | 4 | americano = require('americano'); 5 | 6 | fs = require('fs'); 7 | 8 | path = require('path'); 9 | 10 | expressValidator = require('express-validator'); 11 | 12 | publicPath = __dirname + "/../client/public"; 13 | 14 | staticMiddleware = americano["static"](publicPath, { 15 | maxAge: 86400000 16 | }); 17 | 18 | publicStatic = function(req, res, next) { 19 | var assetsMatched, detectAssets; 20 | detectAssets = /\/(stylesheets|javascripts|images|fonts)+\/(.+)$/; 21 | assetsMatched = detectAssets.exec(req.url); 22 | if (assetsMatched != null) { 23 | req.url = assetsMatched[0]; 24 | } 25 | return staticMiddleware(req, res, function(err) { 26 | return next(err); 27 | }); 28 | }; 29 | 30 | viewsDir = path.resolve(__dirname, '..', 'client', 'public'); 31 | 32 | useBuildView = fs.existsSync(path.resolve(viewsDir, 'index.js')); 33 | 34 | module.exports = { 35 | common: { 36 | use: [ 37 | staticMiddleware, publicStatic, americano.bodyParser({ 38 | keepExtensions: true 39 | }), expressValidator({ 40 | customValidators: { 41 | isString: function(value) { 42 | return typeof value === 'string'; 43 | } 44 | } 45 | }) 46 | ], 47 | useAfter: [ 48 | americano.errorHandler({ 49 | dumpExceptions: true, 50 | showStack: true 51 | }) 52 | ], 53 | set: { 54 | views: viewsDir, 55 | 'view engine': useBuildView ? 'js' : 'jade' 56 | }, 57 | engine: { 58 | js: function(path, locales, callback) { 59 | return callback(null, require(path)(locales)); 60 | } 61 | } 62 | }, 63 | development: [americano.logger('dev')], 64 | production: [americano.logger('short')], 65 | plugins: ['cozydb'] 66 | }; 67 | -------------------------------------------------------------------------------- /build/server/controllers/contacts.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var ContactsController, SimpleController; 3 | 4 | SimpleController = require('cozydb').SimpleController; 5 | 6 | ContactsController = new SimpleController({ 7 | model: require('../models/contact'), 8 | reqProp: 'contact', 9 | reqParamID: 'contactid' 10 | }); 11 | 12 | ContactsController.sendSmall = function(req, res, next) { 13 | return res.send(req.contact.asNameAndEmails()); 14 | }; 15 | 16 | module.exports = ContactsController; 17 | -------------------------------------------------------------------------------- /build/server/controllers/settings.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Settings; 3 | 4 | Settings = require('../models/settings'); 5 | 6 | module.exports = { 7 | update: function(req, res, next) { 8 | req.checkBody({ 9 | defaultCalendar: { 10 | notEmpty: true, 11 | isString: true, 12 | errorMessage: 'Invalid calendar name' 13 | } 14 | }); 15 | return req.getValidationResult().then(function(result) { 16 | var newSettings, responseBody; 17 | if (!result.isEmpty()) { 18 | responseBody = { 19 | success: false, 20 | error: result.array() 21 | }; 22 | return res.status(400).send(responseBody); 23 | } 24 | newSettings = { 25 | defaultCalendar: req.body.defaultCalendar 26 | }; 27 | return Settings.updateCalAppSettings(newSettings, function(err, calSettings) { 28 | if (err) { 29 | return next(err); 30 | } 31 | return res.send({ 32 | success: true, 33 | settings: calSettings 34 | }); 35 | }); 36 | }); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /build/server/controllers/sharings.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Sharing; 3 | 4 | Sharing = require('../models/sharing'); 5 | 6 | module.exports.all = function(req, res) { 7 | var data; 8 | data = req.query; 9 | if (data.shareID) { 10 | return Sharing.byShareID(data.shareID, function(err, sharings) { 11 | if (err) { 12 | res.status(500).send({ 13 | error: "Server error occured" 14 | }); 15 | } 16 | if (!(sharings != null ? sharings.length : void 0)) { 17 | return res.status(404).send({ 18 | error: "Sharing not found" 19 | }); 20 | } else { 21 | return res.send(sharings[0]); 22 | } 23 | }); 24 | } else { 25 | return Sharing.all(function(err, sharings) { 26 | if (err) { 27 | res.status(500).send({ 28 | error: err 29 | }); 30 | } 31 | if (!(sharings != null ? sharings.length : void 0)) { 32 | return res.status(404).send({ 33 | error: "Sharing not found" 34 | }); 35 | } else { 36 | return res.send(sharings[0]); 37 | } 38 | }); 39 | } 40 | }; 41 | 42 | module.exports.fetch = function(req, res, next, id) { 43 | return Sharing.find(id, function(err, sharing) { 44 | if (err) { 45 | return res.status(500).send({ 46 | error: "Server error occured" 47 | }); 48 | } else if (!sharing) { 49 | return res.status(404).send({ 50 | error: "Sharing not found" 51 | }); 52 | } else { 53 | req.sharing = sharing; 54 | return next(); 55 | } 56 | }); 57 | }; 58 | 59 | module.exports.read = function(req, res) { 60 | return res.send(req.sharing); 61 | }; 62 | 63 | module.exports.accept = function(req, res) { 64 | var data, id; 65 | data = req.body; 66 | id = data.id; 67 | return Sharing.accept(id, function(err, response) { 68 | if (err) { 69 | return res.status(500).send({ 70 | error: err 71 | }); 72 | } else { 73 | return res.send(data); 74 | } 75 | }); 76 | }; 77 | 78 | module.exports.refuse = function(req, res) { 79 | var data, id; 80 | data = req.body; 81 | id = data.id; 82 | return Sharing.refuse(id, function(err, response) { 83 | if (err) { 84 | return res.status(500).send({ 85 | error: err 86 | }); 87 | } else { 88 | return res.status(204).send(data); 89 | } 90 | }); 91 | }; 92 | -------------------------------------------------------------------------------- /build/server/controllers/tags.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Tag; 3 | 4 | Tag = require('../models/tag'); 5 | 6 | module.exports.fetch = function(req, res, next, id) { 7 | return Tag.find(id, function(err, tag) { 8 | if (err || !tag) { 9 | return res.send({ 10 | error: "Tag not found" 11 | }, 404); 12 | } else { 13 | req.tag = tag; 14 | return next(); 15 | } 16 | }); 17 | }; 18 | 19 | module.exports.all = function(req, res, next) { 20 | return Tag.byName(function(err, results) { 21 | if (err) { 22 | return next(err); 23 | } 24 | return res.status(200).send(results); 25 | }); 26 | }; 27 | 28 | module.exports.read = function(req, res) { 29 | return res.send(req.tag); 30 | }; 31 | 32 | module.exports.create = function(req, res) { 33 | var data; 34 | data = req.body; 35 | return Tag.getOrCreateByName(data, function(err, tag) { 36 | if (err != null) { 37 | return res.send({ 38 | error: "Server error while creating tag." 39 | }, 500); 40 | } else { 41 | return res.status(201).send(tag); 42 | } 43 | }); 44 | }; 45 | 46 | module.exports.update = function(req, res) { 47 | var data; 48 | data = req.body; 49 | return req.tag.updateAttributes(data, function(err, tag) { 50 | if (err != null) { 51 | return res.send({ 52 | error: "Server error while saving tag" 53 | }, 500); 54 | } else { 55 | return res.status(200).send(tag); 56 | } 57 | }); 58 | }; 59 | 60 | module.exports["delete"] = function(req, res) { 61 | return req.tag.destroy(function(err) { 62 | if (err != null) { 63 | return res.status(500).send({ 64 | error: "Server error while deleting the tag" 65 | }); 66 | } else { 67 | return res.status(200).send({ 68 | success: true 69 | }); 70 | } 71 | }); 72 | }; 73 | -------------------------------------------------------------------------------- /build/server/helpers/client.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var DS_PORT, client, ref, request; 3 | 4 | request = require('request-json'); 5 | 6 | DS_PORT = process.env.DS_PORT || 9101; 7 | 8 | client = request.createClient("http://localhost:" + DS_PORT); 9 | 10 | if ((ref = process.env.NODE_ENV) === "production" || ref === "test") { 11 | client.setBasicAuth(process.env.NAME, process.env.TOKEN); 12 | } else { 13 | client.setBasicAuth(Math.random().toString(36), "token"); 14 | } 15 | 16 | module.exports = client; 17 | -------------------------------------------------------------------------------- /build/server/helpers/utils.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var client, log; 3 | 4 | client = require('./client'); 5 | 6 | log = require('printit')({ 7 | prefix: "utils", 8 | date: true 9 | }); 10 | 11 | module.exports.exist = function(req, res) { 12 | var id; 13 | id = req.params.id; 14 | return client.get("data/exist/" + id + "/", function(error, response, body) { 15 | if (error) { 16 | return res.status(500).send(error); 17 | } else if ((body == null) || (body.exist == null)) { 18 | return res.status(500).send(new Error("Data system returned invalid data.")); 19 | } else { 20 | return res.status(200).send(body.exist); 21 | } 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /build/server/mails/assets/cozy-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/build/server/mails/assets/cozy-logo.png -------------------------------------------------------------------------------- /build/server/mails/en/mail_delete.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html(style="height:100%;") 3 | head 4 | meta(name="viewport", content="width=device-width") 5 | meta(http-equiv="Content-Type", content="text/html; charset=UTF-8") 6 | 7 | body(style="height:100%; margin:0;") 8 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; height: 100%; padding:0; margin:0; background-color: #F4F4F4; font-family: Helvetica,Arial,Verdana,sans-serif; color: #333; font-size: 0.9em; line-height: 1.6;") 9 | tr 10 | td(align="center", valign="top") 11 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; padding: 2em 0.5em;") 12 | tr 13 | td 14 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; background-color: #FFF; border-radius: 8px; border: 1px solid #DDD;") 15 | tr 16 | th(style="border-radius: 8px 8px 0 0; border-bottom: 1px solid #DDD;") 17 | img(src="cid:cozy-logo" width="63" height="48" style="padding: 12px 0; margin: auto;") 18 | tr 19 | // TODO: add user's email inside the brackets #{displayEmail} 20 | td(style="padding: 1.5em;") #{displayName} (#{displayEmail}) canceled the following event: 21 | tr 22 | td(style="padding: 0 1.5em; width: 100%; font-size: 1.4em; font-weight: bold; text-align:center;") 23 | | #{description} 24 | tr 25 | td(style="padding: 0 1.5em 2em; width: 100%; font-size: 1.2em; text-align:center;") 26 | | On #{date} 27 | if place != null && place.length > 0 28 | | at #{place} 29 | tr(style="padding-top: 10px; text-align: center; font-size: 0.85em; font-style: italic; color: #999;") 30 | td 31 | p 32 | | Sent from  33 | a(href="http://cozy.io", style="color: #34A6FF; text-decoration: none;") #{displayName}'s Cozy 34 | | . 35 | -------------------------------------------------------------------------------- /build/server/mails/fr/mail_delete.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html(style="height:100%;") 3 | head 4 | meta(name="viewport", content="width=device-width") 5 | meta(http-equiv="Content-Type", content="text/html; charset=UTF-8") 6 | 7 | body(style="height:100%; margin:0;") 8 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; height: 100%; padding:0; margin:0; background-color: #F4F4F4; font-family: Helvetica,Arial,Verdana,sans-serif; color: #333; font-size: 0.9em; line-height: 1.6;") 9 | tr 10 | td(align="center", valign="top") 11 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; padding: 2em 0.5em;") 12 | tr 13 | td 14 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; background-color: #FFF; border-radius: 8px; border: 1px solid #DDD;") 15 | tr 16 | th(style="border-radius: 8px 8px 0 0; border-bottom: 1px solid #DDD;") 17 | img(src="cid:cozy-logo" width="63" height="48" style="padding: 12px 0; margin: auto;") 18 | tr 19 | td(style="padding: 1.5em;") #{displayName} (#{displayEmail}) a annulé l'événement suivant : 20 | tr 21 | td(style="padding: 0 1.5em; width: 100%; font-size: 1.4em; font-weight: bold; text-align:center;") 22 | | #{description} 23 | tr 24 | td(style="padding: 0 1.5em 2em; width: 100%; font-size: 1.2em; text-align:center;") 25 | | Le #{date} 26 | if place != null && place.length > 0 27 | | à #{place} 28 | tr(style="padding-top: 10px; text-align: center; font-size: 0.85em; font-style: italic; color: #999;") 29 | td 30 | p 31 | | Envoyé depuis le 32 | a(href="http://cozy.io", style="color: #34A6FF; text-decoration: none;") Cozy de #{displayName} 33 | | . 34 | -------------------------------------------------------------------------------- /build/server/models/contact.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Contact, cozydb; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | module.exports = Contact = cozydb.getModel('Contact', { 7 | fn: String, 8 | n: String, 9 | datapoints: [Object], 10 | revision: String, 11 | note: String, 12 | tags: [String], 13 | accounts: String, 14 | title: String, 15 | org: String, 16 | bday: String, 17 | url: String, 18 | initials: String, 19 | sortedName: String, 20 | ref: String, 21 | _attachments: Object 22 | }); 23 | 24 | Contact.prototype.asNameAndEmails = function() { 25 | var cozy, emails, name, ref, ref1, ref2, ref3, simple; 26 | name = this.fn || ((ref = this.n) != null ? ref.split(';').slice(0, 2).join(' ') : void 0); 27 | emails = (ref1 = this.datapoints) != null ? ref1.filter(function(dp) { 28 | return dp.name === 'email'; 29 | }) : void 0; 30 | cozy = (ref2 = this.datapoints) != null ? ref2.filter(function(dp) { 31 | var ref3; 32 | return ((dp.name === 'other') && (dp.type.toLowerCase() === 'cozy')) || ((dp.name === 'url') && ((ref3 = dp.mediatype) != null ? ref3.search('cozy' !== -1) : void 0)); 33 | }) : void 0; 34 | return simple = { 35 | id: this.id, 36 | name: name || '?', 37 | emails: emails || [], 38 | hasPicture: ((ref3 = this._attachments) != null ? ref3.picture : void 0) != null, 39 | cozy: cozy || null 40 | }; 41 | }; 42 | -------------------------------------------------------------------------------- /build/server/models/requests.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var cozydb, tagsView; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | tagsView = { 7 | map: function(doc) { 8 | var ref; 9 | if (!doc.shareID) { 10 | return (ref = doc.tags) != null ? typeof ref.forEach === "function" ? ref.forEach(function(tag, index) { 11 | var type; 12 | type = index === 0 ? 'calendar' : 'tag'; 13 | return emit([type, tag], true); 14 | }) : void 0 : void 0; 15 | } 16 | }, 17 | reduce: "_count" 18 | }; 19 | 20 | module.exports = { 21 | tag: { 22 | byName: cozydb.defaultRequests.by('name') 23 | }, 24 | alarm: { 25 | all: cozydb.defaultRequests.all, 26 | byDate: function(doc) { 27 | return emit(new Date(doc.trigg), doc); 28 | }, 29 | tags: tagsView 30 | }, 31 | event: { 32 | all: cozydb.defaultRequests.all, 33 | byDate: function(doc) { 34 | return emit(new Date(doc.start), doc); 35 | }, 36 | reccuring: function(doc) { 37 | if ((doc.rrule != null) && doc.rrule.length > 0) { 38 | return emit(doc.id, doc); 39 | } 40 | }, 41 | tags: tagsView, 42 | byCalendar: cozydb.defaultRequests.by('tags[0]') 43 | }, 44 | contact: { 45 | all: cozydb.defaultRequests.all 46 | }, 47 | webdavaccount: { 48 | all: cozydb.defaultRequests.all 49 | }, 50 | sharing: { 51 | all: cozydb.defaultRequests.all, 52 | pendingBySharedDocType: function(doc) { 53 | if (!doc.accepted && !doc.targets && doc.rules) { 54 | return doc.rules.forEach(function(rule) { 55 | return emit(rule.docType, doc); 56 | }); 57 | } 58 | }, 59 | byShareID: cozydb.defaultRequests.by('shareID') 60 | }, 61 | settings: { 62 | all: cozydb.defaultRequests.all 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /build/server/models/settings.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Settings, cozydb, log; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | log = require('printit')({ 7 | prefix: 'tag:settings' 8 | }); 9 | 10 | module.exports = Settings = cozydb.getModel('Settings', { 11 | app: String, 12 | defaultCalendar: String 13 | }); 14 | 15 | Settings.getCalAppSettings = function(callback) { 16 | return Settings.request('all', function(err, allSettings) { 17 | var calendarSettings; 18 | if (err) { 19 | return callback(err); 20 | } 21 | allSettings = allSettings.filter(function(settings) { 22 | return settings.app === 'calendar'; 23 | }); 24 | if (allSettings.length === 0) { 25 | calendarSettings = { 26 | app: 'calendar', 27 | defaultCalendar: '' 28 | }; 29 | return Settings.create(calendarSettings, function(err, calendarSettings) { 30 | if (err) { 31 | return callback(err); 32 | } 33 | return callback(null, calendarSettings); 34 | }); 35 | } else { 36 | return callback(null, allSettings[0]); 37 | } 38 | }); 39 | }; 40 | 41 | Settings.updateCalAppSettings = function(data, callback) { 42 | var newSettings; 43 | newSettings = { 44 | defaultCalendar: data.defaultCalendar 45 | }; 46 | return Settings.getCalAppSettings(function(err, calendarSettings) { 47 | if (err) { 48 | return callback(err); 49 | } 50 | return calendarSettings.updateAttributes(newSettings, function(err, calendarSettings) { 51 | if (err) { 52 | return callback(err); 53 | } 54 | return callback(null, calendarSettings); 55 | }); 56 | }); 57 | }; 58 | -------------------------------------------------------------------------------- /build/server/models/sharing.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Sharing, cozydb, sendAnswer; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | module.exports = Sharing = cozydb.getModel('Sharing', { 7 | docType: String, 8 | sharerName: String, 9 | sharerUrl: String, 10 | shareID: String, 11 | desc: String, 12 | rules: [Object], 13 | continuous: Boolean, 14 | targets: [Object], 15 | accepted: Boolean 16 | }); 17 | 18 | Sharing.pendingBySharedDocType = function(docType, callback) { 19 | return Sharing.request('pendingBySharedDocType', { 20 | key: docType 21 | }, callback); 22 | }; 23 | 24 | Sharing.byShareID = function(shareID, callback) { 25 | return Sharing.request('byShareID', { 26 | key: shareID 27 | }, callback); 28 | }; 29 | 30 | sendAnswer = function(data, callback) { 31 | return cozydb.api.answerSharing(data, function(err, body) { 32 | if (err) { 33 | return callback(err, body); 34 | } else { 35 | return callback(null, body); 36 | } 37 | }); 38 | }; 39 | 40 | Sharing.accept = function(id, callback) { 41 | return sendAnswer({ 42 | id: id, 43 | accepted: true 44 | }, callback); 45 | }; 46 | 47 | Sharing.refuse = function(id, callback) { 48 | return sendAnswer({ 49 | id: id, 50 | accepted: false 51 | }, callback); 52 | }; 53 | -------------------------------------------------------------------------------- /build/server/models/tag.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var Tag, cozydb, log; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | log = require('printit')({ 7 | prefix: 'tag:model' 8 | }); 9 | 10 | module.exports = Tag = cozydb.getModel('Tag', { 11 | name: { 12 | type: String 13 | }, 14 | color: { 15 | type: String 16 | } 17 | }); 18 | 19 | Tag.byName = function(options, callback) { 20 | return Tag.request('byName', options, callback); 21 | }; 22 | 23 | Tag.getOrCreateByName = function(data, callback) { 24 | return Tag.byName({ 25 | key: data.name 26 | }, function(err, tags) { 27 | if (err) { 28 | log.error(err); 29 | return Tag.create(data, callback); 30 | } else if (tags.length === 0) { 31 | return Tag.create(data, callback); 32 | } else { 33 | return callback(null, tags[0]); 34 | } 35 | }); 36 | }; 37 | -------------------------------------------------------------------------------- /build/server/models/user.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var User, cozydb; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | module.exports = User = {}; 7 | 8 | User.updateUser = function(callback) { 9 | return cozydb.api.getCozyUser(function(err, user) { 10 | if (err || !user) { 11 | if (err) { 12 | console.log(err); 13 | } 14 | User.timezone = 'Europe/Paris'; 15 | User.email = ''; 16 | } else { 17 | User.timezone = user.timezone || "Europe/Paris"; 18 | User.email = user.email; 19 | } 20 | return typeof callback === "function" ? callback() : void 0; 21 | }); 22 | }; 23 | 24 | User.getUserInfos = function(callback) { 25 | return cozydb.api.getCozyUser(function(err, user) { 26 | var name, ref, words; 27 | if (err) { 28 | return callback(err); 29 | } 30 | name = ((ref = user.public_name) != null ? ref.length : void 0) ? user.public_name : (words = user.email.split('@')[0].replace(/([\.-]+)/g, ' ').split(' '), words.map(function(word) { 31 | return word[0].toUpperCase() + word.slice(1); 32 | }).join(' ')); 33 | return callback(null, { 34 | name: name, 35 | email: user.email 36 | }); 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /build/server/models/webdavaccount.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var WebDAVAccount, cozydb; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | module.exports = WebDAVAccount = cozydb.getModel('WebDAVAccount', { 7 | id: String, 8 | login: String, 9 | token: String, 10 | password: String, 11 | ctag: Number, 12 | cardctag: Number 13 | }); 14 | -------------------------------------------------------------------------------- /build/server/share/share_handler.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.10.0 2 | var cozydb; 3 | 4 | cozydb = require('cozydb'); 5 | 6 | if (!Array.prototype.find) { 7 | Array.prototype.find = function(predicate) { 8 | var i, length, list, thisArg, value; 9 | if (this === null) { 10 | throw new TypeError("Find call on null or undefined."); 11 | } 12 | if ((typeof predicate) !== "function") { 13 | throw new TypeError("Predicate must be a function."); 14 | } 15 | list = Object(this); 16 | length = list.length >>> 0; 17 | thisArg = arguments[1]; 18 | i = 0; 19 | value = list[i]; 20 | while (value) { 21 | if (predicate.call(thisArg, value, i, list)) { 22 | return value; 23 | } 24 | value = list[++i]; 25 | } 26 | return void 0; 27 | }; 28 | } 29 | 30 | module.exports.sendShareInvitations = function(event, callback) { 31 | var data, guests, hasGuestToShare, needSaving; 32 | guests = event.toJSON().attendees; 33 | needSaving = false; 34 | hasGuestToShare = guests.find(function(guest) { 35 | return guest.isSharedWithCozy && (guest.status === 'INVITATION-NOT-SENT'); 36 | }); 37 | if (!hasGuestToShare) { 38 | return callback(); 39 | } 40 | data = { 41 | desc: event.description, 42 | rules: [ 43 | { 44 | id: event.id, 45 | docType: 'event' 46 | } 47 | ], 48 | targets: [], 49 | continuous: true 50 | }; 51 | guests.forEach(function(guest) { 52 | if ((guest.status === 'INVITATION-NOT-SENT') && guest.isSharedWithCozy) { 53 | data.targets.push({ 54 | recipientUrl: guest.cozy 55 | }); 56 | guest.status = "NEEDS-ACTION"; 57 | return needSaving = true; 58 | } 59 | }); 60 | return cozydb.api.createSharing(data, function(err, body) { 61 | if (err != null) { 62 | return callback(err); 63 | } else if (!needSaving) { 64 | return callback(); 65 | } else { 66 | return event.updateAttributes({ 67 | attendees: guests 68 | }, callback); 69 | } 70 | }); 71 | }; 72 | -------------------------------------------------------------------------------- /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 | # map files 14 | *.js.map 15 | *.css.map 16 | 17 | # OS or Editor folders 18 | .DS_Store 19 | .cache 20 | .project 21 | .settings 22 | .tmproj 23 | nbproject 24 | Thumbs.db 25 | 26 | # NPM packages folder. 27 | node_modules/ 28 | 29 | # Brunch folder for temporary files. 30 | tmp/ 31 | -------------------------------------------------------------------------------- /client/app/assets/404_en.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | title 404 - Event not found 5 | link(rel="stylesheet", type="text/css", href="DIGEST(stylesheets/app.css)", media="all") 6 | 7 | body.error-404 8 | .container 9 | .row 10 | .col-lg-12.error-frame 11 | p.headline This event is unaccessible 12 | p Either it does not exist 13 | p or 14 | p You are not invited ;) 15 | -------------------------------------------------------------------------------- /client/app/assets/404_fr.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | title 404 - Événement non trouvé 5 | link(rel="stylesheet", type="text/css", href="DIGEST(stylesheets/app.css)", media="all") 6 | 7 | body.error-404 8 | .container 9 | .row 10 | .col-lg-12.error-frame 11 | p.headline Cet événement n'est pas accessible 12 | p Il n'existe pas 13 | p ou 14 | p Vous n'y êtes pas convié ;) 15 | -------------------------------------------------------------------------------- /client/app/assets/android-chrome-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/android-chrome-144x144.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/android-chrome-36x36.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/android-chrome-48x48.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/android-chrome-72x72.png -------------------------------------------------------------------------------- /client/app/assets/android-chrome-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/android-chrome-96x96.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/apple-touch-icon-114x114.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/apple-touch-icon-144x144.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/apple-touch-icon-57x57.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/apple-touch-icon-72x72.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /client/app/assets/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/apple-touch-icon.png -------------------------------------------------------------------------------- /client/app/assets/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | #ec8e73 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /client/app/assets/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /client/app/assets/event_public_en.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | title Cozy Calendar - Upcoming Event 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,initial-scale=1.0") 8 | script(src="javascripts/modernizr-2.6.1.js") 9 | link(rel="stylesheet", href="/fonts/fonts.css") 10 | link(rel="stylesheet", href="../DIGEST(stylesheets/app.css)") 11 | 12 | body.public.public-event-body 13 | header 14 | img(src="https://docs.cozy.io/assets/images/cozy-logo.png") 15 | main#container.container.public-event-container.well 16 | h2 #{event.description} 17 | h3 18 | | On #{date} 19 | if event.place != null && event.place.length != 0 20 | | at (#{event.place}) 21 | 22 | if visitor.status == 'NEEDS-ACTION' 23 | 24 | p: a.btn(href="?status=ACCEPTED&key=" + key) Going 25 | p: a.btn.btn-negative(href="?status=DECLINED&key=" + key) Decline 26 | 27 | else if visitor.status == 'ACCEPTED' 28 | p 29 | | You are attending this event. 30 | p: a(href="#{event.id}/#{file}.ics?key=" + key) Download event 31 | hr 32 | p.special 33 | | Changed your mind ? 34 | a.btn.btn-negative(href="?status=DECLINED&key=" + key) 35 | | Decline 36 | 37 | else 38 | p 39 | | You are not attending this event. 40 | hr 41 | p.special 42 | | Changed your mind ? 43 | a.btn(href="?status=ACCEPTED&key=" + key) 44 | | Going 45 | -------------------------------------------------------------------------------- /client/app/assets/event_public_fr.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | title Cozy Agenda - Événement à venir 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,initial-scale=1.0") 8 | script(src="javascripts/modernizr-2.6.1.js") 9 | link(rel="stylesheet", href="/fonts/fonts.css") 10 | link(rel="stylesheet", href="../DIGEST(stylesheets/app.css)") 11 | 12 | body.public.public-event-body 13 | header 14 | img(src="https://docs.cozy.io/assets/images/cozy-logo.png") 15 | main#container.container.public-event-container.well 16 | h2 #{event.description} 17 | h3 18 | | Le #{date} 19 | if event.place != null && event.place.length != 0 20 | | à #{event.place} 21 | 22 | if visitor.status == 'NEEDS-ACTION' 23 | 24 | p: a.btn(href="?status=ACCEPTED&key=" + key) Assister 25 | p: a.btn.btn-negative(href="?status=DECLINED&key=" + key) Décliner 26 | 27 | else if visitor.status == 'ACCEPTED' 28 | p 29 | | Vous participerez à cet événement 30 | p: a(href="#{event.id}/#{file}.ics?key=" + key) Télécharger cet événement 31 | hr 32 | p.special 33 | | Avez-vous changé d'avis ? 34 | a.btn.btn-negative(href="?status=DECLINED&key=" + key) 35 | | Décliner 36 | 37 | else 38 | p 39 | | Vous ne participerez pas à cet événement. 40 | hr 41 | p.special 42 | | Avez-vous changé d'avis ? 43 | a.btn(href="?status=ACCEPTED&key=" + key) 44 | | Assister 45 | -------------------------------------------------------------------------------- /client/app/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/favicon-16x16.png -------------------------------------------------------------------------------- /client/app/assets/favicon-194x194.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/favicon-194x194.png -------------------------------------------------------------------------------- /client/app/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/favicon-32x32.png -------------------------------------------------------------------------------- /client/app/assets/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/favicon-96x96.png -------------------------------------------------------------------------------- /client/app/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/favicon.ico -------------------------------------------------------------------------------- /client/app/assets/fonts/adobeblank.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/adobeblank.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/mavenpro-bold.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/mavenpro-bold.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/mavenpro-regular.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/mavenpro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/mavenpro-regular.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcecodepro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/sourcecodepro-bold.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcecodepro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/sourcecodepro-regular.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/sourcesanspro-bold-italic.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/sourcesanspro-bold-italic.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/sourcesanspro-bold.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/sourcesanspro-bold.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/sourcesanspro-italic.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/sourcesanspro-italic.woff2 -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/fonts/sourcesanspro-regular.woff -------------------------------------------------------------------------------- /client/app/assets/fonts/sourcesanspro-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/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 | Lodash, Backbone, 17 | Handlebars, Coffeescript, 18 | Stylus, Brunch 19 | -------------------------------------------------------------------------------- /client/app/assets/icons/main_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/icons/main_icon.png -------------------------------------------------------------------------------- /client/app/assets/img/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/img/background.jpg -------------------------------------------------------------------------------- /client/app/assets/img/color-picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/img/color-picker.png -------------------------------------------------------------------------------- /client/app/assets/img/defaultpicture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/img/defaultpicture.png -------------------------------------------------------------------------------- /client/app/assets/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /client/app/assets/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /client/app/assets/img/spinner-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/app/assets/img/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/app/assets/index.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | meta(charset="utf-8") 5 | meta(http-equiv="X-UA-Compatible",content="IE=edge, chrome=1") 6 | meta(name="viewport", content="width=device-width, initial-scale=1") 7 | 8 | title Cozy - Calendar 9 | 10 | link(rel="apple-touch-icon", sizes="57x57", href="/apps/calendar/apple-touch-icon-57x57.png") 11 | link(rel="apple-touch-icon", sizes="60x60", href="/apps/calendar/apple-touch-icon-60x60.png") 12 | link(rel="apple-touch-icon", sizes="72x72", href="/apps/calendar/apple-touch-icon-72x72.png") 13 | link(rel="apple-touch-icon", sizes="76x76", href="/apps/calendar/apple-touch-icon-76x76.png") 14 | link(rel="apple-touch-icon", sizes="114x114", href="/apps/calendar/apple-touch-icon-114x114.png") 15 | link(rel="apple-touch-icon", sizes="120x120", href="/apps/calendar/apple-touch-icon-120x120.png") 16 | link(rel="apple-touch-icon", sizes="144x144", href="/apps/calendar/apple-touch-icon-144x144.png") 17 | link(rel="apple-touch-icon", sizes="152x152", href="/apps/calendar/apple-touch-icon-152x152.png") 18 | link(rel="apple-touch-icon", sizes="180x180", href="/apps/calendar/apple-touch-icon-180x180.png") 19 | link(rel="icon", type="image/png", href="/apps/calendar/favicon-32x32.png", sizes="32x32") 20 | link(rel="icon", type="image/png", href="/apps/calendar/favicon-194x194.png", sizes="194x194") 21 | link(rel="icon", type="image/png", href="/apps/calendar/favicon-96x96.png", sizes="96x96") 22 | link(rel="icon", type="image/png", href="/apps/calendar/android-chrome-192x192.png", sizes="192x192") 23 | link(rel="icon", type="image/png", href="/apps/calendar/favicon-16x16.png", sizes="16x16") 24 | link(rel="manifest", href="/apps/calendar/manifest.json") 25 | link(rel="shortcut icon", href="/apps/calendar/favicon.ico") 26 | meta(name="msapplication-TileColor", content="#ec8e73") 27 | meta(name="msapplication-TileImage", content="/apps/calendar/mstile-144x144.png") 28 | meta(name="msapplication-config", content="/apps/calendar/browserconfig.xml") 29 | meta(name="theme-color", content="#ec8e73") 30 | 31 | script(src="javascripts/modernizr-2.6.1.js") 32 | link(rel="stylesheet", href="/fonts/fonts.css") 33 | link(rel="stylesheet", href="DIGEST(stylesheets/app.css)") 34 | 35 | script(src="socket.io/socket.io.js") 36 | 37 | body 38 | div(role="application") 39 | main.main-container 40 | 41 | script!= imports 42 | script(src="DIGEST(javascripts/vendor.js)") 43 | script(src="DIGEST(javascripts/app.js)", onload="require('initialize');") 44 | -------------------------------------------------------------------------------- /client/app/assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cozy Calendar", 3 | "icons": [ 4 | { 5 | "src": "\/apps\/calendar\/android-chrome-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/apps\/calendar\/android-chrome-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/apps\/calendar\/android-chrome-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/apps\/calendar\/android-chrome-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/apps\/calendar\/android-chrome-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/apps\/calendar\/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/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/mstile-144x144.png -------------------------------------------------------------------------------- /client/app/assets/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/mstile-150x150.png -------------------------------------------------------------------------------- /client/app/assets/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/mstile-310x150.png -------------------------------------------------------------------------------- /client/app/assets/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/mstile-310x310.png -------------------------------------------------------------------------------- /client/app/assets/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/client/app/assets/mstile-70x70.png -------------------------------------------------------------------------------- /client/app/assets/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /client/app/collections/contacts.coffee: -------------------------------------------------------------------------------- 1 | Contact = require '../models/contact' 2 | 3 | module.exports = class ContactCollection extends Backbone.Collection 4 | 5 | model: Contact 6 | url: 'contacts' 7 | 8 | asTypeaheadSource: (query) -> 9 | regexp = new RegExp query 10 | contacts = @filter (contact) -> contact.match regexp 11 | items = [] 12 | contacts.forEach (contact) -> 13 | contact.get('emails').forEach (email) -> 14 | items.push 15 | id: contact.id 16 | hasPicture: contact.get('hasPicture') 17 | display: "#{contact.get 'name'} <#{email.value}>" 18 | toString: -> "#{email.value};#{contact.id}" 19 | 20 | contact.get('cozy').forEach (cozy) -> 21 | items.push 22 | id : contact.id 23 | hasPicture : contact.get 'hasPicture' 24 | display : "#{contact.get 'name'} <#{cozy.value}>" 25 | toString : -> "#{cozy.value};#{contact.id}" 26 | 27 | return items 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /client/app/collections/daybuckets.coffee: -------------------------------------------------------------------------------- 1 | RealEventCollection = require './realevents' 2 | RealEventGeneratorCollection = require './realeventsgenerator' 3 | 4 | DayBucket = class DayBucket extends Backbone.Model 5 | constructor: (model) -> 6 | super 7 | id: model.getDateHash() 8 | date: moment(model.start).startOf 'day' 9 | 10 | initialize: -> 11 | @items = new RealEventCollection() 12 | 13 | # DayBucket Collection 14 | # split into DayBucket (= group by day) 15 | module.exports = class DayBucketCollection extends Backbone.Collection 16 | 17 | model: DayBucket 18 | comparator: 'id' 19 | 20 | initialize: -> 21 | @eventCollection = new RealEventGeneratorCollection() 22 | 23 | @listenTo @eventCollection, 'add', @onBaseCollectionAdd 24 | @listenTo @eventCollection, 'change:start', @onBaseCollectionChange 25 | @listenTo @eventCollection, 'remove', @onBaseCollectionRemove 26 | @listenTo @eventCollection, 'reset', @resetFromBase 27 | 28 | @resetFromBase() 29 | 30 | resetFromBase: -> 31 | @reset [] 32 | @eventCollection.each (model) => @onBaseCollectionAdd model 33 | 34 | onBaseCollectionChange: (model) -> 35 | oldbucket = @get model.getPreviousDateHash() 36 | newbucket = @get model.getDateHash() 37 | return if oldbucket is newbucket 38 | oldbucket.items.remove model 39 | @remove oldbucket if oldbucket.items.length is 0 40 | @add(newbucket = new DayBucket model) unless newbucket 41 | newbucket.items.add model 42 | 43 | onBaseCollectionAdd: (model) -> 44 | bucket = @get model.getDateHash() 45 | @add(bucket = new DayBucket model) unless bucket 46 | bucket.items.add model 47 | 48 | onBaseCollectionRemove: (model) -> 49 | bucket = @get model.getDateHash() 50 | bucket.items.remove model 51 | @remove bucket if bucket.items.length is 0 52 | 53 | loadNextPage: (callback) -> @eventCollection.loadNextPage callback 54 | 55 | loadPreviousPage: (callback) -> @eventCollection.loadPreviousPage callback 56 | -------------------------------------------------------------------------------- /client/app/collections/events.coffee: -------------------------------------------------------------------------------- 1 | ScheduleItemsCollection = require './scheduleitems' 2 | Event = require '../models/event' 3 | request = require 'lib/request' 4 | 5 | module.exports = class EventCollection extends ScheduleItemsCollection 6 | 7 | model: Event 8 | url: 'events' 9 | 10 | 11 | # Check if given month is already. If not, request the server to retrieve 12 | # events occuring that month. 13 | loadMonth: (monthToLoad, callback) -> 14 | monthKey = monthToLoad.format 'YYYY-MM' 15 | unless window.app.mainStore.loadedMonths[monthKey] 16 | year = monthToLoad.format 'YYYY' 17 | month = monthToLoad.format 'MM' 18 | request.get "events/#{year}/#{month}", (err, events) => 19 | @add events, 20 | silent: true 21 | sort: false 22 | @trigger 'change' 23 | window.app.mainStore.loadedMonths[monthKey] = true 24 | callback() 25 | else 26 | callback() 27 | 28 | -------------------------------------------------------------------------------- /client/app/collections/realevents.coffee: -------------------------------------------------------------------------------- 1 | RealEvent = require '../models/realevent' 2 | 3 | module.exports = class RealEventCollection extends Backbone.Collection 4 | model = RealEvent 5 | comparator: (re1, re2) -> 6 | return re1.start.isBefore re2.start 7 | -------------------------------------------------------------------------------- /client/app/collections/scheduleitems.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class ScheduleItemsCollection extends Backbone.Collection 2 | 3 | model: require '../models/scheduleitem' 4 | comparator: (si1, si2) -> si1.getDateObject().diff si2.getDateObject() 5 | 6 | # The calendar tag for all event was referencing the same instance 7 | # in every event object. This is not the case anymore, so we have 8 | # to check every event calendar in the a given calendars collection 9 | # to verify the visibility 10 | visibleItems: (calendars) -> 11 | new ScheduleItemsCollection @filter (scheduleItem) -> 12 | return true if scheduleItem.hasSharing() 13 | 14 | calendar = calendars.get scheduleItem.getCalendar()?.get('id') 15 | return calendar?.get 'visible' 16 | 17 | 18 | getFCEventSource: (calendars) => 19 | return (start, end, timezone, callback) => 20 | # start and end : ambiguous moments 21 | # only dates if month or week view. 22 | eventsInRange = [] 23 | 24 | @visibleItems(calendars)?.each (item) -> 25 | itemStart = item.getStartDateObject() 26 | itemEnd = item.getEndDateObject() 27 | duration = itemEnd - itemStart 28 | 29 | if item.isRecurrent() 30 | try 31 | eventsInRange = eventsInRange.concat( 32 | item.getRecurrentFCEventBetween start, end) 33 | catch e 34 | console.error e 35 | # sometime the RRule is badly formated, so we try to 36 | # at least display the first item 37 | if item.isInRange start, end 38 | eventsInRange.push( 39 | item.toPunctualFullCalendarEvent()) 40 | 41 | else if item.isInRange(start, end) 42 | eventsInRange.push item.toPunctualFullCalendarEvent() 43 | 44 | callback eventsInRange 45 | 46 | getByCalendar: (calendarName) -> 47 | return @filter (event) -> event.get('tags')[0] is calendarName 48 | -------------------------------------------------------------------------------- /client/app/collections/sharings.coffee: -------------------------------------------------------------------------------- 1 | Sharing = require '../models/sharing' 2 | 3 | module.exports = class SharingCollection extends Backbone.Collection 4 | model: Sharing 5 | 6 | initialize: -> 7 | @on 'accepted refused', @onAnsweredSharing 8 | 9 | 10 | onAnsweredSharing: (sharing) -> 11 | @remove sharing 12 | -------------------------------------------------------------------------------- /client/app/collections/tags.coffee: -------------------------------------------------------------------------------- 1 | Tag = require '../models/tag' 2 | 3 | module.exports = class TagCollection extends Backbone.Collection 4 | 5 | model: Tag 6 | url: 'tags' 7 | 8 | # Uniqueness against Tag.name field. 9 | add: (models, options) -> 10 | # handle singular or arrays 11 | if _.isArray models 12 | models = _.clone models 13 | else 14 | models = if models then [models] else [] 15 | 16 | models = models.filter (model) => 17 | return not @some (collectionModel) -> 18 | name = if model?.name then model.name else model.get 'name' 19 | return collectionModel.get('name') is name 20 | 21 | super models, options 22 | 23 | getByName: (name) -> 24 | return @find (item) -> 25 | return item.get('name') is name 26 | 27 | # Get existing or newly created tag with specified name. 28 | getOrCreateByName: (name) -> 29 | tag = @getByName name 30 | 31 | if not tag 32 | tag = new Tag 33 | name: name 34 | color: ColorHash.getColor(name, 'cozy') 35 | 36 | return tag 37 | -------------------------------------------------------------------------------- /client/app/format.coffee: -------------------------------------------------------------------------------- 1 | 2 | module.exports = 3 | WEEK_URL_HASH_FORMAT: '[week]/YYYY/MM/DD' 4 | MONTH_URL_HASH_FORMAT: '[month]/YYYY/MM' 5 | -------------------------------------------------------------------------------- /client/app/helpers/color-set.coffee: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | '304FFE' 3 | '2979FF' 4 | '00B0FF' 5 | '00DCE9' 6 | '00D5B8' 7 | '00C853' 8 | 'E70505' 9 | 'FF5700' 10 | 'FF7900' 11 | 'FFA300' 12 | 'B3C51D' 13 | '64DD17' 14 | 'FF2828' 15 | 'F819AA' 16 | 'AA00FF' 17 | '6200EA' 18 | '7190AB' 19 | '51658D' 20 | ] 21 | -------------------------------------------------------------------------------- /client/app/lib/popup_view.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require 'lib/base_view' 2 | 3 | # Represent a popup view 4 | module.exports = class PopupView extends BaseView 5 | 6 | initialize: (options) -> 7 | super 8 | @anchor = options.anchor 9 | 10 | @addClickOutListener options.document, => @hide() 11 | .exceptOn @anchor.get 0 12 | 13 | 14 | hide: -> 15 | @$el.hide() 16 | @ 17 | 18 | 19 | toggle: (display)-> 20 | @$el.toggle display 21 | @ 22 | -------------------------------------------------------------------------------- /client/app/lib/random.coffee: -------------------------------------------------------------------------------- 1 | module.exports.randomString = (length=32) -> 2 | string = "" 3 | string += Math.random().toString(36).substr(2) while string.length < length 4 | string.substr 0, length 5 | -------------------------------------------------------------------------------- /client/app/lib/request.coffee: -------------------------------------------------------------------------------- 1 | # Make ajax request easier to write. 2 | 3 | # Expected callbacks: success and error 4 | exports.request = (type, url, data, callback) -> 5 | body = if data? then JSON.stringify data else null 6 | 7 | fired = false 8 | req = $.ajax 9 | type: type 10 | url: url 11 | data: body 12 | contentType: "application/json" 13 | dataType: "json" 14 | success: (data) -> 15 | fired = true 16 | callback null, data if callback? 17 | error: (data) -> 18 | fired = true 19 | if data? 20 | data = JSON.parse data.responseText 21 | if data.msg? and callback? 22 | callback new Error data.msg, data 23 | else if data.error? and callback? 24 | data.msg = data.error 25 | callback new Error data.msg, data 26 | else if callback? 27 | callback new Error "Server error occured", data 28 | req.always -> 29 | unless fired 30 | callback new Error "Server error occured", data 31 | 32 | 33 | # Sends a get request with data as body 34 | # Expected callbacks: success and error 35 | exports.get = (url, callback) -> 36 | exports.request "GET", url, null, callback 37 | 38 | 39 | # Sends a post request with data as body 40 | # Expected callbacks: success and error 41 | exports.post = (url, data, callback) -> 42 | exports.request "POST", url, data, callback 43 | 44 | 45 | # Sends a put request with data as body 46 | # Expected callbacks: success and error 47 | exports.put = (url, data, callback) -> 48 | exports.request "PUT", url, data, callback 49 | 50 | 51 | # Sends a delete request with data as body 52 | # Expected callbacks: success and error 53 | exports.del = (url, callback) -> 54 | exports.request "DELETE", url, null, callback 55 | 56 | 57 | # Sends an exist request 58 | exports.exist = (id, callback) -> 59 | exports.get "data/exist/#{id}/", callback 60 | 61 | -------------------------------------------------------------------------------- /client/app/lib/today_checker.coffee: -------------------------------------------------------------------------------- 1 | # For people that let their computer / browser run during the night 2 | # the 'today' is not properly set anymore the day after. 3 | module.exports = (router) -> 4 | 5 | # Define `waitToChangeToday`, and run it. 6 | do waitToChangeToday = -> 7 | 8 | now = moment() 9 | nextDay = moment(now).add(1, 'days').startOf 'day' 10 | nextTick = nextDay.valueOf() - now.valueOf() 11 | 12 | setTimeout -> 13 | # Completely re-render fullCalendar if it's open. 14 | # fullCalendar doesn't allow to change the 'today' date manually. 15 | view = router.mainView 16 | if view.cal? 17 | view.cal.fullCalendar 'destroy' 18 | view.afterRender() 19 | 20 | # Restart the timer for the next day. 21 | waitToChangeToday() 22 | 23 | , nextTick 24 | -------------------------------------------------------------------------------- /client/app/lib/view.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class View extends Backbone.View 2 | 3 | template: -> 4 | 5 | initialize: -> 6 | 7 | render: (templateOptions) -> 8 | @beforeRender() 9 | render = @template().call null, templateOptions 10 | @$el.html render 11 | @afterRender() 12 | 13 | @ 14 | 15 | beforeRender: -> 16 | 17 | afterRender: -> 18 | 19 | destroy: -> 20 | @undelegateEvents() 21 | @$el.removeData().unbind() 22 | @remove() 23 | Backbone.View::remove.call @ 24 | -------------------------------------------------------------------------------- /client/app/models/calendar.coffee: -------------------------------------------------------------------------------- 1 | Tag = require './tag' 2 | 3 | module.exports = class Calendar extends Tag 4 | 5 | initialize: (options) -> 6 | super options 7 | 8 | # When created via Data System, a calendar should not have any 9 | # color assigned. So we set it at initialization and will try 10 | # to save it as soon as a change is made on the calendar. 11 | color = @get 'color' 12 | @hasValidColorStoredInDB = @isColorValid color 13 | 14 | if not @hasValidColorStoredInDB 15 | # Temporary color until a save is made 16 | @set 'color', ColorHash.getColor @get 'name' 17 | 18 | 19 | isColorValid: (color) -> 20 | return color? and /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test color 21 | 22 | 23 | # Take proffit of a saving to save the color. 24 | save: (attributes, options) -> 25 | attributes = attributes ?= {} 26 | if not @hasValidColorStoredInDB and not attributes?.color? 27 | attributes.color = @get 'color' 28 | 29 | # Add intermediate success callback to set @hasValidColorStoredInDB. 30 | options = options ?= {} 31 | successCallback = options.success 32 | options.success = (model, response, options) => 33 | @hasValidColorStoredInDB = @isColorValid response.color 34 | if typeof successCallback is 'function' 35 | successCallback model, response, options 36 | 37 | super attributes, options 38 | -------------------------------------------------------------------------------- /client/app/models/contact.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class Contact extends Backbone.Model 2 | 3 | urlRoot: 'contacts' 4 | 5 | match: (filter) -> 6 | filter.test(@get('name')) or filter.test(@get('cozy')) or 7 | @get('emails').some (dp) -> 8 | filter.test dp.get('value') 9 | -------------------------------------------------------------------------------- /client/app/models/realevent.coffee: -------------------------------------------------------------------------------- 1 | # Realevents are events that occurs in real world. 2 | # ie, where each instance of recurring events are a RealEvent. 3 | 4 | module.exports = class RealEvent extends Backbone.Model 5 | # TODO : re-think class hierarchy, or way to construct this object. 6 | constructor: (options) -> 7 | super 8 | 9 | @event = options.event 10 | @start = options.start 11 | @end = options.end 12 | @counter = options.counter 13 | @forceAllDay = options.isAllDay 14 | 15 | if @event.isRecurrent() 16 | @set 'id', @event.get('id') + @start.toISOString() 17 | 18 | else if @event.isMultipleDays() 19 | @set 'id', "#{@event.get('id')} #{@start}" 20 | 21 | else 22 | @set 'id', @event.get('id') 23 | @start = @event.getStartDateObject() 24 | @end = @event.getEndDateObject() 25 | 26 | 27 | getCalendar: -> @event.getCalendar() 28 | getColor: -> @event.getColor() 29 | getDateHash: -> 30 | return @start?.format 'YYYYMMDD' 31 | 32 | isAllDay: -> @event.isAllDay() or @forceAllDay 33 | 34 | getFormattedStartDate: (format) -> 35 | return @start?.format format 36 | getFormattedEndDate: (format) -> 37 | return @end?.format format 38 | -------------------------------------------------------------------------------- /client/app/models/settings.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class Settings extends Backbone.Model 2 | 3 | urlRoot: 'settings' 4 | 5 | # Make sure that put requests doesn't add id to the url. 6 | sync: (method, model, options) -> 7 | options.url = 'settings' 8 | return Backbone.sync method, model, options 9 | -------------------------------------------------------------------------------- /client/app/models/sharing.coffee: -------------------------------------------------------------------------------- 1 | request = require 'lib/request' 2 | 3 | 4 | module.exports = class Sharing extends Backbone.Model 5 | 6 | urlRoot: 'sharings' 7 | 8 | accept: (callback) -> 9 | id = @get 'id' 10 | request.post 'sharing/accept', @toJSON(), (err, response) => 11 | if err 12 | callback err, null 13 | else 14 | callback null, response 15 | @trigger 'accepted', @ 16 | 17 | refuse: (callback) -> 18 | id = @get 'id' 19 | request.post 'sharing/refuse', @toJSON(), (err, response) => 20 | if err 21 | callback err, null 22 | else 23 | callback null, response 24 | @trigger 'refused', @ 25 | 26 | 27 | # Returns an array of sharing's targets having errors 28 | getFailedTargets: -> 29 | return @get('targets')?.filter (target) -> 30 | return target.error? 31 | -------------------------------------------------------------------------------- /client/app/models/tag.coffee: -------------------------------------------------------------------------------- 1 | module.exports = class Tag extends Backbone.Model 2 | urlRoot: 'tags' 3 | 4 | # idAttribute: 'name' 5 | defaults: 6 | visible: true 7 | 8 | 9 | toString: -> @get 'name' 10 | -------------------------------------------------------------------------------- /client/app/views/calendar_header.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require '../lib/base_view' 2 | 3 | 4 | module.exports = class CalendarHeader extends BaseView 5 | 6 | tagName: 'div' 7 | id: 'calendarHeader' 8 | className: 'fc-header' 9 | template: require './templates/calendar_header' 10 | 11 | 12 | initialize: (options) -> 13 | @view = options?.view 14 | @cal = options?.cal 15 | @isMobile = options?.isMobile 16 | 17 | 18 | getViewName: -> 19 | return @view 20 | 21 | 22 | getTitle: -> 23 | return t('List') unless @cal 24 | 25 | view = @cal.fullCalendar 'getView' 26 | 27 | if view.name is 'month' 28 | res = view.intervalStart.format 'MMMM YYYY' 29 | 30 | else 31 | from = view.start 32 | to = view.end.subtract 1, 'days' 33 | range = $.fullCalendar.formatRange from, to, 'MMM D YYYY' 34 | res = range 35 | 36 | return res 37 | 38 | 39 | getDates: -> 40 | view = @cal.fullCalendar 'getView' 41 | return [view.start, view.end] 42 | 43 | 44 | isToday: -> 45 | [start, end] = @getDates() 46 | return start < moment() < end 47 | 48 | 49 | getRenderData: -> 50 | return data = 51 | title: @getTitle() 52 | todaytxt: t('today') 53 | calendarMode: @cal? 54 | isMobile: @isMobile 55 | active: (item) => 56 | if item is 'today' and @isToday() or item is @getViewName() 57 | return 'fc-state-active' 58 | 59 | toggleDrawer: -> 60 | $drawer = $ 'aside.drawer' 61 | isVisible = $drawer.attr('aria-expanded') is 'true' 62 | $drawer.attr 'aria-expanded', not isVisible 63 | 64 | 65 | events: -> 66 | 'click .drawer-toggle': 'toggleDrawer' 67 | 'click .fc-button-next': => @trigger 'next' 68 | 'click .fc-button-prev': => @trigger 'prev' 69 | 'click .fc-button-today': => @trigger 'today' 70 | 'click .fc-button-week': => @trigger 'week' 71 | 'click .fc-button-month': => @trigger 'month' 72 | 'click .fc-button-list': => @trigger 'list' 73 | -------------------------------------------------------------------------------- /client/app/views/collection_counter.coffee: -------------------------------------------------------------------------------- 1 | # Fix naming of ViewCollection Class to CollectionView 2 | CollectionView = require 'lib/view_collection' 3 | 4 | module.exports = class CollectionCounterView extends CollectionView 5 | 6 | className: 'collection-counter' 7 | template: require('./templates/collection_counter') 8 | 9 | 10 | initialize: (options) -> 11 | super options 12 | 13 | @stopListening @collection 14 | 15 | @listenTo @collection, 'add remove reset', @render 16 | 17 | afterRender: -> 18 | -------------------------------------------------------------------------------- /client/app/views/event_popover_screen.coffee: -------------------------------------------------------------------------------- 1 | PopoverScreenView = require 'lib/popover_screen_view' 2 | 3 | # Export the formModel property for all Event Popover Screens 4 | module.exports = class EventPopoverScreenView extends PopoverScreenView 5 | 6 | initialize: -> 7 | super() 8 | @formModel = @context.formModel 9 | 10 | 11 | getRenderData: -> 12 | _.extend super(), readOnly: @context.readOnly 13 | -------------------------------------------------------------------------------- /client/app/views/import_event_list.coffee: -------------------------------------------------------------------------------- 1 | ViewCollection = require '../lib/view_collection' 2 | EventView = require './import_event_view' 3 | EventCollection = require '../collections/events' 4 | 5 | module.exports = class EventList extends ViewCollection 6 | 7 | itemview: EventView 8 | collection: new EventCollection() 9 | -------------------------------------------------------------------------------- /client/app/views/import_event_view.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require '../lib/base_view' 2 | 3 | module.exports = class EventView extends BaseView 4 | 5 | tagName: 'div' 6 | className: 'event' 7 | template: require './templates/import_event' 8 | 9 | getRenderData: -> 10 | _.extend @model.toJSON(), 11 | start: @model.getFormattedStartDate 'YYYY/MM/DD HH:mm' 12 | end: @model.getFormattedEndDate 'YYYY/MM/DD HH:mm' 13 | -------------------------------------------------------------------------------- /client/app/views/list_view_bucket.coffee: -------------------------------------------------------------------------------- 1 | ViewCollection = require '../lib/view_collection' 2 | EventPopover = require './event_popover' 3 | 4 | module.exports = class BucketView extends ViewCollection 5 | 6 | tagName: 'div' 7 | className: 'dayprogram' 8 | template: require './templates/list_view_bucket' 9 | itemview: require './list_view_item' 10 | collectionEl: '.alarms' 11 | 12 | events: 13 | 'click .add': 'makeNew' 14 | 15 | initialize: -> 16 | @collection = @model.items 17 | super 18 | 19 | getRenderData: -> 20 | date: @model.get('date').format 'dddd LL' 21 | 22 | # @TODO : seems unplugged. 23 | makeNew: -> 24 | @showPopover 25 | type: 'event' 26 | start: @model.get('date').clone().set hour: 8, minute: 30 27 | end: @model.get('date').clone().set hour: 10, minute: 0 28 | target: @$('.add') 29 | 30 | # @TODO : unused, but also outdated (see calendar_view for popover api). 31 | showPopover: (options) -> 32 | options.parentView = this 33 | options.container = $ 'body' 34 | @popover.close() if @popover 35 | @popover = new EventPopover options 36 | @popover.render() 37 | 38 | getUrlHash: -> 'list' 39 | 40 | appendView: (view) -> 41 | index = @collection.indexOf view.model 42 | el = view.$el 43 | if index is 0 then @$collectionEl.prepend el 44 | else 45 | prevCid = @collection.at(index-1).cid 46 | @views[prevCid].$el.before el 47 | -------------------------------------------------------------------------------- /client/app/views/list_view_item.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require 'lib/base_view' 2 | EventPopover = require './event_popover' 3 | Event = require 'models/event' 4 | 5 | 6 | module.exports = class EventItemView extends BaseView 7 | 8 | className: 'scheduleElement' 9 | template: require './templates/list_view_item' 10 | 11 | events: 12 | 'click .edit': 'editMode' 13 | 'click .delete': 'deleteModel' 14 | 15 | initialize: -> 16 | @listenTo @model, 'change', @render 17 | 18 | deleteModel: -> 19 | return unless confirm t "are you sure" 20 | @$el.spin 'tiny' 21 | @model.event.destroy 22 | error: -> 23 | alert 'server error' 24 | @$el.spin() 25 | 26 | # @TODO : unused, but also outdated (see calendar_view for popover api). 27 | editMode: -> 28 | @popover.close() if @popover 29 | @popover = new EventPopover 30 | model: @model, 31 | target: @$el 32 | parentView: this 33 | container: $ 'body' 34 | @popover.render() 35 | 36 | getUrlHash: -> 'list' 37 | 38 | getRenderData: -> 39 | data = @model.event.toJSON() 40 | 41 | _.extend data, 42 | type: 'event' 43 | start: @model.getFormattedStartDate 'HH:mm' 44 | end: @model.getFormattedEndDate 'HH:mm' 45 | allDay: @model.isAllDay() 46 | color: @model.getColor() 47 | counter: @model.counter 48 | 49 | return data 50 | -------------------------------------------------------------------------------- /client/app/views/pending_event_sharings_button.coffee: -------------------------------------------------------------------------------- 1 | # Fix naming of ViewCollection Class to CollectionView 2 | CollectionView = require 'lib/view_collection' 3 | CollectionCounterView = require './collection_counter' 4 | PopupView = require 'lib/popup_view' 5 | 6 | module.exports = class PendingEventSharingsButtonView extends CollectionView 7 | 8 | id: 'shared-events-button' 9 | template: require('./templates/pending_event_sharings_button') 10 | itemview: require('./pending_event_sharings_button_item') 11 | collectionEl: '#shared-events-popup' 12 | 13 | 14 | events: 15 | 'click button': 'togglePopup' 16 | 'keyup': 'onKeyUp' 17 | 18 | 19 | initialize: (options)-> 20 | super() 21 | 22 | @counterView = new CollectionCounterView 23 | collection: @collection 24 | 25 | @options = options 26 | 27 | 28 | togglePopup: (display) -> 29 | @popup and @popup.toggle() 30 | 31 | 32 | render: -> 33 | super 34 | @counterView.snap(@).render() 35 | 36 | 37 | afterRender: -> 38 | if @collection.length then @$el.show() else @$el.hide() 39 | 40 | super 41 | 42 | @popup = new PopupView 43 | el: @$ @collectionEl 44 | anchor: @$el 45 | document: @options.document 46 | 47 | 48 | addItem: (model) -> 49 | @$el.show() 50 | super model 51 | 52 | 53 | removeItem: (model) -> 54 | super model 55 | 56 | if @collection.length == 0 57 | @popup.hide() 58 | @$el.hide() 59 | 60 | 61 | onKeyUp: (event) -> 62 | keys = ESC: 27 63 | if event.keyCode is keys.ESC 64 | @withdraw 65 | 66 | 67 | withdraw: -> 68 | @togglePopup(false) 69 | -------------------------------------------------------------------------------- /client/app/views/pending_event_sharings_button_item.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require 'lib/base_view' 2 | 3 | module.exports = class PendingEventSharingsButtonItemView extends BaseView 4 | 5 | className: 'event-sharings-button-item' 6 | template: require './templates/pending_event_sharings_button_item' 7 | 8 | events: 9 | 'click .accept': 'onAccept' 10 | 'click .decline': 'onDecline' 11 | 12 | initialize: -> 13 | @listenTo @model, 'accepted refused', @destroy 14 | 15 | 16 | onAccept: -> 17 | @disable() 18 | @setBusy() 19 | @model.accept (err) => 20 | if err 21 | @onAnswerError err 22 | else 23 | @onAnswerSuccess() 24 | 25 | 26 | onDecline: -> 27 | @disable() 28 | @setBusy() 29 | @model.refuse (err) => 30 | if err 31 | @onAnswerError err 32 | else 33 | @onAnswerSuccess() 34 | 35 | 36 | onAnswerSuccess: -> 37 | @setValid() 38 | @remove() 39 | 40 | 41 | onAnswerError: (err) -> 42 | @$errors = @$errors ?= @$ '.errors' 43 | @$errors.html t 'An error occurred. Please try again later.' 44 | @setNotBusy() 45 | @setInvalid() 46 | @enable() 47 | -------------------------------------------------------------------------------- /client/app/views/popover_screens/confirm.coffee: -------------------------------------------------------------------------------- 1 | PopoverScreenView = require 'lib/popover_screen_view' 2 | 3 | module.exports = class ConfirmClosePopoverScreen extends PopoverScreenView 4 | 5 | screenTitle: t('screen confirm title') 6 | 7 | # Override title template. 8 | templateTitle: require 'views/templates/popover_screens/confirm_title' 9 | templateContent: require 'views/templates/popover_screens/confirm' 10 | 11 | events: 12 | 'click .popover-back': (event) -> @onCancel(event) 13 | 'click .answer-no': (event) -> @onCancel(event) 14 | 'click .answer-yes': (event) -> @onConfirm(event) 15 | 'change .dontaskagain': 'onCheckboxChange' 16 | 17 | initialize: (options) -> 18 | super() 19 | 20 | @confirmCallback = options.data?.confirmCallback or 21 | throw new Error 'No confirm callback has been set.' 22 | 23 | @cancelCallback = options.data?.cancelCallback or 24 | throw new Error 'No cancel callback has been set.' 25 | 26 | onConfirm: (event) -> 27 | # Avoid triggering click out for another popover 28 | event.stopPropagation() 29 | @confirmCallback() 30 | 31 | onCancel: (event) -> 32 | # Avoid triggering click out for another popover 33 | event.stopPropagation() 34 | @cancelCallback() 35 | 36 | onCheckboxChange: -> 37 | dontaskagain = $('.dontaskagain').is(':checked') 38 | localStorage.dontConfirmCalendarPopover = dontaskagain 39 | -------------------------------------------------------------------------------- /client/app/views/popover_screens/delete.coffee: -------------------------------------------------------------------------------- 1 | PopoverScreenView = require 'lib/popover_screen_view' 2 | Modal = require 'lib/modal' 3 | 4 | module.exports = class DeletePopoverScreen extends PopoverScreenView 5 | 6 | screenTitle: t('screen delete title') 7 | 8 | # Override title template. 9 | templateTitle: require 'views/templates/popover_screens/delete_title' 10 | templateContent: require 'views/templates/popover_screens/delete' 11 | 12 | events: 13 | 'click .answer-yes': 'onDelete' 14 | 'click .answer-no': -> @switchToScreen('main') 15 | 16 | 17 | afterRender: -> 18 | @$spinner = @$ '.remove-spinner' 19 | @$removeChoices = @$ '.remove-choices' 20 | @$errors = @$ '.errors' 21 | 22 | @$spinner.hide() 23 | @$errors.hide() 24 | 25 | # Display a modal to confirm sending of emails to notify attendees 26 | confirmEmailNotifications: (model, callback) -> 27 | attendees = model.get('attendees') or [] 28 | guestsToInform = attendees.filter (guest) => 29 | return not guest.isSharedWithCozy and 30 | guest.status in ['ACCEPTED', 'NEEDS-ACTION'] 31 | 32 | if guestsToInform.length 33 | guestsList = guestsToInform.map((guest) -> guest.label).join ', ' 34 | modal = Modal.confirm t('modal send mails'), 35 | "#{t 'send delete notifications'} #{guestsList}", \ 36 | t('yes'), 37 | t('no'), 38 | (result) -> callback null, result 39 | 40 | @context.clickOutListener.exceptOn [ 41 | modal.el, 42 | modal.getBackdrop() ] 43 | 44 | else 45 | callback null, false 46 | 47 | 48 | onDelete: -> 49 | @$errors.hide() 50 | @$spinner.show() 51 | @$removeChoices.hide() 52 | 53 | @confirmEmailNotifications @model, (err, sendEmailNotifications) => 54 | @model.destroy 55 | wait: true 56 | url: "#{@model.url()}?sendMails=#{sendEmailNotifications}" 57 | error: => 58 | @$removeChoices.show() 59 | @$errors.html t('server error occured') 60 | @$errors.show() 61 | success: => 62 | @$spinner.hide() 63 | @popover.close() 64 | -------------------------------------------------------------------------------- /client/app/views/popover_screens/details.coffee: -------------------------------------------------------------------------------- 1 | EventPopoverScreenView = require 'views/event_popover_screen' 2 | 3 | module.exports = class DetailsPopoverScreen extends EventPopoverScreenView 4 | 5 | screenTitle: t('screen description title') 6 | templateContent: require 'views/templates/popover_screens/details' 7 | 8 | 9 | afterRender: -> 10 | # Focus the form field. 11 | @$('.input-details').focus() 12 | 13 | 14 | # Save the value when the screen is left. 15 | onLeaveScreen: -> 16 | value = @$('.input-details').val() 17 | @formModel.set 'details', value 18 | 19 | getRenderData: -> 20 | return _.extend super(), details: @formModel.get 'details' 21 | -------------------------------------------------------------------------------- /client/app/views/popover_screens/duplicate.coffee: -------------------------------------------------------------------------------- 1 | PopoverScreenView = require 'lib/popover_screen_view' 2 | 3 | module.exports = class ConfirmDuplicatePopoverScreen extends PopoverScreenView 4 | 5 | screenTitle: t('screen duplicate title') 6 | 7 | # Override title template. 8 | templateTitle: require 'views/templates/popover_screens/duplicate_title' 9 | templateContent: require 'views/templates/popover_screens/duplicate' 10 | 11 | events: 12 | 'click .popover-back': (event) -> @onBack(event) 13 | 'click .answer-no': (event) -> @onNo(event) 14 | 'click .answer-yes': (event) -> @onYes(event) 15 | 16 | initialize: (options) -> 17 | super() 18 | 19 | @duplicateCallback = options.data?.duplicateCallback or 20 | throw new Error 'No duplicate callback has been set.' 21 | 22 | @cancelCallback = options.data?.cancelCallback or 23 | throw new Error 'No cancel callback has been set.' 24 | 25 | onYes: (event) -> 26 | # Avoid triggering click out for another popover 27 | event.stopPropagation() 28 | @duplicateCallback() 29 | 30 | onNo: (event) -> 31 | # Avoid triggering click out for another popover 32 | event.stopPropagation() 33 | @cancelCallback() 34 | 35 | onBack: (event) -> 36 | # Avoid triggering click out for another popover 37 | event.stopPropagation() 38 | @cancelCallback() 39 | -------------------------------------------------------------------------------- /client/app/views/styles/_colors.styl: -------------------------------------------------------------------------------- 1 | // "Cozy" colors palette 2 | // ----------------------------------------------------------------------------- 3 | // blue 4 | basecolor = #34A6FF 5 | basecolor-alpha = rgba(49, 156, 256, 0.5) 6 | 7 | //rainbow 8 | blue = #33A6FF 9 | red = #FF3713 10 | green = #16D943 11 | 12 | // gray and dark gray 13 | lightcolor = grey-01 14 | mediumcolor = grey-04 15 | mediumcolor-contrast = grey-05 16 | darkcolor-contrast = grey-07 17 | darkcolor = grey-08 18 | 19 | // special cozy colors 20 | homeheadercolor = #363a46 21 | bluegreen = #34A6BB 22 | lightorange = #FFC47F 23 | lightblue = #CBE4FB 24 | lightred = #F4F4F4 25 | grey = #666 26 | lightgrey = lightcolor 27 | darkblue = blue - 20% 28 | 29 | // 50 (well, in fact, mostly 8) shades of grey 30 | grey-01 = #ECEFF3 31 | grey-02 = #DFE4E9 32 | grey-03 = #C8D5DF 33 | grey-04 = #ACB8C5 34 | grey-05 = #92A0B2 35 | grey-06 = #748192 36 | grey-07 = #4F5B69 37 | grey-08 = #32363F 38 | 39 | // SHITTY DEPENDENCIES 40 | lightgreyblue = grey-03 41 | darkgreyblue = grey-05 42 | lightgrey = grey-03 43 | grey = grey-04 44 | midgrey = grey-05 45 | darkgrey = grey-08 46 | 47 | 48 | // Defintions 49 | // ----------------------------------------------------------------------------- 50 | .app 51 | background-color grey-01 52 | 53 | 54 | .app [role=menubar] [role=menuitem] 55 | color darkcolor 56 | 57 | &:hover 58 | color basecolor 59 | 60 | .green 61 | color: green 62 | .red 63 | color: red 64 | .blue 65 | color: blue 66 | .orange 67 | color: orange 68 | -------------------------------------------------------------------------------- /client/app/views/styles/_cozy.styl: -------------------------------------------------------------------------------- 1 | a 2 | color: blue 3 | 4 | a:hover 5 | color: darkblue 6 | 7 | input 8 | select 9 | textarea 10 | border: 1px solid blue 11 | border-radius: 0 12 | font-family: 'Source Sans Pro', sans-serif 13 | 14 | input:hover 15 | select:hover 16 | textarea:hover 17 | border: 1px solid darkblue 18 | 19 | input:active 20 | select:active 21 | textarea:active 22 | box-shadow: none 23 | 24 | 25 | .button 26 | border 0 27 | background blue 28 | color white 29 | box-shadow none 30 | text-shadow none 31 | border-radius 0 32 | font-family 'Source Sans Pro', sans-serif 33 | font-weight bold 34 | padding 10px 35 | cursor pointer 36 | 37 | .btn-link 38 | background transparent 39 | color blue 40 | font-weight normal 41 | 42 | .btn-link:hover 43 | color darkblue 44 | text-decoration underline 45 | background transparent 46 | 47 | .btn.btn-link:active 48 | background darkblue 49 | color white 50 | text-decoration none 51 | 52 | .btn.btn-link:focus 53 | background transparent 54 | color blue 55 | text-decoration none 56 | 57 | 58 | .button:hover 59 | background: darkblue 60 | border: 0 61 | color: white 62 | text-decoration: none 63 | 64 | .button:active 65 | background: darkblue - 3% 66 | border: 0 67 | box-shadow: 1px 3px 2px rgba(0, 0, 0, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.0) 68 | color: white 69 | 70 | .button.disabled 71 | .button.disabled:hover 72 | .button.disabled:active 73 | .button.disabled:focus 74 | padding: 10px 75 | cursor: default 76 | text-decoration: none 77 | opacity: .5 78 | 79 | 80 | .button.toggled 81 | background-color: blue - 20% 82 | box-shadow: 1px 3px 2px rgba(0, 0, 0, 0.2) inset, 0 1px 2px rgba(0, 0, 0, 0.0) 83 | 84 | .minor-button 85 | padding: 10px 86 | 87 | .modal-header 88 | padding: 1.2rem 89 | background-color: grey-01 90 | border-bottom: 1px solid grey-03 91 | border-radius: 6px 6px 0 0 92 | color: grey-08 93 | 94 | h2 95 | margin 0 96 | font-size 1.5rem 97 | 98 | 99 | .modal-body 100 | font-family: 'Source Sans Pro', sans-serif 101 | 102 | input 103 | select 104 | textarea 105 | border: 1px solid grey-03 106 | border-radius: 0 107 | 108 | &:hover 109 | border: 1px solid darkblue 110 | 111 | &:focus 112 | &:active 113 | border: 1px solid grey-03 114 | box-shadow: none 115 | -------------------------------------------------------------------------------- /client/app/views/styles/_datepicker.styl: -------------------------------------------------------------------------------- 1 | /** 2 | * Vars 3 | */ 4 | 5 | blue = #34A6FF 6 | 7 | 8 | /** 9 | * Date picker from bootstrap - override 10 | */ 11 | 12 | .datetimepicker 13 | font-family 'Source Sans Pro', sans-serif 14 | 15 | .datetimepicker table tr td.active 16 | .datetimepicker table tr td.active:hover 17 | .datetimepicker table tr td.active.disabled 18 | .datetimepicker table tr td.active.disabled:hover 19 | background-color blue !important 20 | background-image none 21 | text-shadow none 22 | -------------------------------------------------------------------------------- /client/app/views/styles/_drawer.styl: -------------------------------------------------------------------------------- 1 | .drawer 2 | display flex 3 | flex-direction column 4 | 5 | padding .5em 1.25em 6 | 7 | background-color grey-01 8 | border-right 1px solid grey-03 9 | 10 | [class^=tool-] 11 | vertically-spaced 10px 12 | 13 | 14 | [class$=actions] 15 | padding-right 1.25em 16 | 17 | 18 | legend 19 | button 20 | [role=button] 21 | display block 22 | box-sizing border-box 23 | width 100% 24 | 25 | text-align left 26 | 27 | li:not(:last-child) 28 | button 29 | [role=button] 30 | bottom-spaced .5em 31 | 32 | 33 | .tool-labels 34 | vertically-spaced() 35 | flex 1 36 | overflow-y auto 37 | 38 | legend 39 | color grey-05 40 | font-size 14px 41 | text-transform uppercase 42 | 43 | ul 44 | padding-right em(20px) 45 | 46 | li 47 | vertically-spaced .5em 48 | text-transform capitalize 49 | 50 | label 51 | right-spaced .5em 52 | 53 | a 54 | color grey-06 55 | 56 | &[aria-checked=true] 57 | color grey-08 58 | 59 | [data-input=checkbox] label::before 60 | background-color: white 61 | 62 | 63 | .select 64 | vertically-spaced .5em 65 | 66 | li 67 | button 68 | reset() 69 | line-height 1.6em 70 | background-color transparent 71 | color blue 72 | -------------------------------------------------------------------------------- /client/app/views/styles/_layout.styl: -------------------------------------------------------------------------------- 1 | [role=application] 2 | @extend $app-2panes-toolbar 3 | -------------------------------------------------------------------------------- /client/app/views/styles/_mobile.styl: -------------------------------------------------------------------------------- 1 | .is-mobile 2 | main 3 | margin-top 0 4 | 5 | .fc .drawer-toggle 6 | .drawer-toggle 7 | padding .75rem .875rem 8 | background-color grey-01 9 | border-top 0 10 | border-left 0 11 | border-bottom 0 12 | border-right 1px solid grey-03 13 | border-radius 0 14 | outline none 15 | color grey-05 16 | 17 | @media (min-width: (1023/16)rem) 18 | display none 19 | -------------------------------------------------------------------------------- /client/app/views/styles/_pending_event_sharings_button.styl: -------------------------------------------------------------------------------- 1 | .popup 2 | position absolute 3 | background-color white 4 | z-index 2 5 | box-shadow:0 0 .5em rgba(0,0,0,.2) 6 | 7 | padding 1.5em 8 | font-size:small 9 | border-radius 1em 10 | 11 | .event-sharings-button-item 12 | position relative 13 | line-height 1.5em 14 | margin-top 1em 15 | padding-bottom 1em 16 | border-bottom 1px solid lightgrey 17 | 18 | &:first-of-type 19 | margin-top 0 20 | 21 | &:last-of-type 22 | padding-bottom 0 23 | border-bottom none 24 | 25 | .sharer 26 | font-weight bold 27 | 28 | .desc 29 | color grey 30 | 31 | .actions 32 | color blue 33 | 34 | a 35 | cursor pointer 36 | font-size x-small 37 | font-weight bold 38 | text-transform uppercase 39 | 40 | &:focus, 41 | &:hover, 42 | &:active 43 | color darkblue 44 | text-decoration none 45 | 46 | // Alternate states 47 | .disabler, 48 | .spinner, 49 | .errors 50 | display none 51 | 52 | .disabler 53 | background-color rgba(255,255,255,0.75) 54 | 55 | &[aria-disabled], 56 | &[aria-busy] 57 | position relative 58 | 59 | &[aria-disabled] .disabler, 60 | &[aria-busy] .spinner 61 | display block 62 | position absolute 63 | top 0 64 | bottom 0 65 | left 0 66 | right 0 67 | 68 | &[aria-busy] .spinner 69 | display flex 70 | align-items center 71 | justify-content center 72 | 73 | img 74 | height 2em 75 | 76 | &[aria-invalid] .errors 77 | display block 78 | color red 79 | 80 | -------------------------------------------------------------------------------- /client/app/views/styles/_states.styl: -------------------------------------------------------------------------------- 1 | [data-state~="default"], 2 | [data-state~="enabled"] 3 | cursor inherit 4 | opacity 1 5 | transition opacity 0.2s linear 6 | 7 | [data-state~="disabled"], 8 | cursor not-allowed 9 | opacity 0.5 10 | transition opacity 0.2s linear 11 | 12 | [data-state~="hidden"] 13 | display none 14 | 15 | [data-state~="visible"] 16 | visibility visible 17 | 18 | [data-state~="invisible"] 19 | visibility hidden 20 | 21 | [data-state~="waiting"], 22 | [data-state~="loading"] 23 | cursor wait 24 | 25 | [data-state~="success"], 26 | [data-state~="valid"] 27 | border-color green 28 | color green 29 | transition color 0.2s linear, border-color 0.2s linear 30 | 31 | [data-state~="error"], 32 | [data-state~="invalid"] 33 | border-color red 34 | color red 35 | transition color 0.2s linear, border-color 0.2s linear 36 | -------------------------------------------------------------------------------- /client/app/views/styles/mixins/_layout.styl: -------------------------------------------------------------------------------- 1 | $centerized 2 | position absolute 3 | top 50% 4 | left 50% 5 | transform translateX(-50%) translateY(-50%) 6 | 7 | 8 | $content-center 9 | display flex 10 | align-items center 11 | justify-content center 12 | 13 | 14 | $app-2panes-toolbar 15 | display flex 16 | width 100vw 17 | height 100vh 18 | 19 | main 20 | > aside 21 | display flex 22 | flex-direction column 23 | z-index 1 24 | 25 | main 26 | main > [role=contentinfo] 27 | flex 1 28 | height auto 29 | overflow-x hidden 30 | overflow-y auto 31 | 32 | @media (max-width: (1023/16)rem) 33 | toggle-width = (56/16)em 34 | 35 | > aside 36 | z-index 1 37 | position absolute 38 | top 0 39 | left 0 40 | width 100vw 41 | height 100vh 42 | padding-right toggle-width + 1.25 43 | 44 | &[aria-expanded=true] + main 45 | transform translateX("calc(100vw - %s)" % toggle-width) 46 | 47 | main 48 | z-index 2 49 | position relative 50 | transform translateX(0) 51 | transition transform 350ms ease-out 52 | 53 | 54 | $fullbleed 55 | position fixed 56 | bottom 0 57 | top 0 58 | left 0 59 | right 0 60 | -------------------------------------------------------------------------------- /client/app/views/tags.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require 'lib/base_view' 2 | 3 | module.exports = class TagsView extends BaseView 4 | 5 | initialize: -> 6 | super 7 | @$el.hide().tagit 8 | availableTags: app.tags.toArray() 9 | placeholderText: t 'add tags' 10 | afterTagAdded : @tagAdded 11 | 12 | # hack to prevent tagit events 13 | @duringRefresh = false 14 | 15 | return this 16 | 17 | tagAdded: (ev, ui) -> 18 | ui.tag.css 'background-color', ColorHash.getColor(ui.tagLabel, 'cozy') 19 | 20 | getTags: -> 21 | @$el.tagit 'assignedTags' 22 | 23 | refresh: => 24 | @duringRefresh = true 25 | @$el.tagit 'removeAll' 26 | for tag in @model.get('tags') 27 | @$el.tagit 'createTag', tag 28 | @duringRefresh = false 29 | 30 | -------------------------------------------------------------------------------- /client/app/views/templates/calendar_header.jade: -------------------------------------------------------------------------------- 1 | button.drawer-toggle 2 | menu 3 | .fc-header-left 4 | if calendarMode 5 | .btn-group(role="group") 6 | span.btn.fc-button-prev.fc-corner-left 7 | i.fa.fa-angle-left 8 | span.btn.title= title 9 | span.btn.fc-button-next.fc-corner-right 10 | i.fa.fa-angle-right 11 | .btn.fc-button.fc-button-today(class=active('today'))= todaytxt 12 | 13 | #shared-events-button.fc-button-wrapper 14 | span.fc-header-title 15 | 16 | // just preload the image for fast display when used 17 | img.hidden(src="img/spinner-white.svg") 18 | 19 | .fc-header-name= t('upcoming events') 20 | 21 | .fc-header-right 22 | if !isMobile 23 | .btn-group(role="group") 24 | span.btn.fc-button-month(class=active('month') type="button")= t('month') 25 | span.btn.fc-button-week(class=active('agendaWeek') type="button")=t('week') 26 | span.btn.fc-button-list(class=active('list') type="button")= t('list') 27 | -------------------------------------------------------------------------------- /client/app/views/templates/calendarview.jade: -------------------------------------------------------------------------------- 1 | #alarms.well 2 | -------------------------------------------------------------------------------- /client/app/views/templates/collection_counter.jade: -------------------------------------------------------------------------------- 1 | =collection.length 2 | -------------------------------------------------------------------------------- /client/app/views/templates/import_event.jade: -------------------------------------------------------------------------------- 1 | p #{start} - #{end} 2 | | #{description} 3 | - if (place != void(0) && place != null && place.length > 0) 4 | | (#{place}) 5 | -------------------------------------------------------------------------------- /client/app/views/templates/import_view.jade: -------------------------------------------------------------------------------- 1 | #import-form.well 2 | 3 | div.import-form 4 | p 5 | = t('import an ical file') 6 | div#import-button.btn 7 | span= t('select an icalendar file') 8 | input#import-file-input(type="file") 9 | 10 | div.confirmation 11 | div.import-calendar-selection.mb2 12 | span= t('link imported events with calendar') 13 | br 14 | input#import-calendar-combo.mt1 15 | button#confirm-import-button.btn= t('confirm import') 16 | button#cancel-import-button.btn=t ('cancel') 17 | 18 | div.import-progress.mt3 19 | div.import-errors.mt3 20 | 21 | div.results.mt3 22 | h4= t('Events to import') 23 | #import-event-list 24 | -------------------------------------------------------------------------------- /client/app/views/templates/list_view.jade: -------------------------------------------------------------------------------- 1 | #calheader.well.fc-ltr 2 | #add-new-event 3 | #list-container.well.fc-row 4 | a.btn.addNewEvent= t('display event') 5 | a.btn.showbefore= t('display previous events') 6 | #alarm-list 7 | a.btn.showafter= t('display next events') 8 | -------------------------------------------------------------------------------- /client/app/views/templates/list_view_bucket.jade: -------------------------------------------------------------------------------- 1 | h4 #{date} 2 | .alarms 3 | -------------------------------------------------------------------------------- /client/app/views/templates/list_view_item.jade: -------------------------------------------------------------------------------- 1 | if !allDay 2 | .fc-time(style="background-color:"+color+";") #{start} - #{end} 3 | else 4 | .fc-time(style="background-color:"+color+";") #{t("All day")} 5 | .fc-title 6 | | #{description || t("no description")} 7 | - if(counter != void(0) && counter != null) 8 | |  (#{counter.current} / #{counter.total}) 9 | 10 | i.delete.fa.fa-trash 11 | -------------------------------------------------------------------------------- /client/app/views/templates/menu.jade: -------------------------------------------------------------------------------- 1 | .calendars 2 | .title(href="#calendar") 3 | span= t('calendar list title') 4 | 5 | section.calendar-list 6 | ul#menuitems 7 | a.calendar-add 8 | span.add-calendar-icon 9 | add 10 | span.main-spinner 11 | img(src="img/spinner.svg") 12 | span= t('create calendar') 13 | 14 | a.btn(href="#settings") 15 | i.fa.fa-cog 16 | span= t('sync settings button label') 17 | -------------------------------------------------------------------------------- /client/app/views/templates/menu_item.jade: -------------------------------------------------------------------------------- 1 | - back = visible?color:"transparent" 2 | - border = visible?color:"transparent" 3 | 4 | span.badge 5 | span.calendar-name=label 6 | .dropdown 7 | a#dLabel.dropdown-toggle(data-toggle="dropdown") 8 | span.caret 9 | ul.dropdown-menu(aria-labelledBy='dLabel') 10 | li 11 | a.calendar-color= t('change color') 12 | ul.color-picker 13 | each color in colorSet 14 | li.color(style="background-color: ##{color};") 15 | 16 | li 17 | a.calendar-rename= t('rename') 18 | li 19 | a.calendar-remove= t('delete') 20 | li 21 | a.calendar-export= t('export') 22 | img.spinner(src="img/spinner.svg") 23 | -------------------------------------------------------------------------------- /client/app/views/templates/pending_event_sharings_button.jade: -------------------------------------------------------------------------------- 1 | button.btn.fc-button 2 | span.collection-counter 3 | span=' ' + t('shared events') 4 | 5 | #shared-events-popup.popup(style='display:none') 6 | -------------------------------------------------------------------------------- /client/app/views/templates/pending_event_sharings_button_item.jade: -------------------------------------------------------------------------------- 1 | .sharer=model.sharerName || model.sharerUrl 2 | .desc=model.desc 3 | .errors 4 | .actions 5 | a.accept=t('Accept') 6 | span.separator=' • ' 7 | a.decline=t('Decline') 8 | .disabler 9 | .spinner 10 | img(src="img/spinner.svg") 11 | -------------------------------------------------------------------------------- /client/app/views/templates/popover.jade: -------------------------------------------------------------------------------- 1 | .popover 2 | div.screen-indicator 3 | .arrow 4 | h3.popover-title!= title 5 | .popover-content!= content 6 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/alert.jade: -------------------------------------------------------------------------------- 1 | .fixed-height 2 | 3 | if !readOnly 4 | select.new-alert.select-big.with-margin 5 | option(value="-1", selected="true")=t('screen alert default value') 6 | each alertOption in alertOptions 7 | option( 8 | value=alertOption.index 9 | )=t(alertOption.translationKey,{smart_count: alertOption.value}) 10 | 11 | ul.alerts 12 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/alert_row.jade: -------------------------------------------------------------------------------- 1 | li(data-index=index) 2 | .alert-top 3 | .alert-timer= label 4 | if !readOnly 5 | button.alert-delete.fa.fa-trash-o(title=t('screen alert delete tooltip'), role="button") 6 | .type 7 | .notification-mode 8 | if !readOnly 9 | input.action-email(id="email-#{index}", type="checkbox", checked=isEmailChecked) 10 | if readOnly 11 | |( 12 | if ['EMAIL', 'BOTH'].indexOf(action) != -1 13 | =t('screen alert type email') 14 | if action == 'BOTH' 15 | |, 16 | else 17 | label(for="email-#{index}")=t('screen alert type email') 18 | 19 | if !readOnly 20 | input.action-display(id="display-#{index}", type="checkbox", checked=isNotifChecked) 21 | if readOnly 22 | if ['DISPLAY', 'BOTH'].indexOf(action) != -1 23 | =t('screen alert type notification') 24 | |) 25 | else 26 | .notification-mode 27 | label(for="display-#{index}")=t('screen alert type notification') 28 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/confirm.jade: -------------------------------------------------------------------------------- 1 | .fixed-height.delete-screen 2 | 3 | p=t('screen confirm description') 4 | .remove-choices.button-group 5 | button.btn.secondary.answer-no=t('screen confirm no button') 6 | button.btn.danger.answer-yes(autofocus)=t('screen confirm yes button') 7 | 8 | label.dontaskagain-label 9 | input.dontaskagain(type="checkbox") 10 | = t('dont ask again') 11 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/confirm_title.jade: -------------------------------------------------------------------------------- 1 | div.popover-back 2 | i.fa.fa-angle-left 3 | h4= title 4 | div.empty 5 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/delete.jade: -------------------------------------------------------------------------------- 1 | .fixed-height.delete-screen 2 | 3 | p=t('screen delete description', {description: description}) 4 | .spinner-block 5 | img.remove-spinner(src='img/spinner.svg') 6 | p.errors 7 | .remove-choices.button-group 8 | button.btn.secondary.answer-no=t('screen delete no button') 9 | button.btn.danger.answer-yes(autofocus)=t('screen delete yes button') 10 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/delete_title.jade: -------------------------------------------------------------------------------- 1 | div.popover-back 2 | i.fa.fa-angle-left 3 | h4= title 4 | div.empty 5 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/details.jade: -------------------------------------------------------------------------------- 1 | .fixed-height 2 | textarea.input-details(readonly=readOnly,aria-readonly=readOnly)= details 3 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/duplicate.jade: -------------------------------------------------------------------------------- 1 | .fixed-height.delete-screen 2 | 3 | p=t('screen duplicate description') 4 | .remove-choices.button-group 5 | button.btn.secondary.answer-no=t('screen duplicate no button') 6 | button.btn.danger.answer-yes(autofocus)=t('screen duplicate yes button') 7 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/duplicate_title.jade: -------------------------------------------------------------------------------- 1 | div.popover-back 2 | i.fa.fa-angle-left 3 | h4= title 4 | div.empty 5 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/generic_title.jade: -------------------------------------------------------------------------------- 1 | div.popover-back 2 | a(tabindex="0") 3 | button.btn.btn-back 4 | i.fa.fa-angle-left 5 | h4= title 6 | .btn-done-placeholder 7 | if !readOnly 8 | a(tabindex="0") 9 | button.btn.btn-done=t('screen title done button') 10 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/guest_row.jade: -------------------------------------------------------------------------------- 1 | tr.guest-top(data-index=index) 2 | td.status 3 | if status == 'ACCEPTED' 4 | i.fa.fa-check-circle-o.green(title=t('accepted')) 5 | else if status == 'DECLINED' 6 | i.fa.fa-times-circle-o.red(title=t('declined')) 7 | else if status == 'NEED-ACTION' 8 | i.fa.fa-question-circle.blue(title=t('need action')) 9 | else 10 | i.fa.fa-exclamation-circle.orange(title=t('mail not sent')) 11 | 12 | td.guest-label(title=label)= label 13 | 14 | if readOnly 15 | td(colspan=3) 16 | else 17 | td.action 18 | button.guest-delete.fa.fa-trash-o( 19 | title=t('screen guest remove tooltip') 20 | role="button" 21 | ) 22 | td.action 23 | button.guest-share-with-email.fa.fa-envelope-o( 24 | title=t('screen guest share with email tooltip') 25 | role='button' 26 | class=activeEmail ? 'active' : '' 27 | disabled=activeEmail ? 'disabled' : false 28 | aria-hidden=hideEmail 29 | ) 30 | td.action 31 | button.guest-share-with-cozy.fa.fa-cloud( 32 | title=t('screen guest share with cozy tooltip') 33 | role="button" 34 | class=activeShare ? 'active' : '' 35 | disabled=activeShare ? 'disabled' : false 36 | aria-hidden=hideShare 37 | ) 38 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/guests.jade: -------------------------------------------------------------------------------- 1 | .fixed-height 2 | 3 | if !readOnly 4 | .guests-action 5 | input(type="text", name="guest-name", placeholder=t('screen guest input placeholder')) 6 | button.btn.add-new-guest=t('screen guest add button') 7 | 8 | table.guests 9 | -------------------------------------------------------------------------------- /client/app/views/templates/popover_screens/main_title.jade: -------------------------------------------------------------------------------- 1 | div.calendar 2 | input.calendarcombo(value=calendar) 3 | 4 | div.label 5 | input.input-desc.event-description(tabindex="1", 6 | type="text", 7 | value=description, 8 | placeholder=t("placeholder event title"), 9 | data-tabindex-prev="8", 10 | readonly=readOnly, 11 | aria-readonly=readOnly) 12 | 13 | div.controls 14 | button.remove.fa.fa-trash(title=t('delete'), role="button") 15 | img.remove-spinner(src='img/spinner.svg') 16 | if !readOnly 17 | button.duplicate.fa.fa-copy(title=t('duplicate'), role="button") 18 | -------------------------------------------------------------------------------- /client/app/views/templates/settings_modal.jade: -------------------------------------------------------------------------------- 1 | h2= t('sync settings button label') 2 | 3 | .modal-close= t('×') 4 | 5 | section 6 | h3= t('mobile sync') 7 | 8 | if account == null 9 | p= t('to sync your cal with') 10 | ol 11 | li= t('install the sync module') 12 | li= t('connect to it and follow') 13 | else 14 | p= t('sync headline with data') 15 | ul 16 | li #{t('sync url')} https://#{account.domain}/public/sync/principals/me 17 | li #{t('sync login')} #{account.login} 18 | li #{t('sync password') + " "} 19 | span#placeholder= account.placeholder 20 | button.btn.btn-small#show-password= t('show') 21 | button.btn.btn-small#hide-password= t('hide') 22 | p= t('sync help') + " " 23 | a(href='https://docs.cozy.io/mobile/calendar.html', target="_blank")= t('sync help link') 24 | 25 | section 26 | h3= t('icalendar export') 27 | p 28 | = t('download a copy of your calendar') 29 | p.line 30 | span.surrounded-combobox 31 | input#export-calendar(value=calendar) 32 | a#export.btn= t('export your calendar') 33 | 34 | section 35 | h3= t('default calendar') 36 | p.line 37 | span.surrounded-combobox 38 | input#default-calendar(value=calendar) 39 | p.line#default-calendar-loading 40 | 41 | 42 | section 43 | h3= t('icalendar import') 44 | 45 | #importviewplaceholder 46 | 47 | -------------------------------------------------------------------------------- /client/app/views/toggle.coffee: -------------------------------------------------------------------------------- 1 | BaseView = require '../lib/base_view' 2 | 3 | 4 | module.exports = class Toggle extends BaseView 5 | 6 | value: false 7 | tagName: 'span' 8 | className: 'badge' 9 | template: (data) -> 10 | "" 11 | 12 | initialize: (options) -> 13 | @value = options.value 14 | @icon = options.icon 15 | @label = options.label 16 | @render() 17 | @toggle @value 18 | 19 | getRenderData: -> icon: @icon 20 | 21 | events: -> 22 | 'click': => @toggle() 23 | 24 | toggle: (value) -> 25 | value ?= not @value 26 | @value = value 27 | if @value 28 | @$el.addClass 'badge-info' 29 | else 30 | @$el.removeClass 'badge-info' 31 | 32 | title = @label + ' : ' + t(if value then 'ON' else 'OFF') 33 | @$el.attr 'title', title 34 | @trigger 'toggle', value 35 | -------------------------------------------------------------------------------- /client/brunch-config.coffee: -------------------------------------------------------------------------------- 1 | exports.config = 2 | 3 | plugins: 4 | coffeelint: 5 | options: 6 | indentation: value: 4, level: 'error' 7 | 8 | jade: 9 | globals: ['t', 'moment','RRule'] 10 | 11 | digest: 12 | referenceFiles: /\.jade$/ 13 | 14 | files: 15 | javascripts: 16 | joinTo: 17 | 'javascripts/app.js': /^app/ 18 | 'javascripts/vendor.js': /^vendor/ 19 | order: 20 | # Files in `vendor` directories are compiled before other files 21 | # even if they aren't specified in order. 22 | before: [ 23 | 'vendor/scripts/jquery-2.1.1.js' 24 | 'vendor/scripts/jquery-ui-1.10.3.custom.js' 25 | 'vendor/scripts/bootstrap.js' 26 | 'vendor/scripts/bootstrap-datepicker.js' 27 | 'vendor/scripts/bootstrap-timepicker.js' 28 | 'vendor/scripts/underscore-1.4.4.js' 29 | 'vendor/scripts/backbone-1.1.2.js' 30 | 'vendor/scripts/spin.js' 31 | 'vendor/scripts/rrule.js' 32 | 'vendor/scripts/moment.js' 33 | 'vendor/scripts/moment-timezone-with-data.js' 34 | 'vendor/scripts/fullcalendar.min.js' 35 | ] 36 | 37 | stylesheets: 38 | joinTo: 'stylesheets/app.css' 39 | order: 40 | before: [ 41 | 'vendor/styles/normalize.css' 42 | 'vendor/styles/bootstrap.css' 43 | 'vendor/styles/bootstrap-datepicker.css' 44 | 'vendor/styles/bootstrap-timepicker.css' 45 | 'vendor/styles/fullcalendar.min.css' 46 | ] 47 | after: ['vendor/styles/helpers.css'] 48 | 49 | templates: 50 | defaultExtension: 'jade' 51 | joinTo: 'javascripts/app.js' 52 | 53 | overrides: 54 | production: 55 | sourceMaps: true 56 | paths: 57 | public: '../build/client/public' 58 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "clean-css-brunch": "1.8.0", 4 | "coffee-script-brunch": "1.8.3", 5 | "css-brunch": "1.7.0", 6 | "digest-brunch": "1.5.1", 7 | "jade-brunch": "1.8.2", 8 | "javascript-brunch": "1.8.0", 9 | "json-brunch": "1.5.4", 10 | "stylus-brunch": "1.8.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /client/vendor/scripts/ColorHash.js: -------------------------------------------------------------------------------- 1 | var ColorHash = (function () { 2 | 3 | var schemes = { 4 | "base" : [ 5 | "00bb3f", "238c47", "007929", "37dd6f", "63dd8d", 6 | "0f4fa8", "284c7e", "05316d", "4380d3", "6996d3", 7 | "ff9f00", "bf8930", "a66800", "ffb740", "ffca73", 8 | "ff2800", "bf4630", "a61a00", "ff5d40", "ff8973" 9 | ] 10 | }; 11 | 12 | function hashCode(str) { 13 | var h, i, len, max; 14 | 15 | h = 0; 16 | max = Math.pow(2, 32); 17 | 18 | for (i = 0, len = str.length; i < len; i++) { 19 | h = (h * 31 + str.charCodeAt(i)) % max; 20 | } 21 | 22 | return h; 23 | } 24 | 25 | function getColor(str, name) { 26 | var scheme, hash; 27 | 28 | scheme = schemes[name] || schemes.base; 29 | hash = hashCode(str); 30 | 31 | return "#" + scheme[hash % scheme.length]; 32 | } 33 | 34 | function addScheme(name, scheme) { 35 | schemes[name] = scheme; 36 | } 37 | 38 | function getScheme(name) { 39 | return scheme[name]; 40 | } 41 | 42 | function deleteScheme(name) { 43 | if (name !== "base") { 44 | delete schemes[name]; 45 | } 46 | } 47 | 48 | return { 49 | "addScheme" : addScheme, 50 | "getScheme" : getScheme, 51 | "deleteScheme" : deleteScheme, 52 | 53 | "getHash" : hashCode, 54 | "getColor" : getColor 55 | } 56 | 57 | }()); 58 | -------------------------------------------------------------------------------- /client/vendor/scripts/lang/bootstrap-datetimepicker.de.js: -------------------------------------------------------------------------------- 1 | /** 2 | * German translation for bootstrap-datetimepicker 3 | * Sam Zurcher 4 | */ 5 | ;(function($){ 6 | $.fn.datetimepicker.dates['de'] = { 7 | days: ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"], 8 | daysShort: ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam", "Son"], 9 | daysMin: ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"], 10 | months: ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"], 11 | monthsShort: ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"], 12 | today: "Heute" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /client/vendor/scripts/lang/bootstrap-datetimepicker.es.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Spanish translation for bootstrap-datetimepicker 3 | * Bruno Bonamin 4 | */ 5 | ;(function($){ 6 | $.fn.datetimepicker.dates['es'] = { 7 | days: ["Domingo", "Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"], 8 | daysShort: ["Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb", "Dom"], 9 | daysMin: ["Do", "Lu", "Ma", "Mi", "Ju", "Vi", "Sa", "Do"], 10 | months: ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"], 11 | monthsShort: ["Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"], 12 | today: "Hoy" 13 | }; 14 | }(jQuery)); 15 | -------------------------------------------------------------------------------- /client/vendor/scripts/lang/bootstrap-datetimepicker.fr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * French translation for bootstrap-datetimepicker 3 | * Nico Mollet 4 | */ 5 | ;(function($){ 6 | $.fn.datetimepicker.dates['fr'] = { 7 | days: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"], 8 | daysShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"], 9 | daysMin: ["D", "L", "Ma", "Me", "J", "V", "S", "D"], 10 | months: ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"], 11 | monthsShort: ["Jan", "Fev", "Mar", "Avr", "Mai", "Jui", "Jul", "Aou", "Sep", "Oct", "Nov", "Dec"], 12 | today: "Aujourd'hui", 13 | suffix: [], 14 | meridiem: ["am", "pm"], 15 | weekStart: 1, 16 | format: "dd/mm/yyyy" 17 | }; 18 | }(jQuery)); 19 | -------------------------------------------------------------------------------- /client/vendor/scripts/lang/bootstrap-datetimepicker.ko.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Korean translation for bootstrap-datetimepicker 3 | * Gu Youn 4 | */ 5 | ;(function($){ 6 | $.fn.datetimepicker.dates['kr'] = { 7 | days: ["일요일", "월요일", "화요일", "수요일", "목요일", "금요일", "토요일", "일요일"], 8 | daysShort: ["일", "월", "화", "수", "목", "금", "토", "일"], 9 | daysMin: ["일", "월", "화", "수", "목", "금", "토", "일"], 10 | months: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"], 11 | monthsShort: ["1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"] 12 | }; 13 | }(jQuery)); 14 | -------------------------------------------------------------------------------- /client/vendor/scripts/lang/bootstrap-datetimepicker.pt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Portuguese translation for bootstrap-datetimepicker 3 | * Original code: Cauan Cabral 4 | * Tiago Melo 5 | */ 6 | ;(function($){ 7 | $.fn.datetimepicker.dates['pt'] = { 8 | days: ["Domingo", "Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"], 9 | daysShort: ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb", "Dom"], 10 | daysMin: ["Do", "Se", "Te", "Qu", "Qu", "Se", "Sa", "Do"], 11 | months: ["Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"], 12 | monthsShort: ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"], 13 | suffix: [], 14 | meridiem: ["am","pm"], 15 | today: "Hoje" 16 | }; 17 | }(jQuery)); 18 | -------------------------------------------------------------------------------- /client/vendor/scripts/lang/bootstrap-datetimepicker.ro.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Romanian translation for bootstrap-datetimepicker 3 | * Cristian Vasile 4 | */ 5 | ;(function($){ 6 | $.fn.datetimepicker.dates['ro'] = { 7 | days: ["Duminică", "Luni", "Marţi", "Miercuri", "Joi", "Vineri", "Sâmbătă", "Duminică"], 8 | daysShort: ["Dum", "Lun", "Mar", "Mie", "Joi", "Vin", "Sâm", "Dum"], 9 | daysMin: ["Du", "Lu", "Ma", "Mi", "Jo", "Vi", "Sâ", "Du"], 10 | months: ["Ianuarie", "Februarie", "Martie", "Aprilie", "Mai", "Iunie", "Iulie", "August", "Septembrie", "Octombrie", "Noiembrie", "Decembrie"], 11 | monthsShort: ["Ian", "Feb", "Mar", "Apr", "Mai", "Iun", "Iul", "Aug", "Sep", "Oct", "Nov", "Dec"], 12 | today: "Astăzi", 13 | weekStart: 1 14 | }; 15 | }(jQuery)); 16 | -------------------------------------------------------------------------------- /client/vendor/scripts/lang/fullcalendar-de.js: -------------------------------------------------------------------------------- 1 | (function(t){"function"==typeof define&&define.amd?define(["jquery","moment"],t):t(jQuery,moment)})(function(t,e){function n(t,e,n){var i={m:["eine Minute","einer Minute"],h:["eine Stunde","einer Stunde"],d:["ein Tag","einem Tag"],dd:[t+" Tage",t+" Tagen"],M:["ein Monat","einem Monat"],MM:[t+" Monate",t+" Monaten"],y:["ein Jahr","einem Jahr"],yy:[t+" Jahre",t+" Jahren"]};return e?i[n][0]:i[n][1]}(e.defineLocale||e.lang).call(e,"de",{months:"Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember".split("_"),monthsShort:"Jan._Febr._Mrz._Apr._Mai_Jun._Jul._Aug._Sept._Okt._Nov._Dez.".split("_"),weekdays:"Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag".split("_"),weekdaysShort:"So._Mo._Di._Mi._Do._Fr._Sa.".split("_"),weekdaysMin:"So_Mo_Di_Mi_Do_Fr_Sa".split("_"),longDateFormat:{LT:"HH:mm [Uhr]",L:"DD.MM.YYYY",LL:"D. MMMM YYYY",LLL:"D. MMMM YYYY LT",LLLL:"dddd, D. MMMM YYYY LT"},calendar:{sameDay:"[Heute um] LT",sameElse:"L",nextDay:"[Morgen um] LT",nextWeek:"dddd [um] LT",lastDay:"[Gestern um] LT",lastWeek:"[letzten] dddd [um] LT"},relativeTime:{future:"in %s",past:"vor %s",s:"ein paar Sekunden",m:n,mm:"%d Minuten",h:n,hh:"%d Stunden",d:n,dd:n,M:n,MM:n,y:n,yy:n},ordinal:"%d.",week:{dow:1,doy:4}}),t.fullCalendar.datepickerLang("de","de",{closeText:"Schließen",prevText:"<Zurück",nextText:"Vor>",currentText:"Heute",monthNames:["Januar","Februar","März","April","Mai","Juni","Juli","August","September","Oktober","November","Dezember"],monthNamesShort:["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"],dayNames:["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"],dayNamesShort:["So","Mo","Di","Mi","Do","Fr","Sa"],dayNamesMin:["So","Mo","Di","Mi","Do","Fr","Sa"],weekHeader:"KW",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),t.fullCalendar.lang("de",{defaultButtonText:{month:"Monat",week:"Woche",day:"Tag",list:"Terminübersicht"},allDayText:"Ganztägig",eventLimitText:function(t){return"+ weitere "+t}})}); -------------------------------------------------------------------------------- /client/vendor/scripts/lang/fullcalendar-es.js: -------------------------------------------------------------------------------- 1 | (function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){var n="ene._feb._mar._abr._may._jun._jul._ago._sep._oct._nov._dic.".split("_"),i="ene_feb_mar_abr_may_jun_jul_ago_sep_oct_nov_dic".split("_");(t.defineLocale||t.lang).call(t,"es",{months:"enero_febrero_marzo_abril_mayo_junio_julio_agosto_septiembre_octubre_noviembre_diciembre".split("_"),monthsShort:function(e,t){return/-MMM-/.test(t)?i[e.month()]:n[e.month()]},weekdays:"domingo_lunes_martes_miércoles_jueves_viernes_sábado".split("_"),weekdaysShort:"dom._lun._mar._mié._jue._vie._sáb.".split("_"),weekdaysMin:"Do_Lu_Ma_Mi_Ju_Vi_Sá".split("_"),longDateFormat:{LT:"H:mm",L:"DD/MM/YYYY",LL:"D [de] MMMM [del] YYYY",LLL:"D [de] MMMM [del] YYYY LT",LLLL:"dddd, D [de] MMMM [del] YYYY LT"},calendar:{sameDay:function(){return"[hoy a la"+(1!==this.hours()?"s":"")+"] LT"},nextDay:function(){return"[mañana a la"+(1!==this.hours()?"s":"")+"] LT"},nextWeek:function(){return"dddd [a la"+(1!==this.hours()?"s":"")+"] LT"},lastDay:function(){return"[ayer a la"+(1!==this.hours()?"s":"")+"] LT"},lastWeek:function(){return"[el] dddd [pasado a la"+(1!==this.hours()?"s":"")+"] LT"},sameElse:"L"},relativeTime:{future:"en %s",past:"hace %s",s:"unos segundos",m:"un minuto",mm:"%d minutos",h:"una hora",hh:"%d horas",d:"un día",dd:"%d días",M:"un mes",MM:"%d meses",y:"un año",yy:"%d años"},ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("es","es",{closeText:"Cerrar",prevText:"<Ant",nextText:"Sig>",currentText:"Hoy",monthNames:["enero","febrero","marzo","abril","mayo","junio","julio","agosto","septiembre","octubre","noviembre","diciembre"],monthNamesShort:["ene","feb","mar","abr","may","jun","jul","ago","sep","oct","nov","dic"],dayNames:["domingo","lunes","martes","miércoles","jueves","viernes","sábado"],dayNamesShort:["dom","lun","mar","mié","jue","vie","sáb"],dayNamesMin:["D","L","M","X","J","V","S"],weekHeader:"Sm",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("es",{defaultButtonText:{month:"Mes",week:"Semana",day:"Día",list:"Agenda"},allDayHtml:"Todo
el día",eventLimitText:"más"})}); -------------------------------------------------------------------------------- /client/vendor/scripts/lang/fullcalendar-fr.js: -------------------------------------------------------------------------------- 1 | (function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"fr",{months:"janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre".split("_"),monthsShort:"janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.".split("_"),weekdays:"dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi".split("_"),weekdaysShort:"dim._lun._mar._mer._jeu._ven._sam.".split("_"),weekdaysMin:"Di_Lu_Ma_Me_Je_Ve_Sa".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY LT",LLLL:"dddd D MMMM YYYY LT"},calendar:{sameDay:"[Aujourd'hui à] LT",nextDay:"[Demain à] LT",nextWeek:"dddd [à] LT",lastDay:"[Hier à] LT",lastWeek:"dddd [dernier à] LT",sameElse:"L"},relativeTime:{future:"dans %s",past:"il y a %s",s:"quelques secondes",m:"une minute",mm:"%d minutes",h:"une heure",hh:"%d heures",d:"un jour",dd:"%d jours",M:"un mois",MM:"%d mois",y:"un an",yy:"%d ans"},ordinal:function(e){return e+(1===e?"er":"")},week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("fr","fr",{closeText:"Fermer",prevText:"Précédent",nextText:"Suivant",currentText:"Aujourd'hui",monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],monthNamesShort:["janv.","févr.","mars","avril","mai","juin","juil.","août","sept.","oct.","nov.","déc."],dayNames:["dimanche","lundi","mardi","mercredi","jeudi","vendredi","samedi"],dayNamesShort:["dim.","lun.","mar.","mer.","jeu.","ven.","sam."],dayNamesMin:["D","L","M","M","J","V","S"],weekHeader:"Sem.",dateFormat:"dd/mm/yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("fr",{defaultButtonText:{month:"Mois",week:"Semaine",day:"Jour",list:"Mon planning"},allDayHtml:"Toute la
journée",eventLimitText:"en plus"})}); -------------------------------------------------------------------------------- /client/vendor/scripts/lang/fullcalendar-ko.js: -------------------------------------------------------------------------------- 1 | (function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"ko",{months:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),monthsShort:"1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월".split("_"),weekdays:"일요일_월요일_화요일_수요일_목요일_금요일_토요일".split("_"),weekdaysShort:"일_월_화_수_목_금_토".split("_"),weekdaysMin:"일_월_화_수_목_금_토".split("_"),longDateFormat:{LT:"A h시 mm분",L:"YYYY.MM.DD",LL:"YYYY년 MMMM D일",LLL:"YYYY년 MMMM D일 LT",LLLL:"YYYY년 MMMM D일 dddd LT"},meridiem:function(e){return 12>e?"오전":"오후"},calendar:{sameDay:"오늘 LT",nextDay:"내일 LT",nextWeek:"dddd LT",lastDay:"어제 LT",lastWeek:"지난주 dddd LT",sameElse:"L"},relativeTime:{future:"%s 후",past:"%s 전",s:"몇초",ss:"%d초",m:"일분",mm:"%d분",h:"한시간",hh:"%d시간",d:"하루",dd:"%d일",M:"한달",MM:"%d달",y:"일년",yy:"%d년"},ordinal:"%d일",meridiemParse:/(오전|오후)/,isPM:function(e){return"오후"===e}}),e.fullCalendar.datepickerLang("ko","ko",{closeText:"닫기",prevText:"이전달",nextText:"다음달",currentText:"오늘",monthNames:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],monthNamesShort:["1월","2월","3월","4월","5월","6월","7월","8월","9월","10월","11월","12월"],dayNames:["일요일","월요일","화요일","수요일","목요일","금요일","토요일"],dayNamesShort:["일","월","화","수","목","금","토"],dayNamesMin:["일","월","화","수","목","금","토"],weekHeader:"Wk",dateFormat:"yy-mm-dd",firstDay:0,isRTL:!1,showMonthAfterYear:!0,yearSuffix:"년"}),e.fullCalendar.lang("ko",{defaultButtonText:{month:"월",week:"주",day:"일",list:"일정목록"},allDayText:"종일",eventLimitText:"개"})}); -------------------------------------------------------------------------------- /client/vendor/scripts/lang/fullcalendar-pt.js: -------------------------------------------------------------------------------- 1 | (function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){(t.defineLocale||t.lang).call(t,"pt",{months:"janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro".split("_"),monthsShort:"jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez".split("_"),weekdays:"domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado".split("_"),weekdaysShort:"dom_seg_ter_qua_qui_sex_sáb".split("_"),weekdaysMin:"dom_2ª_3ª_4ª_5ª_6ª_sáb".split("_"),longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D [de] MMMM [de] YYYY",LLL:"D [de] MMMM [de] YYYY LT",LLLL:"dddd, D [de] MMMM [de] YYYY LT"},calendar:{sameDay:"[Hoje às] LT",nextDay:"[Amanhã às] LT",nextWeek:"dddd [às] LT",lastDay:"[Ontem às] LT",lastWeek:function(){return 0===this.day()||6===this.day()?"[Último] dddd [às] LT":"[Última] dddd [às] LT"},sameElse:"L"},relativeTime:{future:"em %s",past:"há %s",s:"segundos",m:"um minuto",mm:"%d minutos",h:"uma hora",hh:"%d horas",d:"um dia",dd:"%d dias",M:"um mês",MM:"%d meses",y:"um ano",yy:"%d anos"},ordinal:"%dº",week:{dow:1,doy:4}}),e.fullCalendar.datepickerLang("pt","pt",{closeText:"Fechar",prevText:"Anterior",nextText:"Seguinte",currentText:"Hoje",monthNames:["Janeiro","Fevereiro","Março","Abril","Maio","Junho","Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"],monthNamesShort:["Jan","Fev","Mar","Abr","Mai","Jun","Jul","Ago","Set","Out","Nov","Dez"],dayNames:["Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado"],dayNamesShort:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],dayNamesMin:["Dom","Seg","Ter","Qua","Qui","Sex","Sáb"],weekHeader:"Sem",dateFormat:"dd/mm/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("pt",{defaultButtonText:{month:"Mês",week:"Semana",day:"Dia",list:"Agenda"},allDayText:"Todo o dia",eventLimitText:"mais"})}); -------------------------------------------------------------------------------- /client/vendor/scripts/lang/fullcalendar-ro.js: -------------------------------------------------------------------------------- 1 | (function(e){"function"==typeof define&&define.amd?define(["jquery","moment"],e):e(jQuery,moment)})(function(e,t){function n(e,t,n){var i={mm:"minute",hh:"ore",dd:"zile",MM:"luni",yy:"ani"},a=" ";return(e%100>=20||e>=100&&0===e%100)&&(a=" de "),e+a+i[n]}(t.defineLocale||t.lang).call(t,"ro",{months:"ianuarie_februarie_martie_aprilie_mai_iunie_iulie_august_septembrie_octombrie_noiembrie_decembrie".split("_"),monthsShort:"ian._febr._mart._apr._mai_iun._iul._aug._sept._oct._nov._dec.".split("_"),weekdays:"duminică_luni_marți_miercuri_joi_vineri_sâmbătă".split("_"),weekdaysShort:"Dum_Lun_Mar_Mie_Joi_Vin_Sâm".split("_"),weekdaysMin:"Du_Lu_Ma_Mi_Jo_Vi_Sâ".split("_"),longDateFormat:{LT:"H:mm",L:"DD.MM.YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY H:mm",LLLL:"dddd, D MMMM YYYY H:mm"},calendar:{sameDay:"[azi la] LT",nextDay:"[mâine la] LT",nextWeek:"dddd [la] LT",lastDay:"[ieri la] LT",lastWeek:"[fosta] dddd [la] LT",sameElse:"L"},relativeTime:{future:"peste %s",past:"%s în urmă",s:"câteva secunde",m:"un minut",mm:n,h:"o oră",hh:n,d:"o zi",dd:n,M:"o lună",MM:n,y:"un an",yy:n},week:{dow:1,doy:7}}),e.fullCalendar.datepickerLang("ro","ro",{closeText:"Închide",prevText:"« Luna precedentă",nextText:"Luna următoare »",currentText:"Azi",monthNames:["Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie","Iulie","August","Septembrie","Octombrie","Noiembrie","Decembrie"],monthNamesShort:["Ian","Feb","Mar","Apr","Mai","Iun","Iul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Duminică","Luni","Marţi","Miercuri","Joi","Vineri","Sâmbătă"],dayNamesShort:["Dum","Lun","Mar","Mie","Joi","Vin","Sâm"],dayNamesMin:["Du","Lu","Ma","Mi","Jo","Vi","Sâ"],weekHeader:"Săpt",dateFormat:"dd.mm.yy",firstDay:1,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""}),e.fullCalendar.lang("ro",{defaultButtonText:{prev:"precedentă",next:"următoare",month:"Lună",week:"Săptămână",day:"Zi",list:"Agendă"},allDayText:"Toată ziua",eventLimitText:function(e){return"+alte "+e}})}); -------------------------------------------------------------------------------- /client/vendor/scripts/lang/moment-locale-fr.js: -------------------------------------------------------------------------------- 1 | // moment.js locale configuration 2 | // locale : french (fr) 3 | // author : John Fischer : https://github.com/jfroffice 4 | 5 | (function (factory) { 6 | if (typeof define === 'function' && define.amd) { 7 | define(['moment'], factory); // AMD 8 | } else if (typeof exports === 'object') { 9 | module.exports = factory(require('../moment')); // Node 10 | } else { 11 | factory(window.moment); // Browser global 12 | } 13 | }(function (moment) { 14 | return moment.defineLocale('fr', { 15 | months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'), 16 | monthsShort : 'janv._févr._mars_avr._mai_juin_juil._août_sept._oct._nov._déc.'.split('_'), 17 | weekdays : 'dimanche_lundi_mardi_mercredi_jeudi_vendredi_samedi'.split('_'), 18 | weekdaysShort : 'dim._lun._mar._mer._jeu._ven._sam.'.split('_'), 19 | weekdaysMin : 'Di_Lu_Ma_Me_Je_Ve_Sa'.split('_'), 20 | longDateFormat : { 21 | LT : 'HH:mm', 22 | L : 'DD/MM/YYYY', 23 | LL : 'D MMMM YYYY', 24 | LLL : 'D MMMM YYYY LT', 25 | LLLL : 'dddd D MMMM YYYY LT' 26 | }, 27 | calendar : { 28 | sameDay: '[Aujourd\'hui à] LT', 29 | nextDay: '[Demain à] LT', 30 | nextWeek: 'dddd [à] LT', 31 | lastDay: '[Hier à] LT', 32 | lastWeek: 'dddd [dernier à] LT', 33 | sameElse: 'L' 34 | }, 35 | relativeTime : { 36 | future : 'dans %s', 37 | past : 'il y a %s', 38 | s : 'quelques secondes', 39 | m : 'une minute', 40 | mm : '%d minutes', 41 | h : 'une heure', 42 | hh : '%d heures', 43 | d : 'un jour', 44 | dd : '%d jours', 45 | M : 'un mois', 46 | MM : '%d mois', 47 | y : 'un an', 48 | yy : '%d ans' 49 | }, 50 | ordinal : function (number) { 51 | return number + (number === 1 ? 'er' : ''); 52 | }, 53 | week : { 54 | dow : 1, // Monday is the first day of the week. 55 | doy : 4 // The week that contains Jan 4th is the first week of the year. 56 | } 57 | }); 58 | })); 59 | -------------------------------------------------------------------------------- /client/vendor/scripts/lang/moment-locale-ko.js: -------------------------------------------------------------------------------- 1 | //! moment.js locale configuration 2 | //! locale : korean (ko) 3 | //! 4 | //! authors 5 | //! 6 | //! - Kyungwook, Park : https://github.com/kyungw00k 7 | //! - Jeeeyul Lee 8 | 9 | ;(function (global, factory) { 10 | typeof exports === 'object' && typeof module !== 'undefined' 11 | && typeof require === 'function' ? factory(require('../moment')) : 12 | typeof define === 'function' && define.amd ? define(['moment'], factory) : 13 | factory(global.moment) 14 | }(this, function (moment) { 'use strict'; 15 | 16 | 17 | var ko = moment.defineLocale('ko', { 18 | months : '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'), 19 | monthsShort : '1월_2월_3월_4월_5월_6월_7월_8월_9월_10월_11월_12월'.split('_'), 20 | weekdays : '일요일_월요일_화요일_수요일_목요일_금요일_토요일'.split('_'), 21 | weekdaysShort : '일_월_화_수_목_금_토'.split('_'), 22 | weekdaysMin : '일_월_화_수_목_금_토'.split('_'), 23 | longDateFormat : { 24 | LT : 'A h시 m분', 25 | LTS : 'A h시 m분 s초', 26 | L : 'YYYY.MM.DD', 27 | LL : 'YYYY년 MMMM D일', 28 | LLL : 'YYYY년 MMMM D일 A h시 m분', 29 | LLLL : 'YYYY년 MMMM D일 dddd A h시 m분' 30 | }, 31 | calendar : { 32 | sameDay : '오늘 LT', 33 | nextDay : '내일 LT', 34 | nextWeek : 'dddd LT', 35 | lastDay : '어제 LT', 36 | lastWeek : '지난주 dddd LT', 37 | sameElse : 'L' 38 | }, 39 | relativeTime : { 40 | future : '%s 후', 41 | past : '%s 전', 42 | s : '몇초', 43 | ss : '%d초', 44 | m : '일분', 45 | mm : '%d분', 46 | h : '한시간', 47 | hh : '%d시간', 48 | d : '하루', 49 | dd : '%d일', 50 | M : '한달', 51 | MM : '%d달', 52 | y : '일년', 53 | yy : '%d년' 54 | }, 55 | ordinalParse : /\d{1,2}일/, 56 | ordinal : '%d일', 57 | meridiemParse : /오전|오후/, 58 | isPM : function (token) { 59 | return token === '오후'; 60 | }, 61 | meridiem : function (hour, minute, isUpper) { 62 | return hour < 12 ? '오전' : '오후'; 63 | } 64 | }); 65 | 66 | return ko; 67 | 68 | })); -------------------------------------------------------------------------------- /client/vendor/scripts/lang/moment-locale-pt.js: -------------------------------------------------------------------------------- 1 | // moment.js locale configuration 2 | // locale : portuguese (pt) 3 | // author : Jefferson : https://github.com/jalex79 4 | 5 | (function (factory) { 6 | if (typeof define === 'function' && define.amd) { 7 | define(['moment'], factory); // AMD 8 | } else if (typeof exports === 'object') { 9 | module.exports = factory(require('../moment')); // Node 10 | } else { 11 | factory(window.moment); // Browser global 12 | } 13 | }(function (moment) { 14 | return moment.defineLocale('pt', { 15 | months : 'janeiro_fevereiro_março_abril_maio_junho_julho_agosto_setembro_outubro_novembro_dezembro'.split('_'), 16 | monthsShort : 'jan_fev_mar_abr_mai_jun_jul_ago_set_out_nov_dez'.split('_'), 17 | weekdays : 'domingo_segunda-feira_terça-feira_quarta-feira_quinta-feira_sexta-feira_sábado'.split('_'), 18 | weekdaysShort : 'dom_seg_ter_qua_qui_sex_sáb'.split('_'), 19 | weekdaysMin : 'dom_2ª_3ª_4ª_5ª_6ª_sáb'.split('_'), 20 | longDateFormat : { 21 | LT : 'HH:mm', 22 | L : 'DD/MM/YYYY', 23 | LL : 'D [de] MMMM [de] YYYY', 24 | LLL : 'D [de] MMMM [de] YYYY LT', 25 | LLLL : 'dddd, D [de] MMMM [de] YYYY LT' 26 | }, 27 | calendar : { 28 | sameDay: '[Hoje às] LT', 29 | nextDay: '[Amanhã às] LT', 30 | nextWeek: 'dddd [às] LT', 31 | lastDay: '[Ontem às] LT', 32 | lastWeek: function () { 33 | return (this.day() === 0 || this.day() === 6) ? 34 | '[Último] dddd [às] LT' : // Saturday + Sunday 35 | '[Última] dddd [às] LT'; // Monday - Friday 36 | }, 37 | sameElse: 'L' 38 | }, 39 | relativeTime : { 40 | future : 'em %s', 41 | past : 'há %s', 42 | s : 'segundos', 43 | m : 'um minuto', 44 | mm : '%d minutos', 45 | h : 'uma hora', 46 | hh : '%d horas', 47 | d : 'um dia', 48 | dd : '%d dias', 49 | M : 'um mês', 50 | MM : '%d meses', 51 | y : 'um ano', 52 | yy : '%d anos' 53 | }, 54 | ordinal : '%dº', 55 | week : { 56 | dow : 1, // Monday is the first day of the week. 57 | doy : 4 // The week that contains Jan 4th is the first week of the year. 58 | } 59 | }); 60 | })); 61 | -------------------------------------------------------------------------------- /client/vendor/styles/helpers.css: -------------------------------------------------------------------------------- 1 | .chromeframe { margin: 0.2em 0; background: #ccc; color: #000; padding: 0.2em 0; } 2 | .ir { display: block; border: 0; text-indent: -999em; overflow: hidden; background-color: transparent; background-repeat: no-repeat; text-align: left; direction: ltr; *line-height: 0; } 3 | .ir br { display: none; } 4 | .hidden { display: none !important; visibility: hidden; } 5 | .visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } 6 | .visuallyhidden.focusable:active, .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; } 7 | .invisible { visibility: hidden; } 8 | .clearfix:before, .clearfix:after { content: ""; display: table; } 9 | .clearfix:after { clear: both; } 10 | .clearfix { *zoom: 1; } 11 | 12 | @media print { 13 | * { background: transparent !important; color: black !important; box-shadow:none !important; text-shadow: none !important; filter:none !important; -ms-filter: none !important; } 14 | a, a:visited { text-decoration: underline; } 15 | a[href]:after { content: " (" attr(href) ")"; } 16 | abbr[title]:after { content: " (" attr(title) ")"; } 17 | .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } 18 | pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } 19 | thead { display: table-header-group; } 20 | tr, img { page-break-inside: avoid; } 21 | img { max-width: 100% !important; } 22 | @page { margin: 0.5cm; } 23 | p, h2, h3 { orphans: 3; widows: 3; } 24 | h2, h3 { page-break-after: avoid; } 25 | } 26 | -------------------------------------------------------------------------------- /client/vendor/styles/jquery.tagit.css: -------------------------------------------------------------------------------- 1 | ul.tagit { 2 | padding: 1px 5px; 3 | overflow: auto; 4 | margin-left: inherit; /* usually we don't want the regular ul margins. */ 5 | margin-right: inherit; 6 | } 7 | ul.tagit li { 8 | display: block; 9 | float: left; 10 | margin: 2px 5px 2px 0; 11 | } 12 | ul.tagit li.tagit-choice { 13 | position: relative; 14 | line-height: inherit; 15 | } 16 | input.tagit-hidden-field { 17 | display: none; 18 | } 19 | ul.tagit li.tagit-choice-read-only { 20 | padding: .2em .5em .2em .5em; 21 | } 22 | 23 | ul.tagit li.tagit-choice-editable { 24 | padding: .2em 18px .2em .5em; 25 | } 26 | 27 | ul.tagit li.tagit-new { 28 | padding: .25em 4px .25em 0; 29 | } 30 | 31 | ul.tagit li.tagit-choice a.tagit-label { 32 | cursor: pointer; 33 | text-decoration: none; 34 | } 35 | ul.tagit li.tagit-choice .tagit-close { 36 | cursor: pointer; 37 | position: absolute; 38 | right: .1em; 39 | top: 50%; 40 | margin-top: -8px; 41 | line-height: 17px; 42 | } 43 | 44 | /* used for some custom themes that don't need image icons */ 45 | ul.tagit li.tagit-choice .tagit-close .text-icon { 46 | display: none; 47 | } 48 | 49 | ul.tagit li.tagit-choice input { 50 | display: block; 51 | float: left; 52 | margin: 2px 5px 2px 0; 53 | } 54 | ul.tagit input[type="text"] { 55 | -moz-box-sizing: border-box; 56 | -webkit-box-sizing: border-box; 57 | box-sizing: border-box; 58 | 59 | -moz-box-shadow: none; 60 | -webkit-box-shadow: none; 61 | box-shadow: none; 62 | 63 | border: none; 64 | margin: 0; 65 | padding: 0; 66 | width: inherit; 67 | background-color: inherit; 68 | outline: none; 69 | } 70 | -------------------------------------------------------------------------------- /coffeelint.json: -------------------------------------------------------------------------------- 1 | { 2 | "indentation" : { 3 | "value" : 4, 4 | "level" : "error" 5 | }, 6 | "no_empty_param_list": { 7 | "level": "warn" 8 | }, 9 | "prefer_english_operator": { 10 | "level": "warn" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server.coffee: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | 3 | start = (port, callback) -> 4 | require('americano').start 5 | name: 'Calendar' 6 | port: port 7 | host: process.env.HOST or "0.0.0.0" 8 | root: __dirname 9 | , (err, app, server) -> 10 | cozydb = require 'cozydb' 11 | User = require './server/models/user' 12 | localization = require './server/libs/localization_manager' 13 | Realtimer = require 'cozy-realtime-adapter' 14 | realtime = Realtimer server, ['event.*', 'contact.*', 'sharing.*'] 15 | realtime.on 'user.*', -> User.updateUser() 16 | 17 | # Update localization engine if the language changes. 18 | realtime.on 'cozyinstance.*', -> 19 | cozydb.api.getCozyInstance (err, instance) -> 20 | locale = instance?.locale or null 21 | localization.updateLocale locale 22 | 23 | User.updateUser (err) -> localization.initialize -> 24 | # Migration scripts. Relies on User. 25 | Event = require './server/models/event' 26 | Alarm = require './server/models/alarm' 27 | Event.migrateAll -> Alarm.migrateAll -> 28 | 29 | Event.initializeData (err2, event) -> 30 | callback err, app, server 31 | 32 | 33 | if not module.parent 34 | port = process.env.PORT or 9113 35 | start port, (err) -> 36 | if err 37 | console.log "Initialization failed, not starting" 38 | console.log err.stack 39 | process.exit 1 40 | else 41 | module.exports = start 42 | -------------------------------------------------------------------------------- /server/config.coffee: -------------------------------------------------------------------------------- 1 | americano = require 'americano' 2 | fs = require 'fs' 3 | path = require 'path' 4 | expressValidator = require 'express-validator' 5 | 6 | publicPath = "#{__dirname}/../client/public" 7 | staticMiddleware = americano.static publicPath, maxAge: 86400000 8 | publicStatic = (req, res, next) -> 9 | 10 | # Allows assets to be loaded from any route 11 | detectAssets = /\/(stylesheets|javascripts|images|fonts)+\/(.+)$/ 12 | assetsMatched = detectAssets.exec req.url 13 | 14 | if assetsMatched? 15 | req.url = assetsMatched[0] 16 | 17 | staticMiddleware req, res, (err) -> next err 18 | 19 | viewsDir = path.resolve __dirname, '..', 'client', 'public' 20 | useBuildView = fs.existsSync path.resolve viewsDir, 'index.js' 21 | 22 | module.exports = 23 | 24 | common: 25 | use: [ 26 | staticMiddleware 27 | publicStatic 28 | americano.bodyParser keepExtensions: true 29 | expressValidator 30 | customValidators: 31 | isString: (value) -> return typeof(value) is 'string' 32 | ] 33 | useAfter: [ 34 | americano.errorHandler 35 | dumpExceptions: true 36 | showStack: true 37 | ] 38 | set: 39 | views: viewsDir 40 | 'view engine': if useBuildView then 'js' else 'jade' 41 | 42 | engine: 43 | js: (path, locales, callback) -> 44 | callback null, require(path)(locales) 45 | 46 | development: [ 47 | americano.logger 'dev' 48 | ] 49 | 50 | production: [ 51 | americano.logger 'short' 52 | ] 53 | 54 | plugins: [ 55 | 'cozydb' 56 | ] 57 | -------------------------------------------------------------------------------- /server/controllers/contacts.coffee: -------------------------------------------------------------------------------- 1 | {SimpleController} = require 'cozydb' 2 | ContactsController = new SimpleController 3 | model: require('../models/contact') 4 | reqProp: 'contact' 5 | reqParamID: 'contactid' 6 | 7 | ContactsController.sendSmall = (req, res, next) -> 8 | res.send req.contact.asNameAndEmails() 9 | 10 | module.exports = ContactsController 11 | -------------------------------------------------------------------------------- /server/controllers/routes.coffee: -------------------------------------------------------------------------------- 1 | tags = require './tags' 2 | events = require './events' 3 | index = require './index' 4 | ical = require './ical' 5 | contacts = require './contacts' 6 | sharings = require './sharings' 7 | settings = require './settings' 8 | utils = require '../helpers/utils' 9 | 10 | 11 | module.exports = 12 | 13 | '': 14 | get: index.index 15 | 16 | # Params management 17 | 'settings': 18 | put: settings.update 19 | 20 | # Tag management 21 | 'tags': 22 | get: tags.all 23 | post: tags.create 24 | 'tagid': 25 | param: tags.fetch 26 | 'tags/:tagid': 27 | get: tags.read 28 | put: tags.update 29 | delete : tags.delete 30 | 31 | # Event management 32 | 'eventid': 33 | param: events.fetch 34 | 'events': 35 | get: events.all 36 | post: events.create 37 | 'events/bulk': 38 | post: events.createBulk 39 | 'events/:year/:month': 40 | get: events.monthEvents 41 | 'events/rename-calendar': 42 | post: events.bulkCalendarRename 43 | 'events/delete': 44 | post: events.bulkDelete 45 | 'events/:eventid': 46 | get: events.read 47 | put: events.update 48 | delete: events.delete 49 | 'events/:eventid/:name.ics': 50 | get: events.ical 51 | 'public/events/:eventid/:name.ics': 52 | get: events.publicIcal 53 | 'public/events/:publiceventid': 54 | get: events.public 55 | 56 | # ICal 57 | 'export/:calendarid.ics': 58 | get : ical.export 59 | 'exportzip/:ids': 60 | get : ical.zipExport 61 | 'import/ical': 62 | post : ical.import 63 | #'public/calendars/:calendarname.ics': 64 | #get : ical.calendar 65 | 66 | # Contacts 67 | 'contacts': 68 | get: contacts.listAll 69 | 'contacts/:contactid.jpg': 70 | get: contacts.sendAttachment(filename: 'picture') 71 | 'contacts/:contactid': 72 | get: [contacts.find, contacts.sendSmall] 73 | 74 | # log client errors 75 | 'log': 76 | post: index.logClient 77 | 78 | # Sharing 79 | 'sharingid': 80 | param: sharings.fetch 81 | 'sharings': 82 | get: sharings.all 83 | 'sharings/:sharingid': 84 | get: sharings.read 85 | 'sharing/accept': 86 | post: sharings.accept 87 | 'sharing/refuse': 88 | post: sharings.refuse 89 | 90 | # Data management 91 | 'data/exist/:id/': 92 | get: utils.exist 93 | -------------------------------------------------------------------------------- /server/controllers/settings.coffee: -------------------------------------------------------------------------------- 1 | Settings = require '../models/settings' 2 | 3 | 4 | module.exports = 5 | 6 | # Get the setting object and update it with given values. Currently, only 7 | # the default calendar parameter is handled. 8 | update: (req, res, next) -> 9 | req.checkBody 10 | defaultCalendar: 11 | notEmpty: true 12 | isString: true 13 | errorMessage: 'Invalid calendar name' 14 | req.getValidationResult().then (result) -> 15 | if not result.isEmpty() 16 | responseBody = 17 | success: false 18 | error: result.array() 19 | return res.status(400).send(responseBody) 20 | 21 | newSettings = 22 | defaultCalendar: req.body.defaultCalendar 23 | 24 | Settings.updateCalAppSettings newSettings, (err, calSettings) -> 25 | return next err if err 26 | 27 | res.send 28 | success: true 29 | settings: calSettings 30 | -------------------------------------------------------------------------------- /server/controllers/sharings.coffee: -------------------------------------------------------------------------------- 1 | Sharing = require '../models/sharing' 2 | 3 | module.exports.all = (req, res) -> 4 | data = req.query 5 | if data.shareID 6 | Sharing.byShareID data.shareID, (err, sharings) -> 7 | if err 8 | res.status(500).send error: "Server error occured" 9 | if not sharings?.length 10 | res.status(404).send error: "Sharing not found" 11 | else 12 | res.send sharings[0] 13 | else 14 | Sharing.all (err, sharings)-> 15 | if err 16 | res.status(500).send error: err 17 | if not sharings?.length 18 | res.status(404).send error: "Sharing not found" 19 | else 20 | res.send sharings[0] 21 | 22 | 23 | module.exports.fetch = (req, res, next, id) -> 24 | Sharing.find id, (err, sharing) -> 25 | if err 26 | res.status(500).send error: "Server error occured" 27 | else if not sharing 28 | res.status(404).send error: "Sharing not found" 29 | else 30 | req.sharing = sharing 31 | next() 32 | 33 | 34 | module.exports.read = (req, res) -> 35 | res.send req.sharing 36 | 37 | 38 | module.exports.accept = (req, res) -> 39 | data = req.body 40 | id = data.id 41 | 42 | Sharing.accept id, (err, response) -> 43 | if err 44 | res.status(500).send error: err 45 | else 46 | res.send data 47 | 48 | 49 | module.exports.refuse = (req, res) -> 50 | data = req.body 51 | id = data.id 52 | 53 | Sharing.refuse id, (err, response) -> 54 | if err 55 | res.status(500).send error: err 56 | else 57 | res.status(204).send data 58 | -------------------------------------------------------------------------------- /server/controllers/tags.coffee: -------------------------------------------------------------------------------- 1 | Tag = require '../models/tag' 2 | 3 | 4 | module.exports.fetch = (req, res, next, id) -> 5 | Tag.find id, (err, tag) -> 6 | if err or not tag 7 | res.send error: "Tag not found", 404 8 | else 9 | req.tag = tag 10 | next() 11 | 12 | 13 | module.exports.all = (req, res, next) -> 14 | Tag.byName (err, results) -> 15 | return next err if err 16 | 17 | res.status(200).send results 18 | 19 | 20 | module.exports.read = (req, res) -> 21 | res.send req.tag 22 | 23 | 24 | module.exports.create = (req, res) -> 25 | data = req.body 26 | Tag.getOrCreateByName data, (err, tag) -> 27 | if err? 28 | res.send error: "Server error while creating tag.", 500 29 | else 30 | res.status(201).send tag 31 | 32 | 33 | module.exports.update = (req, res) -> 34 | data = req.body 35 | req.tag.updateAttributes data, (err, tag) -> 36 | if err? 37 | res.send error: "Server error while saving tag", 500 38 | else 39 | res.status(200).send tag 40 | 41 | 42 | module.exports.delete = (req, res) -> 43 | req.tag.destroy (err) -> 44 | if err? 45 | res.status(500).send error: "Server error while deleting the tag" 46 | else 47 | res.status(200).send success: true 48 | 49 | -------------------------------------------------------------------------------- /server/helpers/client.coffee: -------------------------------------------------------------------------------- 1 | # A simple client to be able to send request directly to the data-system. 2 | 3 | request = require 'request-json' 4 | 5 | 6 | # The data-system port is configurable. 7 | DS_PORT = process.env.DS_PORT or 9101 8 | client = request.createClient "http://localhost:#{DS_PORT}" 9 | 10 | if process.env.NODE_ENV in ["production", "test"] 11 | client.setBasicAuth process.env.NAME, process.env.TOKEN 12 | else 13 | client.setBasicAuth Math.random().toString(36), "token" 14 | 15 | 16 | module.exports = client 17 | 18 | -------------------------------------------------------------------------------- /server/helpers/utils.coffee: -------------------------------------------------------------------------------- 1 | client = require './client' 2 | log = require('printit') 3 | prefix: "utils" 4 | date : true 5 | 6 | 7 | module.exports.exist = (req, res) -> 8 | id = req.params.id 9 | 10 | client.get "data/exist/#{id}/", (error, response, body) -> 11 | if error 12 | res.status(500).send error 13 | else if not body? or not body.exist? 14 | res.status(500).send new Error "Data system returned invalid data." 15 | else 16 | res.status(200).send body.exist 17 | 18 | -------------------------------------------------------------------------------- /server/mails/assets/cozy-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cozy/cozy-calendar/786b1f6070cc3382db72dc473b45a8abf496fe16/server/mails/assets/cozy-logo.png -------------------------------------------------------------------------------- /server/mails/en/mail_delete.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html(style="height:100%;") 3 | head 4 | meta(name="viewport", content="width=device-width") 5 | meta(http-equiv="Content-Type", content="text/html; charset=UTF-8") 6 | 7 | body(style="height:100%; margin:0;") 8 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; height: 100%; padding:0; margin:0; background-color: #F4F4F4; font-family: Helvetica,Arial,Verdana,sans-serif; color: #333; font-size: 0.9em; line-height: 1.6;") 9 | tr 10 | td(align="center", valign="top") 11 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; padding: 2em 0.5em;") 12 | tr 13 | td 14 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; background-color: #FFF; border-radius: 8px; border: 1px solid #DDD;") 15 | tr 16 | th(style="border-radius: 8px 8px 0 0; border-bottom: 1px solid #DDD;") 17 | img(src="cid:cozy-logo" width="63" height="48" style="padding: 12px 0; margin: auto;") 18 | tr 19 | // TODO: add user's email inside the brackets #{displayEmail} 20 | td(style="padding: 1.5em;") #{displayName} (#{displayEmail}) canceled the following event: 21 | tr 22 | td(style="padding: 0 1.5em; width: 100%; font-size: 1.4em; font-weight: bold; text-align:center;") 23 | | #{description} 24 | tr 25 | td(style="padding: 0 1.5em 2em; width: 100%; font-size: 1.2em; text-align:center;") 26 | | On #{date} 27 | if place != null && place.length > 0 28 | | at #{place} 29 | tr(style="padding-top: 10px; text-align: center; font-size: 0.85em; font-style: italic; color: #999;") 30 | td 31 | p 32 | | Sent from  33 | a(href="http://cozy.io", style="color: #34A6FF; text-decoration: none;") #{displayName}'s Cozy 34 | | . 35 | -------------------------------------------------------------------------------- /server/mails/fr/mail_delete.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html(style="height:100%;") 3 | head 4 | meta(name="viewport", content="width=device-width") 5 | meta(http-equiv="Content-Type", content="text/html; charset=UTF-8") 6 | 7 | body(style="height:100%; margin:0;") 8 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; height: 100%; padding:0; margin:0; background-color: #F4F4F4; font-family: Helvetica,Arial,Verdana,sans-serif; color: #333; font-size: 0.9em; line-height: 1.6;") 9 | tr 10 | td(align="center", valign="top") 11 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; padding: 2em 0.5em;") 12 | tr 13 | td 14 | table(cellpadding="0" cellspacing="0" border="0", style="width: 100%; max-width: 600px; background-color: #FFF; border-radius: 8px; border: 1px solid #DDD;") 15 | tr 16 | th(style="border-radius: 8px 8px 0 0; border-bottom: 1px solid #DDD;") 17 | img(src="cid:cozy-logo" width="63" height="48" style="padding: 12px 0; margin: auto;") 18 | tr 19 | td(style="padding: 1.5em;") #{displayName} (#{displayEmail}) a annulé l'événement suivant : 20 | tr 21 | td(style="padding: 0 1.5em; width: 100%; font-size: 1.4em; font-weight: bold; text-align:center;") 22 | | #{description} 23 | tr 24 | td(style="padding: 0 1.5em 2em; width: 100%; font-size: 1.2em; text-align:center;") 25 | | Le #{date} 26 | if place != null && place.length > 0 27 | | à #{place} 28 | tr(style="padding-top: 10px; text-align: center; font-size: 0.85em; font-style: italic; color: #999;") 29 | td 30 | p 31 | | Envoyé depuis le 32 | a(href="http://cozy.io", style="color: #34A6FF; text-decoration: none;") Cozy de #{displayName} 33 | | . 34 | -------------------------------------------------------------------------------- /server/models/contact.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | 3 | module.exports = Contact = cozydb.getModel 'Contact', 4 | fn : String 5 | n : String 6 | datapoints : [Object] 7 | revision : String 8 | note : String 9 | tags : [String] 10 | accounts : String 11 | title : String 12 | org : String 13 | bday : String 14 | url : String 15 | initials : String 16 | sortedName : String 17 | ref : String 18 | _attachments : Object 19 | 20 | 21 | Contact::asNameAndEmails = -> 22 | name = @fn or @n?.split(';')[0..1].join ' ' 23 | emails = @datapoints?.filter (dp) -> dp.name is 'email' 24 | 25 | # XXX What if several Cozy instances are linked to one user? 26 | cozy = @datapoints?.filter (dp) -> ((dp.name is 'other') and 27 | (dp.type.toLowerCase() is 'cozy')) or ((dp.name is 'url') and 28 | (dp.mediatype?.search 'cozy' isnt -1)) 29 | 30 | return simple = 31 | id : @id 32 | name : name or '?' 33 | emails : emails or [] 34 | hasPicture : @_attachments?.picture? 35 | cozy : cozy or null 36 | -------------------------------------------------------------------------------- /server/models/requests.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | 3 | tagsView = 4 | map: (doc) -> 5 | if not doc.shareID 6 | doc.tags?.forEach? (tag, index) -> 7 | type = if index is 0 then 'calendar' else 'tag' 8 | emit [type, tag], true 9 | reduce: "_count" 10 | 11 | 12 | module.exports = 13 | 14 | tag: 15 | byName : cozydb.defaultRequests.by 'name' 16 | 17 | alarm: 18 | all : cozydb.defaultRequests.all 19 | byDate : (doc) -> emit new Date(doc.trigg), doc 20 | tags : tagsView 21 | 22 | event: 23 | all : cozydb.defaultRequests.all 24 | byDate : (doc) -> emit new Date(doc.start), doc 25 | reccuring : (doc) -> 26 | emit doc.id, doc if doc.rrule? and doc.rrule.length > 0 27 | tags : tagsView 28 | byCalendar: cozydb.defaultRequests.by 'tags[0]' 29 | 30 | contact: 31 | all : cozydb.defaultRequests.all 32 | 33 | webdavaccount: 34 | all : cozydb.defaultRequests.all 35 | 36 | sharing: 37 | all : cozydb.defaultRequests.all 38 | pendingBySharedDocType: (doc) -> 39 | if not doc.accepted && not doc.targets && doc.rules 40 | doc.rules.forEach (rule) -> 41 | emit(rule.docType, doc) 42 | byShareID: cozydb.defaultRequests.by 'shareID' 43 | 44 | settings: 45 | all : cozydb.defaultRequests.all 46 | -------------------------------------------------------------------------------- /server/models/settings.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | log = require('printit') 3 | prefix: 'tag:settings' 4 | 5 | # Object used to store the user settings such as the default calendar used for 6 | # event creation. 7 | module.exports = Settings = cozydb.getModel 'Settings', 8 | app: String 9 | defaultCalendar: String 10 | 11 | # Return the settings object for the Calendar application. It creates it if it 12 | # doesn't exist. 13 | Settings.getCalAppSettings = (callback) -> 14 | Settings.request 'all', (err, allSettings) -> 15 | return callback err if err 16 | 17 | allSettings = allSettings.filter (settings) -> 18 | settings.app is 'calendar' 19 | 20 | if allSettings.length is 0 21 | calendarSettings = 22 | app: 'calendar' 23 | defaultCalendar: '' 24 | Settings.create calendarSettings, (err, calendarSettings) -> 25 | return callback err if err 26 | callback null, calendarSettings 27 | 28 | else 29 | callback null, allSettings[0] 30 | 31 | # Change calendar settings values. 32 | Settings.updateCalAppSettings = (data, callback) -> 33 | newSettings = 34 | defaultCalendar: data.defaultCalendar 35 | 36 | Settings.getCalAppSettings (err, calendarSettings) -> 37 | return callback(err) if err 38 | 39 | calendarSettings.updateAttributes( 40 | newSettings, 41 | (err, calendarSettings) -> 42 | return callback(err) if err 43 | callback(null, calendarSettings) 44 | ) 45 | 46 | -------------------------------------------------------------------------------- /server/models/sharing.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | 3 | 4 | module.exports = Sharing = cozydb.getModel 'Sharing', 5 | docType: String 6 | sharerName: String 7 | sharerUrl: String 8 | shareID: String 9 | desc: String 10 | rules: [Object] 11 | # id: String 12 | # docType: String 13 | continuous: Boolean 14 | targets: [Object] 15 | # url: String 16 | # pretoken: String 17 | accepted: Boolean 18 | 19 | 20 | Sharing.pendingBySharedDocType = (docType, callback) -> 21 | Sharing.request 'pendingBySharedDocType', key: docType, callback 22 | 23 | 24 | Sharing.byShareID = (shareID, callback) -> 25 | Sharing.request 'byShareID', key: shareID, callback 26 | 27 | 28 | sendAnswer = (data, callback) -> 29 | cozydb.api.answerSharing data, (err, body) -> 30 | if err 31 | callback err, body 32 | else 33 | callback null, body 34 | 35 | 36 | Sharing.accept = (id, callback) -> 37 | sendAnswer {id: id, accepted: true}, callback 38 | 39 | 40 | Sharing.refuse = (id, callback) -> 41 | sendAnswer {id: id, accepted: false}, callback 42 | -------------------------------------------------------------------------------- /server/models/tag.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | log = require('printit') 3 | prefix: 'tag:model' 4 | 5 | module.exports = Tag = cozydb.getModel 'Tag', 6 | name : type: String 7 | color : type: String 8 | 9 | Tag.byName = (options, callback) -> 10 | Tag.request 'byName', options, callback 11 | 12 | Tag.getOrCreateByName = (data, callback) -> 13 | # Name is a primary key. 14 | Tag.byName key: data.name, (err, tags)-> 15 | if err 16 | log.error err 17 | Tag.create data, callback 18 | 19 | else if tags.length is 0 20 | Tag.create data, callback 21 | 22 | else # Tag already exists. 23 | callback null, tags[0] 24 | -------------------------------------------------------------------------------- /server/models/user.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | 3 | module.exports = User = {} 4 | 5 | User.updateUser = (callback) -> 6 | cozydb.api.getCozyUser (err, user) -> 7 | if err or not user 8 | console.log err if err 9 | User.timezone = 'Europe/Paris' 10 | User.email = '' 11 | else 12 | User.timezone = user.timezone or "Europe/Paris" 13 | User.email = user.email 14 | callback?() 15 | 16 | 17 | User.getUserInfos = (callback) -> 18 | cozydb.api.getCozyUser (err, user) -> 19 | return callback err if err 20 | 21 | name = if user.public_name?.length 22 | user.public_name 23 | else 24 | # - Get first part of mail 25 | # - Replace '.' and '-' with space 26 | # - Split on spaces 27 | # - Capitalize string 28 | words = user.email.split('@')[0] 29 | .replace /([\.-]+)/g, ' ' 30 | .split ' ' 31 | words.map((word) -> word[0].toUpperCase() + word[1...]).join ' ' 32 | 33 | callback null, 34 | name: name 35 | email: user.email 36 | -------------------------------------------------------------------------------- /server/models/webdavaccount.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | 3 | # Object required to store the automatically generated webdav credentials. 4 | module.exports = WebDAVAccount = cozydb.getModel 'WebDAVAccount', 5 | id: String 6 | login: String 7 | token: String 8 | password: String # old token, kept for retrocompatiblity 9 | ctag: Number # used to keep track of changes in the calendar 10 | cardctag: Number # used to keep track of changes in the addressbook 11 | -------------------------------------------------------------------------------- /server/share/share_handler.coffee: -------------------------------------------------------------------------------- 1 | cozydb = require 'cozydb' 2 | 3 | # Polyfill to provide the function `find` if it is not already implemented 4 | # See : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ 5 | # Global_Objects/Array/find 6 | unless Array.prototype.find 7 | Array.prototype.find = (predicate) -> 8 | if this is null 9 | throw new TypeError "Find call on null or undefined." 10 | 11 | if (typeof predicate) isnt "function" 12 | throw new TypeError "Predicate must be a function." 13 | 14 | list = Object this 15 | length = list.length >>> 0 16 | thisArg = arguments[1] 17 | i = 0 18 | value = list[i] 19 | 20 | while value 21 | if predicate.call thisArg, value, i, list 22 | return value 23 | value = list[++i] 24 | 25 | return undefined 26 | 27 | 28 | module.exports.sendShareInvitations = (event, callback) -> 29 | guests = event.toJSON().attendees 30 | needSaving = false 31 | 32 | # Before proceeding any further we check here that we have to share the 33 | # event with at least one guest. 34 | hasGuestToShare = guests.find (guest) -> 35 | return (guest.isSharedWithCozy and 36 | (guest.status is 'INVITATION-NOT-SENT')) 37 | 38 | # If we haven't found a single guest to share the event with, we stop here. 39 | unless hasGuestToShare 40 | return callback() 41 | 42 | # The format of the req.body to send must match: 43 | # 44 | # desc : the sharing description 45 | # rules : [{docType: "event", id: id_of_the_event}] 46 | # targets : [ 47 | # {recipientUrl: guest-1-url-cozy}, 48 | # {recipientUrl: guest-2-url-cozy}, ... 49 | # ] 50 | # continuous: true 51 | data = 52 | desc : event.description 53 | rules : [{id: event.id, docType: 'event'}] 54 | targets : [] 55 | continuous : true 56 | 57 | # only process relevant guests 58 | guests.forEach (guest) -> 59 | if (guest.status is 'INVITATION-NOT-SENT') and guest.isSharedWithCozy 60 | data.targets.push recipientUrl: guest.cozy 61 | guest.status = "NEEDS-ACTION" 62 | needSaving = true 63 | 64 | # Send the request to the datasystem 65 | cozydb.api.createSharing data, (err, body) -> 66 | if err? 67 | callback err 68 | else unless needSaving 69 | callback() 70 | else 71 | event.updateAttributes attendees: guests, callback 72 | 73 | 74 | -------------------------------------------------------------------------------- /test/apple.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | VERSION:2.0 3 | PRODID:-//Apple Inc.//iCal 4.0.4//EN 4 | CALSCALE:GREGORIAN 5 | BEGIN:VEVENT 6 | TRANSP:OPAQUE 7 | DTEND;TZID=Europe/Paris:20130726T132400 8 | X-APPLE-DONTSCHEDULE:TRUE 9 | UID:E06F3512-5065-476F-9A78-9471819EA40F 10 | DTSTAMP:20130729T145702Z 11 | LOCATION:Un autre lieu 12 | SEQUENCE:4 13 | X-APPLE-EWS-BUSYSTATUS:BUSY 14 | SUMMARY:Un autre événément 15 | DTSTART;TZID=Europe/Paris:20130726T122400 16 | CREATED:20130729T145649Z 17 | END:VEVENT 18 | BEGIN:VEVENT 19 | TRANSP:OPAQUE 20 | DTEND;TZID=Europe/Paris:20130718T180000 21 | X-APPLE-DONTSCHEDULE:TRUE 22 | UID:B2C20F1D-D6E3-4B46-BEAB-A85AD0C536AE 23 | DTSTAMP:20130729T145559Z 24 | LOCATION:Un lieu au hasard 25 | SEQUENCE:3 26 | X-APPLE-EWS-BUSYSTATUS:BUSY 27 | SUMMARY:Titre de l'événement 28 | DTSTART;TZID=Europe/Paris:20130718T170000 29 | CREATED:20 30 | END:VCALENDAR 31 | -------------------------------------------------------------------------------- /test/calendar.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | VERSION:2.0 3 | PRODID:-//Cozy Cloud//NONSGML Cozy Agenda//EN 4 | BEGIN:VTODO 5 | UID:aeba6310b07a22a72423b2b11f325cd2 6 | DTSTAMP:20141110T084800Z 7 | DTSTART:20141108T090000Z 8 | SUMMARY:Cozy alarm! 9 | BEGIN:VALARM 10 | ACTION:DISPLAY 11 | TRIGGER:PT0M 12 | DESCRIPTION:Cozy alarm! 13 | END:VALARM 14 | END:VTODO 15 | BEGIN:VEVENT 16 | UID:aeba6310b07a22a72423b2b11f323d9b 17 | DTSTAMP:20141110T084800Z 18 | DTSTART:20141105T140000Z 19 | DTEND:20141105T150000Z 20 | LOCATION:Dentist office 21 | SUMMARY:Dentist 22 | BEGIN:VALARM 23 | ACTION:DISPLAY 24 | TRIGGER:-PT15M 25 | DESCRIPTION:Dentist 26 | END:VALARM 27 | BEGIN:VALARM 28 | ACTION:EMAIL 29 | TRIGGER:-P1D 30 | ATTENDEE:mailto: 31 | DESCRIPTION: 32 | SUMMARY:Dentist 33 | END:VALARM 34 | END:VEVENT 35 | BEGIN:VEVENT 36 | UID:aeba6310b07a22a72423b2b11f323ddf 37 | DTSTAMP:20141110T084800Z 38 | DTSTART;TZID=Europe/Paris:20141106T120000 39 | DTEND;TZID=Europe/Paris:20141106T130000 40 | DESCRIPTION:Crawling a hidden dungeon 41 | LOCATION:Hidden dungeon 42 | RRULE:FREQ=WEEKLY;INTERVAL=1;UNTIL=20150101T000000Z;BYDAY=TH 43 | SUMMARY:Recurring event 44 | END:VEVENT 45 | BEGIN:VEVENT 46 | UID:aeba6310b07a22a72423b2b11f324d66 47 | DTSTAMP:20141110T084800Z 48 | DTSTART;VALUE=DATE:20141107 49 | DTEND;VALUE=DATE:20141108 50 | DESCRIPTION:Bring a present! 51 | LOCATION:Friend's appartment 52 | RRULE:FREQ=YEARLY;INTERVAL=1 53 | SUMMARY:Friend's birthday 54 | BEGIN:VALARM 55 | ACTION:DISPLAY 56 | TRIGGER:-P1W 57 | DESCRIPTION:Friend's birthday 58 | END:VALARM 59 | END:VEVENT 60 | END:VCALENDAR -------------------------------------------------------------------------------- /test/empty_desc.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//Google Inc//Google Calendar 70.9054//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | X-WR-CALNAME:arne 7 | X-WR-TIMEZONE:Europe/Amsterdam 8 | BEGIN:VEVENT 9 | DTSTART:20141021T100000Z 10 | DTEND:20141021T110000Z 11 | DTSTAMP:20141004T225944Z 12 | UID:1mhopf2uffsd9fasdflkgfcelk@google.com 13 | CREATED:20100930T094346Z 14 | DESCRIPTION: 15 | LAST-MODIFIED:20141002T060542Z 16 | LOCATION:xxxxxx 17 | SEQUENCE:0 18 | STATUS:CONFIRMED 19 | SUMMARY: 20 | TRANSP:OPAQUE 21 | END:VEVENT 22 | END:VCALENDAR -------------------------------------------------------------------------------- /test/fixtures/events_generated.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /test/fixtures/generator.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | moment = require 'moment-timezone' 3 | 4 | # Return a random number between 0 and `max` (included). 5 | getRandom = (max) -> Math.round Math.random() * max 6 | getRandomElmt = (array) -> array[getRandom(array.length - 1)] 7 | 8 | eventsNum = process.argv[2] or 500 9 | calendarName = process.argv[3] or 'default calendar' 10 | 11 | events = [] 12 | 13 | # Initial value to generate dates. 14 | today = moment() 15 | year = today.year() 16 | 17 | 18 | for j in [0..eventsNum - 1] by 1 19 | 20 | # Month is between 1 and 12. 21 | month = getRandom(11) + 1 22 | 23 | # Day is between 1 and 31. If there is less day then `day` in the month, 24 | # it will bubble up to the next month. 25 | day = getRandom(30) + 1 26 | 27 | # Hour is between 0 and 23. 28 | hour = getRandom(23) 29 | 30 | # Minute is 0 or 30. 31 | minute = getRandomElmt [0, 30] 32 | 33 | start = moment(today) 34 | .month(month) 35 | .day(day) 36 | .hour(hour) 37 | .minute(minute) 38 | .second(0) 39 | .millisecond(0) 40 | 41 | end = moment(start).add(2, 'hours') 42 | 43 | events.push 44 | start: start.toISOString() 45 | end: end.toISOString() 46 | place: '' 47 | description: "Event n°#{j}" 48 | details: '' 49 | tags: [calendarName] 50 | rrule: '' 51 | created: new Date().toISOString() 52 | lastModification: new Date().toISOString() 53 | docType: 'Event' 54 | 55 | 56 | targetFile = './test/fixtures/events_generated.json' 57 | json = JSON.stringify events, null, ' ' 58 | fs.writeFile targetFile, json, flag: 'w+', (err) -> 59 | console.log err if err? 60 | console.log "Done generating #{eventsNum} events." 61 | 62 | -------------------------------------------------------------------------------- /test/google.ics: -------------------------------------------------------------------------------- 1 | BEGIN:VCALENDAR 2 | PRODID:-//Google Inc//Google Calendar 70.9054//EN 3 | VERSION:2.0 4 | CALSCALE:GREGORIAN 5 | METHOD:PUBLISH 6 | X-WR-CALNAME:random.test@gmail.com 7 | X-WR-TIMEZONE:Europe/Paris 8 | BEGIN:VTIMEZONE 9 | TZID:Europe/Paris 10 | X-LIC-LOCATION:Europe/Paris 11 | BEGIN:DAYLIGHT 12 | TZOFFSETFROM:+0100 13 | TZOFFSETTO:+0200 14 | TZNAME:CEST 15 | DTSTART:19700329T020000 16 | RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU 17 | END:DAYLIGHT 18 | BEGIN:STANDARD 19 | TZOFFSETFROM:+0200 20 | TZOFFSETTO:+0100 21 | TZNAME:CET 22 | DTSTART:19701025T030000 23 | RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU 24 | END:STANDARD 25 | END:VTIMEZONE 26 | BEGIN:VEVENT 27 | DTSTART;TZID=Europe/Paris:20130731T160000 28 | DTEND;TZID=Europe/Paris:20130731T170000 29 | RRULE:FREQ=WEEKLY;BYDAY=WE,TH 30 | DTSTAMP:20130729T150353Z 31 | UID:8l8ib6mvg28f51506bn3u5fihs@google.com 32 | CREATED:20130729T150300Z 33 | DESCRIPTION:un événement plus complet 34 | LAST-MODIFIED:20130729T150331Z 35 | LOCATION:dans un lieu plus complet 36 | SEQUENCE:1 37 | STATUS:CONFIRMED 38 | SUMMARY:un événement plus complet 39 | TRANSP:OPAQUE 40 | END:VEVENT 41 | BEGIN:VEVENT 42 | DTSTART:20130730T123000Z 43 | DTEND:20130730T133000Z 44 | DTSTAMP:20130729T150353Z 45 | UID:jmnril7qo4s94bpp96q4c062i8@google.com 46 | CREATED:20130729T150235Z 47 | DESCRIPTION:description de l'événement 48 | LAST-MODIFIED:20130729T150254Z 49 | LOCATION:dans un lieu 50 | SEQUENCE:0 51 | STATUS:CONFIRMED 52 | SUMMARY:Un événement 53 | TRANSP:OPAQUE 54 | END:VEVENT 55 | END:VCALENDAR 56 | -------------------------------------------------------------------------------- /test/helpers.coffee: -------------------------------------------------------------------------------- 1 | Client = require('request-json').JsonClient 2 | client = new Client "http://localhost:8888/" 3 | ds = new Client "http://localhost:9101/" 4 | ds.setBasicAuth process.env.NAME, process.env.TOKEN 5 | 6 | module.exports = helpers = {} 7 | 8 | if process.env.USE_JS 9 | helpers.prefix = '../build/' 10 | else 11 | helpers.prefix = '../' 12 | 13 | Event = require "#{helpers.prefix}server/models/event" 14 | Settings = require "#{helpers.prefix}server/models/settings" 15 | User = require "#{helpers.prefix}server/models/user" 16 | 17 | userID = null 18 | 19 | helpers.before = (done) -> 20 | @timeout 10000 21 | start = require "#{helpers.prefix}server" 22 | start 8888, (err, app, server) => 23 | @server = server 24 | data = 25 | docType: 'User' 26 | email: 'test@cozycloud.cc' 27 | password: 'password' 28 | timezone: 'Europe/Paris' 29 | ds.post '/data/', data, (err, response, body) -> 30 | return done err if err 31 | userID = body._id 32 | # wait a little for User.timezone to be updated through Realtime 33 | setTimeout done, 1000 34 | 35 | helpers.after = (done) -> 36 | @server.close() if @server? 37 | helpers.cleanDb -> 38 | ds.del "/data/#{userID}/", done 39 | 40 | # Remove all events and settings. 41 | helpers.cleanDb = (callback) -> 42 | Event.destroyAll (err) -> 43 | return callback(err) if err 44 | Settings.destroyAll (err) -> 45 | return callback(err) if err 46 | callback() 47 | 48 | # Get all the alarams 49 | helpers.getAllEvents = (callback) -> 50 | Event.all callback 51 | 52 | # Create an event from values 53 | helpers.createEvent = (start, end, place, description, callback) -> 54 | (callback) -> 55 | evt = 56 | start: start 57 | end: end 58 | place: place 59 | description: description 60 | tags: ['my calendar'] 61 | 62 | helpers.createEventFromObject evt, callback 63 | 64 | # Create an alarm from object 65 | helpers.createEventFromObject = (data, callback) -> 66 | Event.create data, callback 67 | 68 | helpers.getEventByID = (id, callback) -> 69 | Event.find id, callback 70 | 71 | helpers.doesEventExist = (id, callback) -> 72 | Event.exists id, callback 73 | -------------------------------------------------------------------------------- /test/index_test.coffee: -------------------------------------------------------------------------------- 1 | Client = require('request-json').JsonClient 2 | 3 | client = new Client "http://localhost:8888/" 4 | helpers = require './helpers' 5 | should = require 'should' 6 | 7 | describe "Index Page", -> 8 | 9 | before helpers.before 10 | after helpers.after 11 | 12 | describe "GET /", -> 13 | 14 | before helpers.cleanDb 15 | before helpers.createEvent "2013-04-23T14:40:00.000Z", 16 | "2013-04-23T15:40:00.000Z", "Place", 3, 17 | "Something to do" 18 | before helpers.createEvent "2013-04-24T13:30:00.000Z", 19 | "2013-04-24T14:00:00.000Z", "Other place", 0, 20 | "Something else to do" 21 | 22 | after helpers.cleanDb 23 | 24 | it "should return a 200", (done) -> 25 | client.get "", (error, response, body) -> 26 | 27 | should.not.exist error 28 | should.exist response 29 | should.exist body 30 | 31 | response.should.have.property 'statusCode', 200 32 | done() 33 | 34 | , false #response is HTML, dont parse it -------------------------------------------------------------------------------- /test/settings_model_test.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | 3 | helpers = require './helpers' 4 | Settings = require '../server/models/settings' 5 | 6 | 7 | describe "Settings model", -> 8 | 9 | beforeEach helpers.cleanDb 10 | afterEach helpers.cleanDb 11 | 12 | it "getCalAppSettings - with no previous calendar", (done) -> 13 | Settings.getCalAppSettings (err, calendarSettings) -> 14 | should.ifError err 15 | calendarSettings.app.should.equal 'calendar' 16 | calendarSettings.defaultCalendar.should.equal '' 17 | done() 18 | 19 | it "getCalAppSettings - with previous calendar", (done) -> 20 | settings = 21 | app: 'calendar' 22 | defaultCalendar: 'Meetups' 23 | 24 | Settings.create settings, (err) -> 25 | should.ifError err 26 | 27 | Settings.getCalAppSettings (err, calendarSettings) -> 28 | should.ifError err 29 | calendarSettings.app.should.equal 'calendar' 30 | calendarSettings.defaultCalendar.should.equal 'Meetups' 31 | done() 32 | 33 | it "updateForCalendar", (done) -> 34 | settings = 35 | defaultCalendar: 'Meetups' 36 | 37 | Settings.updateCalAppSettings settings, (err, calendarSettings) -> 38 | should.ifError err 39 | calendarSettings.app.should.equal 'calendar' 40 | calendarSettings.defaultCalendar.should.equal 'Meetups' 41 | 42 | Settings.getCalAppSettings (err, calendarSettings) -> 43 | should.ifError err 44 | calendarSettings.app.should.equal 'calendar' 45 | calendarSettings.defaultCalendar.should.equal 'Meetups' 46 | done() 47 | -------------------------------------------------------------------------------- /test/settings_test.coffee: -------------------------------------------------------------------------------- 1 | should = require 'should' 2 | async = require 'async' 3 | request = require 'request-json' 4 | helpers = require './helpers' 5 | Settings = require '../server/models/settings' 6 | 7 | 8 | describe "Settings routes", -> 9 | client = request.createClient "http://localhost:8888/" 10 | 11 | before helpers.before 12 | after helpers.after 13 | 14 | beforeEach helpers.cleanDb 15 | afterEach helpers.cleanDb 16 | 17 | it "PUT settings/ - good data", (done) -> 18 | newSettings = 19 | defaultCalendar: 'Meetups' 20 | client.put 'settings', newSettings, (err, res, body) -> 21 | should.ifError err 22 | body.success.should.be.ok 23 | body.settings.defaultCalendar.should.equal( 24 | newSettings.defaultCalendar 25 | ) 26 | 27 | Settings.getCalAppSettings (err, calendarSettings) -> 28 | should.ifError err 29 | calendarSettings.defaultCalendar.should.equal( 30 | newSettings.defaultCalendar 31 | ) 32 | done() 33 | 34 | it "PUT settings/ - empty data", (done) -> 35 | newSettings = 36 | other: 'test' 37 | client.put 'settings', newSettings, (err, res, body) -> 38 | should.ifError err 39 | res.statusCode.should.equal 400 40 | body.success.should.not.be.ok 41 | done() 42 | 43 | it "PUT settings/ - wrong data", (done) -> 44 | newSettings = 45 | defaultCalendar: 123 46 | client.put 'settings', newSettings, (err, res, body) -> 47 | should.ifError err 48 | res.statusCode.should.equal 400 49 | body.success.should.not.be.ok 50 | done() 51 | --------------------------------------------------------------------------------