diff --git a/.eslintrc.js b/.eslintrc.js index 8b2640f3..ffc1ec9d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,18 +1,20 @@ module.exports = { root: true, parserOptions: { - ecmaVersion: 2017, - sourceType: 'module' + ecmaVersion: 2018, + sourceType: 'module', + ecmaFeatures: { + legacyDecorators: true, + }, }, - plugins: [ - 'ember' - ], + plugins: ['ember'], extends: [ 'eslint:recommended', - 'plugin:ember/recommended' + 'plugin:ember/recommended', + 'plugin:prettier/recommended', ], env: { - browser: true + browser: true, }, rules: { 'semi': 'error', @@ -22,19 +24,35 @@ module.exports = { // node files { files: [ - 'testem.js', - 'ember-cli-build.js', - 'config/**/*.js', - 'lib/*/index.js' + './.eslintrc.js', + './.prettierrc.js', + './.template-lintrc.js', + './ember-cli-build.js', + './testem.js', + './blueprints/*/index.js', + './config/**/*.js', + './lib/*/index.js', + './server/**/*.js', ], parserOptions: { sourceType: 'script', - ecmaVersion: 2015 }, env: { browser: false, - node: true - } - } - ] + node: true, + }, + plugins: ['node'], + extends: ['plugin:node/recommended'], + rules: { + // this can be removed once the following is fixed + // https://github.com/mysticatea/eslint-plugin-node/issues/77 + 'node/no-unpublished-require': 'off', + }, + }, + { + // Test files: + files: ['tests/**/*-test.{js,ts}'], + extends: ['plugin:qunit/recommended'], + }, + ], }; diff --git a/.github/workflows/buildDeploy.yml b/.github/workflows/buildDeploy.yml index 53b7cb7b..7f82e6ee 100644 --- a/.github/workflows/buildDeploy.yml +++ b/.github/workflows/buildDeploy.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: - node-version: [10.x] + node-version: [14.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: diff --git a/.gitignore b/.gitignore index 941c1204..d84d5370 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ # misc /.vscode /.sass-cache +/.eslintcache /connect.lock /coverage/* /libpeerconnection.log diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..92216555 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,21 @@ +# unconventional js +/blueprints/*/files/ +/vendor/ + +# compiled output +/dist/ +/tmp/ + +# dependencies +/bower_components/ +/node_modules/ + +# misc +/coverage/ +!.* +.eslintcache + +# ember-try +/.node_modules.ember-try/ +/bower.json.ember-try +/package.json.ember-try diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000..534e6d35 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = { + singleQuote: true, +}; diff --git a/.travis.yml b/.travis.yml index 239370a2..6a3d28d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ --- language: node_js node_js: - - "10" + - "12" sudo: false dist: trusty diff --git a/README.md b/README.md index e7e3eaf7..01f165b0 100644 --- a/README.md +++ b/README.md @@ -64,8 +64,8 @@ Make use of the many generators for code, try `ember help generate` for more det ### Linting -* `npm run lint:js` -* `npm run lint:js -- --fix` +* `npm run lint` +* `npm run lint:fix` ### Building diff --git a/app/components/event-list-search.js b/app/components/event-list-search.js index 0d1fe314..cc97e8d5 100644 --- a/app/components/event-list-search.js +++ b/app/components/event-list-search.js @@ -5,6 +5,8 @@ import { setParameterByName, getParameterByName } from 'kursausschreibung/framew import { sortAs } from '../framework/gui-helpers'; import { getSortAs } from '../framework/storage'; import settings from '../framework/settings'; +import { getString } from '../framework/translate'; +import { htmlSafe } from '@ember/string'; // tests if a query matches a value function match(value, query) { @@ -32,15 +34,15 @@ export default Component.extend({ this.send('queryChanged'); } - let sortOptions = []; + let options = ''; if(settings.sortOptions === undefined) { - sortOptions.push({key:'error', value:'configure key sortoptions array in settings'}); + options = ''; } else { settings.sortOptions.forEach(option => { - sortOptions.push({key:option, value:"sort"+option}); + options = options + ''; }); } - this.set('sortOptions',sortOptions); + this.set('sortOptions',htmlSafe(options)); }, didRender() { @@ -50,7 +52,9 @@ export default Component.extend({ filteredEvents: oneWay('events'), keyUp(){ + this.set('query',document.getElementById('searchEvents').value) setParameterByName('search',this.get('query')); + this.send('queryChanged'); }, actions: { diff --git a/app/components/input/input-dropdown.js b/app/components/input/input-dropdown.js index 03b5f1d8..cc3af4c7 100644 --- a/app/components/input/input-dropdown.js +++ b/app/components/input/input-dropdown.js @@ -1,7 +1,17 @@ import Component from '@ember/component'; import { vssDependency } from 'kursausschreibung/framework/form-helpers'; +import { htmlSafe } from '@ember/string'; export default Component.extend({ + willRender() { + let options = this.get('field.options.options'); + let dropdownOptions = ''; + options.forEach(option => { + dropdownOptions = dropdownOptions + ''; + }); + this.set('dropdownOptions',htmlSafe(dropdownOptions)); + }, + change(){ let field = this.get('field'); let currentValue = null; diff --git a/app/components/input/input-email.js b/app/components/input/input-email.js index 28ca9894..c52a8817 100644 --- a/app/components/input/input-email.js +++ b/app/components/input/input-email.js @@ -1,10 +1,11 @@ import Component from '@ember/component'; import { formFieldError } from 'kursausschreibung/framework/form-helpers'; +import jQuery from 'jquery'; export default Component.extend({ change() { // show an error message for duplicate e-mails - const emailFields = this.$().closest('form').find('input[type="email"]').toArray(); + const emailFields = jQuery('#subscriptionForm').closest('form').find('input[type="email"]').toArray(); const emailFieldValues = emailFields.map(field => field.value); emailFields.forEach((field, fieldIndex) => { diff --git a/app/components/input/input-freeform-dropdown.js b/app/components/input/input-freeform-dropdown.js index eec79f12..1cf30b54 100644 --- a/app/components/input/input-freeform-dropdown.js +++ b/app/components/input/input-freeform-dropdown.js @@ -1,5 +1,6 @@ import Component from '@ember/component'; import { vssDependency } from 'kursausschreibung/framework/form-helpers'; +import jQuery from 'jquery'; export default Component.extend({ didInsertElement() { @@ -7,7 +8,7 @@ export default Component.extend({ let options = this.get('field.options').options.map(option => option.Value); - this.$('.typeahead').typeahead( + jQuery('.typeahead').typeahead( { hint: true, highlight: true, @@ -26,7 +27,7 @@ export default Component.extend({ }, willDestroyElement() { - this.$('.typeahead').typeahead('destroy'); + jQuery('.typeahead').typeahead('destroy'); this._super(...arguments); }, diff --git a/app/components/input/input-postal-code.js b/app/components/input/input-postal-code.js index 38143df7..e6f6fda8 100644 --- a/app/components/input/input-postal-code.js +++ b/app/components/input/input-postal-code.js @@ -1,6 +1,7 @@ import Component from '@ember/component'; import { debounce } from '@ember/runloop'; import { getPostalCodes } from 'kursausschreibung/framework/api'; +import jQuery from 'jquery'; export default Component.extend({ @@ -11,8 +12,9 @@ export default Component.extend({ getPostalCodes(query).then(response => asyncResults(response)); }; - let $typeahead = this.$('.typeahead'); - let $locationFields = this.$().closest('fieldset').find('input[name="Location"]'); + let elementId = '#'+this.elementId + let $typeahead = jQuery(elementId).children(0); + let $locationFields = jQuery(elementId).closest('fieldset').find('input[name="Location"]'); $typeahead.typeahead( { @@ -38,7 +40,7 @@ export default Component.extend({ }, willDestroyElement() { - this.$('.typeahead').typeahead('destroy'); + jQuery('.typeaheadZip').typeahead('destroy'); this._super(...arguments); } }); diff --git a/app/components/subscription-form.js b/app/components/subscription-form.js index 6f0eb845..698818e2 100644 --- a/app/components/subscription-form.js +++ b/app/components/subscription-form.js @@ -1,6 +1,6 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; -import $ from 'jquery'; +import jQuery from 'jquery'; import { formatDate, getDMY, getYMD } from 'kursausschreibung/framework/date-helpers'; import { setDataToSubmit } from 'kursausschreibung/framework/storage'; import { getString } from 'kursausschreibung/framework/translate'; @@ -30,10 +30,18 @@ export default Component.extend({ submit(event) { event.preventDefault(); - subscribe(this.$('form'), this); + subscribe(jQuery('form'), this); this.get('subscribe')(); }, + useCompanyAddress(){ + var value = this.get('useCompanyAddress'); + if(value){ + this.set('useCompanyAddress',false); + } else { + this.set('useCompanyAddress',true); + } + }, addPerson() { if (this.get('event.FreeSeats') - 1 - this.get('additionalPeopleCount') <= 0) { uikit.modal.alert(getString('noSeatsAvailable')); diff --git a/app/controllers/list/category/event/index.js b/app/controllers/list/category/event/index.js index bf0b0eff..53048261 100644 --- a/app/controllers/list/category/event/index.js +++ b/app/controllers/list/category/event/index.js @@ -1,15 +1,9 @@ import Controller from '@ember/controller'; import settings from 'kursausschreibung/framework/settings'; -import LinkComponent from '@ember/routing/link-component'; let badgeFreeSeatsEnabled = typeof settings.badgeFreeSeats === 'object' && settings.badgeFreeSeats.enabled === true; export default Controller.extend({ showBreadcrumbs: settings.showBreadcrumbs, badgeFreeSeatsEnabled -}); - -// bindings for tooltip and disabled attributes -LinkComponent.reopen({ - attributeBindings: ['data-uk-tooltip', 'disabled'] -}); +}); \ No newline at end of file diff --git a/app/controllers/list/category/event/subscribe.js b/app/controllers/list/category/event/subscribe.js index 32fc6eb4..d35cd293 100644 --- a/app/controllers/list/category/event/subscribe.js +++ b/app/controllers/list/category/event/subscribe.js @@ -1,9 +1,11 @@ import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; +import { action } from '@ember/object'; -export default Controller.extend({ - actions: { - subscribe() { - this.transitionToRoute('list.category.event.confirmation'); +export default class subscribeController extends Controller { + @service router; + @action + subscribe() { + this.router.transitionTo('list.category.event.confirmation'); } - } -}); +}; diff --git a/app/framework/store.js b/app/framework/store.js index 9fe9c570..5eb0b46f 100644 --- a/app/framework/store.js +++ b/app/framework/store.js @@ -319,7 +319,7 @@ function prepareEvent(event) { addDisplayData(event); //settings subscriptionWithLoginURL - event.subscriptionWithLoginURL = settings.subscriptionWithLoginURL; + event.subscriptionWithLoginURL = encodeURI(settings.subscriptionWithLoginURL+'/'+event.EventCategory+'/'+event.Id+'/subscribe'); //event subtitle when > inside string let eventSubtitle = event.Designation.split(settings.eventSubtitle); diff --git a/app/index.html b/app/index.html index fd8dc8d4..0297bd69 100644 --- a/app/index.html +++ b/app/index.html @@ -26,7 +26,8 @@ - + + diff --git a/app/router.js b/app/router.js index fa811696..a7f2f749 100644 --- a/app/router.js +++ b/app/router.js @@ -9,23 +9,24 @@ const Router = EmberRouter.extend({ location: config.locationType, rootURL: config.rootURL, - didTransition() { - this._super(...arguments); - - var subscriptionProcessId = 'subscriptionProcess'; - - setInterval(function () { - if (document.getElementById(subscriptionProcessId) !== null) { - setOffsetStickyHeader(subscriptionProcessId); + init(){ + this.on('routeDidChange', transition => { + this._super(...arguments); + + var subscriptionProcessId = 'subscriptionProcess'; + + setInterval(function () { + if (document.getElementById(subscriptionProcessId) !== null) { + setOffsetStickyHeader(subscriptionProcessId); + } + }, 1000); + + if (this.currentPath === 'list.category.event.subscribe') { + scrollToTimeout(subscriptionProcessId); + } else if (this.currentPath !== 'list.index') { + scrollToTimeout(rootElement.id); } - }, 1000); - - if (this.currentPath === 'list.category.event.subscribe') { - scrollToTimeout(subscriptionProcessId); - } else if (this.currentPath !== 'list.index') { - scrollToTimeout(rootElement.id); - } - + }); } }); diff --git a/app/routes/index.js b/app/routes/index.js index e0df8536..0d961a4e 100644 --- a/app/routes/index.js +++ b/app/routes/index.js @@ -1,7 +1,9 @@ import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; import { isInitialized } from 'kursausschreibung/framework/store'; -export default Route.extend({ +export default class permalink extends Route { + @service router; beforeModel() { let applicationModel = this.modelFor('application'); @@ -14,6 +16,6 @@ export default Route.extend({ throw new Error('failed to load.'); } - this.replaceWith('list', applicationModel.areaKeys[0]); + this.router.transitionTo('list', applicationModel.areaKeys[0]); } -}); +} diff --git a/app/routes/list/category.js b/app/routes/list/category.js index 6ae25d67..eda9c6de 100644 --- a/app/routes/list/category.js +++ b/app/routes/list/category.js @@ -1,7 +1,9 @@ import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; import { underscore } from '@ember/string'; -export default Route.extend({ +export default class category extends Route { + @service router; model(params) { let categories = this.modelFor('list').categories; @@ -10,10 +12,10 @@ export default Route.extend({ // check if category exists if (!(categories.hasOwnProperty(params.category))) { - this.replaceWith('list'); + this.router.transitionTo('list'); return; } return categories[params.category]; } -}); +} diff --git a/app/routes/list/category/event.js b/app/routes/list/category/event.js index 50939001..98e4b73e 100644 --- a/app/routes/list/category/event.js +++ b/app/routes/list/category/event.js @@ -1,8 +1,10 @@ import Route from '@ember/routing/route'; import { underscore } from '@ember/string'; import store from 'kursausschreibung/framework/store'; +import { inject as service } from '@ember/service'; -export default Route.extend({ +export default class event extends Route { + @service router; model(params) { let event = store.getEventById(params.event_id); @@ -15,10 +17,10 @@ export default Route.extend({ event.areaKey !== areaKey || event.categoryKey !== categoryKey ) { - this.replaceWith('list.category'); + this.router.transitionTo('list.category'); return; } return event; } -}); +}; diff --git a/app/routes/list/category/event/confirmation.js b/app/routes/list/category/event/confirmation.js index 782c7bdd..bc9c2d86 100644 --- a/app/routes/list/category/event/confirmation.js +++ b/app/routes/list/category/event/confirmation.js @@ -7,14 +7,16 @@ import { autoCheckForLogin } from 'kursausschreibung/framework/login-helpers'; import settings from 'kursausschreibung/framework/settings'; import { SUBSCRIPTION_DETAIL_ALLOW_MULTIPLE_PEOPLE } from 'kursausschreibung/framework/api'; import { getString } from 'kursausschreibung/framework/translate'; +import { inject as service } from '@ember/service'; -export default Route.extend({ +export default class confirmation extends Route { + @service router; model() { let dataToSubmit = getDataToSubmit(); let event = this.modelFor('list.category.event'); if (dataToSubmit === null) { - this.replaceWith('list.category.event'); + this.router.transitionTo('list.category.event'); return; } @@ -100,7 +102,7 @@ export default Route.extend({ throw { message: message }; }); } -}); +} // this function creates an address, a company address (if requested) and returns a // promise for a personId diff --git a/app/routes/list/category/event/subscribe.js b/app/routes/list/category/event/subscribe.js index b2d26310..ecf66758 100644 --- a/app/routes/list/category/event/subscribe.js +++ b/app/routes/list/category/event/subscribe.js @@ -158,11 +158,10 @@ export default Route.extend({ let model = this.modelFor('list.category.event'); if (model.externalSubscriptionURL !== null) { - this.replaceWith('list.category.event.index'); + transition.abort(); } if (model.get('canDoSubscription') === false) { - this.replaceWith('list.category.event'); transition.abort(); return; } diff --git a/app/routes/permalink.js b/app/routes/permalink.js index bebc4ff7..d1ee5222 100644 --- a/app/routes/permalink.js +++ b/app/routes/permalink.js @@ -1,15 +1,18 @@ import Route from '@ember/routing/route'; import { getEventById } from 'kursausschreibung/framework/store'; +import { inject as service } from '@ember/service'; + +export default class permalink extends Route { + @service router; -export default Route.extend({ model(params) { const event = getEventById(params.event_id); // redirect to event if it exists if (event !== undefined) { - this.replaceWith('list.category.event', event.areaKey, event.categoryKey, event.Id); + this.router.transitionTo('list.category.event', event.areaKey, event.categoryKey, event.Id); } else { - this.replaceWith(''); + this.router.transitionTo(''); } } -}); +}; diff --git a/app/styles/app.css b/app/styles/app.css index 46b2c036..0e2160fb 100644 --- a/app/styles/app.css +++ b/app/styles/app.css @@ -17,9 +17,13 @@ border-bottom: 1px solid #e5e5e5; } -.uk-scope .uk-navbar-nav > li.uk-active > a { +.uk-scope .uk-navbar-nav > li.uk-active > a, +.uk-scope .uk-navbar-nav > li > a.active { border-bottom: 2px solid var(--mainColor); } +.uk-scope a.active { + color: #666; +} .uk-scope h2 { overflow-wrap: break-word; diff --git a/app/templates/application.hbs b/app/templates/application.hbs index fa66f036..e885a9dd 100644 --- a/app/templates/application.hbs +++ b/app/templates/application.hbs @@ -4,18 +4,17 @@