From a57548c7da0ad03d0364403b7835a2f762250620 Mon Sep 17 00:00:00 2001 From: Benjamin ter Kuile Date: Thu, 21 Dec 2017 11:42:41 +0100 Subject: [PATCH] initial github commit --- .bowerrc | 4 + .editorconfig | 20 ++ .ember-cli | 9 + .eslintrc.js | 13 ++ .gitignore | 18 ++ .npmignore | 16 ++ .travis.yml | 40 ++++ .watchmanconfig | 3 + LICENSE.md | 9 + README.md | 44 ++++ addon/.gitkeep | 0 addon/adapters/application.coffee | 56 +++++ addon/components/collapsible-content.coffee | 28 +++ addon/components/editable-attribute.coffee | 33 +++ addon/components/error-handling.coffee | 28 +++ addon/components/number-input.coffee | 18 ++ addon/components/page-title.coffee | 24 +++ addon/components/pagination-progress.coffee | 16 ++ addon/components/push-action.coffee | 39 ++++ addon/components/ui-calendar-moment.coffee | 24 +++ addon/components/ui-markdown-popup.coffee | 14 ++ .../global-functions.coffee | 191 ++++++++++++++++++ .../helpers/any-permission-starts-with.coffee | 16 ++ addon/helpers/dunlop-can.coffee | 13 ++ addon/helpers/format-date.coffee | 9 + addon/helpers/has-permission-of-type.coffee | 17 ++ addon/helpers/minute-of-day.coffee | 13 ++ addon/helpers/number-to-xls-header.coffee | 41 ++++ addon/helpers/t.coffee | 13 ++ .../ember-cli-dunlop-overrides.coffee | 18 ++ addon/mixins/application-route.coffee | 165 +++++++++++++++ addon/mixins/modal-base.coffee | 121 +++++++++++ addon/models/dunlop-user.coffee | 8 + addon/overrides/component.coffee | 5 + addon/overrides/controller.coffee | 4 + addon/overrides/object.coffee | 20 ++ addon/overrides/route.coffee | 5 + addon/serializers/create-and-update.coffee | 27 +++ addon/styles/_convenience-helpers.sass | 69 +++++++ addon/styles/addon.scss | 17 ++ .../components/_collapsible-content.sass | 3 + addon/styles/components/_error-handling.sass | 3 + addon/styles/components/_flash-message.sass | 26 +++ addon/styles/components/_forms.sass | 19 ++ addon/styles/components/_modal.sass | 74 +++++++ addon/styles/components/_page-title.sass | 6 + addon/styles/components/_pagination.sass | 33 +++ addon/styles/components/_table-filtering.sass | 81 ++++++++ addon/styles/components/_title-box.sass | 71 +++++++ .../components/_ui-calendar-additions.sass | 24 +++ .../styles/components/_ui-markdown-popup.sass | 8 + addon/styles/mixins/_animations.sass | 27 +++ .../components/collapsible-content.emblem | 9 + .../components/editable-attribute.emblem | 26 +++ .../components/error-handling.emblem | 3 + .../components/pagination-progress.emblem | 3 + addon/templates/components/push-action.emblem | 3 + .../components/ui-markdown-popup.emblem | 1 + addon/test-helpers/all.coffee | 1 + addon/test-helpers/freeze-moment.coffee | 11 + addon/transforms/datemoment.coffee | 16 ++ addon/transforms/moment.coffee | 16 ++ addon/utils/date-sort.coffee | 17 ++ addon/utils/get-max-suffix.coffee | 10 + addon/utils/integer-to-string.coffee | 11 + addon/utils/map-action.coffee | 46 +++++ addon/utils/merge-objects.coffee | 12 ++ addon/utils/range-parser.coffee | 17 ++ addon/utils/sort-numeric.coffee | 12 ++ addon/validators/conditional-presence.coffee | 34 ++++ app/.gitkeep | 0 app/adapters/user.coffee | 10 + app/authenticators/devise.coffee | 5 + app/authorizers/devise.coffee | 3 + app/components/collapsible-content.js | 1 + app/components/d-breadcrumb.coffee | 15 ++ app/components/d-breadcrumbs.coffee | 20 ++ app/components/editable-attribute.js | 1 + app/components/error-handling.coffee | 1 + app/components/flash-message.coffee | 11 + app/components/login-form.coffee | 43 ++++ app/components/modals/base.coffee | 4 + app/components/number-input.js | 1 + app/components/page-title.coffee | 2 + app/components/pagination-progress.js | 1 + app/components/push-action.js | 1 + app/components/table-filters.coffee | 32 +++ app/components/table-filters/actions.coffee | 13 ++ .../table-filters/date-range.coffee | 90 +++++++++ app/components/table-filters/date.coffee | 15 ++ .../dropdown-select-multiple.coffee | 61 ++++++ .../table-filters/dropdown-select.coffee | 43 ++++ app/components/table-filters/input.coffee | 40 ++++ .../table-filters/number-range.coffee | 41 ++++ .../table-filters/option-buttons.coffee | 23 +++ .../select-multiple-modal.coffee | 47 +++++ .../table-filters/select-multiple.coffee | 40 ++++ app/components/ui-calendar-moment.js | 1 + app/components/ui-markdown-popup.js | 1 + app/components/validated-attribute.coffee | 20 ++ app/components/validated-input.coffee | 23 +++ app/components/validated-select.coffee | 37 ++++ app/helpers/any-permission-starts-with.js | 1 + app/helpers/dunlop-can.js | 1 + app/helpers/format-date.js | 1 + app/helpers/has-permission-of-type.js | 1 + app/helpers/minute-of-day.js | 1 + app/helpers/number-to-xls-header.js | 1 + app/helpers/t.js | 1 + .../ember-cli-dunlop-overrides.js | 1 + app/serializers/create-and-update.js | 1 + app/templates/components/d-breadcrumb.emblem | 3 + app/templates/components/d-breadcrumbs.hbs | 3 + app/templates/components/flash-message.emblem | 1 + app/templates/components/login-form.emblem | 9 + app/templates/components/page-title.emblem | 5 + app/templates/components/table-filters.hbs | 11 + .../-select-multiple-modal.emblem | 39 ++++ .../components/table-filters/actions.emblem | 5 + .../table-filters/date-range.emblem | 27 +++ .../components/table-filters/date.emblem | 2 + .../dropdown-select-multiple.emblem | 22 ++ .../table-filters/dropdown-select.emblem | 16 ++ .../components/table-filters/input.emblem | 7 + .../table-filters/number-range.emblem | 10 + .../table-filters/option-buttons.emblem | 6 + .../table-filters/select-multiple.emblem | 4 + .../components/validated-attribute.emblem | 3 + .../components/validated-input.emblem | 12 ++ .../components/validated-select.emblem | 22 ++ app/templates/global/error-messages.emblem | 23 +++ app/templates/modals/-confirm.emblem | 4 + app/templates/modals/-sort-records.emblem | 8 + app/templates/modals/layout.emblem | 11 + app/templates/modals/nothing.emblem | 1 + app/transforms/datemoment.js | 1 + app/transforms/moment.js | 1 + app/utils/date-sort.js | 1 + app/utils/integer-to-string.js | 1 + bower.json | 7 + config/ember-try.js | 91 +++++++++ config/environment.js | 6 + ember-cli-build.js | 17 ++ index.js | 9 + package.json | 60 ++++++ test.coffee | 0 testem.js | 12 ++ tests/.eslintrc.js | 5 + tests/dummy/app/app.js | 20 ++ tests/dummy/app/components/.gitkeep | 0 tests/dummy/app/controllers/.gitkeep | 0 .../controllers/table-filters/test1.coffee | 10 + tests/dummy/app/helpers/.gitkeep | 0 tests/dummy/app/i18n/translations.coffee | 62 ++++++ tests/dummy/app/index.html | 25 +++ tests/dummy/app/models/.gitkeep | 0 tests/dummy/app/resolver.js | 3 + tests/dummy/app/router.coffee | 16 ++ tests/dummy/app/routes/.gitkeep | 0 .../app/routes/table-filters/test1.coffee | 28 +++ tests/dummy/app/styles/app.scss | 1 + .../app/styles/semantic-ui-additions.sass | 48 +++++ tests/dummy/app/templates/application.emblem | 28 +++ tests/dummy/app/templates/components/.gitkeep | 0 .../app/templates/global/-top-menu.emblem | 9 + tests/dummy/app/templates/helpers.emblem | 10 + .../helpers/number-to-xls-header.emblem | 15 ++ .../dummy/app/templates/table-filters.emblem | 4 + .../app/templates/table-filters/test1.emblem | 36 ++++ .../app/templates/table-filters/test2.emblem | 1 + tests/dummy/config/environment.js | 50 +++++ tests/dummy/public/crossdomain.xml | 15 ++ tests/dummy/public/robots.txt | 3 + tests/helpers/destroy-app.js | 5 + tests/helpers/module-for-acceptance.js | 23 +++ tests/helpers/resolver.js | 11 + tests/helpers/start-app.js | 15 ++ tests/index.html | 33 +++ tests/integration/.gitkeep | 0 .../collapsible-content-test.coffee | 25 +++ .../components/editable-attribute-test.coffee | 25 +++ .../components/error-handling-test.coffee | 25 +++ .../components/number-input-test.coffee | 25 +++ .../pagination-progress-test.coffee | 25 +++ .../components/push-action-test.coffee | 25 +++ .../components/ui-calendar-moment-test.coffee | 25 +++ .../components/ui-markdown-popup-test.coffee | 25 +++ tests/test-helper.js | 6 + tests/test-helpers/freeze-moment-test.coffee | 10 + tests/unit/.gitkeep | 0 .../any-permission-starts-with-test.coffee | 9 + tests/unit/helpers/date-test.coffee | 9 + tests/unit/helpers/dunlop-can-test.coffee | 9 + .../has-permission-of-type-test.coffee | 9 + tests/unit/helpers/minute-of-day-test.coffee | 9 + .../helpers/number-to-xls-header-test.coffee | 9 + tests/unit/helpers/t-test.coffee | 9 + .../ember-cli-dunlop-overrides-test.coffee | 20 ++ tests/unit/models/dunlop-user-test.coffee | 11 + .../serializers/create-and-update-test.coffee | 13 ++ tests/unit/transforms/moment-test.coffee | 11 + tests/unit/utils/date-sort-test.coffee | 9 + tests/unit/utils/get-max-suffix-test.coffee | 13 ++ .../unit/utils/integer-to-string-test.coffee | 9 + tests/unit/utils/merge-objects-test.coffee | 9 + tests/unit/utils/range-parser-test.coffee | 9 + tests/unit/utils/sort-numeric-test.coffee | 9 + .../conditional-presence-test.coffee | 7 + vendor/.gitkeep | 0 209 files changed, 3841 insertions(+) create mode 100644 .bowerrc create mode 100644 .editorconfig create mode 100644 .ember-cli create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .travis.yml create mode 100644 .watchmanconfig create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 addon/.gitkeep create mode 100644 addon/adapters/application.coffee create mode 100644 addon/components/collapsible-content.coffee create mode 100644 addon/components/editable-attribute.coffee create mode 100644 addon/components/error-handling.coffee create mode 100644 addon/components/number-input.coffee create mode 100644 addon/components/page-title.coffee create mode 100644 addon/components/pagination-progress.coffee create mode 100644 addon/components/push-action.coffee create mode 100644 addon/components/ui-calendar-moment.coffee create mode 100644 addon/components/ui-markdown-popup.coffee create mode 100644 addon/dunlop-translations/global-functions.coffee create mode 100644 addon/helpers/any-permission-starts-with.coffee create mode 100644 addon/helpers/dunlop-can.coffee create mode 100644 addon/helpers/format-date.coffee create mode 100644 addon/helpers/has-permission-of-type.coffee create mode 100644 addon/helpers/minute-of-day.coffee create mode 100644 addon/helpers/number-to-xls-header.coffee create mode 100644 addon/helpers/t.coffee create mode 100644 addon/initializers/ember-cli-dunlop-overrides.coffee create mode 100644 addon/mixins/application-route.coffee create mode 100644 addon/mixins/modal-base.coffee create mode 100644 addon/models/dunlop-user.coffee create mode 100644 addon/overrides/component.coffee create mode 100644 addon/overrides/controller.coffee create mode 100644 addon/overrides/object.coffee create mode 100644 addon/overrides/route.coffee create mode 100644 addon/serializers/create-and-update.coffee create mode 100644 addon/styles/_convenience-helpers.sass create mode 100644 addon/styles/addon.scss create mode 100644 addon/styles/components/_collapsible-content.sass create mode 100644 addon/styles/components/_error-handling.sass create mode 100644 addon/styles/components/_flash-message.sass create mode 100644 addon/styles/components/_forms.sass create mode 100644 addon/styles/components/_modal.sass create mode 100644 addon/styles/components/_page-title.sass create mode 100644 addon/styles/components/_pagination.sass create mode 100644 addon/styles/components/_table-filtering.sass create mode 100644 addon/styles/components/_title-box.sass create mode 100644 addon/styles/components/_ui-calendar-additions.sass create mode 100644 addon/styles/components/_ui-markdown-popup.sass create mode 100644 addon/styles/mixins/_animations.sass create mode 100644 addon/templates/components/collapsible-content.emblem create mode 100644 addon/templates/components/editable-attribute.emblem create mode 100644 addon/templates/components/error-handling.emblem create mode 100644 addon/templates/components/pagination-progress.emblem create mode 100644 addon/templates/components/push-action.emblem create mode 100644 addon/templates/components/ui-markdown-popup.emblem create mode 100644 addon/test-helpers/all.coffee create mode 100644 addon/test-helpers/freeze-moment.coffee create mode 100644 addon/transforms/datemoment.coffee create mode 100644 addon/transforms/moment.coffee create mode 100644 addon/utils/date-sort.coffee create mode 100644 addon/utils/get-max-suffix.coffee create mode 100644 addon/utils/integer-to-string.coffee create mode 100644 addon/utils/map-action.coffee create mode 100644 addon/utils/merge-objects.coffee create mode 100644 addon/utils/range-parser.coffee create mode 100644 addon/utils/sort-numeric.coffee create mode 100644 addon/validators/conditional-presence.coffee create mode 100644 app/.gitkeep create mode 100644 app/adapters/user.coffee create mode 100644 app/authenticators/devise.coffee create mode 100644 app/authorizers/devise.coffee create mode 100644 app/components/collapsible-content.js create mode 100644 app/components/d-breadcrumb.coffee create mode 100644 app/components/d-breadcrumbs.coffee create mode 100644 app/components/editable-attribute.js create mode 100644 app/components/error-handling.coffee create mode 100644 app/components/flash-message.coffee create mode 100644 app/components/login-form.coffee create mode 100644 app/components/modals/base.coffee create mode 100644 app/components/number-input.js create mode 100644 app/components/page-title.coffee create mode 100644 app/components/pagination-progress.js create mode 100644 app/components/push-action.js create mode 100644 app/components/table-filters.coffee create mode 100644 app/components/table-filters/actions.coffee create mode 100644 app/components/table-filters/date-range.coffee create mode 100644 app/components/table-filters/date.coffee create mode 100644 app/components/table-filters/dropdown-select-multiple.coffee create mode 100644 app/components/table-filters/dropdown-select.coffee create mode 100644 app/components/table-filters/input.coffee create mode 100644 app/components/table-filters/number-range.coffee create mode 100644 app/components/table-filters/option-buttons.coffee create mode 100644 app/components/table-filters/select-multiple-modal.coffee create mode 100644 app/components/table-filters/select-multiple.coffee create mode 100644 app/components/ui-calendar-moment.js create mode 100644 app/components/ui-markdown-popup.js create mode 100644 app/components/validated-attribute.coffee create mode 100644 app/components/validated-input.coffee create mode 100644 app/components/validated-select.coffee create mode 100644 app/helpers/any-permission-starts-with.js create mode 100644 app/helpers/dunlop-can.js create mode 100644 app/helpers/format-date.js create mode 100644 app/helpers/has-permission-of-type.js create mode 100644 app/helpers/minute-of-day.js create mode 100644 app/helpers/number-to-xls-header.js create mode 100644 app/helpers/t.js create mode 100644 app/initializers/ember-cli-dunlop-overrides.js create mode 100644 app/serializers/create-and-update.js create mode 100644 app/templates/components/d-breadcrumb.emblem create mode 100644 app/templates/components/d-breadcrumbs.hbs create mode 100644 app/templates/components/flash-message.emblem create mode 100644 app/templates/components/login-form.emblem create mode 100644 app/templates/components/page-title.emblem create mode 100644 app/templates/components/table-filters.hbs create mode 100644 app/templates/components/table-filters/-select-multiple-modal.emblem create mode 100644 app/templates/components/table-filters/actions.emblem create mode 100644 app/templates/components/table-filters/date-range.emblem create mode 100644 app/templates/components/table-filters/date.emblem create mode 100644 app/templates/components/table-filters/dropdown-select-multiple.emblem create mode 100644 app/templates/components/table-filters/dropdown-select.emblem create mode 100644 app/templates/components/table-filters/input.emblem create mode 100644 app/templates/components/table-filters/number-range.emblem create mode 100644 app/templates/components/table-filters/option-buttons.emblem create mode 100644 app/templates/components/table-filters/select-multiple.emblem create mode 100644 app/templates/components/validated-attribute.emblem create mode 100644 app/templates/components/validated-input.emblem create mode 100644 app/templates/components/validated-select.emblem create mode 100644 app/templates/global/error-messages.emblem create mode 100644 app/templates/modals/-confirm.emblem create mode 100644 app/templates/modals/-sort-records.emblem create mode 100644 app/templates/modals/layout.emblem create mode 100644 app/templates/modals/nothing.emblem create mode 100644 app/transforms/datemoment.js create mode 100644 app/transforms/moment.js create mode 100644 app/utils/date-sort.js create mode 100644 app/utils/integer-to-string.js create mode 100644 bower.json create mode 100644 config/ember-try.js create mode 100644 config/environment.js create mode 100644 ember-cli-build.js create mode 100644 index.js create mode 100644 package.json create mode 100644 test.coffee create mode 100644 testem.js create mode 100644 tests/.eslintrc.js create mode 100644 tests/dummy/app/app.js create mode 100644 tests/dummy/app/components/.gitkeep create mode 100644 tests/dummy/app/controllers/.gitkeep create mode 100644 tests/dummy/app/controllers/table-filters/test1.coffee create mode 100644 tests/dummy/app/helpers/.gitkeep create mode 100644 tests/dummy/app/i18n/translations.coffee create mode 100644 tests/dummy/app/index.html create mode 100644 tests/dummy/app/models/.gitkeep create mode 100644 tests/dummy/app/resolver.js create mode 100644 tests/dummy/app/router.coffee create mode 100644 tests/dummy/app/routes/.gitkeep create mode 100644 tests/dummy/app/routes/table-filters/test1.coffee create mode 100644 tests/dummy/app/styles/app.scss create mode 100644 tests/dummy/app/styles/semantic-ui-additions.sass create mode 100644 tests/dummy/app/templates/application.emblem create mode 100644 tests/dummy/app/templates/components/.gitkeep create mode 100644 tests/dummy/app/templates/global/-top-menu.emblem create mode 100644 tests/dummy/app/templates/helpers.emblem create mode 100644 tests/dummy/app/templates/helpers/number-to-xls-header.emblem create mode 100644 tests/dummy/app/templates/table-filters.emblem create mode 100644 tests/dummy/app/templates/table-filters/test1.emblem create mode 100644 tests/dummy/app/templates/table-filters/test2.emblem create mode 100644 tests/dummy/config/environment.js create mode 100644 tests/dummy/public/crossdomain.xml create mode 100644 tests/dummy/public/robots.txt create mode 100644 tests/helpers/destroy-app.js create mode 100644 tests/helpers/module-for-acceptance.js create mode 100644 tests/helpers/resolver.js create mode 100644 tests/helpers/start-app.js create mode 100644 tests/index.html create mode 100644 tests/integration/.gitkeep create mode 100644 tests/integration/components/collapsible-content-test.coffee create mode 100644 tests/integration/components/editable-attribute-test.coffee create mode 100644 tests/integration/components/error-handling-test.coffee create mode 100644 tests/integration/components/number-input-test.coffee create mode 100644 tests/integration/components/pagination-progress-test.coffee create mode 100644 tests/integration/components/push-action-test.coffee create mode 100644 tests/integration/components/ui-calendar-moment-test.coffee create mode 100644 tests/integration/components/ui-markdown-popup-test.coffee create mode 100644 tests/test-helper.js create mode 100644 tests/test-helpers/freeze-moment-test.coffee create mode 100644 tests/unit/.gitkeep create mode 100644 tests/unit/helpers/any-permission-starts-with-test.coffee create mode 100644 tests/unit/helpers/date-test.coffee create mode 100644 tests/unit/helpers/dunlop-can-test.coffee create mode 100644 tests/unit/helpers/has-permission-of-type-test.coffee create mode 100644 tests/unit/helpers/minute-of-day-test.coffee create mode 100644 tests/unit/helpers/number-to-xls-header-test.coffee create mode 100644 tests/unit/helpers/t-test.coffee create mode 100644 tests/unit/initializers/ember-cli-dunlop-overrides-test.coffee create mode 100644 tests/unit/models/dunlop-user-test.coffee create mode 100644 tests/unit/serializers/create-and-update-test.coffee create mode 100644 tests/unit/transforms/moment-test.coffee create mode 100644 tests/unit/utils/date-sort-test.coffee create mode 100644 tests/unit/utils/get-max-suffix-test.coffee create mode 100644 tests/unit/utils/integer-to-string-test.coffee create mode 100644 tests/unit/utils/merge-objects-test.coffee create mode 100644 tests/unit/utils/range-parser-test.coffee create mode 100644 tests/unit/utils/sort-numeric-test.coffee create mode 100644 tests/unit/validators/conditional-presence-test.coffee create mode 100644 vendor/.gitkeep diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..959e169 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,4 @@ +{ + "directory": "bower_components", + "analytics": false +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..219985c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 2 + +[*.hbs] +insert_final_newline = false + +[*.{diff,md}] +trim_trailing_whitespace = false diff --git a/.ember-cli b/.ember-cli new file mode 100644 index 0000000..ee64cfe --- /dev/null +++ b/.ember-cli @@ -0,0 +1,9 @@ +{ + /** + Ember CLI sends analytics information by default. The data is completely + anonymous, but there are times when you might want to disable this behavior. + + Setting `disableAnalytics` to true will prevent any data from being sent. + */ + "disableAnalytics": false +} diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..fbfc364 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,13 @@ +module.exports = { + root: true, + parserOptions: { + ecmaVersion: 6, + sourceType: 'module' + }, + extends: 'eslint:recommended', + env: { + browser: true + }, + rules: { + } +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54dc6b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp + +# dependencies +/node_modules +/bower_components + +# misc +/.sass-cache +/connect.lock +/coverage/* +/libpeerconnection.log +/addon/**/*.js +npm-debug.log* +testem.log diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..889b2bf --- /dev/null +++ b/.npmignore @@ -0,0 +1,16 @@ +/bower_components +/config/ember-try.js +/dist +/tests +/tmp +**/.gitkeep +.bowerrc +.editorconfig +.ember-cli +.gitignore +.eslintrc.js +.watchmanconfig +.travis.yml +bower.json +ember-cli-build.js +testem.js diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5235bf5 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,40 @@ +--- +language: node_js +node_js: + - "6" + +sudo: false + +cache: + directories: + - $HOME/.npm + - $HOME/.cache # includes bowers cache + +env: + # we recommend testing LTS's and latest stable release (bonus points to beta/canary) + - EMBER_TRY_SCENARIO=ember-lts-2.4 + - EMBER_TRY_SCENARIO=ember-lts-2.8 + - EMBER_TRY_SCENARIO=ember-release + - EMBER_TRY_SCENARIO=ember-beta + - EMBER_TRY_SCENARIO=ember-canary + - EMBER_TRY_SCENARIO=ember-default + +matrix: + fast_finish: true + allow_failures: + - env: EMBER_TRY_SCENARIO=ember-canary + +before_install: + - npm config set spin false + - npm install -g bower phantomjs-prebuilt + - bower --version + - phantomjs --version + +install: + - npm install + - bower install + +script: + # Usually, it's ok to finish the test scenario without reverting + # to the addon's original dependency state, skipping "cleanup". + - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO test --skip-cleanup diff --git a/.watchmanconfig b/.watchmanconfig new file mode 100644 index 0000000..e7834e3 --- /dev/null +++ b/.watchmanconfig @@ -0,0 +1,3 @@ +{ + "ignore_dirs": ["tmp", "dist"] +} diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..0d5e3ef --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2017 + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b2b3ab3 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# ember-cli-dunlop + +## Almost expected dependencies +One of the awesome powers of this support package is it's strong opinionated approach to +buildinng applications. This will result in a system of consistent +working applications having a similar experience and layout. This will +hugely simplify maintenance and rolling out improvements. + +The downside can be to implement applications that do not fit inside +this opinionated model. Consider not to use this package in that case. + +* ember-cli-coffeescript +* ember-cli-emblem +* ember-changeset-validations +* ember-one-way-controls +* ember-cpm +* ember-simple-auth +* ember-truth-helpers +* ember-composable-helpers +* semantic-ui-ember + +## Installation + +* `git clone ` this repository +* `cd ember-cli-dunlop` +* `npm install` +* `bower install` + +## Running + +* `ember serve` +* Visit your app at [http://localhost:4200](http://localhost:4200). + +## Running Tests + +* `npm test` (Runs `ember try:each` to test your addon against multiple Ember versions) +* `ember test` +* `ember test --server` + +## Building + +* `ember build` + +For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). diff --git a/addon/.gitkeep b/addon/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/addon/adapters/application.coffee b/addon/adapters/application.coffee new file mode 100644 index 0000000..0c951cf --- /dev/null +++ b/addon/adapters/application.coffee @@ -0,0 +1,56 @@ +import Ember from 'ember' +import DS from 'ember-data' +import config from 'ember-get-config' +import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin' + +export default DS.JSONAPIAdapter.extend DataAdapterMixin, + namespace: config.apiNamespace + host: config.apiHost + authorizer: 'authorizer:devise' + #pathForType: (type)-> + # type.underscore().pluralize() + + # Code for createRecord and updateRecord taken from ember data v2.0.0 restadapter + createRecord: (store, type, snapshot) -> + data = {} + #var serializer = store.serializerFor(type.modelName); + serializer = Ember.get(snapshot.record, 'store').serializerFor('create-and-update') + url = @buildURL(type.modelName, null, snapshot, 'createRecord') + + serializer.serializeIntoHash data, type, snapshot, includeId: true + + @ajax url, 'POST', data: data + + # return pluralized underscored version + pathForType: (modelName) -> + base = Ember.String.underscore(modelName) + base = base.replace(/\./g, '/') + #base.split(/\//).map( (str) -> Ember.String.pluralize(str) ).join('/') # all parts pluralized + Ember.String.pluralize(base) + + updateRecord: (store, type, snapshot) -> + data = {} + #var serializer = store.serializerFor(type.modelName); + serializer = Ember.get(snapshot.record, 'store').serializerFor('create-and-update') + id = snapshot.id + url = @buildURL(type.modelName, id, snapshot, 'updateRecord') + serializer.serializeIntoHash data, type, snapshot, includeId: true + @ajax url, 'PUT', data: data + + handleResponse: (status, headers, payload) -> + switch status + when 401, 403 + application_route = Ember.getOwner(@).lookup('route:application') + application_route.set 'router.globals.flash_message', "Unauthorized action" + #@get('session')?.invalidate() + #application_route.set 'router.globals.current_user', null + #application_route.transitionTo('login') + else + @_super arguments... + + #buildURL: (modelName, id, snapshot, requestType, query)-> + # response = @_super arguments... + # debugger + # response + + diff --git a/addon/components/collapsible-content.coffee b/addon/components/collapsible-content.coffee new file mode 100644 index 0000000..331ea20 --- /dev/null +++ b/addon/components/collapsible-content.coffee @@ -0,0 +1,28 @@ +import Ember from 'ember' +import layout from '../templates/components/collapsible-content' + +Component = Ember.Component.extend + layout: layout + collapsed: true + classNames: ['collapsible-content-container'] + + title: Ember.computed 'titleSpec', -> + title = "#{@get('titleSpec')}" + options = {} + + # allow modelPath as argument, useful for page-title 'action.new_model' modelPath='models.projects.site' etc + if modelPath = @get('modelPath') + options.model = t(modelPath) + + # interpret the given argument as a translation string if it matches the characteristics + title = tspan(title, options) if is_translation_path(title) + + title.htmlSafe() + + actions: + toggleCollapsed: -> @toggleProperty('collapsed') + +Component.reopenClass + positionalParams: ['titleSpec'] + +export default Component diff --git a/addon/components/editable-attribute.coffee b/addon/components/editable-attribute.coffee new file mode 100644 index 0000000..37f2991 --- /dev/null +++ b/addon/components/editable-attribute.coffee @@ -0,0 +1,33 @@ +import Ember from 'ember' +import layout from '../templates/components/editable-attribute' + +export default Ember.Component.extend + layout: layout + attributeName: 'name' + can_make_editable: true + isEditing: false + validation_message: '' + make_editable: (-> @set 'isEditing', true ).on 'doubleClick' + init: -> + @_super arguments... + @set 'isEditing', true if @get('model.isNew') + actions: + destroyRecord: -> + @set 'validation_message', '' + model = @get('model') + model.rollbackAttributes() + model.destroyRecord() + editRecord: -> @set('isEditing', true) + saveRecord: -> + model = @get('model') + @set 'validation_message', '' + if validate = @get('validate') + @set 'validation_message', 'Must be given' if validate is 'presence' and not @get('model.name') + unless @get 'validation_message' + @set 'isEditing', false + model.save() + rollbackRecord: -> + @set 'validation_message', '' + model = @get('model') + model.rollbackAttributes() + @set 'isEditing', false diff --git a/addon/components/error-handling.coffee b/addon/components/error-handling.coffee new file mode 100644 index 0000000..2538c72 --- /dev/null +++ b/addon/components/error-handling.coffee @@ -0,0 +1,28 @@ +import Ember from 'ember' +#import layout from '../templates/components/error-handling' + +export default Ember.Component.extend + #classNames: ['dunlop-global-error', 'ui', 'negative', 'message'] + #classNameBindings: ['show_error:active:hidden'] + #layout: layout + error: {} + show_error: true + show_as_list: Ember.computed.gt 'error.errors.length', 1 + + error_header: Ember.computed 'error.message', -> + @get('error.message') || 'Oops' + + error_body: Ember.computed 'show_as_list', -> + '' + single_error: Ember.computed 'error.message', -> + Ember.Object.create + title: @get('error.message') + status: @get('error.status') + detail: @get('error.backtrace') + + actions: + reload_page: -> window.location.reload() + clear_error: -> @set 'show_error', false + clear_error_and_go_home: -> + @set 'show_error', false + Ember.getOwner(this).lookup('route:application').transitionTo('index') diff --git a/addon/components/number-input.coffee b/addon/components/number-input.coffee new file mode 100644 index 0000000..a98ff06 --- /dev/null +++ b/addon/components/number-input.coffee @@ -0,0 +1,18 @@ +import Ember from 'ember' + +# = number-input numericValue=path.to.numeric_value, taken from mozo +export default Ember.TextField.extend + type: 'number' + attributeBindings: ['min', 'max', 'step'] + classNames: ['number-input'] + focusIn: -> @$().select() + init: -> + @_super arguments... + value = @get 'numericValue' + # value? returns true on 0 + @set 'value', if value? then String(value) else '' + handle_value_change: Ember.observer 'value', -> + string_value = @get('value') + setValue = parseFloat(string_value) + @set "numericValue", if Number.isFinite(setValue) then setValue else null + diff --git a/addon/components/page-title.coffee b/addon/components/page-title.coffee new file mode 100644 index 0000000..71974b3 --- /dev/null +++ b/addon/components/page-title.coffee @@ -0,0 +1,24 @@ +import Ember from 'ember' + +Component = Ember.Component.extend + tagName: 'h2' + classNames: ['page-title', 'ui', 'header'] + back: null + + title: Ember.computed 'titleSpec', -> + title = "#{@get('titleSpec')}" + options = {} + + # allow modelPath as argument, useful for page-title 'action.new_model' modelPath='models.projects.site' etc + if modelPath = @get('modelPath') + options.model = t(modelPath) + + # interpret the given argument as a translation string if it matches the characteristics + title = tspan(title, options) if is_translation_path(title) + + title.htmlSafe() + +Component.reopenClass + positionalParams: ['titleSpec'] + +export default Component diff --git a/addon/components/pagination-progress.coffee b/addon/components/pagination-progress.coffee new file mode 100644 index 0000000..13557f1 --- /dev/null +++ b/addon/components/pagination-progress.coffee @@ -0,0 +1,16 @@ +import Ember from 'ember' +import layout from '../templates/components/pagination-progress' + +export default Ember.Component.extend + tagName: 'span' + layout: layout + progressText: Ember.computed 'content.page', 'content.content.length', 'content.perPage', 'content.total_count', -> + per_page = @get('content.perPage') || 10 + current_page = @get('content.page') || 1 + records_displayed = @get('content.content.length') || 0 + total_count = @get('content.meta.total_count') || 0 + + from_item = (current_page - 1) * per_page + 1 + to_item = Math.min(from_item + records_displayed - 1, total_count) + from_item = 0 unless total_count + "#{from_item} - #{to_item} / #{total_count}" diff --git a/addon/components/push-action.coffee b/addon/components/push-action.coffee new file mode 100644 index 0000000..41a68ef --- /dev/null +++ b/addon/components/push-action.coffee @@ -0,0 +1,39 @@ +import Ember from 'ember' +import layout from '../templates/components/push-action' + +export default Ember.Component.extend + layout: layout + tagName: 'button' + type: '' + size: '' + model: null + classNames: ['push-action', 'ui', 'icon', 'button'] + classNameBindings: ['type', 'size', 'colorClass'] + click: -> + @sendAction 'action', @get('model') + + colorClass: Ember.computed 'type', -> + return unless type = @get('type') + switch type + when 'save' then 'primary' + when 'destroy' then 'negative' + when 'new' then 'grey' + else '' + + + iconClass: Ember.computed 'type', -> + return unless type = @get('type') + switch type + when 'edit' then 'pencil' + when 'new' then 'plus' + when 'show' then 'eye' + when 'sort' then 'sort' + when 'rollback' then 'undo' + when 'save' then 'save' + when 'destroy' then 'trash' + when 'authorization' then 'users' # 'unlock alternate' + when 'constraints' then 'connectdevelop' + when 'position' then 'random' + when 'contract' then 'file text' + when 'raster' then 'grid layout' + else type diff --git a/addon/components/ui-calendar-moment.coffee b/addon/components/ui-calendar-moment.coffee new file mode 100644 index 0000000..02749c6 --- /dev/null +++ b/addon/components/ui-calendar-moment.coffee @@ -0,0 +1,24 @@ +import Ember from 'ember' +import UiCalendarComponent from 'ember-semantic-ui-calendar/components/ui-calendar' +{ computed, inject, merge } = Ember +export default UiCalendarComponent.extend + momentValue: null + firstDayOfWeek: null + is_date: false + + # Provide the Date object that is used by ui-calendar + date: computed('momentValue', -> + if momentValue = @get('momentValue') then momentValue.toDate() else null + ).readOnly() + + willInitSemantic: (settings) -> + settings.firstDayOfWeek = if Number.isFinite(@get('firstDayOfWeek')) then @get('firstDayOfWeek') else (if Number.isFinite(@get('globals.first_day_of_week')) then @get('globals.first_day_of_week') else 1) + @_super arguments... + @set('is_date', true) if @get('momentValue._f') is 'YYYY-MM-DD' or @get('momentValue._ambigTime') or @get('type') is 'date' + merge settings, + onChange: (date) -> + # Wraps the Date in a moment object an triggers the onChange action + momentDate = if date then moment(date) else null + momentDate._ambigTime = true if momentDate and @get('is_date') + @sendAction 'onChange', momentDate + diff --git a/addon/components/ui-markdown-popup.coffee b/addon/components/ui-markdown-popup.coffee new file mode 100644 index 0000000..2fca71e --- /dev/null +++ b/addon/components/ui-markdown-popup.coffee @@ -0,0 +1,14 @@ +import Ember from 'ember' +import layout from '../templates/components/ui-markdown-popup' +import UiPopup from 'semantic-ui-ember/components/ui-popup' +import showdown from 'showdown' + +export default UiPopup.extend + extensions: [] + init: -> + markdown = @get('markdown') + converter = new showdown.Converter(@get('extensions')) + html = converter.makeHtml(markdown) + @attrs.html = html + @_super arguments... + layout: layout diff --git a/addon/dunlop-translations/global-functions.coffee b/addon/dunlop-translations/global-functions.coffee new file mode 100644 index 0000000..18dafc3 --- /dev/null +++ b/addon/dunlop-translations/global-functions.coffee @@ -0,0 +1,191 @@ +Qstorage = localStorage +window.day_minutes_to_time = (minutes)-> + return "" unless minutes + [("0" + Math.floor(minutes/60)).substr(-2,2), ("0" + Math.floor(minutes%60)).substr(-2,2)].join(":") + +# This is the same as setTimeout but then +# with switched arguments for coffeescript friendlyness. +# @a = 3 +# delay => +# console.log "The value of a is #{@a}" +# or to delay not a second but a self defined amount of time in milliseconds: +# delay 10000, => +# console.log "The value of a is #{@a}" +window.delay = (time = 1000, fn, args...) -> + setTimeout fn, time, args... + +# based on http://www.thecodeship.com/web-development/alternative-to-javascript-evil-setinterval/ +# can be used as: +# to keep scope and execute every second: +# @a = 3 +# interval => +# console.log "Hi there with a is: #{@a}" +# to log every 200 milliseconds without conservation of this scope: +# interval 200, -> console.log "Hi there" +# to execute a maximum number (30) of times: +# interval 200, 30, -> console.log "Hi there" +window.interval = (wait = 1000, extra_args..., func)-> + times = extra_args[0] + interv = do (wait, times)-> + -> + return if times? and times-- <= 0 + setTimeout interv, wait + try + func.call null + catch e + times = 0 + throw e.toString() + setTimeout interv, wait + +window.is_translation_path = (path_or_value) -> + return true if path_or_value is 'application_name' + String(path_or_value).match /^[a-z0-9_]+\.[a-z0-9\._]+$/ + +window.ttry = (path, vars={})-> + t(path, $.extend(vars, emptyWhenNotFound: true)) + +# return translation in the form +# Tafel +window.tspan = (path, vars={}) -> + result = t(path, vars) + return '' if not result and vars.emptyWhenNotFound + "#{result}" + +window.t = (path, vars={}) -> + #result = undefined + #m = undefined + #translatable = undefined + #isafety = undefined + #replacable = undefined + if vars.scope + path = "#{vars.scope}.#{path}" + delete vars.scope + if vars.suffix + path = "#{path}.#{vars.suffix}" + delete vars.suffix + locale = Qstorage.getItem('locale') || 'en' + parts = path.split(".") + #accessor = "$translations.#{$locale}[\"#{parts.join("\"][\"")}\"]" + result = $translations[locale] + try + result = result[part] for part in parts + catch err # when nesting gets too deep + console.log "[TRANSLATION] Cannot find translation: #{path}" + return vars.default if vars.default? # questionmark is important to allow {default: ''} setting + result = parts[parts.length - 1].capitalize() + return if vars.emptyWhenNotFound then "" else result #parts[parts.length - 1].capitalize() + unless result? + console.log "[TRANSLATION] Cannot find translation: #{path}" + return vars.default if vars.default? and not result # questionmark is important to allow {default: ''}. This is however valid for all non truthy result values, so no question mark here + return "" if result is "" + return '' if not result? and vars.emptyWhenNotFound # When latest translation part cannot be found and empty result is required + return parts[parts.length - 1].capitalize() unless result + + + # allow t('test', {var: 'Benjamin'}) statements + # with $translations.nl.test = "Hello %{var}" + for variable, value of vars + # allow t 'action.select_model' modelPath='models.project' + if match = String(variable).match(/^(\w+)Path$/) + value = window.t value + variable = match[1] + result = result.replace("%{#{variable}}", value) + + isafety = 0 + try + while result.indexOf("${") > -1 + m = result.match(/\${([\w\.]+(\|\w+)?)}/) + if m[2] + translatable = m[1].replace(m[2], "") + operation = $transformation_mappings[m[2].substr(1) or m[2].substr(1)] + else + translatable = m[1] + operation = null + replacable = t(translatable) + replacable = replacable[operation]() if operation + result = result.replace(m[0], replacable) + break if isafety > 10 # referencing other translations may cause infinite loops + isafety += 1 + catch err + console.log "translation #{result} cannot be interpolated" + result + +window.setLocale = (locale, options={}) -> + locale ||= Qstorage.getItem('locale') || options.default || 'en' + Qstorage.setItem "locale", locale + window.$locale = locale + setTranslations() + +window.setTranslations = (selector) -> + #list = $("#top-navigation-list") + locale = Qstorage.getItem('locale') || 'en' + selector = $( selector || document) + selector.find(".locale-select").show() + selector.find(".locale-select-" + locale).hide() + moment.locale locale + if selector + selector.find("[data-t]").each -> + $(this).html t($(this).data("t"), $(this).data("tAttributes")) + + selector.find("*[data-time]").each -> + $(this).text moment($(this).data("time")).format($(this).data("timeFormat") or "dd D MMM HH:mm") + + else + $("[data-t]").each -> + $(this).html t($(this).data("t"), $(this).data("tAttributes")) + + $("*[data-time]").each -> + $(this).text moment($(this).data("time")).format($(this).data("timeFormat") or "dd D MMM HH:mm") + + # jQuery UI datepicker support + $(".datepicker").datepicker "option", $.datepicker.regional[locale] if $.fn.datepicker + + # pickadate support + if $.fn.pickadate + if selector.hasClass('datepicker') + datepicker_object = selector + selector.siblings('.pickadate-display').remove() + else + datepicker_object = selector.find('.datepicker') + selector.find('.pickadate-display').remove() + datepicker_object.pickadate('stop') if datepicker_object.data('pickadate') + $.extend( $.fn.pickadate.defaults, $pickadate_translations[locale] ) if $pickadate_translations?[locale] + window.pickadate_options ||= {} + datepicker_object.pickadate(window.pickadate_options) + datepicker_object.change() + +# use like +# error_message +# attribute: t('attributes.section.title') +# message: 'blank' +window.error_message = (options = {})-> + locale = options.locale || Qstorage.getItem('locale') || 'en' + #message = $translations[locale].errors.messages[options.message] + message = t "errors.messages.#{options.message}", options + t 'errors.format', + attribute: options.attribute + message: message + +window.setupTranslations = (options = {})-> + locale = options.locale || Qstorage.getItem('locale') || 'en' + if $.fn.pickadate + $(document).on 'change', '.datepicker', -> + input = $(@) + input.next().remove() if input.next().hasClass('pickadate-display') + if input.data('pickadate') + if input.val() + #display_format = $pickadate_translations[$locale].displayFormat + #display_date = input.data('pickadate').get('select', display_format) + display_date = input.data('pickadate').get('select') + display_date = $('').text(" #{display_date}") # add space between the icon and the date + else + display_date = $('').data('t', 'datepicker.no_date').text(t('datepicker.no_date')) + display_tag = $('').addClass('pickadate-display').append('').append(display_date) + #display_tag.click (e)->(e.preventDefault();input.click().focus();false ) + display_tag.click (e)->(e.preventDefault();input.pickadate('open');false ) + $(@).after(display_tag) + setLocale(locale) + $('.datepicker').change().hide() +window.$transformation_mappings = + downcase: "toLowerCase" + upcase: "toUpperCase" diff --git a/addon/helpers/any-permission-starts-with.coffee b/addon/helpers/any-permission-starts-with.coffee new file mode 100644 index 0000000..264b8b3 --- /dev/null +++ b/addon/helpers/any-permission-starts-with.coffee @@ -0,0 +1,16 @@ +import Ember from 'ember' + +## This function receives the params `params, hash` +#export anyPermissionStartsWith = (params) -> +# return params + +#export default Ember.Helper.helper anyPermissionStartsWith +export default Ember.Helper.extend + globals: Ember.inject.service('globals') + compute: ([expression], options) -> + return false unless @get('globals.current_user') + return true if @get('globals.current_user.admin') + regexp = new RegExp("^\\w+-\\w+-#{expression}") + roles = @get('globals.current_user.role_names') + roles.some (role) -> + regexp.test role diff --git a/addon/helpers/dunlop-can.coffee b/addon/helpers/dunlop-can.coffee new file mode 100644 index 0000000..0eda797 --- /dev/null +++ b/addon/helpers/dunlop-can.coffee @@ -0,0 +1,13 @@ +import Ember from 'ember' + +export default Ember.Helper.extend + globals: Ember.inject.service('globals') + custom_compute: (authorization, type, target, roles) -> + false + compute: ([authorization, type, target], options) -> + return false unless @get('globals.current_user') + return true if @get('globals.current_user.admin') + roles = @get('globals.current_user.role_names') || [] + return true if "manage-#{type}-#{target}" in roles + return true if "#{authorization}-#{type}-#{target}" in roles + @custom_compute.call(@, authorization, type, target, roles) diff --git a/addon/helpers/format-date.coffee b/addon/helpers/format-date.coffee new file mode 100644 index 0000000..225ef50 --- /dev/null +++ b/addon/helpers/format-date.coffee @@ -0,0 +1,9 @@ +import Ember from 'ember' + +export formatDate = ([date], options={}) -> + return '' unless date + return date if typeof date is 'string' + date = moment(date) unless date._isAMomentObject + date.format(options.format || 'll') + +export default Ember.Helper.helper formatDate diff --git a/addon/helpers/has-permission-of-type.coffee b/addon/helpers/has-permission-of-type.coffee new file mode 100644 index 0000000..341f922 --- /dev/null +++ b/addon/helpers/has-permission-of-type.coffee @@ -0,0 +1,17 @@ +import Ember from 'ember' + +## This function receives the params `params, hash` +#export hasPermissionOfType = (params) -> +# return params + +#export default Ember.Helper.helper hasPermissionOfType +export default Ember.Helper.extend + globals: Ember.inject.service('globals') + compute: ([permission_type], options) -> + return false unless permission_type + return false unless @get('globals.current_user') + return true if @get('globals.current_user.admin') + regexp = new RegExp("^\\w+-#{permission_type}-") + roles = @get('globals.current_user.role_names') + roles.some (role) -> + regexp.test role diff --git a/addon/helpers/minute-of-day.coffee b/addon/helpers/minute-of-day.coffee new file mode 100644 index 0000000..42f32d7 --- /dev/null +++ b/addon/helpers/minute-of-day.coffee @@ -0,0 +1,13 @@ +import Ember from 'ember' + +# This function receives the params `params, hash` +export minuteOfDay = ([number]) -> + return '-' unless number? + hour = Math.floor(number / 60) + minute = number % 60 + hour_string = "0#{hour}".slice(-2) + minute_string = "0#{minute}".slice(-2) + "#{hour_string}:#{minute_string}" + +export default Ember.Helper.helper minuteOfDay + diff --git a/addon/helpers/number-to-xls-header.coffee b/addon/helpers/number-to-xls-header.coffee new file mode 100644 index 0000000..7046654 --- /dev/null +++ b/addon/helpers/number-to-xls-header.coffee @@ -0,0 +1,41 @@ +import Ember from 'ember' + +headers = [ + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AI", "AJ", "AK", "AL", "AM", "AN", "AO", "AP", "AQ", "AR", "AS", "AT", "AU", "AV", + "AW", "AX", "AY", "AZ", "BA", "BB", "BC", "BD", "BE", "BF", "BG", "BH", "BI", "BJ", "BK", "BL", "BM", "BN", "BO", "BP", "BQ", "BR", + "BS", "BT", "BU", "BV", "BW", "BX", "BY", "BZ", "CA", "CB", "CC", "CD", "CE", "CF", "CG", "CH", "CI", "CJ", "CK", "CL", "CM", "CN", + "CO", "CP", "CQ", "CR", "CS", "CT", "CU", "CV", "CW", "CX", "CY", "CZ", "DA", "DB", "DC", "DD", "DE", "DF", "DG", "DH", "DI", "DJ", + "DK", "DL", "DM", "DN", "DO", "DP", "DQ", "DR", "DS", "DT", "DU", "DV", "DW", "DX", "DY", "DZ", "EA", "EB", "EC", "ED", "EE", "EF", + "EG", "EH", "EI", "EJ", "EK", "EL", "EM", "EN", "EO", "EP", "EQ", "ER", "ES", "ET", "EU", "EV", "EW", "EX", "EY", "EZ", "FA", "FB", + "FC", "FD", "FE", "FF", "FG", "FH", "FI", "FJ", "FK", "FL", "FM", "FN", "FO", "FP", "FQ", "FR", "FS", "FT", "FU", "FV", "FW", "FX", + "FY", "FZ", "GA", "GB", "GC", "GD", "GE", "GF", "GG", "GH", "GI", "GJ", "GK", "GL", "GM", "GN", "GO", "GP", "GQ", "GR", "GS", "GT", + "GU", "GV", "GW", "GX", "GY", "GZ", "HA", "HB", "HC", "HD", "HE", "HF", "HG", "HH", "HI", "HJ", "HK", "HL", "HM", "HN", "HO", "HP", + "HQ", "HR", "HS", "HT", "HU", "HV", "HW", "HX", "HY", "HZ", "IA", "IB", "IC", "ID", "IE", "IF", "IG", "IH", "II", "IJ", "IK", "IL", + "IM", "IN", "IO", "IP", "IQ", "IR", "IS", "IT", "IU", "IV", "IW", "IX", "IY", "IZ", "JA", "JB", "JC", "JD", "JE", "JF", "JG", "JH", + "JI", "JJ", "JK", "JL", "JM", "JN", "JO", "JP", "JQ", "JR", "JS", "JT", "JU", "JV", "JW", "JX", "JY", "JZ", "KA", "KB", "KC", "KD", + "KE", "KF", "KG", "KH", "KI", "KJ", "KK", "KL", "KM", "KN", "KO", "KP", "KQ", "KR", "KS", "KT", "KU", "KV", "KW", "KX", "KY", "KZ", + "LA", "LB", "LC", "LD", "LE", "LF", "LG", "LH", "LI", "LJ", "LK", "LL", "LM", "LN", "LO", "LP", "LQ", "LR", "LS", "LT", "LU", "LV", + "LW", "LX", "LY", "LZ", "MA", "MB", "MC", "MD", "ME", "MF", "MG", "MH", "MI", "MJ", "MK", "ML", "MM", "MN", "MO", "MP", "MQ", "MR", + "MS", "MT", "MU", "MV", "MW", "MX", "MY", "MZ", "NA", "NB", "NC", "ND", "NE", "NF", "NG", "NH", "NI", "NJ", "NK", "NL", "NM", "NN", + "NO", "NP", "NQ", "NR", "NS", "NT", "NU", "NV", "NW", "NX", "NY", "NZ", "OA", "OB", "OC", "OD", "OE", "OF", "OG", "OH", "OI", "OJ", + "OK", "OL", "OM", "ON", "OO", "OP", "OQ", "OR", "OS", "OT", "OU", "OV", "OW", "OX", "OY", "OZ", "PA", "PB", "PC", "PD", "PE", "PF", + "PG", "PH", "PI", "PJ", "PK", "PL", "PM", "PN", "PO", "PP", "PQ", "PR", "PS", "PT", "PU", "PV", "PW", "PX", "PY", "PZ", "QA", "QB", + "QC", "QD", "QE", "QF", "QG", "QH", "QI", "QJ", "QK", "QL", "QM", "QN", "QO", "QP", "QQ", "QR", "QS", "QT", "QU", "QV", "QW", "QX", + "QY", "QZ", "RA", "RB", "RC", "RD", "RE", "RF", "RG", "RH", "RI", "RJ", "RK", "RL", "RM", "RN", "RO", "RP", "RQ", "RR", "RS", "RT", + "RU", "RV", "RW", "RX", "RY", "RZ", "SA", "SB", "SC", "SD", "SE", "SF", "SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", "SO", "SP", + "SQ", "SR", "SS", "ST", "SU", "SV", "SW", "SX", "SY", "SZ", "TA", "TB", "TC", "TD", "TE", "TF", "TG", "TH", "TI", "TJ", "TK", "TL", + "TM", "TN", "TO", "TP", "TQ", "TR", "TS", "TT", "TU", "TV", "TW", "TX", "TY", "TZ", "UA", "UB", "UC", "UD", "UE", "UF", "UG", "UH", + "UI", "UJ", "UK", "UL", "UM", "UN", "UO", "UP", "UQ", "UR", "US", "UT", "UU", "UV", "UW", "UX", "UY", "UZ", "VA", "VB", "VC", "VD", + "VE", "VF", "VG", "VH", "VI", "VJ", "VK", "VL", "VM", "VN", "VO", "VP", "VQ", "VR", "VS", "VT", "VU", "VV", "VW", "VX", "VY", "VZ", + "WA", "WB", "WC", "WD", "WE", "WF", "WG", "WH", "WI", "WJ", "WK", "WL", "WM", "WN", "WO", "WP", "WQ", "WR", "WS", "WT", "WU", "WV", + "WW", "WX", "WY", "WZ", "XA", "XB", "XC", "XD", "XE", "XF", "XG", "XH", "XI", "XJ", "XK", "XL", "XM", "XN", "XO", "XP", "XQ", "XR", + "XS", "XT", "XU", "XV", "XW", "XX", "XY", "XZ", "YA", "YB", "YC", "YD", "YE", "YF", "YG", "YH", "YI", "YJ", "YK", "YL", "YM", "YN", + "YO", "YP", "YQ", "YR", "YS", "YT", "YU", "YV", "YW", "YX", "YY", "YZ", "ZA", "ZB", "ZC", "ZD", "ZE", "ZF", "ZG", "ZH", "ZI", "ZJ", + "ZK", "ZL", "ZM", "ZN", "ZO", "ZP", "ZQ", "ZR", "ZS", "ZT", "ZU", "ZV", "ZW", "ZX", "ZY", "ZZ" +] +# This function receives the params `params, hash` +export numberToXlsHeader = ([index], options) -> + headers[index] + +export default Ember.Helper.helper numberToXlsHeader diff --git a/addon/helpers/t.coffee b/addon/helpers/t.coffee new file mode 100644 index 0000000..ee6a76d --- /dev/null +++ b/addon/helpers/t.coffee @@ -0,0 +1,13 @@ +import Ember from 'ember' + +# This function receives the params `params, hash` +export translate = (params, options) -> + path = params[0] + toptions = options + if params.length > 1 and typeof(params[1].toJSON) is 'function' + $.extend toptions, params[1].toJSON() + text = t(path, toptions) + tag = if options.bare then text else "#{text}" + tag.htmlSafe() + +export default Ember.Helper.helper translate diff --git a/addon/initializers/ember-cli-dunlop-overrides.coffee b/addon/initializers/ember-cli-dunlop-overrides.coffee new file mode 100644 index 0000000..272f03d --- /dev/null +++ b/addon/initializers/ember-cli-dunlop-overrides.coffee @@ -0,0 +1,18 @@ +import ComponentOverrides from 'ember-cli-dunlop/overrides/component' +import ControllerOverrides from 'ember-cli-dunlop/overrides/controller' +import ObjectOverrides from 'ember-cli-dunlop/overrides/object' +import RouteOverrides from 'ember-cli-dunlop/overrides/route' +import DunlopTranslationFunctions from 'ember-cli-dunlop/dunlop-translations/global-functions' +#included: function(app) { +# this._super.included(app); +# app.import('ember-cli-dunlop/overrides/component'); +# app.import('ember-cli-dunlop/overrides/controller'); +#} +# Takes two parameters: container and application +export initialize = () -> + # application.register 'route', 'foo', 'service:foo' + +export default { + name: 'ember-cli-dunlop-overrides' + initialize: initialize +} diff --git a/addon/mixins/application-route.coffee b/addon/mixins/application-route.coffee new file mode 100644 index 0000000..0258306 --- /dev/null +++ b/addon/mixins/application-route.coffee @@ -0,0 +1,165 @@ +import Ember from 'ember' +import config from 'ember-get-config' +import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin' + +export default Ember.Mixin.create ApplicationRouteMixin, + current_user_loaded: (user) -> + beforeModel: (transition) -> + @_loadCurrentUser arguments... unless transition?.targetName is 'login' + + sessionAuthenticated: -> + @_super arguments... + @_loadCurrentUser() + + _loadCurrentUser: (transition) -> + #if userId = @get('session.data.authenticated.user_id') + return if transition?.targetName is 'login' + @get('store').queryRecord('user', me: true).then (user) => + @set 'router.globals.current_user', user + @current_user_loaded user # overloadable + , => + @set 'router.globals.current_user', null + #@transitionTo('login').then (loginRoute) -> loginRoute.set('router.globals.flash.notice', 'Please login') + @transitionTo('login') + + actions: + error: (error) -> + return unless @connections?.length # cannot use outlet at this moment + console.log error + container = Ember.getOwner(@) + if status = error.status || error?.errors?[0].status + switch parseInt(status) + when 401, 403 + @set 'router.globals.current_user', null + @transitionTo 'login' + return + controller = container.lookup "component:error-handling" + return unless controller?.set + controller.set 'error', error + @render 'global/error-messages', + into: 'application' + outlet: 'error-handling' + controller: controller + #@set "router.globals.flash_message", "Oops! Something went wrong!
#{error.message}".htmlSafe() + + clear_error: -> + #@disconnectOutlet # https://github.com/emberjs/ember-inspector/issues/625 + # outlet: 'error-handling' + # parentView: 'application' + @render 'modals/nothing', + outlet: 'error-handling' + into: 'application' + controller: 'application' + @set 'router.globals.modal_active', false + + clear_error_and_go_home: -> + @send 'clear_error' + @transitionTo 'index' + + didTransition: -> + @_super arguments... + Ember.run.next => + return unless path = @get('controller.currentPath') + @set 'router.globals.current_route', Ember.Object.create + path: path # a.b.c + node: path.replace(/.*\./, '') # c + primary: '.' in path + + openModal: (modalName, options={}) -> + if typeof options is 'function' + console.warn "Modal #{modalName} call has function as argument in stead of options!" + return + else if options.promise? and options.content + options = {model: options.content} + else if typeof options isnt 'object' + options = {model: options} + options.model = options.model.content if options.model?.promise? and options.model.content + container = Ember.getOwner(@) + + modalNameParts = modalName.split('/') + partialName = modalNameParts.pop() + + controller = container.lookup "component:modals/#{modalName}" + if not controller and modalNameParts.length + controller = container.lookup "component:#{modalNameParts.join('/')}/modals/#{partialName}" + controller ||= container.lookup "component:#{modalName}" + controller ||= container.lookup('component:modals/base') + #try + # controller = @controllerFor("modals/#{modalName}") + #catch error + # controller = @controllerFor("modals/base") + #controller ||= @controllerFor("modals/base") + controller.set 'model', options.model + defaultModalOptions = + closeOnOverlay: true + closeOnModalClick: false + modal_options = Ember.Object.create($.extend(defaultModalOptions, options)) + controller.set 'modal_options', modal_options + + # use root level partials for wizards and elementary use case + # Try to resolve the modalName traversing up the hierarchy so a modalName + # of wizard/create-distribution-point/copper/edit-distribution-point will look for partials: + # + # wizard/create-distribution-point/copper/-edit-distribution-point + # wizard/create-distribution-point/copper/modals/-edit-distribution-point + # modals/wizard/create-distribution-point/copper/-edit-distribution-point + # + # wizard/create-distribution-point/-edit-distribution-point + # wizard/create-distribution-point/modals/-edit-distribution-point + # modals/wizard/create-distribution-point/-edit-distribution-point + # + # wizard/-edit-distribution-point + # wizard/modals/-edit-distribution-point + # modals/wizard/-edit-distribution-point + # + # -edit-distribution-point + # modals/-edit-distribution-point + # + partialPath = null + while not partialPath and modalNameParts.length + if container.lookup "template:#{modalNameParts.join('/')}/-#{partialName}" + partialPath = "#{modalNameParts.join('/')}/#{partialName}" # without the dash + else if container.lookup "template:#{modalNameParts.join('/')}/modals/-#{partialName}" + partialPath = "#{modalNameParts.join('/')}/modals/#{partialName}" # without the dash + else if container.lookup "template:modals/#{modalNameParts.join('/')}/modals/-#{partialName}" + partialPath = "modals/#{modalNameParts.join('/')}/modals/#{partialName}" # without the dash + else if container.lookup "template:components/#{modalNameParts.join('/')}/-#{partialName}" + partialPath = "components/#{modalNameParts.join('/')}/#{partialName}" # without the dash + modalNameParts.pop() + partialPath ||= partialName if container.lookup("template:-#{partialName}") # can be root template + partialPath ||= "modals/#{partialName}" if container.lookup("template:modals/-#{partialName}") # can be root template + unless partialPath + message = "Cannot find template for modal '#{modalName}'" + console.log message + @set "router.globals.flash_message", message + return + + @incrementProperty 'router.globals.modals_counter' + controller.set 'partialName', partialPath + controller.willOpenModal() # can be implemented to setup stuff before rendering and hooks + @render 'modals/layout', + into: 'application' + outlet: 'modal' + controller: controller + @set 'router.globals.modal_active', true + false # return false since this result will in most cases the the last statement of an action. So prevent bubbling + + closeModal: -> + #@disconnectOutlet # https://github.com/emberjs/ember-inspector/issues/625 + # outlet: 'modal' + # parentView: 'application' + @render 'modals/nothing', + outlet: 'modal' + into: 'application' + controller: 'application' + @set 'router.globals.modal_active', false + + signOut: -> + @set 'router.globals.current_user', null + @get('session').invalidate() if @get('session.isAuthenticated') + @transitionTo 'login' + try + $.ajax + url: "#{config.apiHost}/users/sign_out" + type: 'DELETE' + dataType: 'json' diff --git a/addon/mixins/modal-base.coffee b/addon/mixins/modal-base.coffee new file mode 100644 index 0000000..4547684 --- /dev/null +++ b/addon/mixins/modal-base.coffee @@ -0,0 +1,121 @@ +import Ember from 'ember' +export default Ember.Component.extend + alert_message: "" + modal_options: {} + layoutName: 'modals/layout' + willOpenModal: -> # can be implemented, is fired after loading attributes, before rendering + title: (-> + # return title if directly set by options + return @get('modal_options.title') if @get('modal_options.title') + # return translated title_path if directly set by controller + translation_params = {} + if title_params = @get('modal_options.title_params') + translation_params = title_params + else if model = @get('model') + if model.toJSON + translation_params = model.toJSON() + else if model.__changeset__ and model._content?.toJSON + translation_params = model._content.toJSON() + + # return translated title_path if directly set by options + # return translated title_path if directly set by controller + if title_path = @get('modal_options.title_path') || @get('title_path') + return tspan(title_path, translation_params).htmlSafe() + + translation_params.emptyWhenNotFound = true # allow for fallbacks + + partial_title_path = "#{@get('partialName').replace(/-/g, '_').replace(/\W/g, '.')}.title" + return partialNameTranslation.htmlSafe() if partialNameTranslation = tspan(partial_title_path, translation_params) + + # @toString() => + underscored = @toString().replace(/.*(controller|component):/, '').replace(/::ember.*$/, '').replace(/\W+$/, '').underscore().replace(/\//g, '.') + return convention_translation.htmlSafe() if convention_translation = tspan("#{underscored}.title", translation_params) + + # Return a kind of humanized version of the title + underscored.replace(/.*\./,'').capitalize().replace(/_/, ' ') + ).property('model.id', 'modal_options.title_path') + body: (-> + @get('modal_options.body') + ).property('modal_options.body') + save_error: (error)-> + if typeof error is 'string' + @set 'alert_message', error + else if error?.status is 403 or error?.status is 401 + @set 'alert_message', 'Unauthorized action' + else + @set 'alert_message', 'Something went wrong' + save_success: -> + @set 'alert_message', '' + pre_hook_modals_count = @get('globals.modals_counter') + if save_callback = @get('modal_options.save') + save_callback.apply(@, arguments) + after_hook_modals_count = @get('globals.modals_counter') + @send 'closeModal' unless after_hook_modals_count > pre_hook_modals_count # when modals count changed, another modal is opened by the hook. Do not close + + actions: + close: -> + pre_hook_modals_count = @get('globals.modals_counter') + if close = @get('modal_options.close') + close.apply(@) + after_hook_modals_count = @get('globals.modals_counter') + unless @preventClose or after_hook_modals_count > pre_hook_modals_count # when modals count changed, another modal is opened by the hook. Do not close + @set 'alert_message', '' + @send 'closeModal' + closeOnOverlay: -> + @send('rollback_and_close') if @get('modal_options.closeOnOverlay') + false + modalClick: -> + @send('close') if @get('modal_options.closeOnModalClick') + false + ok: -> + if ok = @get('modal_options.ok') + ok.call(@) + @set 'alert_message', '' + @send 'closeModal' unless @preventClose + confirm: -> @send('ok') + saveRecord: -> + model = @get("model") + if typeof model.validate is 'function' + model.validate() # validate if method exists + if changeset = model.get('changeset') + changeset.validate() + if changeset.get('isValid') + changeset.save().then @save_success.bind(@), @save_error.bind(@) + else + if model.get('isValid') + model.save().then @save_success.bind(@), @save_error.bind(@) + #@send 'closeModal' unless @preventClose + else + console.log "[VALIDATION ERRORS]" + console.log model.get("errors") + rollback_and_close: -> + if model = @get("model") + if model.__changeset__ + model.rollback() + if model.get("_content.isNew") # delete new records on modal rollback + model.get("_content").deleteRecord() #NOTE: maybe this should be unloadRecord --force=true (state/dirty tracking issues) + else + model.rollbackAttributes() if typeof model.rollbackAttributes is 'function' + @send 'close' + destroyRecord: -> + my_scope = @ + destroy_callback = @get('modal_options.destroy_callback') || -> + # transition to top level route if current is nested inside a model.show route + if modelName = my_scope.get('model.constructor.modelName') + flat_model_name = modelName.replace(/^\w+\//, '').replace('/', '.') # panda/projec => project + if my_scope.get('router.globals.current_route.path')?.match("^#{flat_model_name}.show") + my_scope.get('router').transitionTo flat_model_name + + @modal 'confirm', + title_path: @get('modal_options.destroy_text_path') || 'general.destroy.are_you_sure' + model: @get('model') + ok: -> + model = @get("model") + model = model.get("_content") if model.__changeset__ + model.destroyRecord().then -> destroy_callback.call(my_scope, model) + @send 'closeModal' unless @preventClose + + closeModal: -> + container = Ember.getOwner(@) + ar = container.lookup('route:application') + ar.send 'closeModal' diff --git a/addon/models/dunlop-user.coffee b/addon/models/dunlop-user.coffee new file mode 100644 index 0000000..e166558 --- /dev/null +++ b/addon/models/dunlop-user.coffee @@ -0,0 +1,8 @@ +import DS from 'ember-data' + +export default DS.Model.extend + nickname: DS.attr('string') + email: DS.attr('string') + admin: DS.attr('boolean', defaultValue: false) + role_names: DS.attr(defaultValue: -> []) + diff --git a/addon/overrides/component.coffee b/addon/overrides/component.coffee new file mode 100644 index 0000000..c4929e6 --- /dev/null +++ b/addon/overrides/component.coffee @@ -0,0 +1,5 @@ +import Ember from 'ember' +export default Ember.Component.reopen + modal: (name, options={}) -> + target = Ember.getOwner(@).lookup('route:application') + target.send "openModal", name, options diff --git a/addon/overrides/controller.coffee b/addon/overrides/controller.coffee new file mode 100644 index 0000000..1fbf602 --- /dev/null +++ b/addon/overrides/controller.coffee @@ -0,0 +1,4 @@ +import Ember from 'ember' +export default Ember.Controller.reopen + modal: (name, options={})-> + @send "openModal", name, options diff --git a/addon/overrides/object.coffee b/addon/overrides/object.coffee new file mode 100644 index 0000000..a54e1de --- /dev/null +++ b/addon/overrides/object.coffee @@ -0,0 +1,20 @@ +import Ember from 'ember' +export default Ember.Object.reopen + getPropertyValues: (names...) -> + result = Ember.A() + for name in names + result.push @get(name) + result + + toProperties: -> + result = {} + for key, value of @ + continue unless @hasOwnProperty(key) + continue if key is 'changeset' + if typeof(value?.format) is 'function' + value = value.format() + else if typeof(value?.toISOString) is 'function' + value = value.toISOString() + result[key] = value + result + diff --git a/addon/overrides/route.coffee b/addon/overrides/route.coffee new file mode 100644 index 0000000..966fbf7 --- /dev/null +++ b/addon/overrides/route.coffee @@ -0,0 +1,5 @@ +import Ember from 'ember' + +Ember.Route.reopen + modal: (name, options = {})-> + @send "openModal", name, options diff --git a/addon/serializers/create-and-update.coffee b/addon/serializers/create-and-update.coffee new file mode 100644 index 0000000..cf9c354 --- /dev/null +++ b/addon/serializers/create-and-update.coffee @@ -0,0 +1,27 @@ +import { ActiveModelSerializer } from 'active-model-adapter' +export default ActiveModelSerializer.extend #DS.EmbeddedRecordsMixin, + + #serializeIntoHash: -> + # debugger + # @_super arguments... + # debugger + # 3 + + #payloadKeyFromModelName: (modelName) -> + # key = @_super(modelName) + # key.replace(/\//g, '.') + + serializeBelongsTo: (snapshot, json, relationship)-> + payloadKey = @keyForRelationship(relationship.key, 'belongsTo', 'serialize') + belongsToId = snapshot.record.get("#{relationship.key}.id") + belongsToId?= null + json[payloadKey] = belongsToId + # the super method looks up the inverse (workflow_step_definition) in stead of the constraint_workflow_step_definition id + #debugger + #@_super(arguments...) + + # If a relationship has configuration serialize: 'ids' then add the association's ids + # comments: DS.hasMany('comment', serialize: 'ids') + serializeHasMany: (snapshot, json, relationship)-> + if relationship.options.serialize is 'ids' + json["#{relationship.key.singularize()}_ids"] = Ember.get(snapshot.record, relationship.key).mapBy('id').toArray() diff --git a/addon/styles/_convenience-helpers.sass b/addon/styles/_convenience-helpers.sass new file mode 100644 index 0000000..cbb82fa --- /dev/null +++ b/addon/styles/_convenience-helpers.sass @@ -0,0 +1,69 @@ +.pointable + cursor: pointer + +.comma-separated-items + > * + &:after + content: ", " + &:last-child + &:after + content: "" + +.pull-right + float: right + +.unpadded + padding: 0 !important + +.ui.table + td + &.compact + padding: 0.5em 0.7em + +input:disabled + background-color: #ccc !important + +// .ui.grid might end up with columns having a negative bottom margin that captures the click event of the svg below it +.without-bottom-margin + margin-bottom: 0 !important + +.between-brackets + &:before + content: '(' + &:after + content: ')' + &.curly + &:before + content: '{' + &:after + content: '}' + &.angle + &:before + content: '<' + &:after + content: '>' + &.square + &:before + content: '[' + &:after + content: ']' + &.spaced + &:before + content: '( ' + &:after + content: ' )' + &.curly + &:before + content: '{ ' + &:after + content: ' }' + &.angle + &:before + content: '< ' + &:after + content: ' >' + &.square + &:before + content: '[ ' + &:after + content: ' ]' diff --git a/addon/styles/addon.scss b/addon/styles/addon.scss new file mode 100644 index 0000000..9ff14cf --- /dev/null +++ b/addon/styles/addon.scss @@ -0,0 +1,17 @@ +$non-default-attention-color: #f2711c; + +@import "./mixins/animations"; + +@import './convenience-helpers'; + +@import "./components/collapsible-content"; +@import "./components/error-handling"; +@import "./components/modal"; +@import "./components/flash-message"; +@import "./components/forms"; +@import "./components/page-title"; +@import "./components/pagination"; +@import "./components/table-filtering"; +@import "./components/title-box"; +@import "./components/ui-calendar-additions"; +@import "./components/ui-markdown-popup"; diff --git a/addon/styles/components/_collapsible-content.sass b/addon/styles/components/_collapsible-content.sass new file mode 100644 index 0000000..296af4d --- /dev/null +++ b/addon/styles/components/_collapsible-content.sass @@ -0,0 +1,3 @@ +.collapsible-content-container + .header + cursor: pointer diff --git a/addon/styles/components/_error-handling.sass b/addon/styles/components/_error-handling.sass new file mode 100644 index 0000000..1d05841 --- /dev/null +++ b/addon/styles/components/_error-handling.sass @@ -0,0 +1,3 @@ +.dunlop-global-error + &.hidden + display: none diff --git a/addon/styles/components/_flash-message.sass b/addon/styles/components/_flash-message.sass new file mode 100644 index 0000000..2212850 --- /dev/null +++ b/addon/styles/components/_flash-message.sass @@ -0,0 +1,26 @@ +.flash-message + position: fixed + top: 60px + right: -440px + background-color: rgba(200,200,200,0.8) + padding: 20px + width: 400px + border-radius: 9999px + font-weight: bold + font-size: 1.4 + z-index: 8976 + +animation(flashMessageInactive 0.5s) + &.active + +animation(flashMessageActive 0.5s) + right: 20px + ++keyframes(flashMessageActive) + from + right: -440px + to + right: 20px ++keyframes(flashMessageInactive) + from + right: 20px + to + right: -440px diff --git a/addon/styles/components/_forms.sass b/addon/styles/components/_forms.sass new file mode 100644 index 0000000..73f48b7 --- /dev/null +++ b/addon/styles/components/_forms.sass @@ -0,0 +1,19 @@ +.ui.form + .form-label + display: inline-block + vertical-align: top + width: 35% + .form-field + display: inline-block + width: 64% + @media screen and (max-width: 723px) + .form-label + width: 99% + .form-field + width: 99% + +.form-actions + clear: both + margin-top: 12px + padding-top: 8px + border-top: 1px solid #ddd diff --git a/addon/styles/components/_modal.sass b/addon/styles/components/_modal.sass new file mode 100644 index 0000000..63d0a2b --- /dev/null +++ b/addon/styles/components/_modal.sass @@ -0,0 +1,74 @@ +.modal + margin: 10px auto + width: 1122px + max-width: 100% + background-color: #fff + padding: 1em + padding-bottom: 0 + max-height: calc(100% - 20px) + overflow-y: scroll + z-index: 6524 + .modal-body + // Add space for modal-actions + padding-bottom: 86px + position: relative + min-height: 392px + .modal-rollback + //+button($bg: $secondary-color) + //@extend .ui, .button + .modal-close + //+button($bg: #ddd) + //@extend .ui, .button + .modal-confirm, .modal-ok, .modal-save, .modal-add + //+button + //@extend .primary + .modal-destroy + //+button($bg: $alert-color) + //@extend .negative + margin-right: 8px + .modal-edit + //+button($bg: $warning-color) + margin-right: 8px + .done + text-decoration: line-through + .modal-actions + border-top: 1px solid #999 + padding-top: 7px + padding-bottom: 7px + margin-bottom: -86px + > a + margin-bottom: 0 + &.sticky + position: absolute + margin-bottom: 0 + box-shadow: 0 -3px 9px -2px rgba(0, 0, 0, 0.8) + background-color: #eee + padding-left: 1em + padding-right: 1em + bottom: 0 + width: calc(100% + 2em) + //Width: 100% + margin-left: -1em + //margin-right: -1em + border-top: 1px solid #999 + .pull-right + .ui.action.input + margin-right: 122px + .ui.message + margin: 0 +.modal-alert + //@extend .alert-box + //@extend .alert + //color: $alert-color + +.overlay + height: 100% + width: 100% + position: fixed + top: 0 + left: 0 + background-color: rgba(0, 0, 0, 0.5) + z-index: 6522 + +.flush--top + margin-top: 0 diff --git a/addon/styles/components/_page-title.sass b/addon/styles/components/_page-title.sass new file mode 100644 index 0000000..9135dc7 --- /dev/null +++ b/addon/styles/components/_page-title.sass @@ -0,0 +1,6 @@ +.page-title + &.ui.header + margin-top: 12px + .between-brackets + &:before + margin-left: 1rem diff --git a/addon/styles/components/_pagination.sass b/addon/styles/components/_pagination.sass new file mode 100644 index 0000000..e9f6545 --- /dev/null +++ b/addon/styles/components/_pagination.sass @@ -0,0 +1,33 @@ +ul.pagination + padding: 0 + list-style: none + a + cursor: pointer + &.disabled + cursor: default + li + display: inline + &.arrow + a + padding: 0 6px + &.next + padding-left: 3px // subtract margin-right from li + &.page-number + margin-right: 3px + a + background-color: #ccc + padding: 4px + color: #666 + border-radius: 3px + &.active + a + color: white + background-color: black + font-weight: bold + +.ui.top-right.pagination.menu + float: right + border-bottom-left-radius: 0 + border-bottom-right-radius: 0 + margin-top: 8px + border-bottom-width: 0 diff --git a/addon/styles/components/_table-filtering.sass b/addon/styles/components/_table-filtering.sass new file mode 100644 index 0000000..601dc3d --- /dev/null +++ b/addon/styles/components/_table-filtering.sass @@ -0,0 +1,81 @@ +// ransack componennt integration +table thead tr.table-filters-row, table.ui.table thead tr.table-filters-row + input + margin: 0 + margin-top: 3px + padding: 3px + button + margin: 0 + margin-top: 3px + th + padding: 0 4px + &.active-filter + background-color: rgba(242, 112, 28, 0.15) + color: $non-default-attention-color + .active-button + background-color: $non-default-attention-color + color: white + font-weight: bold + i, i.ui.icon + cursor: pointer + pointer-events: auto + // Allow force by means of extra class + &.action + cursor: pointer !important + pointer-events: auto !important + &.filter-date-range + .menu + .divider + .item + padding-top: 0 !important + .item + .divider + margin-top: 0 + .select-week + input + width: 72px + .select-year + input + width: 92px + .ui.horizontal.divider + // Without this css it renders a double border if text is used + border-top: 0 + + + +// semantic-ui implementation +table + td, th + &.actions + // semantic-ui .collapsing + width: 1px + white-space: nowrap + text-align: right + .ui.icon.button + padding: 0.4rem +table.ui.table + thead + // custom + tr.filters + th + padding: 0 + i.close.icon + cursor: pointer + margin-left: 6px + .active-filter + .active-button + background-color: $non-default-attention-color + color: white + font-weight: bold + tr.table-filters-row + .filter-dropdown-select-multiple + .ui.action.input + margin-right: 49px // space for action button + input + margin-top: 0 + .ui.icon.button + i + // This one is somehow needed for the right filter icon fit + padding-top: 0 +.ui.header + &.active-filter + color: $non-default-attention-color + diff --git a/addon/styles/components/_title-box.sass b/addon/styles/components/_title-box.sass new file mode 100644 index 0000000..5cd5222 --- /dev/null +++ b/addon/styles/components/_title-box.sass @@ -0,0 +1,71 @@ +/* Based on the bootstrap 3+ panel, of course under a different name. + The conversion is done based on: + http://stackoverflow.com/questions/19220027/can-you-use-panels-in-bootstrap-2-3 +.title-box + padding: 15px + margin-bottom: 20px + background-color: #ffffff + border: 1px solid #dddddd + border-radius: 4px + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05) + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05) + +.title-box-heading + padding: 10px 15px + margin: -15px -15px 15px + font-size: 17.5px + font-weight: 500 + background-color: #f5f5f5 + border-bottom: 1px solid #dddddd + border-top-right-radius: 3px + border-top-left-radius: 3px + +.title-box-footer + padding: 10px 15px + margin: 15px -15px -15px + background-color: #f5f5f5 + border-top: 1px solid #dddddd + border-bottom-right-radius: 3px + border-bottom-left-radius: 3px + +.title-box-primary + border-color: #428bca + .title-box-heading + color: #ffffff + background-color: #428bca + border-color: #428bca + +.title-box-success + border-color: #d6e9c6 + + .title-box-heading + color: #468847 + background-color: #dff0d8 + border-color: #d6e9c6 + +.title-box-warning + border-color: #fbeed5 + + .title-box-heading + color: #c09853 + background-color: #fcf8e3 + border-color: #fbeed5 + +.title-box-danger + border-color: #eed3d7 + + .title-box-heading + color: #b94a48 + background-color: #f2dede + border-color: #eed3d7 + +.title-box-info + border-color: #bce8f1 + + .title-box-heading + color: #3a87ad + background-color: #d9edf7 + border-color: #bce8f1 + +//State specific additions + diff --git a/addon/styles/components/_ui-calendar-additions.sass b/addon/styles/components/_ui-calendar-additions.sass new file mode 100644 index 0000000..58ec328 --- /dev/null +++ b/addon/styles/components/_ui-calendar-additions.sass @@ -0,0 +1,24 @@ +// This fixes the issue of ember-semantic-ui-calendar not functioning inside a definition table +.ui.definition.table .ui.calendar + padding: 0 + table + //border: 1px solid rgba(34,36,38,.15) + border-radius: .28571429rem + box-shadow: none + color: rgba(0,0,0,.87) + tr + th + pointer-events: all + font-weight: bold + color: rgba(0,0,0,.87) + background-color: #F9FAFB + &:nth-child(2) + border-left: 0 + td + pointer-events: all + &:first-child:not(.ignored) + background-color: white + font-weight: normal + &.disabled + color: rgba(40, 40, 40, 0.3) + diff --git a/addon/styles/components/_ui-markdown-popup.sass b/addon/styles/components/_ui-markdown-popup.sass new file mode 100644 index 0000000..46510eb --- /dev/null +++ b/addon/styles/components/_ui-markdown-popup.sass @@ -0,0 +1,8 @@ +body .ui.popup + z-index: 9021 + > + ul, ol + margin-top: 4px + margin-bottom: 4px + padding-left: 1em + diff --git a/addon/styles/mixins/_animations.sass b/addon/styles/mixins/_animations.sass new file mode 100644 index 0000000..1f9fea7 --- /dev/null +++ b/addon/styles/mixins/_animations.sass @@ -0,0 +1,27 @@ +=animation($animate...) + $max: length($animate) + $animations: "" + + @for $i from 1 through $max + $animations: #{$animations + nth($animate, $i)} + + @if $i < $max + $animations: #{$animations + ", "} + + -webkit-animation: $animations + -moz-animation: $animations + -o-animation: $animations + animation: $animations + +=keyframes($animationName) + @-webkit-keyframes #{$animationName} + @content + + @-moz-keyframes #{$animationName} + @content + + @-o-keyframes #{$animationName} + @content + + @keyframes #{$animationName} + @content diff --git a/addon/templates/components/collapsible-content.emblem b/addon/templates/components/collapsible-content.emblem new file mode 100644 index 0000000..29f70b7 --- /dev/null +++ b/addon/templates/components/collapsible-content.emblem @@ -0,0 +1,9 @@ +if collapsed + .ui.header{action 'toggleCollapsed'} + i.arrow.right.icon + .content= title +else + .ui.header{action 'toggleCollapsed'} + i.arrow.down.icon + .content= title + = yield diff --git a/addon/templates/components/editable-attribute.emblem b/addon/templates/components/editable-attribute.emblem new file mode 100644 index 0000000..fb8a241 --- /dev/null +++ b/addon/templates/components/editable-attribute.emblem @@ -0,0 +1,26 @@ +/.ui.form + .ui.right.floated.icon.buttons + if isEditing + = push-action type='rollback' action='rollbackRecord' + = push-action type='destroy' action='destroyRecord' + = push-action type='save' action='saveRecord' + else + if can_make_editable + /= push-action type='edit' action='editRecord' + if isEditing + = input value=model.name enter='saveRecord' + else + span= model.name +if isEditing + .ui.action.input + = input value=model.name enter='saveRecord' + = push-action type='rollback' action='rollbackRecord' + = push-action type='destroy' action='destroyRecord' + = push-action type='save' action='saveRecord' + if validation_message + br + .ui.pointing.red.basic.label= validation_message +else + span= model.name + = yield + diff --git a/addon/templates/components/error-handling.emblem b/addon/templates/components/error-handling.emblem new file mode 100644 index 0000000..5647dbb --- /dev/null +++ b/addon/templates/components/error-handling.emblem @@ -0,0 +1,3 @@ +i.close.icon +.header= error_header += yield diff --git a/addon/templates/components/pagination-progress.emblem b/addon/templates/components/pagination-progress.emblem new file mode 100644 index 0000000..c98811f --- /dev/null +++ b/addon/templates/components/pagination-progress.emblem @@ -0,0 +1,3 @@ += progressText += yield + diff --git a/addon/templates/components/push-action.emblem b/addon/templates/components/push-action.emblem new file mode 100644 index 0000000..afad889 --- /dev/null +++ b/addon/templates/components/push-action.emblem @@ -0,0 +1,3 @@ +i.icon class=iconClass +if hasBlock + span.push-action-content= yield diff --git a/addon/templates/components/ui-markdown-popup.emblem b/addon/templates/components/ui-markdown-popup.emblem new file mode 100644 index 0000000..0a90f09 --- /dev/null +++ b/addon/templates/components/ui-markdown-popup.emblem @@ -0,0 +1 @@ += yield diff --git a/addon/test-helpers/all.coffee b/addon/test-helpers/all.coffee new file mode 100644 index 0000000..207335a --- /dev/null +++ b/addon/test-helpers/all.coffee @@ -0,0 +1 @@ +import './freeze-moment' diff --git a/addon/test-helpers/freeze-moment.coffee b/addon/test-helpers/freeze-moment.coffee new file mode 100644 index 0000000..2d6b849 --- /dev/null +++ b/addon/test-helpers/freeze-moment.coffee @@ -0,0 +1,11 @@ +window.freezeMoment = (time, callback) -> + #time = "#{time}Z" if typeof time is 'string' and time.slice(-1) isnt 'Z' # work with UTC dates? + time = moment(time) unless time._isAMomentObject + originalMomentNow = moment.now + date = time.toDate() + moment.now = -> date + callback.call null, time + moment.now = originalMomentNow + time + + diff --git a/addon/transforms/datemoment.coffee b/addon/transforms/datemoment.coffee new file mode 100644 index 0000000..063391d --- /dev/null +++ b/addon/transforms/datemoment.coffee @@ -0,0 +1,16 @@ +import DS from 'ember-data' +import moment from 'moment' + +# mozo magick, better than ember-cli-moment-transform, since it preserves dates through time zones!!!! +export default DS.Transform.extend + deserialize: (serialized)-> + return serialized unless serialized + m = moment(serialized) + m.type = 'date' + m + + serialize: (deserialized, options = {})-> + return deserialized unless deserialized + deserialized = moment(deserialized) unless moment.isMoment(deserialized) + deserialized.format(options.format || 'YYYY-MM-DD') + diff --git a/addon/transforms/moment.coffee b/addon/transforms/moment.coffee new file mode 100644 index 0000000..dfc0a0a --- /dev/null +++ b/addon/transforms/moment.coffee @@ -0,0 +1,16 @@ +import DS from 'ember-data' +import moment from 'moment' + +# mozo magick, better than ember-cli-moment-transform, since it preserves dates through time zones!!!! +export default DS.Transform.extend + deserialize: (serialized)-> + return serialized unless serialized + m = moment(serialized) + m.type = 'datetime' + m + + serialize: (deserialized, options = {})-> + return deserialized unless deserialized + deserialized = moment(deserialized) unless moment.isMoment(deserialized) + deserialized.format(options.format) + diff --git a/addon/utils/date-sort.coffee b/addon/utils/date-sort.coffee new file mode 100644 index 0000000..afcab1c --- /dev/null +++ b/addon/utils/date-sort.coffee @@ -0,0 +1,17 @@ +dateSort = (date_a, date_b) -> + if date_a + a = if date_a.toISOString then date_a.toISOString() else date_a + else + a = null + if date_b + b = if date_b.toISOString then date_b.toISOString() else date_b + else + b = null + Ember.compare a, b + +create_date_sort = (attribute) -> + (object_a, object_b) -> + dateSort object_a.get(attribute), object_b.get(attribute) +export { create_date_sort } +export default dateSort + diff --git a/addon/utils/get-max-suffix.coffee b/addon/utils/get-max-suffix.coffee new file mode 100644 index 0000000..be12972 --- /dev/null +++ b/addon/utils/get-max-suffix.coffee @@ -0,0 +1,10 @@ +getMaxSuffix = (array) -> + max = 0 + return 0 unless array + array.forEach (element)-> + return unless element + max = Math.max(match[0], max) if match = String(element).match /\d+$/ + max + +window.get_max_suffix = getMaxSuffix # make globally available +export default getMaxSuffix diff --git a/addon/utils/integer-to-string.coffee b/addon/utils/integer-to-string.coffee new file mode 100644 index 0000000..066a475 --- /dev/null +++ b/addon/utils/integer-to-string.coffee @@ -0,0 +1,11 @@ +# based on https://github.com/alisdair/horses-and-chickens +import Ember from 'ember' + +export default (attribute) -> + Ember.computed attribute, + get: -> + @get(attribute).toString() + + set: (key, value) -> + @set(attribute, parseInt(value, 10)) + return value diff --git a/addon/utils/map-action.coffee b/addon/utils/map-action.coffee new file mode 100644 index 0000000..bff663c --- /dev/null +++ b/addon/utils/map-action.coffee @@ -0,0 +1,46 @@ +mapSum = (target, property) -> + Ember.computed "#{target}.@each.#{property}", -> + return 0 unless ary_result = @get(target).mapBy(property) + return 0 unless ary_result.length + ary_result.reduce( ((sum, item) -> sum + item), 0) + +mapFilter = (target, property, filter_by_key) -> + Ember.computed "#{target}.@each.#{property}", filter_by_key, -> + @get(target).filterBy(property, @get(filter_by_key)) + +mapHasMany = (target, has_many_relation) -> + Ember.computed "#{target}.@each.#{has_many_relation}", -> + result = Ember.A() + Ember.RSVP.all(@get(target).mapBy(has_many_relation)).then (targets_has_many_associations) -> + Ember.run -> + targets_has_many_associations.forEach (target_has_many_records) -> + result.pushObjects target_has_many_records.toArray() + result + +mapMany = (target, many_attribute) -> + Ember.computed "#{target}.@each.#{many_attribute}", -> + result = Ember.A() + Ember.run => + targets = @get(target) + if Ember.isArray(targets) + targets.forEach (record) -> + result.pushObjects record.get(many_attribute) + else + targets.then (records) -> + records.forEach (record) -> + result.pushObjects record.get(many_attribute) + result + +mapMax = (target, property, options={}) -> + Ember.computed "#{target}.@each.#{property}", -> + return 0 unless ary_result = @get(target).mapBy(property) + return 0 unless ary_result.length + ary_result.reduce (max, item) -> + Math.max(max, item) + , -Infinity + +export { mapSum, mapFilter, mapHasMany, mapMany, mapMax } +export default Ember.Object.create + mapSum: mapSum + mapFilter: mapFilter + mapHasMany: mapHasMany diff --git a/addon/utils/merge-objects.coffee b/addon/utils/merge-objects.coffee new file mode 100644 index 0000000..fbe2bf3 --- /dev/null +++ b/addon/utils/merge-objects.coffee @@ -0,0 +1,12 @@ +# This is an objects merge helper but in stead of +# mergin into the first object, it merges into the last object. +# This is a lot safer and better in coffeescript environments, since the +# last argument is the indented subject(focus) of configuration. +# Since the attributes of these have prevalence, this merger comes last +# in the used Ember.assign +mergeObjects = (objects..., target) -> + result = {} + Ember.assign result, objects..., target + result +window.merge_objects = mergeObjects +export default mergeObjects diff --git a/addon/utils/range-parser.coffee b/addon/utils/range-parser.coffee new file mode 100644 index 0000000..adcb99a --- /dev/null +++ b/addon/utils/range-parser.coffee @@ -0,0 +1,17 @@ +export default Ember.Object.create + # go from string: + # 19, 21-27, 98-101 + # to + # [19,21,22,23,24,25,26,27,98,99,100,101] + parse: (identifier) -> + ids = [] + return ids unless identifier + String(identifier).replace(/\s/g, '').split(',').forEach (id_spec) -> + [id_start, id_end] = id_spec.split('-') + id_start = parseInt(id_start) + if id_end + range_result = [id_start..id_end] + ids = ids.concat range_result + else + ids.push id_start + ids diff --git a/addon/utils/sort-numeric.coffee b/addon/utils/sort-numeric.coffee new file mode 100644 index 0000000..b79b30e --- /dev/null +++ b/addon/utils/sort-numeric.coffee @@ -0,0 +1,12 @@ +sortNumeric = (target, keys...) -> + Ember.computed.sort target, (a, b) -> + [int_a, int_b] = [parseInt(a.get(keys[0])), parseInt(b.get(keys[0]))] + if int_a > int_b + 1 + else if int_a < int_b + -1 + else + 0 + +export { sortNumeric } +export default sortNumeric diff --git a/addon/validators/conditional-presence.coffee b/addon/validators/conditional-presence.coffee new file mode 100644 index 0000000..d711b5f --- /dev/null +++ b/addon/validators/conditional-presence.coffee @@ -0,0 +1,34 @@ +import Ember from 'ember' +import buildMessage from 'ember-changeset-validations/utils/validation-errors' + +_isPresent = (value) -> + if (value instanceof Ember.ObjectProxy || value instanceof Ember.ArrayProxy) + return Ember._isPresent(Ember.get(value, 'content')) + Ember.isPresent(value) + +_testPresence = (key, value, presence, context = {}) -> + if presence + return Ember._isPresent(value) || buildMessage(key, 'present', value, context) + else + return Ember.isBlank(value) || buildMessage(key, 'blank', value, context) + +export default (opts)-> + (key, value, oldValue, changes, content) => + if typeof opts is "function" + presence = true + condition = opts + else + presence = if "presence" in opts then opts.presence else true + condition = opts.condition + + # since contentn is the model we have to emulate the changes for now + condition_object = Ember.assign(content.toJSON(), changes) + if condition.call(condition_object) # perform validation when condition returns true + if presence + _isPresent(value) || buildMessage(key, 'present', value, opts) + else + isBlank(value) || buildMessage(key, 'blank', value, opts) + else + true + #return _testPresence(key, value, opts) + diff --git a/app/.gitkeep b/app/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/adapters/user.coffee b/app/adapters/user.coffee new file mode 100644 index 0000000..f1c3813 --- /dev/null +++ b/app/adapters/user.coffee @@ -0,0 +1,10 @@ +import ApplicationAdapter from './application' +import config from 'ember-get-config' + +export default ApplicationAdapter.extend + urlForQueryRecord: (query) -> + if query.me + delete query.me + return config.userMeEndpoint || "#{config.apiHost}#{config.rootURL}users/me" + @_super arguments... + diff --git a/app/authenticators/devise.coffee b/app/authenticators/devise.coffee new file mode 100644 index 0000000..1f2274e --- /dev/null +++ b/app/authenticators/devise.coffee @@ -0,0 +1,5 @@ +import Base from 'ember-simple-auth/authenticators/devise' +import config from 'ember-get-config' + +export default Base.extend + serverTokenEndpoint: config.serverTokenEndpoint || "#{config.apiHost}/users/sign_in" diff --git a/app/authorizers/devise.coffee b/app/authorizers/devise.coffee new file mode 100644 index 0000000..30ac374 --- /dev/null +++ b/app/authorizers/devise.coffee @@ -0,0 +1,3 @@ +import Devise from 'ember-simple-auth/authorizers/devise' + +export default Devise.extend {} diff --git a/app/components/collapsible-content.js b/app/components/collapsible-content.js new file mode 100644 index 0000000..c9c7b01 --- /dev/null +++ b/app/components/collapsible-content.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/components/collapsible-content'; diff --git a/app/components/d-breadcrumb.coffee b/app/components/d-breadcrumb.coffee new file mode 100644 index 0000000..7302fdb --- /dev/null +++ b/app/components/d-breadcrumb.coffee @@ -0,0 +1,15 @@ +import Ember from 'ember' + +export default Ember.Component.extend + classNames: ['section'] + active: false + isFirst: false + classNameBindings: ['active'] + didInsertElement: -> + @_super arguments... + Ember.run.scheduleOnce 'afterRender', => @get('register')(this) + + willDestroyElement: -> + @get('unregister')(this) + @_super arguments... + diff --git a/app/components/d-breadcrumbs.coffee b/app/components/d-breadcrumbs.coffee new file mode 100644 index 0000000..5ca2251 --- /dev/null +++ b/app/components/d-breadcrumbs.coffee @@ -0,0 +1,20 @@ +import Ember from 'ember' + +export default Ember.Component.extend + css_type: 'semantic' + classNameBindings: ['css_type_classes'] + css_type_classes: Ember.computed 'css_type', -> + switch @get('css_type') + when 'semantic' then 'dunlop-breadcrumbs ui container bottom attached block header' + else 'dunlop-breadcrumbs' + + items: Ember.computed -> Ember.A([]) # emberx-select example + + actions: + registerItem: (item) -> + item.set('isFirst', true) unless @get('items.length') + @get('items').push(item) + + unregisterItem: (item) -> + @get('items').removeObject(item) + diff --git a/app/components/editable-attribute.js b/app/components/editable-attribute.js new file mode 100644 index 0000000..518b93b --- /dev/null +++ b/app/components/editable-attribute.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/components/editable-attribute'; diff --git a/app/components/error-handling.coffee b/app/components/error-handling.coffee new file mode 100644 index 0000000..ee796d0 --- /dev/null +++ b/app/components/error-handling.coffee @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/components/error-handling' diff --git a/app/components/flash-message.coffee b/app/components/flash-message.coffee new file mode 100644 index 0000000..88d9d9d --- /dev/null +++ b/app/components/flash-message.coffee @@ -0,0 +1,11 @@ +import Ember from 'ember' + +export default Ember.Component.extend + #layoutName: 'components/flash-message' + classNames: ['flash-message'] + classNameBindings: ['message:active'] + message: Ember.computed.alias 'globals.flash_message' + click: -> @set 'globals.flash_message', '' + inactivator: Ember.observer 'message', -> + if @get('message') + Ember.run.later((=> @set 'globals.flash_message', ''), 4000) diff --git a/app/components/login-form.coffee b/app/components/login-form.coffee new file mode 100644 index 0000000..390d1e2 --- /dev/null +++ b/app/components/login-form.coffee @@ -0,0 +1,43 @@ +import Ember from 'ember' +#import config from '../config/environment' + +export default Ember.Component.extend + session: Ember.inject.service('session') + identification: '' + password: '' + actions: + submitLogin: -> + unless email=@get('identification') + return @$('.login-email').focus() + unless password=@get('password') + return @$('.login-password').focus() + + ## STRATEGY 1 HARD TOKEN FROM API + #uri = "#{config.apiHost}/#{config.apiNamespace}/users/obtain_token" + #Ember.$.post uri, email: email, password: password, (result) => + # result = JSON.parse(result) if typeof result is 'string' + # unless result.valid + # @set 'globals.flash_message', 'Invalid login' + # return false + # localStorage.setItem 'authentication_token', result.authentication_token + # localStorage.setItem 'user_id', result.user_id + # @get('router').transitionTo('index').then (index_route) -> + # # do not set on globals of current route, since that one will be 'destroyed' in trasition + # index_route.store.findRecord('user', result.user_id).then (current_user) -> index_route.set('router.globals.current_user', current_user) + + ## STRATEGY 2 DEVISE SESSION BASED + #uri = "#{config.apiHost}/users/sign_in.json" + #Ember.$.post uri, email: email, password: password, (result) => + # debugger + #.fail (error) => + # # if error.status is 401 + # @set 'globals.flash_message', 'Invalid login' + + # STRATEGY 2 DEVISE EMBER SIMPLE AUTH BASED + @get('session').authenticate('authenticator:devise', email, password).catch (reason) => + message = if typeof(reason) is 'string' then reason else reason.error + message ||= reason.message + message ||= 'Invalid login' + @set 'globals.flash_message', message + false + diff --git a/app/components/modals/base.coffee b/app/components/modals/base.coffee new file mode 100644 index 0000000..15233d7 --- /dev/null +++ b/app/components/modals/base.coffee @@ -0,0 +1,4 @@ +import BaseModal from 'ember-cli-dunlop/mixins/modal-base' + +export default BaseModal.extend + dunlop_base: true diff --git a/app/components/number-input.js b/app/components/number-input.js new file mode 100644 index 0000000..55888fc --- /dev/null +++ b/app/components/number-input.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/components/number-input'; diff --git a/app/components/page-title.coffee b/app/components/page-title.coffee new file mode 100644 index 0000000..1178704 --- /dev/null +++ b/app/components/page-title.coffee @@ -0,0 +1,2 @@ +import Addon from 'ember-cli-dunlop/components/page-title' +export default Addon diff --git a/app/components/pagination-progress.js b/app/components/pagination-progress.js new file mode 100644 index 0000000..9711e06 --- /dev/null +++ b/app/components/pagination-progress.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/components/pagination-progress'; diff --git a/app/components/push-action.js b/app/components/push-action.js new file mode 100644 index 0000000..c8f0797 --- /dev/null +++ b/app/components/push-action.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/components/push-action'; diff --git a/app/components/table-filters.coffee b/app/components/table-filters.coffee new file mode 100644 index 0000000..c89b29f --- /dev/null +++ b/app/components/table-filters.coffee @@ -0,0 +1,32 @@ +import Ember from 'ember' + +export default Ember.Component.extend + #filter_calls: 0 + tagName: 'tr' + classNames: ['table-filters-row', 'ui', 'form'] + filters: Ember.Object.create() + content: null # PagedRemoteArray from ember-cli-pagination + key: 'name_eq' + apply_filters: null # allow custom apply filters with application logic + + init: -> + @_super arguments... + Ember.defineProperty @, 'filter_calls', Ember.computed.alias("filters.filter_calls") + + actions: + apply_filters: -> + query = @get('filters')?.toProperties() || {} + if @get('apply_filters') + @get('apply_filters')(query) + else # use standard implementation + filter_calls = @incrementProperty('filter_calls') + console.log "QUERY" + console.log query + @set 'content.page', 1 + @set 'content.lastPage', null + @get('content').setOtherParam?('q', query) + clear_filters: -> + @set 'filters', Ember.Object.create() + @send 'apply_filters' + @set 'filter_calls', 0 + diff --git a/app/components/table-filters/actions.coffee b/app/components/table-filters/actions.coffee new file mode 100644 index 0000000..29d842f --- /dev/null +++ b/app/components/table-filters/actions.coffee @@ -0,0 +1,13 @@ +import Ember from 'ember' + +export default Ember.Component.extend + tagName: 'th' + classNames: ['actions'] + apply_filters: -> + clear_filters: -> + + actions: + apply_filters: -> + @get('apply_filters')(@) + clear_filters: -> + @get('clear_filters')(@) diff --git a/app/components/table-filters/date-range.coffee b/app/components/table-filters/date-range.coffee new file mode 100644 index 0000000..7627614 --- /dev/null +++ b/app/components/table-filters/date-range.coffee @@ -0,0 +1,90 @@ +import Ember from 'ember' + +export default Ember.Component.extend + tagName: 'th' + classNames: ['filter', 'filter-date-range'] + classNameBindings: ['has_active_filter_value:active-filter'] + expandingTime: false + weekSelector: false + placeholder: '-/-' + format: 'll' + apply_filters: -> + eager_filter: true # filter on enter + week_selector_week: '' + week_selector_year: String(new Date().getFullYear()) + has_active_filter_value: Ember.computed.or 'lower_bound', 'upper_bound' + target_date_filter_text: Ember.computed '{lower_bound,upper_bound}', -> + format = @get('format') + if gteq = @get('lower_bound') + gteq = gteq.format(format) + else + gteq = '-' + if lteq = @get('upper_bound') + lteq = lteq.format(format) + else + lteq = '-' + "#{gteq} / #{lteq}" + + + has_valid_week_selector_value: Ember.computed '{week_selector_week,week_selector_year}', -> + return false unless week = @get('week_selector_week') + week = parseInt(week) + return false unless Number.isFinite(week) + return false unless week < 54 + return false unless year = String(@get('week_selector_year')) + return false unless year.length is 4 + true + + init: -> + @_super() + Ember.defineProperty @, 'lower_bound', Ember.computed.alias("filters.#{@get('key')}_gteq") + Ember.defineProperty @, 'upper_bound', Ember.computed.alias("filters.#{@get('key')}_lteq") + + actions: + set_filter: (key, value) -> + @set key, value + @$('.dropdown').dropdown('hide') if key is 'upper_bound' + @get('apply_filters')(@) if @get('eager_filter') + clear_filters: -> + @set 'lower_bound', '' + @set 'upper_bound', '' + @$('.dropdown').dropdown('hide') + @get('apply_filters')(@) if @get('eager_filter') + + set_filter_today: -> + today = moment() + @setProperties + lower_bound: today + upper_bound: today + @get('apply_filters')(@) + @$('.dropdown').dropdown('hide') + + set_filter_this_week: -> + today = moment() + @setProperties + lower_bound: today.clone().startOf('isoWeek') + upper_bound: today.endOf('isoWeek') + @get('apply_filters')(@) + @$('.dropdown').dropdown('hide') + + set_filter_this_month: -> + today = moment() + @setProperties + lower_bound: today.clone().startOf('month') + upper_bound: today.endOf('month') + @get('apply_filters')(@) + @$('.dropdown').dropdown('hide') + + week_selector_activate: -> + return unless week = @get('week_selector_week') + week = parseInt(week) + return unless Number.isFinite(week) + return unless week < 54 + return unless year = String(@get('week_selector_year')) + return unless year.match /^\d+$/ + week_start = moment(year).add(week, 'weeks').startOf('isoWeek') + @setProperties + lower_bound: week_start + upper_bound: week_start.clone().endOf('isoWeek') + @get('apply_filters')(@) + @$('.dropdown').dropdown('hide') diff --git a/app/components/table-filters/date.coffee b/app/components/table-filters/date.coffee new file mode 100644 index 0000000..5596177 --- /dev/null +++ b/app/components/table-filters/date.coffee @@ -0,0 +1,15 @@ +import Ember from 'ember' + +export default Ember.Component.extend + placeholder: '-' + eager_filter: true # filter on enter + has_active_filter_value: Ember.computed.bool 'value' + + init: -> + @_super() + Ember.defineProperty @, 'value', Ember.computed.alias("filters.#{@get('key')}") + + actions: + select_date: (date) -> + @set "filters.#{@get('key')}", date?.toISOString() + @get('apply_filters')(@) if @get('eager_filter') diff --git a/app/components/table-filters/dropdown-select-multiple.coffee b/app/components/table-filters/dropdown-select-multiple.coffee new file mode 100644 index 0000000..1beeaf9 --- /dev/null +++ b/app/components/table-filters/dropdown-select-multiple.coffee @@ -0,0 +1,61 @@ +import Ember from 'ember' +{get} = Ember +export default Ember.Component.extend + tagName: 'th' + classNames: ['filter', 'filter-dropdown-select-multiple'] + classNameBindings: ['has_active_filter_value:active-filter', 'key'] + apply_filters: -> + eager_filter: false # filter on single select + has_active_filter_value: Ember.computed.bool 'selected.length' + search_string: '' + search_placeholder: 'Search...' #.htmlSafe() + filtered_options: Ember.computed 'options.length', 'search_string', -> + return @get('options') unless @get('search_string.length') > 1 + filtered_options = [] + tester = new RegExp(@get('search_string'), 'im') + @get('options').forEach (option) => + value = if @get('labelPath') then get(option, @get('labelPath')) else option + filtered_options.push option if tester.test value + filtered_options + + options: [] + labelPath: '' # if set, indicating an object select + valuePath: 'id' + selected: [] + selected_popup_content: Ember.computed 'selected.length', -> + return '' unless @get('selected.length') + if labelPath = @get('labelPath') + result_html = '' + @get('options').forEach (option) => + if get(option, @get('valuePath')) in @get('selected') + result_html += '

' if result_html + result_html += get(option, labelPath) + result_html + else + @get('selected').join('

') + selected_display: Ember.computed 'selected.length', -> + if count = @get('selected.length') + return '1 item' if count is 1 + "#{count} items" + else + ''.htmlSafe() + + init: -> + @_super() + Ember.defineProperty @, 'selected', Ember.computed.alias("filters.#{@get('key')}") + + actions: + apply_filters: -> + @$('.dropdown').dropdown('hide') + @get('apply_filters')(@) + toggle_filter: (option, checked) -> + value = if @get('labelPath') then get(option, @get('valuePath')) else option + if checked + @get('selected').pushObject value + else + @get('selected').removeObject value + @apply_filters() if @get('eager_filter') + clear_filter: -> + @set 'selected', [] + @get('apply_filters')(@) # if @get('eager_filter') on clear always reload! + @apply_filters() diff --git a/app/components/table-filters/dropdown-select.coffee b/app/components/table-filters/dropdown-select.coffee new file mode 100644 index 0000000..5d270bf --- /dev/null +++ b/app/components/table-filters/dropdown-select.coffee @@ -0,0 +1,43 @@ +import Ember from 'ember' +{get} = Ember +export default Ember.Component.extend + tagName: 'th' + classNames: ['filter', 'filter-input'] + classNameBindings: ['has_active_filter_value:active-filter', 'key'] + apply_filters: -> + eager_filter: true # filter on enter + has_active_filter_value: Ember.computed.bool 'selected' + options: [] + labelPath: '' # if set, indicating an object select + translationPath: '' # if set indicating a flat select requiring translated values + valuePath: 'id' + selected: null + selected_display: Ember.computed 'selected', -> + if selected = @get('selected') + if labelPath = @get('labelPath') + if obj = @get('options').findBy(@get('valuePath'), selected) + get(obj, labelPath) + else + selected + else if translationPath = @get('translationPath') + t selected, scope: translationPath, default: selected + else + selected + else + ''.htmlSafe() + + init: -> + @_super() + Ember.defineProperty @, 'selected', Ember.computed.alias("filters.#{@get('key')}") + + actions: + set_filter: (value) -> + if @get('labelPath') + @set 'selected', get(value, @get('valuePath')) + else + @set 'selected', value + @$('.dropdown').dropdown('hide') + @get('apply_filters')(@) if @get('eager_filter') + clear_filter: -> + @set 'selected', '' + @get('apply_filters')(@) if @get('eager_filter') diff --git a/app/components/table-filters/input.coffee b/app/components/table-filters/input.coffee new file mode 100644 index 0000000..5a56975 --- /dev/null +++ b/app/components/table-filters/input.coffee @@ -0,0 +1,40 @@ +import Ember from 'ember' + +export default Ember.Component.extend + tagName: 'th' + classNames: ['filter', 'filter-input'] + classNameBindings: ['has_active_filter_value:active-filter'] + value: '' # defined on init + apply_filters: -> + eager_filter: true # filter on enter + has_active_filter_value: Ember.computed.bool 'value' + key: 'name_eq' + placeholder: '' + showSearchIcon: false # default false, too crowded ui if all fields have this icon + + smart_placeholder: Ember.computed 'key', 'placeholder', -> + return @get('placeholder') if @get('placeholder') # can be set from template + return '' unless key = @get('key') + switch + when /_cont$/.test key then '~' + when /_eq$/.test key then '==' + when /_gt$/.test key then '>' + when /_gteq$/.test key then '>=' + when /_lt$/.test key then '<' + when /_lteq$/.test key then '<=' + when /_matches$/.test key then '%~%' + when /_end$/.test key then '%--' + when /_start/.test key then '--%' + else '' + + init: -> + @_super() + Ember.defineProperty @, 'value', Ember.computed.alias("filters.#{@get('key')}") + + actions: + enterPressed: -> + return unless @get('eager_filter') + @get('apply_filters')(@) if @get('eager_filter') + clear_query: -> + @set 'value', '' + @get('apply_filters')(@) if @get('eager_filter') diff --git a/app/components/table-filters/number-range.coffee b/app/components/table-filters/number-range.coffee new file mode 100644 index 0000000..407671b --- /dev/null +++ b/app/components/table-filters/number-range.coffee @@ -0,0 +1,41 @@ +import Ember from 'ember' + +export default Ember.Component.extend + tagName: 'th' + classNames: ['filter', 'filter-number-range'] + classNameBindings: ['has_active_filter_value:active-filter'] + placeholder: '-/-' + display_addition: '' # allow extra display value on cell. Year for example when number-range is weeks + apply_filters: -> + eager_filter: true # filter on enter + has_active_filter_value: Ember.computed.or 'lower_bound', 'upper_bound' + lower_bound_candidate: '' + upper_bound_candidate: '' + filter_text: Ember.computed '{lower_bound,upper_bound,display_addition}', -> + gteq = @get('lower_bound') || '-' + lteq = @get('upper_bound') || '-' + range = "#{gteq} / #{lteq}" + range += " #{@get('display_addition')}" if @get('display_addition') + range + + + init: -> + @_super() + Ember.defineProperty @, 'lower_bound', Ember.computed.alias("filters.#{@get('key')}_gteq") + Ember.defineProperty @, 'upper_bound', Ember.computed.alias("filters.#{@get('key')}_lteq") + @set 'lower_bound_candidate', @get('lower_bound') + @set 'upper_bound_candidate', @get('upper_bound') + + actions: + set_filter: (key, value) -> + #@set key, value + @set 'lower_bound', parseInt(@get('lower_bound_candidate')) || '' + @set 'upper_bound', parseInt(@get('upper_bound_candidate')) || '' + @$('.dropdown').dropdown('hide') + @get('apply_filters')(@) if @get('eager_filter') + clear_filters: -> + @set 'lower_bound', '' + @set 'upper_bound', '' + @set 'lower_bound_candidate', '' + @set 'upper_bound_candidate', '' + @get('apply_filters')(@) if @get('eager_filter') diff --git a/app/components/table-filters/option-buttons.coffee b/app/components/table-filters/option-buttons.coffee new file mode 100644 index 0000000..dda6c6a --- /dev/null +++ b/app/components/table-filters/option-buttons.coffee @@ -0,0 +1,23 @@ +import Ember from 'ember' + +export default Ember.Component.extend + tagName: 'th' + classNames: ['filter', 'filter-input'] + classNameBindings: ['has_active_filter_value:active-filter', 'key'] + apply_filters: -> + eager_filter: true # filter on enter + has_active_filter_value: Ember.computed.bool 'selected' + options: [] + selected: null + + init: -> + @_super() + Ember.defineProperty @, 'selected', Ember.computed.alias("filters.#{@get('key')}") + + actions: + set_filter: (value) -> + @set 'selected', value + @get('apply_filters')(@) if @get('eager_filter') + clear_filter: -> + @set 'selected', '' + @get('apply_filters')(@) if @get('eager_filter') diff --git a/app/components/table-filters/select-multiple-modal.coffee b/app/components/table-filters/select-multiple-modal.coffee new file mode 100644 index 0000000..8ae7220 --- /dev/null +++ b/app/components/table-filters/select-multiple-modal.coffee @@ -0,0 +1,47 @@ +import Ember from 'ember' +import BaseModal from 'ember-cli-dunlop/components/modals/base' +export default BaseModal.extend + current_selected: Ember.computed.alias 'modal_options.base.value' + options: Ember.computed.alias 'modal_options.base.options' + valueKey: Ember.computed.alias 'modal_options.base.valueKey' + textKey: Ember.computed.alias 'modal_options.base.textKey' + eager_filter: Ember.computed.alias 'modal_options.base.eager_filter' + apply_filters: Ember.computed.alias 'modal_options.base.apply_filters' + options_map: {} + + willOpenModal: -> + options_map = Ember.Object.create() + valueKey = @get('valueKey') + current_selected = @get('current_selected') || [] + @get('options').forEach (option) -> + value_name = if valueKey then Ember.get(option, valueKey) else option + options_map.set value_name, current_selected.includes(value_name) + @set 'options_map', options_map + + + actions: + update_selection: -> + valueKey = @get('valueKey') + selected_options = @get('options').filter (option) => + value_name = if valueKey then Ember.get(option, valueKey) else option + @get "options_map.#{value_name}" + if valueKey + selected_options = selected_options.mapBy valueKey + @set 'current_selected', selected_options + @get('apply_filters')(@) if @get('eager_filter') + @send 'close' + + toggle_selection: (option)-> + value_name = if @get('valueKey') then Ember.get(option, @get('valueKey')) else option + @toggleProperty "options_map.#{value_name}" + + select_all: -> + Object.keys(@get('options_map')).forEach (value_name) => + @set "options_map.#{value_name}", true + + select_none: -> + Object.keys(@get('options_map')).forEach (value_name) => + @set "options_map.#{value_name}", false + + + diff --git a/app/components/table-filters/select-multiple.coffee b/app/components/table-filters/select-multiple.coffee new file mode 100644 index 0000000..6649113 --- /dev/null +++ b/app/components/table-filters/select-multiple.coffee @@ -0,0 +1,40 @@ +import Ember from 'ember' + +export default Ember.Component.extend + tagName: 'th' + textKey: 'name' + classNames: ['filter', 'filter-select-multiple'] + classNameBindings: ['has_active_filter_values:active-filter'] + valueKey: null # assumed flat array + value: [] + options: [] + key: 'name_in' # ransack key + eager_filter: true # filter on enter + apply_filters: -> + has_active_filter_values: Ember.computed.bool 'value.length' + + selectedTitle: Ember.computed 'value.length', -> + return '--' unless @get('value.length') + @get('value').join(",\n") + + selectedDisplay: Ember.computed 'value.length', -> + return '' unless @get('value.length') + if @get('value.length') is 1 + value = @get("value.firstObject") + if valueKey = @get('valueKey') + option = @get('options').findBy(valueKey, value) + Ember.get(option, @get('textKey')) + else + value + else + @get('value.length') + + init: -> + @_super(arguments...) + Ember.defineProperty @, 'value', Ember.computed.alias("filters.#{@get('key')}") + + actions: + edit_selection: -> + @modal 'table-filters/select-multiple-modal', + title: "Select options" + base: @ diff --git a/app/components/ui-calendar-moment.js b/app/components/ui-calendar-moment.js new file mode 100644 index 0000000..d0b6485 --- /dev/null +++ b/app/components/ui-calendar-moment.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/components/ui-calendar-moment'; diff --git a/app/components/ui-markdown-popup.js b/app/components/ui-markdown-popup.js new file mode 100644 index 0000000..43241e4 --- /dev/null +++ b/app/components/ui-markdown-popup.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/components/ui-markdown-popup'; diff --git a/app/components/validated-attribute.coffee b/app/components/validated-attribute.coffee new file mode 100644 index 0000000..7624f61 --- /dev/null +++ b/app/components/validated-attribute.coffee @@ -0,0 +1,20 @@ +import Ember from 'ember' + +export default Ember.Component.extend + classNameBindings: ['hasError:error', 'containerClass'] + classNames: ['field'] + disabled: false + error_text: 'Please set...' + + containerClass: Ember.computed 'attribute', -> + return 'validated-attribute' unless record = @get('changeset._content') + record_name = record.constructor.modelName || 'record' + "validated-attribute-#{record_name}-#{Ember.String.dasherize(@get('attribute'))}".replace(/[\W_]/g, '-') + + init: -> + @_super() + Ember.defineProperty(@, "hasError", Ember.computed.bool("changeset.error.#{@get("attribute")}")) + + actions: + validateAttribute: (changeset, attribute) -> + changeset.validate(attribute) diff --git a/app/components/validated-input.coffee b/app/components/validated-input.coffee new file mode 100644 index 0000000..c193e8e --- /dev/null +++ b/app/components/validated-input.coffee @@ -0,0 +1,23 @@ +import Ember from 'ember' +import { invokeAction } from 'ember-invoke-action' + +export default Ember.Component.extend + classNameBindings: ["hasError:error", "containerClass"] + classNames: ['field'] + disabled: false + + containerClass: Ember.computed "attribute", -> + return "validated-input" unless record = @get('changeset._content') + record_name = record.constructor.modelName || 'record' + "validated-input-#{record_name}-#{Ember.String.dasherize(@get("attribute"))}".replace(/[\W_]/g, '-') + + init: -> + @_super() + Ember.defineProperty(@, "hasError", Ember.computed.bool("changeset.error.#{@get("attribute")}")) + + actions: + validateProperty: (changeset, property) -> + changeset.validate(property) + inputEnter: (value)-> + #https://github.com/DockYard/ember-one-way-controls/blob/master/addon/components/one-way-input.js + invokeAction(@, enter, value) if enter = @get('enter') diff --git a/app/components/validated-select.coffee b/app/components/validated-select.coffee new file mode 100644 index 0000000..45a1997 --- /dev/null +++ b/app/components/validated-select.coffee @@ -0,0 +1,37 @@ +import Ember from 'ember' + +export default Ember.Component.extend + classNameBindings: ['hasError:error', 'containerClass'] + classNames: ['field'] + includeBlank: false + disabled: false + valueKey: null # assumed flat array + textKey: 'name' + + containerClass: Ember.computed 'attribute', -> + return 'validated-select' unless record = @get('changeset._content') + record_name = record.constructor.modelName || 'record' + "validated-select-#{record_name}-#{Ember.String.dasherize(@get('attribute'))}".replace(/[\W_]/g, '-') + + set_value_to_first_option_on_non_blank_list: Ember.observer 'options.length', -> + return unless @get('options.length') + attribute = @get('attribute') + if not @get('includeBlank') and not @get("changeset.#{attribute}") + value = if @get('valueKey') then @get("options.firstObject.#{@get('valueKey')}") else @get('options.firstObject') + @set "changeset.#{attribute}", value + + init: -> + @_super() + console.log('No attribute given for validated-select') unless attribute = @get('attribute') + Ember.defineProperty(@, 'hasError', Ember.computed.bool("changeset.error.#{attribute}")) + + blankContent: Ember.computed 'includeBlank', -> + setting = @get('includeBlank') + return setting if typeof setting is 'string' + '--' + + actions: + hasChanged: (value) -> + [changeset, property] = [@get('changeset'), @get('attribute')] + changeset.set property, value + changeset.validate(property) diff --git a/app/helpers/any-permission-starts-with.js b/app/helpers/any-permission-starts-with.js new file mode 100644 index 0000000..76dc159 --- /dev/null +++ b/app/helpers/any-permission-starts-with.js @@ -0,0 +1 @@ +export { default, anyPermissionStartsWith } from 'ember-cli-dunlop/helpers/any-permission-starts-with'; diff --git a/app/helpers/dunlop-can.js b/app/helpers/dunlop-can.js new file mode 100644 index 0000000..506d808 --- /dev/null +++ b/app/helpers/dunlop-can.js @@ -0,0 +1 @@ +export { default, dunlopCan } from 'ember-cli-dunlop/helpers/dunlop-can'; diff --git a/app/helpers/format-date.js b/app/helpers/format-date.js new file mode 100644 index 0000000..034f09c --- /dev/null +++ b/app/helpers/format-date.js @@ -0,0 +1 @@ +export { default, formatDate } from 'ember-cli-dunlop/helpers/format-date'; diff --git a/app/helpers/has-permission-of-type.js b/app/helpers/has-permission-of-type.js new file mode 100644 index 0000000..aaa1862 --- /dev/null +++ b/app/helpers/has-permission-of-type.js @@ -0,0 +1 @@ +export { default, hasPermissionOfType } from 'ember-cli-dunlop/helpers/has-permission-of-type'; diff --git a/app/helpers/minute-of-day.js b/app/helpers/minute-of-day.js new file mode 100644 index 0000000..a2b0720 --- /dev/null +++ b/app/helpers/minute-of-day.js @@ -0,0 +1 @@ +export { default, minuteOfDay } from 'ember-cli-dunlop/helpers/minute-of-day'; diff --git a/app/helpers/number-to-xls-header.js b/app/helpers/number-to-xls-header.js new file mode 100644 index 0000000..379b82f --- /dev/null +++ b/app/helpers/number-to-xls-header.js @@ -0,0 +1 @@ +export { default, numberToXlsHeader } from 'ember-cli-dunlop/helpers/number-to-xls-header'; diff --git a/app/helpers/t.js b/app/helpers/t.js new file mode 100644 index 0000000..bcd93e0 --- /dev/null +++ b/app/helpers/t.js @@ -0,0 +1 @@ +export { default, t } from 'ember-cli-dunlop/helpers/t'; diff --git a/app/initializers/ember-cli-dunlop-overrides.js b/app/initializers/ember-cli-dunlop-overrides.js new file mode 100644 index 0000000..de2197c --- /dev/null +++ b/app/initializers/ember-cli-dunlop-overrides.js @@ -0,0 +1 @@ +export { default, initialize } from 'ember-cli-dunlop/initializers/ember-cli-dunlop-overrides'; diff --git a/app/serializers/create-and-update.js b/app/serializers/create-and-update.js new file mode 100644 index 0000000..7edd2ba --- /dev/null +++ b/app/serializers/create-and-update.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/serializers/create-and-update'; diff --git a/app/templates/components/d-breadcrumb.emblem b/app/templates/components/d-breadcrumb.emblem new file mode 100644 index 0000000..b249718 --- /dev/null +++ b/app/templates/components/d-breadcrumb.emblem @@ -0,0 +1,3 @@ +unless isFirst + i.right.angle.icon.divider += yield diff --git a/app/templates/components/d-breadcrumbs.hbs b/app/templates/components/d-breadcrumbs.hbs new file mode 100644 index 0000000..d21e9ba --- /dev/null +++ b/app/templates/components/d-breadcrumbs.hbs @@ -0,0 +1,3 @@ + diff --git a/app/templates/components/flash-message.emblem b/app/templates/components/flash-message.emblem new file mode 100644 index 0000000..754ec50 --- /dev/null +++ b/app/templates/components/flash-message.emblem @@ -0,0 +1 @@ += message diff --git a/app/templates/components/login-form.emblem b/app/templates/components/login-form.emblem new file mode 100644 index 0000000..db0b275 --- /dev/null +++ b/app/templates/components/login-form.emblem @@ -0,0 +1,9 @@ +.field + .ui.left.icon.input + i.user.icon + = input type='email' value=identification enter='submitLogin' placeholder="e-mail" class='login-email' +.field + .ui.left.icon.input + i.lock.icon + = input type='password' value=password enter='submitLogin' placeholder='password' class='login-password' +.ui.fluid.large.orange.submit.button{action 'submitLogin'} Login diff --git a/app/templates/components/page-title.emblem b/app/templates/components/page-title.emblem new file mode 100644 index 0000000..7f8d8e8 --- /dev/null +++ b/app/templates/components/page-title.emblem @@ -0,0 +1,5 @@ +if back + = link-to back class='title-back-link' + i.left.arrow.icon += title += yield diff --git a/app/templates/components/table-filters.hbs b/app/templates/components/table-filters.hbs new file mode 100644 index 0000000..0e6cddc --- /dev/null +++ b/app/templates/components/table-filters.hbs @@ -0,0 +1,11 @@ +{{yield (hash + input=(component "table-filters/input" apply_filters=(action 'apply_filters') filters=filters) + date=(component "table-filters/date" apply_filters=(action 'apply_filters') filters=filters) + date-range=(component "table-filters/date-range" apply_filters=(action 'apply_filters') filters=filters) + number-range=(component "table-filters/number-range" apply_filters=(action 'apply_filters') filters=filters) + dropdown-select=(component "table-filters/dropdown-select" apply_filters=(action 'apply_filters') filters=filters) + dropdown-select-multiple=(component "table-filters/dropdown-select-multiple" apply_filters=(action 'apply_filters') filters=filters) + option-buttons=(component "table-filters/option-buttons" apply_filters=(action 'apply_filters') filters=filters) + select-multiple=(component "table-filters/select-multiple" apply_filters=(action 'apply_filters') filters=filters) + actions=(component 'table-filters/actions' apply_filters=(action 'apply_filters') clear_filters=(action 'clear_filters') filter_calls=filter_calls) +)}} diff --git a/app/templates/components/table-filters/-select-multiple-modal.emblem b/app/templates/components/table-filters/-select-multiple-modal.emblem new file mode 100644 index 0000000..5f2ab0a --- /dev/null +++ b/app/templates/components/table-filters/-select-multiple-modal.emblem @@ -0,0 +1,39 @@ +each options as |option| + .ui.grid{action 'toggle_selection' option} + .four.wide-column + if valueKey + with (get option valueKey) as |option_value| + if (get options_map option_value) + /a.fa.fa-2x.fa-check-square-o + button.ui.basic.icon.button: i.checkmark.box.icon + else + button.ui.basic.icon.button: i.square.outline.icon + /a.fa.fa-2x.fa-square-o + /= input type='checkbox' checked=(get options_map option_value) + else + if (get options_map option) + /a.fa.fa-2x.fa-check-square-o + button.ui.basic.icon.button: i.checkmark.box.icon + else + /a.fa.fa-2x.fa-square-o + button.ui.basic.icon.button: i.square.outline.icon + /= input type='checkbox' checked=(get options_map option) + .twelve.wide.column + if valueKey + = get option textKey + else + = option +//UNCOMMENT THE SECTION BELOW FOR DEBUGGING +/each-in options_map as |value is_selected| + .row + .small-2.columns: if is_selected 'Ja' 'Nee' + .small-10.columns= value +.modal-actions.sticky + button.ui.primary.button.modal-save.right{action 'update_selection'}= t 'general.update' + button.ui.button.modal-rollback{action 'close'}= t 'general.rollback' + span   + /span.fa.fa-lg.fa-check-square-o{action 'select_all'} + button.ui.basic.icon.button{action 'select_all'}: i.checkmark.box.icon + span   + /span.fa.fa-lg.fa-square-o{action 'select_none'} + button.ui.basic.icon.button{action 'select_none'}: i.square.outline.icon diff --git a/app/templates/components/table-filters/actions.emblem b/app/templates/components/table-filters/actions.emblem new file mode 100644 index 0000000..a22226f --- /dev/null +++ b/app/templates/components/table-filters/actions.emblem @@ -0,0 +1,5 @@ +button.ui.tiny.primary.basic.icon.button{ action 'apply_filters'}: i.filter.icon +if filter_calls + |   + button.ui.tiny.secondary.basic.icon.button{ action 'clear_filters'}: i.remove.icon += yield diff --git a/app/templates/components/table-filters/date-range.emblem b/app/templates/components/table-filters/date-range.emblem new file mode 100644 index 0000000..c4bbca7 --- /dev/null +++ b/app/templates/components/table-filters/date-range.emblem @@ -0,0 +1,27 @@ +ui-dropdown class='basic pointing' action='nothing' + = target_date_filter_text + .menu + .item= ui-calendar-moment type='date' momentValue=lower_bound onChange=(action 'set_filter' 'lower_bound') allowClear=true + .divider + .item= ui-calendar-moment type='date' momentValue=upper_bound onChange=(action 'set_filter' 'upper_bound') allowClear=true + if expandingTime + .divider + .item + button.ui.mini.primary.button{action 'set_filter_today'}= t 'date.today' + ' + button.ui.mini.primary.button{action 'set_filter_this_week'}= t 'date.this_week' + ' + button.ui.mini.primary.button{action 'set_filter_this_month'}= t 'date.this_month' + if weekSelector + .ui.horizontal.divider= t 'date.unit.week' + .divider + .item + .ui.input.select-week= input type='number' value=week_selector_week placeholder='week' size=3 enter='week_selector_activate' + ' / + .ui.input.select-year= input type='number' value=week_selector_year placeholder='year' size=5 enter='week_selector_activate' + if has_valid_week_selector_value + ' + button.ui.mini.secondary.icon.button{action 'week_selector_activate'}: i.filter.icon + = yield +if has_active_filter_value + i.close.icon.clear-target-date-filter{action 'clear_filters'} diff --git a/app/templates/components/table-filters/date.emblem b/app/templates/components/table-filters/date.emblem new file mode 100644 index 0000000..dc891ec --- /dev/null +++ b/app/templates/components/table-filters/date.emblem @@ -0,0 +1,2 @@ += pick-a-date date=(readonly (get filters key)) on-selected=(action 'select_date') placeholder=placeholder += yield diff --git a/app/templates/components/table-filters/dropdown-select-multiple.emblem b/app/templates/components/table-filters/dropdown-select-multiple.emblem new file mode 100644 index 0000000..166c36e --- /dev/null +++ b/app/templates/components/table-filters/dropdown-select-multiple.emblem @@ -0,0 +1,22 @@ +ui-dropdown class='right basic labeled icon' action='nothing' + if selected.length + = ui-popup html=selected_popup_content position='bottom center' + span= selected_display + else + i.filter.icon + i.dropdown.icon + .menu + .ui.icon.action.input + i.search.icon + = input value=search_string placeholder=search_placeholder + button.ui.secondary.icon.button{action 'apply_filters'} + i.filter.icon + .scrolling.menu + each filtered_options as |option| + if labelPath + ui-checkbox class='item' checked=(contains (get option valuePath) selected) label=(get option labelPath) onChange=(action 'toggle_filter' option) + else + ui-checkbox class='item' checked=(contains option selected) label=option onChange=(action 'toggle_filter' option) +if selected.length + i.pointable.remove.icon{action 'clear_filter'} + diff --git a/app/templates/components/table-filters/dropdown-select.emblem b/app/templates/components/table-filters/dropdown-select.emblem new file mode 100644 index 0000000..63494c3 --- /dev/null +++ b/app/templates/components/table-filters/dropdown-select.emblem @@ -0,0 +1,16 @@ +ui-dropdown class='right basic labeled icon' action='nothing' + = selected_display + i.dropdown.icon + .menu + each options as |option| + .item{action 'set_filter' option} + if labelPath + = get option labelPath + else + if translationPath + = t option scope=translationPath + else + = option +if has_active_filter_value + i.pointable.close.icon{action 'clear_filter'} + diff --git a/app/templates/components/table-filters/input.emblem b/app/templates/components/table-filters/input.emblem new file mode 100644 index 0000000..2551528 --- /dev/null +++ b/app/templates/components/table-filters/input.emblem @@ -0,0 +1,7 @@ +.ui.fluid.icon.input + = input value=value enter='enterPressed' placeholder=smart_placeholder + if has_active_filter_value + i.close.action.icon{action 'clear_query'} + else if showSearchIcon + i.search.icon += yield diff --git a/app/templates/components/table-filters/number-range.emblem b/app/templates/components/table-filters/number-range.emblem new file mode 100644 index 0000000..34a350f --- /dev/null +++ b/app/templates/components/table-filters/number-range.emblem @@ -0,0 +1,10 @@ +ui-dropdown class='right basic labeled top right pointing icon' action='nothing' + = filter_text + .menu + .item= input type='number' value=lower_bound_candidate enter=(action 'set_filter') + .divider + .item= input type='number' value=upper_bound_candidate enter=(action 'set_filter') + = yield +if has_active_filter_value + i.close.icon.clear-target-date-filter{action 'clear_filters'} + diff --git a/app/templates/components/table-filters/option-buttons.emblem b/app/templates/components/table-filters/option-buttons.emblem new file mode 100644 index 0000000..cdb84e2 --- /dev/null +++ b/app/templates/components/table-filters/option-buttons.emblem @@ -0,0 +1,6 @@ +.ui.mini.buttons + each options as |option| + button.ui.button click="action 'set_filter' option" class="{{if (eq selected option) 'active-button'}}" + = option +if has_active_filter_value + i.close.icon{action 'clear_filter'} diff --git a/app/templates/components/table-filters/select-multiple.emblem b/app/templates/components/table-filters/select-multiple.emblem new file mode 100644 index 0000000..6a16c22 --- /dev/null +++ b/app/templates/components/table-filters/select-multiple.emblem @@ -0,0 +1,4 @@ +button.ui.tiny.icon.button{action 'edit_selection'} title=selectedTitle + i.list.icon +== selectedDisplay += yield diff --git a/app/templates/components/validated-attribute.emblem b/app/templates/components/validated-attribute.emblem new file mode 100644 index 0000000..c9732ff --- /dev/null +++ b/app/templates/components/validated-attribute.emblem @@ -0,0 +1,3 @@ += yield +if hasError + .ui.pointing.red.basic.label= error_text diff --git a/app/templates/components/validated-input.emblem b/app/templates/components/validated-input.emblem new file mode 100644 index 0000000..29d3cfb --- /dev/null +++ b/app/templates/components/validated-input.emblem @@ -0,0 +1,12 @@ +/ based on https://www.emberscreencasts.com/posts/172-displaying-errors-inline-with-ember-changeset-validations +if label + label= label +else + if labelPath + label= t labelPath + +one-way-input value=(get changeset attribute) update=(action (mut (get changeset attribute))) onblur=(action 'validateProperty' changeset attribute) disabled=disabled onenter='inputEnter' + +if (get changeset.error attribute) + each (get (get changeset.error attribute) 'validation') as |error| + .ui.pointing.red.basic.label= error diff --git a/app/templates/components/validated-select.emblem b/app/templates/components/validated-select.emblem new file mode 100644 index 0000000..9dab1f7 --- /dev/null +++ b/app/templates/components/validated-select.emblem @@ -0,0 +1,22 @@ +/ based on validated-input +if label + label= label +else + if labelPath + label= t labelPath + else + label= attribute + +x-select value=(get changeset attribute) on-change=(action "hasChanged") disabled=disabled as |xs| + if includeBlank + option= blankContent + each options as |option| + if valueKey + xs.option value=(get option valueKey) + = get option textKey + else + xs.option value=option + = option +if (get changeset.error attribute) + each (get (get changeset.error attribute) 'validation') as |error| + .ui.pointing.red.basic.label= error diff --git a/app/templates/global/error-messages.emblem b/app/templates/global/error-messages.emblem new file mode 100644 index 0000000..2abba7a --- /dev/null +++ b/app/templates/global/error-messages.emblem @@ -0,0 +1,23 @@ +if show_error + .dunlop-global-error.ui.negative.message + if show_as_list + ul + each error.errors as |single_error| + li + = single_error.status + span - + = single_error.title + else + .ui.grid + .row + .four.wide.column= single_error.status + .twelve.wide.column= single_error.title + .row + /collapsible-content collapsed=true + / == single_error.detail + == single_error.detail + .row + button.ui.small.basic.button.pull-right{action 'reload_page'} Reload page! + button.ui.small.primary.button{ action 'clear_error_and_go_home'} Go to homepage + span   + button.ui.small.button{ action 'clear_error'} Close diff --git a/app/templates/modals/-confirm.emblem b/app/templates/modals/-confirm.emblem new file mode 100644 index 0000000..9e246b0 --- /dev/null +++ b/app/templates/modals/-confirm.emblem @@ -0,0 +1,4 @@ +p= body +.modal-actions.sticky + button.ui.basic.button.modal-close{action "close"}= t 'confirm.cancel' + button.ui.primary.button.modal-confirm.pull-right{action "confirm"}= t 'confirm.confirm' diff --git a/app/templates/modals/-sort-records.emblem b/app/templates/modals/-sort-records.emblem new file mode 100644 index 0000000..31f1d0b --- /dev/null +++ b/app/templates/modals/-sort-records.emblem @@ -0,0 +1,8 @@ +sortable-group tagName="ul" onChange="reorderItems" class="sortable-list" as |group| + each sorted_records as |record| + sortable-item tagName="li" model=record group=group handle=".handle" + /span.handle + i.sort.icon.handle + = record.name +.modal-actions.sticky + button.ui.basic.button.modal-close.pull-right{ action "close"}= t 'general.close' diff --git a/app/templates/modals/layout.emblem b/app/templates/modals/layout.emblem new file mode 100644 index 0000000..7943e45 --- /dev/null +++ b/app/templates/modals/layout.emblem @@ -0,0 +1,11 @@ +.overlay{action "closeOnOverlay"} + .modal{action "modalClick" bubbles=false preventDefault=false} + .modal-header + h3.flush--top= title + .ui.divider + .modal-body.ui.form + if alert_message + .ui.negative.message.modal-alert + i.close.icon{action (mut alert_message) ''} + == alert_message + = partial partialName diff --git a/app/templates/modals/nothing.emblem b/app/templates/modals/nothing.emblem new file mode 100644 index 0000000..1640cb0 --- /dev/null +++ b/app/templates/modals/nothing.emblem @@ -0,0 +1 @@ +/ to fix a disconnected outlet issue https://github.com/emberjs/ember-inspector/issues/625 diff --git a/app/transforms/datemoment.js b/app/transforms/datemoment.js new file mode 100644 index 0000000..96f8394 --- /dev/null +++ b/app/transforms/datemoment.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/transforms/datemoment'; diff --git a/app/transforms/moment.js b/app/transforms/moment.js new file mode 100644 index 0000000..2b81217 --- /dev/null +++ b/app/transforms/moment.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/transforms/moment'; diff --git a/app/utils/date-sort.js b/app/utils/date-sort.js new file mode 100644 index 0000000..4fd36cd --- /dev/null +++ b/app/utils/date-sort.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/utils/date-sort'; diff --git a/app/utils/integer-to-string.js b/app/utils/integer-to-string.js new file mode 100644 index 0000000..b82c55d --- /dev/null +++ b/app/utils/integer-to-string.js @@ -0,0 +1 @@ +export { default } from 'ember-cli-dunlop/utils/integer-to-string'; diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..a582824 --- /dev/null +++ b/bower.json @@ -0,0 +1,7 @@ +{ + "name": "ember-cli-dunlop", + "dependencies": { + "semantic-ui": "^2.2.13", + "semantic-ui-calendar": "^0.0.8" + } +} diff --git a/config/ember-try.js b/config/ember-try.js new file mode 100644 index 0000000..a070124 --- /dev/null +++ b/config/ember-try.js @@ -0,0 +1,91 @@ +/* eslint-env node */ +module.exports = { + scenarios: [ + { + name: 'ember-lts-2.4', + bower: { + dependencies: { + 'ember': 'components/ember#lts-2-4' + }, + resolutions: { + 'ember': 'lts-2-4' + } + }, + npm: { + devDependencies: { + 'ember-source': null + } + } + }, + { + name: 'ember-lts-2.8', + bower: { + dependencies: { + 'ember': 'components/ember#lts-2-8' + }, + resolutions: { + 'ember': 'lts-2-8' + } + }, + npm: { + devDependencies: { + 'ember-source': null + } + } + }, + { + name: 'ember-release', + bower: { + dependencies: { + 'ember': 'components/ember#release' + }, + resolutions: { + 'ember': 'release' + } + }, + npm: { + devDependencies: { + 'ember-source': null + } + } + }, + { + name: 'ember-beta', + bower: { + dependencies: { + 'ember': 'components/ember#beta' + }, + resolutions: { + 'ember': 'beta' + } + }, + npm: { + devDependencies: { + 'ember-source': null + } + } + }, + { + name: 'ember-canary', + bower: { + dependencies: { + 'ember': 'components/ember#canary' + }, + resolutions: { + 'ember': 'canary' + } + }, + npm: { + devDependencies: { + 'ember-source': null + } + } + }, + { + name: 'ember-default', + npm: { + devDependencies: {} + } + } + ] +}; diff --git a/config/environment.js b/config/environment.js new file mode 100644 index 0000000..012a412 --- /dev/null +++ b/config/environment.js @@ -0,0 +1,6 @@ +/* eslint-env node */ +'use strict'; + +module.exports = function(/* environment, appConfig */) { + return { }; +}; diff --git a/ember-cli-build.js b/ember-cli-build.js new file mode 100644 index 0000000..5e6f037 --- /dev/null +++ b/ember-cli-build.js @@ -0,0 +1,17 @@ +/* eslint-env node */ +const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); + +module.exports = function(defaults) { + var app = new EmberAddon(defaults, { + // Add options here + }); + + /* + This build file specifies the options for the dummy test app of this + addon, located in `/tests/dummy` + This build file does *not* influence how the addon or the app using it + behave. You most likely want to be modifying `./index.js` or app's build file + */ + + return app.toTree(); +}; diff --git a/index.js b/index.js new file mode 100644 index 0000000..46e2450 --- /dev/null +++ b/index.js @@ -0,0 +1,9 @@ +/* eslint-env node */ +'use strict'; + +module.exports = { + name: 'ember-cli-dunlop', + included: function(/* app */) { + this._super.included.apply(this, arguments); + } +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..b920822 --- /dev/null +++ b/package.json @@ -0,0 +1,60 @@ +{ + "name": "ember-cli-dunlop", + "version": "0.0.0", + "description": "The default blueprint for ember-cli addons.", + "keywords": [ + "ember-addon" + ], + "license": "MIT", + "author": "", + "directories": { + "doc": "doc", + "test": "tests" + }, + "repository": "", + "scripts": { + "build": "ember build", + "start": "ember server", + "test": "ember try:each", + "coffee": "coffee --bare --compile --watch addon" + }, + "dependencies": { + "ember-cli-babel": "^5.1.7", + "ember-cli-emblem": "^0.8.2-beta.2", + "ember-cli-htmlbars": "^1.1.1", + "ember-cli-sass": ">=6.1.2" + }, + "devDependencies": { + "broccoli-asset-rev": "^2.4.5", + "ember-ajax": "^2.4.1", + "ember-cli": "2.12.1", + "ember-cli-coffeescript": "^1.16.1", + "ember-cli-dependency-checker": "^1.3.0", + "ember-cli-eslint": "^3.0.0", + "ember-cli-htmlbars-inline-precompile": "^0.3.6", + "ember-cli-inject-live-reload": "^1.4.1", + "ember-cli-moment-shim": "^3.5.0", + "ember-cli-pagination": "^3.0.2", + "ember-cli-qunit": "^3.1.0", + "ember-cli-shims": "^1.0.2", + "ember-cli-sri": "^2.1.0", + "ember-cli-uglify": "^1.2.0", + "ember-export-application-global": "^1.0.5", + "ember-get-config": "^0.2.1", + "ember-load-initializers": "^0.6.0", + "ember-one-way-controls": "^2.0.1", + "ember-resolver": "^2.0.3", + "ember-semantic-ui-calendar": "^0.2.0", + "ember-simple-auth": "^1.2.2", + "ember-source": "~2.12.0", + "ember-truth-helpers": "^1.3.0", + "loader.js": "^4.2.3", + "semantic-ui-ember": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "ember-addon": { + "configPath": "tests/dummy/config" + } +} diff --git a/test.coffee b/test.coffee new file mode 100644 index 0000000..e69de29 diff --git a/testem.js b/testem.js new file mode 100644 index 0000000..b234048 --- /dev/null +++ b/testem.js @@ -0,0 +1,12 @@ +/* eslint-env node */ +module.exports = { + "test_page": "tests/index.html?hidepassed", + "disable_watching": true, + "launch_in_ci": [ + "PhantomJS" + ], + "launch_in_dev": [ + "PhantomJS", + "Chrome" + ] +}; diff --git a/tests/.eslintrc.js b/tests/.eslintrc.js new file mode 100644 index 0000000..fbf2555 --- /dev/null +++ b/tests/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + env: { + embertest: true + } +}; diff --git a/tests/dummy/app/app.js b/tests/dummy/app/app.js new file mode 100644 index 0000000..c0a8438 --- /dev/null +++ b/tests/dummy/app/app.js @@ -0,0 +1,20 @@ +/*eslint no-unused-vars: "off"*/ +import Ember from 'ember'; +import Resolver from './resolver'; +import loadInitializers from 'ember-load-initializers'; +import config from './config/environment'; +import translations from 'dummy/i18n/translations'; + +let App; + +Ember.MODEL_FACTORY_INJECTIONS = true; + +App = Ember.Application.extend({ + modulePrefix: config.modulePrefix, + podModulePrefix: config.podModulePrefix, + Resolver +}); + +loadInitializers(App, config.modulePrefix); + +export default App; diff --git a/tests/dummy/app/components/.gitkeep b/tests/dummy/app/components/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/dummy/app/controllers/.gitkeep b/tests/dummy/app/controllers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/dummy/app/controllers/table-filters/test1.coffee b/tests/dummy/app/controllers/table-filters/test1.coffee new file mode 100644 index 0000000..3d2ca07 --- /dev/null +++ b/tests/dummy/app/controllers/table-filters/test1.coffee @@ -0,0 +1,10 @@ +import Ember from 'ember' + +export default Ember.Controller.extend + scenario_classification_options: ['B1', 'B2'] + scenario_classification_objects: [ + {id: "B1", grumpy: "Cool name for B1!"} + {id: "B2", grumpy: "Cool name for B2!"} + ] + + a: 3 diff --git a/tests/dummy/app/helpers/.gitkeep b/tests/dummy/app/helpers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/dummy/app/i18n/translations.coffee b/tests/dummy/app/i18n/translations.coffee new file mode 100644 index 0000000..d51472a --- /dev/null +++ b/tests/dummy/app/i18n/translations.coffee @@ -0,0 +1,62 @@ +window.$translations = + en: + application_name: 'ember-cli-dunlop Dummy app' + total: 'Total' + user: + sign_out: 'Uitloggen' + dunlop: + module: + wba_mailbox: 'WBA Mailbox' + kbt_endpoint: 'KBT endpoint' + general: + save: 'Save' + rollback: 'Cancel' + destroy: + are_you_sure: 'Weet je dit zeker?' + button: 'Delete' + close: 'Close' + confirm: + cancel: 'Cancel' + confirm: 'Yes' + modals: + edit_project: + title: "Edit ${models.project} %{name}" + edit_organization: + title: "Edit ${models.organization} %{name}" + edit_tracker_subject: + title: "Edit ${models.tracker_subject} %{name}" + edit_tracker_group: + title: "Edit ${models.tracker_group} %{name}" + + confirm: 'Ja' + action: + new_model: "New %{model}" + select_model: 'Select %{model}' + add_model: 'Add %{model}' + + date: + today: 'Today' + this_week: 'This week' + this_month: 'This month' + unit: + second: 'second' + minute: 'minute' + businessday: 'business day' + day: 'day' + week: 'week' + year: 'year' + plural: + second: 'seconds' + minute: 'minutes' + businessday: 'business days' + day: 'days' + week: 'weeks' + year: 'years' + week_day: + monday: 'Maandag' + tuesday: 'Dinsdag' + wednesday: 'Woensdag' + thursday: 'Donderdag' + friday: 'Vrijdag' + saturday: 'Zaterdag' + sunday: 'Zondag' diff --git a/tests/dummy/app/index.html b/tests/dummy/app/index.html new file mode 100644 index 0000000..5120bd7 --- /dev/null +++ b/tests/dummy/app/index.html @@ -0,0 +1,25 @@ + + + + + + Dummy + + + + {{content-for "head"}} + + + + + {{content-for "head-footer"}} + + + {{content-for "body"}} + + + + + {{content-for "body-footer"}} + + diff --git a/tests/dummy/app/models/.gitkeep b/tests/dummy/app/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/dummy/app/resolver.js b/tests/dummy/app/resolver.js new file mode 100644 index 0000000..2fb563d --- /dev/null +++ b/tests/dummy/app/resolver.js @@ -0,0 +1,3 @@ +import Resolver from 'ember-resolver'; + +export default Resolver; diff --git a/tests/dummy/app/router.coffee b/tests/dummy/app/router.coffee new file mode 100644 index 0000000..c47b72d --- /dev/null +++ b/tests/dummy/app/router.coffee @@ -0,0 +1,16 @@ +import Ember from 'ember' +import config from './config/environment' + +Router = Ember.Router.extend + location: config.locationType + rootURL: config.rootURL + +Router.map -> + @route 'test1' + @route 'table-filters', -> + @route 'test1' + @route 'test2' + @route 'helpers', -> + @route 'number-to-xls-header' + +export default Router diff --git a/tests/dummy/app/routes/.gitkeep b/tests/dummy/app/routes/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/dummy/app/routes/table-filters/test1.coffee b/tests/dummy/app/routes/table-filters/test1.coffee new file mode 100644 index 0000000..41c74ad --- /dev/null +++ b/tests/dummy/app/routes/table-filters/test1.coffee @@ -0,0 +1,28 @@ +import Ember from 'ember' +import PagedArray from 'ember-cli-pagination/local/paged-array' + +export default_filter = Ember.Object.create + name: '' + target_week: '' + target_date_gteq: moment() + target_date_lteq: null + s: 'name asc' + +export default Ember.Route.extend + queryParams: + q: + refreshModel: true + setupController: (controller, model) -> + controller.set 'filters', default_filter + controller.set 'option_buttons_options', [1, 'two'] + @_super arguments... + + model: (params) -> + #@store.findAll('epots/migration-window-planning') + otherParams = {q: default_filter.toProperties()} + result = PagedArray.create [], perPage: 7 + result.setOtherParam = (param, q) -> + window.last_set_param ||= {} + window.last_set_param[param] = q # all is allowed for the purpose of testing and time conservation :devil: + result + diff --git a/tests/dummy/app/styles/app.scss b/tests/dummy/app/styles/app.scss new file mode 100644 index 0000000..f32c04d --- /dev/null +++ b/tests/dummy/app/styles/app.scss @@ -0,0 +1 @@ +@import './semantic-ui-additions'; diff --git a/tests/dummy/app/styles/semantic-ui-additions.sass b/tests/dummy/app/styles/semantic-ui-additions.sass new file mode 100644 index 0000000..fb6e480 --- /dev/null +++ b/tests/dummy/app/styles/semantic-ui-additions.sass @@ -0,0 +1,48 @@ +.ui.main + padding-top: 40px + +.hide + display: none + +.pull-right + float: right + +.right.floated.buttons + margin-top: -0.5rem + +.menu.fixed + .header.item + padding: 0 1rem + .grid + margin-top: -1rem + margin-left: 0 + +.ui.header + .push-action.icon + padding: 0.6rem + margin-right: 0 + +.ui.segment.title-box + padding-top: 3rem +.unpadded + padding: 0 !important + +.ui.table + td + &.compact + padding: 0.5em 0.7em + &.compact + pre + margin: 0 + +input:disabled + background-color: #ccc !important + +// .ui.grid might end up with columns having a negative bottom margin that captures the click event of the svg below it +.without-bottom-margin + margin-bottom: 0 !important + +body .ui.popup + z-index: 9021 + + diff --git a/tests/dummy/app/templates/application.emblem b/tests/dummy/app/templates/application.emblem new file mode 100644 index 0000000..7c21e96 --- /dev/null +++ b/tests/dummy/app/templates/application.emblem @@ -0,0 +1,28 @@ += flash-message += partial 'global/top-menu' +.ui.main + /bread-crumbs linkable=false + /= bread-crumbs tagName='ul' outputStyle='foundation' linkable=false as |component rrr| + = bread-crumb route=rrr breadCrumbs=component + span= rrr.title + = outlet 'error-handling' + if globals.flash.alert + .ui.negative.floating.message + i.close.icon{action (mut globals.flash.alert) ''} + = globals.flash.alert + if globals.flash.notice + .ui.floating.info.message + i.close.icon{action (mut globals.flash.notice) ''} + = globals.flash.notice + = outlet + /if globals.preload_resources_fulfilled + / = outlet + /else + / .ui.icon.message + / i.notched.circle.loading.icon + / .content + / .header Preloading resources... + / p Loading resources globally required before continuing with the application. + /button.basic.button{ action 'debug'} debug += outlet 'modal' + diff --git a/tests/dummy/app/templates/components/.gitkeep b/tests/dummy/app/templates/components/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/dummy/app/templates/global/-top-menu.emblem b/tests/dummy/app/templates/global/-top-menu.emblem new file mode 100644 index 0000000..024e042 --- /dev/null +++ b/tests/dummy/app/templates/global/-top-menu.emblem @@ -0,0 +1,9 @@ +.ui.inverted.menu.fixed + /.ui.container + = link-to 'index' class='header item' + span ember-cli-dunlop dummy  + = link-to 'table-filters.test1' class='item' + span Filters + = link-to 'helpers' class='item' + span Helpers + diff --git a/tests/dummy/app/templates/helpers.emblem b/tests/dummy/app/templates/helpers.emblem new file mode 100644 index 0000000..1a73731 --- /dev/null +++ b/tests/dummy/app/templates/helpers.emblem @@ -0,0 +1,10 @@ += page-title 'Helpers' +.ui.grid.manual + .four.wide.column.nav-column + ui-sticky + .ui.vertical.menu + /.header.item= link-to 'user-manual': span= t 'manual.title' + .item= link-to 'number-to-xls-header' 'helpers.number-to-xls-header' + /.item= link-to 'Hi there' 'user-manual.attribute-mapping' + .twelve.wide.column= outlet + diff --git a/tests/dummy/app/templates/helpers/number-to-xls-header.emblem b/tests/dummy/app/templates/helpers/number-to-xls-header.emblem new file mode 100644 index 0000000..c9e7c22 --- /dev/null +++ b/tests/dummy/app/templates/helpers/number-to-xls-header.emblem @@ -0,0 +1,15 @@ += page-title 'number-to-xls-header' +/code number-to-xls-header 4 +table.ui.compact.celled.definition.table + tr + td 0 + td= number-to-xls-header 0 + tr + td 7 + td= number-to-xls-header 7 + tr + td 16 + td= number-to-xls-header '16' + tr + td 77 + td= number-to-xls-header 77 diff --git a/tests/dummy/app/templates/table-filters.emblem b/tests/dummy/app/templates/table-filters.emblem new file mode 100644 index 0000000..f9854e6 --- /dev/null +++ b/tests/dummy/app/templates/table-filters.emblem @@ -0,0 +1,4 @@ +.ui.two.item.stackable.tabs.menu + = link-to 'table-filters.test1' class='item': span Filters Test1 + = link-to 'table-filters.test2' class='item': span Filters Test2 += outlet diff --git a/tests/dummy/app/templates/table-filters/test1.emblem b/tests/dummy/app/templates/table-filters/test1.emblem new file mode 100644 index 0000000..59325eb --- /dev/null +++ b/tests/dummy/app/templates/table-filters/test1.emblem @@ -0,0 +1,36 @@ +table.ui.celled.striped.compact.table + thead + tr + th input + th option-buttons + th date-range + th select-multiple + th select-multiple-modal + th CUSTOM + th number-range + th dropdown-select + th dropdown-select objects + table-filters content=model filters=filters as |filter| + = filter.input key='name' + = filter.option-buttons key='number_of_migration_windows' options=option_buttons_options + = filter.date-range key='target_date' expandingTime=true weekSelector=true + .ui.horizontal.divider custom + .item + button.ui.mini.primary.button{action 'set_filter_target_date_today'}= t 'date.today' + ' + button.ui.mini.primary.button{action 'set_filter_target_date_this_week'}= t 'date.this_week' + ' + button.ui.mini.primary.button{action 'set_filter_target_date_next_fifteen_weeks'} Nu ↔ 15 weken + th + th + th class=filters.target_week:active-filter + if filters.target_week + |   + span= filters.target_week + i.close.icon.clear-target-week-filter{action 'clear_filter' 'target_week'} + = filter.number-range key='age' inclusive=true + = filter.dropdown-select key='location_planning_scenario_classification' options=scenario_classification_options + = filter.dropdown-select key='location_planning_scenario_classification' options=scenario_classification_objects labelPath='grumpy' + + tbody + diff --git a/tests/dummy/app/templates/table-filters/test2.emblem b/tests/dummy/app/templates/table-filters/test2.emblem new file mode 100644 index 0000000..a2b84a0 --- /dev/null +++ b/tests/dummy/app/templates/table-filters/test2.emblem @@ -0,0 +1 @@ +h1 Test222 diff --git a/tests/dummy/config/environment.js b/tests/dummy/config/environment.js new file mode 100644 index 0000000..0b7c1d2 --- /dev/null +++ b/tests/dummy/config/environment.js @@ -0,0 +1,50 @@ +/* eslint-env node */ + +module.exports = function(environment) { + var ENV = { + modulePrefix: 'dummy', + environment: environment, + rootURL: '/app/', + locationType: 'auto', + EmberENV: { + FEATURES: { + // Here you can enable experimental features on an ember canary build + // e.g. 'with-controller': true + }, + EXTEND_PROTOTYPES: { + // Prevent Ember Data from overriding Date.parse. + Date: false + } + }, + + APP: { + // Here you can pass flags/options to your application instance + // when it is created + } + }; + + if (environment === 'development') { + // ENV.APP.LOG_RESOLVER = true; + // ENV.APP.LOG_ACTIVE_GENERATION = true; + // ENV.APP.LOG_TRANSITIONS = true; + // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; + // ENV.APP.LOG_VIEW_LOOKUPS = true; + } + + if (environment === 'test') { + // Testem prefers this... + ENV.locationType = 'none'; + + // keep test console output quieter + ENV.APP.LOG_ACTIVE_GENERATION = false; + ENV.APP.LOG_VIEW_LOOKUPS = false; + + ENV.APP.rootElement = '#ember-testing'; + } + + if (environment === 'production') { + + } + + return ENV; +}; diff --git a/tests/dummy/public/crossdomain.xml b/tests/dummy/public/crossdomain.xml new file mode 100644 index 0000000..0c16a7a --- /dev/null +++ b/tests/dummy/public/crossdomain.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/tests/dummy/public/robots.txt b/tests/dummy/public/robots.txt new file mode 100644 index 0000000..f591645 --- /dev/null +++ b/tests/dummy/public/robots.txt @@ -0,0 +1,3 @@ +# http://www.robotstxt.org +User-agent: * +Disallow: diff --git a/tests/helpers/destroy-app.js b/tests/helpers/destroy-app.js new file mode 100644 index 0000000..c3d4d1a --- /dev/null +++ b/tests/helpers/destroy-app.js @@ -0,0 +1,5 @@ +import Ember from 'ember'; + +export default function destroyApp(application) { + Ember.run(application, 'destroy'); +} diff --git a/tests/helpers/module-for-acceptance.js b/tests/helpers/module-for-acceptance.js new file mode 100644 index 0000000..76996fd --- /dev/null +++ b/tests/helpers/module-for-acceptance.js @@ -0,0 +1,23 @@ +import { module } from 'qunit'; +import Ember from 'ember'; +import startApp from '../helpers/start-app'; +import destroyApp from '../helpers/destroy-app'; + +const { RSVP: { Promise } } = Ember; + +export default function(name, options = {}) { + module(name, { + beforeEach() { + this.application = startApp(); + + if (options.beforeEach) { + return options.beforeEach.apply(this, arguments); + } + }, + + afterEach() { + let afterEach = options.afterEach && options.afterEach.apply(this, arguments); + return Promise.resolve(afterEach).then(() => destroyApp(this.application)); + } + }); +} diff --git a/tests/helpers/resolver.js b/tests/helpers/resolver.js new file mode 100644 index 0000000..b208d38 --- /dev/null +++ b/tests/helpers/resolver.js @@ -0,0 +1,11 @@ +import Resolver from '../../resolver'; +import config from '../../config/environment'; + +const resolver = Resolver.create(); + +resolver.namespace = { + modulePrefix: config.modulePrefix, + podModulePrefix: config.podModulePrefix +}; + +export default resolver; diff --git a/tests/helpers/start-app.js b/tests/helpers/start-app.js new file mode 100644 index 0000000..9a605eb --- /dev/null +++ b/tests/helpers/start-app.js @@ -0,0 +1,15 @@ +import Ember from 'ember'; +import Application from '../../app'; +import config from '../../config/environment'; + +export default function startApp(attrs) { + let attributes = Ember.merge({}, config.APP); + attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; + + return Ember.run(() => { + let application = Application.create(attributes); + application.setupForTesting(); + application.injectTestHelpers(); + return application; + }); +} diff --git a/tests/index.html b/tests/index.html new file mode 100644 index 0000000..5209b85 --- /dev/null +++ b/tests/index.html @@ -0,0 +1,33 @@ + + + + + + Dummy Tests + + + + {{content-for "head"}} + {{content-for "test-head"}} + + + + + + {{content-for "head-footer"}} + {{content-for "test-head-footer"}} + + + {{content-for "body"}} + {{content-for "test-body"}} + + + + + + + + {{content-for "body-footer"}} + {{content-for "test-body-footer"}} + + diff --git a/tests/integration/.gitkeep b/tests/integration/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/components/collapsible-content-test.coffee b/tests/integration/components/collapsible-content-test.coffee new file mode 100644 index 0000000..0708490 --- /dev/null +++ b/tests/integration/components/collapsible-content-test.coffee @@ -0,0 +1,25 @@ +import { test, moduleForComponent } from 'ember-qunit' +import hbs from 'htmlbars-inline-precompile' + +moduleForComponent 'collapsible-content', 'Integration | Component | collapsible content', { + integration: true +} + +test 'it renders', (assert) -> + assert.expect 2 + + # Set any properties with @set 'myProperty', 'value' + # Handle any actions with @on 'myAction', (val) -> + + @render hbs """{{collapsible-content}}""" + + assert.equal @$().text().trim(), '' + + # Template block usage: + @render hbs """ + {{#collapsible-content}} + template block text + {{/collapsible-content}} + """ + + assert.equal @$().text().trim(), 'template block text' diff --git a/tests/integration/components/editable-attribute-test.coffee b/tests/integration/components/editable-attribute-test.coffee new file mode 100644 index 0000000..e0cf4ae --- /dev/null +++ b/tests/integration/components/editable-attribute-test.coffee @@ -0,0 +1,25 @@ +import { test, moduleForComponent } from 'ember-qunit' +import hbs from 'htmlbars-inline-precompile' + +moduleForComponent 'editable-attribute', 'Integration | Component | editable attribute', { + integration: true +} + +test 'it renders', (assert) -> + assert.expect 2 + + # Set any properties with @set 'myProperty', 'value' + # Handle any actions with @on 'myAction', (val) -> + + @render hbs """{{editable-attribute}}""" + + assert.equal @$().text().trim(), '' + + # Template block usage: + @render hbs """ + {{#editable-attribute}} + template block text + {{/editable-attribute}} + """ + + assert.equal @$().text().trim(), 'template block text' diff --git a/tests/integration/components/error-handling-test.coffee b/tests/integration/components/error-handling-test.coffee new file mode 100644 index 0000000..1715340 --- /dev/null +++ b/tests/integration/components/error-handling-test.coffee @@ -0,0 +1,25 @@ +import { test, moduleForComponent } from 'ember-qunit' +import hbs from 'htmlbars-inline-precompile' + +moduleForComponent 'error-handling', 'Integration | Component | error handling', { + integration: true +} + +test 'it renders', (assert) -> + assert.expect 2 + + # Set any properties with @set 'myProperty', 'value' + # Handle any actions with @on 'myAction', (val) -> + + @render hbs """{{error-handling}}""" + + assert.equal @$().text().trim(), '' + + # Template block usage: + @render hbs """ + {{#error-handling}} + template block text + {{/error-handling}} + """ + + assert.equal @$().text().trim(), 'template block text' diff --git a/tests/integration/components/number-input-test.coffee b/tests/integration/components/number-input-test.coffee new file mode 100644 index 0000000..1a8f538 --- /dev/null +++ b/tests/integration/components/number-input-test.coffee @@ -0,0 +1,25 @@ +import { test, moduleForComponent } from 'ember-qunit' +import hbs from 'htmlbars-inline-precompile' + +moduleForComponent 'number-input', 'Integration | Component | number input', { + integration: true +} + +test 'it renders', (assert) -> + assert.expect 2 + + # Set any properties with @set 'myProperty', 'value' + # Handle any actions with @on 'myAction', (val) -> + + @render hbs """{{number-input}}""" + + assert.equal @$().text().trim(), '' + + # Template block usage: + @render hbs """ + {{#number-input}} + template block text + {{/number-input}} + """ + + assert.equal @$().text().trim(), 'template block text' diff --git a/tests/integration/components/pagination-progress-test.coffee b/tests/integration/components/pagination-progress-test.coffee new file mode 100644 index 0000000..2693af3 --- /dev/null +++ b/tests/integration/components/pagination-progress-test.coffee @@ -0,0 +1,25 @@ +import { test, moduleForComponent } from 'ember-qunit' +import hbs from 'htmlbars-inline-precompile' + +moduleForComponent 'pagination-progress', 'Integration | Component | pagination progress', { + integration: true +} + +test 'it renders', (assert) -> + assert.expect 2 + + # Set any properties with @set 'myProperty', 'value' + # Handle any actions with @on 'myAction', (val) -> + + @render hbs """{{pagination-progress}}""" + + assert.equal @$().text().trim(), '' + + # Template block usage: + @render hbs """ + {{#pagination-progress}} + template block text + {{/pagination-progress}} + """ + + assert.equal @$().text().trim(), 'template block text' diff --git a/tests/integration/components/push-action-test.coffee b/tests/integration/components/push-action-test.coffee new file mode 100644 index 0000000..9f5f07c --- /dev/null +++ b/tests/integration/components/push-action-test.coffee @@ -0,0 +1,25 @@ +import { test, moduleForComponent } from 'ember-qunit' +import hbs from 'htmlbars-inline-precompile' + +moduleForComponent 'push-action', 'Integration | Component | push action', { + integration: true +} + +test 'it renders', (assert) -> + assert.expect 2 + + # Set any properties with @set 'myProperty', 'value' + # Handle any actions with @on 'myAction', (val) -> + + @render hbs """{{push-action}}""" + + assert.equal @$().text().trim(), '' + + # Template block usage: + @render hbs """ + {{#push-action}} + template block text + {{/push-action}} + """ + + assert.equal @$().text().trim(), 'template block text' diff --git a/tests/integration/components/ui-calendar-moment-test.coffee b/tests/integration/components/ui-calendar-moment-test.coffee new file mode 100644 index 0000000..c73e36d --- /dev/null +++ b/tests/integration/components/ui-calendar-moment-test.coffee @@ -0,0 +1,25 @@ +import { test, moduleForComponent } from 'ember-qunit' +import hbs from 'htmlbars-inline-precompile' + +moduleForComponent 'ui-calendar-moment', 'Integration | Component | ui calendar moment', { + integration: true +} + +test 'it renders', (assert) -> + assert.expect 2 + + # Set any properties with @set 'myProperty', 'value' + # Handle any actions with @on 'myAction', (val) -> + + @render hbs """{{ui-calendar-moment}}""" + + assert.equal @$().text().trim(), '' + + # Template block usage: + @render hbs """ + {{#ui-calendar-moment}} + template block text + {{/ui-calendar-moment}} + """ + + assert.equal @$().text().trim(), 'template block text' diff --git a/tests/integration/components/ui-markdown-popup-test.coffee b/tests/integration/components/ui-markdown-popup-test.coffee new file mode 100644 index 0000000..9052f60 --- /dev/null +++ b/tests/integration/components/ui-markdown-popup-test.coffee @@ -0,0 +1,25 @@ +import { test, moduleForComponent } from 'ember-qunit' +import hbs from 'htmlbars-inline-precompile' + +moduleForComponent 'ui-markdown-popup', 'Integration | Component | ui markdown popup', { + integration: true +} + +test 'it renders', (assert) -> + assert.expect 2 + + # Set any properties with @set 'myProperty', 'value' + # Handle any actions with @on 'myAction', (val) -> + + @render hbs """{{ui-markdown-popup}}""" + + assert.equal @$().text().trim(), '' + + # Template block usage: + @render hbs """ + {{#ui-markdown-popup}} + template block text + {{/ui-markdown-popup}} + """ + + assert.equal @$().text().trim(), 'template block text' diff --git a/tests/test-helper.js b/tests/test-helper.js new file mode 100644 index 0000000..e6cfb70 --- /dev/null +++ b/tests/test-helper.js @@ -0,0 +1,6 @@ +import resolver from './helpers/resolver'; +import { + setResolver +} from 'ember-qunit'; + +setResolver(resolver); diff --git a/tests/test-helpers/freeze-moment-test.coffee b/tests/test-helpers/freeze-moment-test.coffee new file mode 100644 index 0000000..b0553d7 --- /dev/null +++ b/tests/test-helpers/freeze-moment-test.coffee @@ -0,0 +1,10 @@ +import { module, test } from 'qunit' + +module 'Test helpers | Utility | freeze moment' +test 'freezeMoment', (assert) -> + freezeMoment '2016-12-21T14:22:00Z', -> + assert.equal moment().toISOString().substring(0, 19), '2016-12-21T14:22:00' # check if the specially for this case developed freezeMoment function + + assert.notEqual moment().toISOString().substring(0, 10), '2016-12-21' + + diff --git a/tests/unit/.gitkeep b/tests/unit/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/helpers/any-permission-starts-with-test.coffee b/tests/unit/helpers/any-permission-starts-with-test.coffee new file mode 100644 index 0000000..d058dff --- /dev/null +++ b/tests/unit/helpers/any-permission-starts-with-test.coffee @@ -0,0 +1,9 @@ +import { anyPermissionStartsWith } from 'dummy/helpers/any-permission-starts-with' +import { module, test } from 'qunit' + +module 'Unit | Helper | any permission starts with' + +# Replace this with your real tests. +test 'it works', (assert) -> + result = anyPermissionStartsWith 42 + assert.ok result diff --git a/tests/unit/helpers/date-test.coffee b/tests/unit/helpers/date-test.coffee new file mode 100644 index 0000000..2686b50 --- /dev/null +++ b/tests/unit/helpers/date-test.coffee @@ -0,0 +1,9 @@ +import { date } from 'dummy/helpers/date' +import { module, test } from 'qunit' + +module 'Unit | Helper | date' + +# Replace this with your real tests. +test 'it works', (assert) -> + result = date 42 + assert.ok result diff --git a/tests/unit/helpers/dunlop-can-test.coffee b/tests/unit/helpers/dunlop-can-test.coffee new file mode 100644 index 0000000..beac04f --- /dev/null +++ b/tests/unit/helpers/dunlop-can-test.coffee @@ -0,0 +1,9 @@ +import { dunlopCan } from 'dummy/helpers/dunlop-can' +import { module, test } from 'qunit' + +module 'Unit | Helper | dunlop can' + +# Replace this with your real tests. +test 'it works', (assert) -> + result = dunlopCan 42 + assert.ok result diff --git a/tests/unit/helpers/has-permission-of-type-test.coffee b/tests/unit/helpers/has-permission-of-type-test.coffee new file mode 100644 index 0000000..b01e823 --- /dev/null +++ b/tests/unit/helpers/has-permission-of-type-test.coffee @@ -0,0 +1,9 @@ +import { hasPermissionOfType } from 'dummy/helpers/has-permission-of-type' +import { module, test } from 'qunit' + +module 'Unit | Helper | has permission of type' + +# Replace this with your real tests. +test 'it works', (assert) -> + result = hasPermissionOfType 42 + assert.ok result diff --git a/tests/unit/helpers/minute-of-day-test.coffee b/tests/unit/helpers/minute-of-day-test.coffee new file mode 100644 index 0000000..e9e8cb0 --- /dev/null +++ b/tests/unit/helpers/minute-of-day-test.coffee @@ -0,0 +1,9 @@ +import { minuteOfDay } from 'dummy/helpers/minute-of-day' +import { module, test } from 'qunit' + +module 'Unit | Helper | minute of day' + +# Replace this with your real tests. +test 'it works', (assert) -> + result = minuteOfDay 42 + assert.ok result diff --git a/tests/unit/helpers/number-to-xls-header-test.coffee b/tests/unit/helpers/number-to-xls-header-test.coffee new file mode 100644 index 0000000..2d386a6 --- /dev/null +++ b/tests/unit/helpers/number-to-xls-header-test.coffee @@ -0,0 +1,9 @@ +import { numberToXlsHeader } from 'dummy/helpers/number-to-xls-header' +import { module, test } from 'qunit' + +module 'Unit | Helper | number to xls header' + +# Replace this with your real tests. +test 'it works', (assert) -> + result = numberToXlsHeader 42 + assert.ok result diff --git a/tests/unit/helpers/t-test.coffee b/tests/unit/helpers/t-test.coffee new file mode 100644 index 0000000..9088bae --- /dev/null +++ b/tests/unit/helpers/t-test.coffee @@ -0,0 +1,9 @@ +import { t } from 'dummy/helpers/t' +import { module, test } from 'qunit' + +module 'Unit | Helper | t' + +# Replace this with your real tests. +test 'it works', (assert) -> + result = t 42 + assert.ok result diff --git a/tests/unit/initializers/ember-cli-dunlop-overrides-test.coffee b/tests/unit/initializers/ember-cli-dunlop-overrides-test.coffee new file mode 100644 index 0000000..4e756e3 --- /dev/null +++ b/tests/unit/initializers/ember-cli-dunlop-overrides-test.coffee @@ -0,0 +1,20 @@ +import Ember from 'ember' +import { initialize } from 'dummy/initializers/ember-cli-dunlop-overrides' +import { module, test } from 'qunit' + +application = null +registry = null + +module 'Unit | Initializer | ember cli dunlop overrides', + beforeEach: -> + Ember.run -> + application = Ember.Application.create() + registry = application.registry + application.deferReadiness() + +# Replace this with your real tests. +test 'it works', (assert) -> + initialize registry, application + + # you would normally confirm the results of the initializer here + assert.ok true diff --git a/tests/unit/models/dunlop-user-test.coffee b/tests/unit/models/dunlop-user-test.coffee new file mode 100644 index 0000000..f5f7aca --- /dev/null +++ b/tests/unit/models/dunlop-user-test.coffee @@ -0,0 +1,11 @@ +import { moduleForModel, test } from 'ember-qunit' + +moduleForModel 'dunlop-user', 'Unit | Model | dunlop user', { + # Specify the other units that are required for this test. + needs: [] +} + +test 'it exists', (assert) -> + model = @subject() + # store = @store() + assert.ok !!model diff --git a/tests/unit/serializers/create-and-update-test.coffee b/tests/unit/serializers/create-and-update-test.coffee new file mode 100644 index 0000000..72a8a36 --- /dev/null +++ b/tests/unit/serializers/create-and-update-test.coffee @@ -0,0 +1,13 @@ +import { moduleForModel, test } from 'ember-qunit' + +moduleForModel 'create-and-update', 'Unit | Serializer | create and update', + # Specify the other units that are required for this test. + needs: ['serializer:create-and-update'] + +# Replace this with your real tests. +test 'it serializes records', (assert) -> + record = @subject() + + serializedRecord = record.serialize() + + assert.ok serializedRecord diff --git a/tests/unit/transforms/moment-test.coffee b/tests/unit/transforms/moment-test.coffee new file mode 100644 index 0000000..4299a7a --- /dev/null +++ b/tests/unit/transforms/moment-test.coffee @@ -0,0 +1,11 @@ +import { moduleFor, test } from 'ember-qunit' + +moduleFor 'transform:moment', 'Unit | Transform | moment', { + # Specify the other units that are required for this test. + # needs: ['serializer:foo'] +} + +# Replace this with your real tests. +test 'it exists', (assert) -> + transform = @subject() + assert.ok transform diff --git a/tests/unit/utils/date-sort-test.coffee b/tests/unit/utils/date-sort-test.coffee new file mode 100644 index 0000000..5c298b3 --- /dev/null +++ b/tests/unit/utils/date-sort-test.coffee @@ -0,0 +1,9 @@ +import dateSort from '../../../utils/date-sort' +import { module, test } from 'qunit' + +module 'Unit | Utility | date sort' + +# Replace this with your real tests. +test 'it works', (assert) -> + result = dateSort() + assert.ok result diff --git a/tests/unit/utils/get-max-suffix-test.coffee b/tests/unit/utils/get-max-suffix-test.coffee new file mode 100644 index 0000000..35cb26f --- /dev/null +++ b/tests/unit/utils/get-max-suffix-test.coffee @@ -0,0 +1,13 @@ +import getMaxSuffix from 'ember-cli-dunlop/utils/get-max-suffix' +import { module, test } from 'qunit' + +module 'Unit | Utility | get max suffix' + +# Replace this with your real tests. +test 'it works as module', (assert) -> + result = getMaxSuffix(["Asdzo-AAV2-4", "Asdzo-AAV2", "Asdzo-AAV3"]) + assert.equal result, 4 + +test 'it works globally', (assert) -> + result = get_max_suffix(["Asdzo-AAV2-4", "Asdzo-AAV2", "Asdzo-AAV3"]) + assert.equal result, 4 diff --git a/tests/unit/utils/integer-to-string-test.coffee b/tests/unit/utils/integer-to-string-test.coffee new file mode 100644 index 0000000..32e6e77 --- /dev/null +++ b/tests/unit/utils/integer-to-string-test.coffee @@ -0,0 +1,9 @@ +import integerToString from '../../../utils/integer-to-string' +import { module, test } from 'qunit' + +module 'Unit | Utility | integer to string' + +# Replace this with your real tests. +test 'it works', (assert) -> + result = integerToString() + assert.ok result diff --git a/tests/unit/utils/merge-objects-test.coffee b/tests/unit/utils/merge-objects-test.coffee new file mode 100644 index 0000000..1bd28f3 --- /dev/null +++ b/tests/unit/utils/merge-objects-test.coffee @@ -0,0 +1,9 @@ +import mergeObjects from '../../../utils/merge-objects' +import { module, test } from 'qunit' + +module 'Unit | Utility | merge objects' + +# Replace this with your real tests. +test 'it works', (assert) -> + result = mergeObjects() + assert.ok result diff --git a/tests/unit/utils/range-parser-test.coffee b/tests/unit/utils/range-parser-test.coffee new file mode 100644 index 0000000..e79b2d2 --- /dev/null +++ b/tests/unit/utils/range-parser-test.coffee @@ -0,0 +1,9 @@ +import RangeParser from 'ember-cli-dunlop/utils/range-parser' +import { module, test } from 'qunit' + +module 'Unit | Utility | range parser' + +# Replace this with your real tests. +test 'it works sunny', (assert) -> + result = RangeParser.parse(' 3, 5 - 9 , 17-19, 29') + assert.deepEqual result, [3, 5, 6, 7, 8, 9, 17, 18, 19, 29] diff --git a/tests/unit/utils/sort-numeric-test.coffee b/tests/unit/utils/sort-numeric-test.coffee new file mode 100644 index 0000000..28c9370 --- /dev/null +++ b/tests/unit/utils/sort-numeric-test.coffee @@ -0,0 +1,9 @@ +import sortNumeric from '../../../utils/sort-numeric' +import { module, test } from 'qunit' + +module 'Unit | Utility | sort numeric' + +# Replace this with your real tests. +test 'it works', (assert) -> + result = sortNumeric() + assert.ok result diff --git a/tests/unit/validators/conditional-presence-test.coffee b/tests/unit/validators/conditional-presence-test.coffee new file mode 100644 index 0000000..afe0123 --- /dev/null +++ b/tests/unit/validators/conditional-presence-test.coffee @@ -0,0 +1,7 @@ +import { module, test } from 'qunit' +import validateConditionalPresence from 'frontend/validators/conditional-presence' + +module('Unit | Validator | conditional-presence') + +test "it exists", (assert) -> + assert.ok validateConditionalPresence() diff --git a/vendor/.gitkeep b/vendor/.gitkeep new file mode 100644 index 0000000..e69de29