diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/application.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/application.js
new file mode 100644
index 00000000..7645bd8e
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/application.js
@@ -0,0 +1,9 @@
+define([
+ 'ember',
+ 'application'
+], function (ember, app) {
+ app.ApplicationController = ember.Controller.extend({
+ subscriptionDetails: null, // allow all Routes to access the subscriptionDetails
+ resetInputTextarea: false, // let the result Route reset the textArea when the details get sent
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/input.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/input.js
new file mode 100644
index 00000000..051fe9ca
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/input.js
@@ -0,0 +1,79 @@
+define([
+ 'ember',
+ 'application'
+], function (ember, app) {
+
+ // Dependencies of the computed properties:
+ //
+ // +-------------+
+ // |TextareaValue|
+ // +-----+-------+
+ // |
+ // +-----v----------------+
+ // | strippedTextareaValue|
+ // +-----+-------------+--+
+ // | |
+ // +-----v---------+ +-v-----------------+
+ // |textareaIsEmpty| |subscriptionDetails|
+ // +-----+---------+ +-+-----------------+
+ // | |
+ // +-----v---+ |
+ // |showError<---------+
+ // +---------+
+ app.InputController = ember.Controller.extend({
+
+ // the content of the textarea
+ textareaValue: '',
+
+ // textareaValue without whitespace
+ strippedTextareaValue: ember.computed('textareaValue', function () {
+ var textareaValue = this.get('textareaValue');
+
+ // remove empty lines
+ textareaValue = textareaValue.replace(/(?:(?:^|\n)[\t ]*)+(?:$|\n)/g, '\n');
+
+ // remove first and last newline (if they exist)
+ textareaValue = textareaValue.replace(/^\n|\n$/g, '');
+
+ return textareaValue;
+ }),
+
+ // this property is true when the textarea is empty
+ textareaIsEmpty: ember.computed('strippedTextareaValue', function () {
+ return this.get('strippedTextareaValue') === '';
+ }),
+
+ // this property contains the parsed subscriptionDetails of the textarea
+ subscriptionDetails: ember.computed('strippedTextareaValue', function () {
+ var rows = this.get('strippedTextareaValue').split('\n');
+ var subscriptionDetails = [];
+
+ for (var i = 0; i < rows.length; i++) {
+ var result = rows[i].match(/^(\d+)\t(\d+)\t(\d+)\t([^\t]+)$/);
+
+ if (result === null) {
+ return null;
+ }
+
+ subscriptionDetails.push(ember.Object.create({
+ eventId: result[1],
+ personId: result[2],
+ subscriptionDetailId: result[3],
+ newValue: result[4]
+ }));
+ }
+
+ return subscriptionDetails;
+ }),
+
+ // the textarea is not empty and cannot be parsed
+ showError: ember.computed('textareaIsEmpty', 'subscriptionDetails', function () {
+ return this.get('textareaIsEmpty') === false && this.get('subscriptionDetails') === null;
+ }),
+
+ // it's not possible to go to the next step when no subscriptionDetail can be parsed
+ cannotContinue: ember.computed('subscriptionDetails', function () {
+ return this.get('subscriptionDetails') === null;
+ })
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/validation.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/validation.js
new file mode 100644
index 00000000..6f0cff0b
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/validation.js
@@ -0,0 +1,13 @@
+define([
+ 'ember',
+ 'application'
+], function (ember, app) {
+ app.ValidationController = ember.Controller.extend({
+
+ cannotGoBack: ember.computed.not('model.everythingIsChecked'),
+
+ cannotContinue: ember.computed('model.everythingIsChecked', 'model.thereAreOnlyErrors', function () {
+ return !this.get('model.everythingIsChecked') || this.get('model.thereAreOnlyErrors');
+ }),
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/vssver2.scc b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/vssver2.scc
new file mode 100644
index 00000000..0320c00b
Binary files /dev/null and b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Controllers/vssver2.scc differ
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/application.html b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/application.html
new file mode 100644
index 00000000..e1075c67
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/application.html
@@ -0,0 +1,3 @@
+
+ {{outlet}}
+
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/input.html b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/input.html
new file mode 100644
index 00000000..8343b524
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/input.html
@@ -0,0 +1,17 @@
+
+ {{outlet "navigation"}}
+
+
{{translate "readSubscriptionDetailsInfo"}}
{{translate "readSubscriptionDetailsFields"}}
+ {{textarea value=textareaValue rows=27 spellcheck=false class=(if showError "textarea--error")}}
+
+
+
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/navigation.html b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/navigation.html
new file mode 100644
index 00000000..909d5893
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/navigation.html
@@ -0,0 +1,9 @@
+
+
+ {{#link-to "input" disabled=true}}{{translate "input"}}{{/link-to}}
+ ›
+ {{#link-to "validation" disabled=true}}{{translate "validation"}}{{/link-to}}
+ ›
+ {{#link-to "result" disabled=true}}{{translate "result"}}{{/link-to}}
+
+
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/result.html b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/result.html
new file mode 100644
index 00000000..17d9459a
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/result.html
@@ -0,0 +1,60 @@
+
+ {{outlet "navigation"}}
+
+
+
+ {{translate "event"}}
+ {{translate "name"}}
+ {{translate "subscriptionDetail"}}
+ {{translate "oldValue"}}
+ {{translate "newValue"}}
+ {{translate "status"}}
+ {{translate "fehler"}}
+
+
+
+ {{#each model.subscriptionDetails as |subscriptionDetail|}}
+ {{#unless subscriptionDetail.error}}
+
+ {{subscriptionDetail.event}}
+ {{subscriptionDetail.name}}
+ {{subscriptionDetail.subscriptionDetail}}
+ {{subscriptionDetail.oldValue}}
+ {{subscriptionDetail.newValue}}
+
+
+ {{#if subscriptionDetail.loading}}
+
+
+ {{else}}
+ {{#if subscriptionDetail.uploaded}}
+
check_circle
+ {{else}}
+
cancel
+ {{/if}}
+ {{/if}}
+
+
+ {{subscriptionDetail.errorUpload}}
+
+ {{/unless}}
+ {{/each}}
+
+
+
+
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/validation.html b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/validation.html
new file mode 100644
index 00000000..67b5ebbe
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/validation.html
@@ -0,0 +1,65 @@
+
+ {{outlet "navigation"}}
+
+
+
+ {{translate "event"}}
+ {{translate "name"}}
+ {{translate "subscriptionDetail"}}
+ {{translate "oldValue"}}
+ {{translate "newValue"}}
+ {{translate "status"}}
+ {{translate "fehler"}}
+
+
+
+ {{#each model.subscriptionDetails as |subscriptionDetail|}}
+
+ {{#if subscriptionDetail.checked}}
+ {{subscriptionDetail.event}}
+ {{subscriptionDetail.name}}
+ {{subscriptionDetail.subscriptionDetail}}
+ {{subscriptionDetail.oldValue}}
+ {{subscriptionDetail.newValue}}
+
+
+ {{#if subscriptionDetail.error}}
+ cancel
+ {{else}}
+ check_circle
+ {{/if}}
+
+
+ {{subscriptionDetail.error}}
+ {{else}}
+ {{! we don't yet know the values of the other fields }}
+
+
+
+
+
+
+
+
+
+ {{/if}}
+
+ {{/each}}
+
+
+
+
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/vssver2.scc b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/vssver2.scc
new file mode 100644
index 00000000..b53ae4eb
Binary files /dev/null and b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/HtmlTemplates/vssver2.scc differ
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/index.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/index.js
new file mode 100644
index 00000000..8ce512db
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/index.js
@@ -0,0 +1,12 @@
+define([
+ 'ember',
+ 'application',
+ 'translate'
+], function (ember, app, translate) {
+ app.IndexRoute = ember.Route.extend({
+ beforeModel: function (transition) {
+ // always transition to input
+ this.transitionTo('input');
+ }
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/input.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/input.js
new file mode 100644
index 00000000..d15b2991
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/input.js
@@ -0,0 +1,48 @@
+define([
+ 'ember',
+ 'application',
+ 'translate',
+ 'App/Templates/input',
+ 'App/Controllers/input'
+], function (ember, app, translate) {
+ app.InputRoute = ember.Route.extend({
+ renderTemplate: function () {
+ this.render();
+
+ // render the navigation
+ this.render('navigation', {
+ into: 'input',
+ outlet: 'navigation'
+ });
+ },
+
+ actions: {
+ didTransition: function () {
+ var applicationController = this.controllerFor('application');
+
+ if (applicationController.get('resetInputTextarea') === true) {
+ applicationController.set('resetInputTextarea', false);
+ this.controller.set('textareaValue', '');
+ }
+ },
+
+ willTransition: function (transition) {
+ if (transition.targetName === 'result') {
+ // it's not possible to go directly to the result route
+ transition.abort();
+ return;
+ }
+
+ if (this.controller.get('cannotContinue')) {
+ transition.abort();
+ return;
+ }
+
+ // put the subScriptiondetails to the application controller so it can be accessed
+ // by the other routes
+ var subscriptionDetails = this.controller.get('subscriptionDetails');
+ this.controllerFor('application').set('subscriptionDetails', subscriptionDetails);
+ }
+ }
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/result.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/result.js
new file mode 100644
index 00000000..44ea7019
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/result.js
@@ -0,0 +1,102 @@
+define([
+ 'ember',
+ 'application',
+ 'api',
+ 'App/Templates/result'
+], function (ember, app, api) {
+ app.ResultRoute = ember.Route.extend({
+ renderTemplate: function () {
+ this.render();
+
+ // render the navigation
+ this.render('navigation', {
+ into: 'result',
+ outlet: 'navigation'
+ });
+ },
+
+ model: function () {
+ var model = ember.Object.create();
+
+ // get the subscriptionDetails
+ var applicationController = this.controllerFor('application');
+ var subscriptionDetails = applicationController.get('subscriptionDetails');
+
+ if (subscriptionDetails === null) {
+ // there are no subscriptionDetails
+ this.transitionTo('input');
+ return;
+ }
+
+ // put a reference to the subscriptionDetails object on the model
+ model.set('subscriptionDetails', subscriptionDetails);
+ model.set('error',false);
+
+ // put the new subscriptionDetails on the server one after another
+ subscriptionDetails.reduce(function (previous, item) {
+ // only send checked items without errors
+ if (!item.get('checked') || item.get('error') !== undefined) {
+ return previous;
+ }
+
+ return previous.then(function () {
+ var raw = item.get('raw');
+ raw.Value = item.get('newValue');
+
+ item.set('loading', true);
+ new ember.RSVP.Promise(function (resolve) {
+ // set the new value of the subscriptionDetail
+ api.updateSubscriptionDetail(raw, function () {
+ item.set('uploaded', true);
+ item.set('loading', false);
+ resolve();
+ });
+ ember.Instrumentation.subscribe('validationErrorOccurred',{
+ before(name, timestamp, payload) {
+ var message = payload.responseJSON.Issues[0].Message;
+ if (payload.status === 409 && message.search(item.subscriptionDetailId) > 0 && message.search('= '+item.newValue) > 0) {
+ item.set('errorUpload', message);
+ item.set('uploaded', false);
+ item.set('loading', false);
+ model.set('error',true);
+
+ }
+ },
+ after(name, timestamp, payload) {
+
+ }
+ });
+
+ });
+
+ });
+ }, ember.RSVP.Promise.resolve()).then(function () {
+ // every detail has bee sent
+ model.set('everythingHasBeenSent', true);
+ });
+
+ return model;
+ },
+
+ actions: {
+ willTransition: function (transition) {
+ if (transition.targetName === 'validation') {
+ // don't allow transitions to validation
+ transition.abort();
+ return;
+ }
+
+ if (this.controller.get('model.everythingHasBeenSent') !== true) {
+ // not every detail has been checked
+ transition.abort();
+ }
+ },
+
+ didTransition: function () {
+ // set the resetInputTextarea property on the application controller
+ // to inform the input route that the textarea can be cleared
+ this.controllerFor('application').set('resetInputTextarea', true);
+ }
+ }
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/validation.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/validation.js
new file mode 100644
index 00000000..17e91217
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/validation.js
@@ -0,0 +1,86 @@
+define([
+ 'ember',
+ 'application',
+ 'App/Controllers/validation',
+ 'App/Templates/validation',
+ 'App/Services/validationHelpers',
+], function (ember, app) {
+ app.ValidationRoute = ember.Route.extend({
+ renderTemplate: function () {
+ this.render();
+
+ // render the navigation
+ this.render('navigation', {
+ into: 'validation',
+ outlet: 'navigation'
+ });
+ },
+
+ // service
+ validationHelpers: ember.inject.service('validationHelpers'),
+
+ model: function (params, transition) {
+
+ var model = ember.Object.create();
+
+ // get the subscriptionDetails
+ var applicationController = this.controllerFor('application');
+ var subscriptionDetails = applicationController.get('subscriptionDetails');
+
+ if (subscriptionDetails === null) {
+ // there are no subscriptionDetails
+ this.transitionTo('input');
+ return;
+ }
+
+ // put a reference to the subscriptionDetails object on the model
+ model.set('subscriptionDetails', subscriptionDetails);
+
+ // when there are only errors
+ // there is no reason to continue
+ var thereAreOnlyErrors = true;
+
+ // cache api responses until
+ // every row is validated
+ var cache = {
+ events: {},
+ subscriptionDetails: {},
+ names: {}
+ };
+
+ var validationHelpers = this.get('validationHelpers');
+
+ // validate the subscriptionDetails
+ subscriptionDetails.reduce(function (previous, item) {
+ return previous.then(function () {
+ return validationHelpers.validateSubscriptionDetail(cache, item).then(function () {
+ if (item.get('error') === undefined) {
+ thereAreOnlyErrors = false;
+ }
+ });
+ });
+ }, ember.RSVP.Promise.resolve()).then(function () {
+
+ // every row has been checked
+ model.set('everythingIsChecked', true);
+ model.set('thereAreOnlyErrors', thereAreOnlyErrors);
+ });
+
+ return model;
+ },
+
+ actions: {
+ willTransition: function (transition) {
+ if (transition.targetName === 'result') {
+ if (this.controller.get('cannotContinue')) {
+ transition.abort();
+ }
+ } else {
+ if (this.controller.get('cannotGoBack')) {
+ transition.abort();
+ }
+ }
+ }
+ }
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/vssver2.scc b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/vssver2.scc
new file mode 100644
index 00000000..1f5c77cb
Binary files /dev/null and b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Routes/vssver2.scc differ
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Services/validationHelpers.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Services/validationHelpers.js
new file mode 100644
index 00000000..e97d7f9c
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Services/validationHelpers.js
@@ -0,0 +1,203 @@
+define([
+ 'ember',
+ 'application',
+ 'api',
+ 'translate',
+ 'constants'
+], function (ember, app, api, translate, constants) {
+ app.ValidationHelpersService = ember.Service.extend({
+ // returns true if value is a possible value for type
+ // this could be extended to work with more datatypes
+ checkDatatype: function (type, value) {
+ switch (type) {
+ case constants.vssType.IntField:
+ return /^\d+$/.test(value);
+
+ case constants.vssType.Text:
+ return true;
+
+ case constants.vssType.MemoText:
+ return true;
+
+ case constants.vssType.Currency:
+ return !isNaN(value);
+
+ default:
+ return false;
+ }
+ },
+
+ // this function will be able to use the
+ // api.ember.getPerson api function when it gets implemented
+ getName: function (personId) {
+
+ return api.ember.getPerson(personId).then(function(data){
+ return data.LastName + ' ' + data.FirstName;
+ });
+
+ },
+
+ // this function validates a subscriptionDetail and translates ids into strings
+ // (e.g. personId 12345 => 'John Doe')
+ // in case there is an error the error property is set
+ // a promise is returned
+ validateSubscriptionDetail: function (cache, subscriptionDetail) {
+
+ if (subscriptionDetail.get('checked') === true) {
+ // this item has already been checked
+ return ember.RSVP.Promise.resolve();
+ }
+
+ var that = this;
+
+ var personId = subscriptionDetail.get('personId');
+ var eventId = subscriptionDetail.get('eventId');
+ var subscriptionDetailId = subscriptionDetail.get('subscriptionDetailId');
+ var newValue = subscriptionDetail.get('newValue');
+
+
+ return new ember.RSVP.Promise(function (resolve, reject) {
+
+ // Step 1: check if the event exists
+ if (cache.events[eventId] !== undefined) {
+ resolve(cache.events[eventId]);
+ return;
+ }
+
+ api.ember.getEvent(eventId).then(function (response) {
+ cache.events[eventId] = response;
+ resolve(response);
+ });
+
+ }).then(function (response) {
+ if (response == null) {
+ subscriptionDetail.set('error', translate.getString('eventDoesNotExist'));
+ throw Error('event does not exist');
+ } else if (response.hasError) {
+ subscriptionDetail.set('error', translate.getString('eventDoesNotExist'));
+ throw Error('event does not exist');
+ }
+
+ subscriptionDetail.set('event', response.Number);
+
+ eventPersonId = eventId + '_' + personId
+ // Step 2: get the subscription for the event
+ if (cache.subscriptionDetails[eventPersonId] !== undefined) {
+
+ if(cache.subscriptionDetails[eventPersonId].name == undefined) {
+ that.getName(personId).then(function (personResponse) {
+ subscriptionDetail.set('name', personResponse);
+ });
+ }
+ return cache.subscriptionDetails[eventPersonId];
+ }
+
+ return api.ember.getSubscriptionByIdEventPerson(eventId, personId).then(function (response) {
+
+ if (response.length === 0) {
+ // the student does not exist or is not subscribed to the event
+ subscriptionDetail.set('error', translate.getString('noSubscription'));
+ throw Error('student is not subscribed to the event');
+ } else {
+ cache.subscriptionDetails[eventPersonId] = response;
+ // Step 3: get the name of the person
+ that.getName(personId).then(function (personResponse) {
+ subscriptionDetail.set('name', personResponse);
+ });
+ }
+
+ return response;
+ });
+
+ }).then(function (response) {
+
+ var idSubscription = response[0].IdSubscription;
+ if (cache.subscriptionDetails[idSubscription] !== undefined) {
+ return cache.subscriptionDetails[idSubscription];
+ }
+
+ return api.ember.SubscriptionDetailsByIdSubscription(idSubscription).then(function (response) {
+ cache.subscriptionDetails[idSubscription] = response;
+ return response;
+ });
+
+ }).then(function (response) {
+ var subscriptionDetailsByEvent = {};
+
+ // transform the response into the following format:
+ // {
+ // : {
+ // name : string,
+ // students: {
+ // : {
+ // value : string,
+ // type : int, (dataType)
+ // raw : object (returned by api
+ // },
+ // ...
+ // }
+ // },
+ // ...
+ // }
+ response.forEach(function (item) {
+ if (subscriptionDetailsByEvent[item.IdAnmeldeVSS] === undefined) {
+ subscriptionDetailsByEvent[item.IdAnmeldeVSS] = {
+ name: item.VssBezeichnung,
+ students: {}
+ };
+ }
+
+ subscriptionDetailsByEvent[item.IdAnmeldeVSS].students[item.IdPerson] = {
+ value: item.Value,
+ type: item.VssTypEx,
+ raw: item
+ };
+ });
+
+ var subscriptionDetailByEvent = subscriptionDetailsByEvent[subscriptionDetailId];
+
+ if (subscriptionDetailByEvent === undefined) {
+ // the event doesn't have that subscriptionDetail
+ subscriptionDetail.set('error', translate.getString('subscriptionDetailDoesNotExist'));
+ throw Error('subscriptionDetail does not exist');
+ }
+
+ var subscriptionDetailName = subscriptionDetailByEvent.name;
+ subscriptionDetail.set('subscriptionDetail', subscriptionDetailName);
+
+ var subscriptionDetailForStudent = subscriptionDetailByEvent.students[personId];
+
+ var raw = subscriptionDetailForStudent.raw;
+
+ if (raw.VssStyle !== 'TX' || raw.VssInternet !== 'E' && (
+ raw.VssTypEx !== constants.vssType.IntField ||
+ raw.VssTypEx !== constants.vssType.Currency ||
+ raw.VssTypEx !== constants.vssType.Text ||
+ raw.VssTypEx !== constants.vssType.MemoText
+ )) {
+ // the subscriptiondetails is not (yet) meant to be edited on the internet
+ subscriptionDetail.set('error', translate.getString('notEditable'));
+ throw Error('subscriptionDetail not editable');
+ }
+
+ subscriptionDetail.set('oldValue', subscriptionDetailForStudent.value);
+ subscriptionDetail.set('raw', subscriptionDetailForStudent.raw);
+
+ if (!that.checkDatatype(subscriptionDetailForStudent.type, newValue)) {
+ // the new value is invalid
+ subscriptionDetail.set('error', translate.getString('invalidSubscriptionDetailValue'));
+ throw Error('the new value is invalid');
+ }
+
+
+
+
+ }).catch(ember.K /* ignore rejections */).then(function () {
+
+ // done
+ subscriptionDetail.set('checked', true);
+
+ });
+ }
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Services/vssver2.scc b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Services/vssver2.scc
new file mode 100644
index 00000000..a6953efd
Binary files /dev/null and b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Services/vssver2.scc differ
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/application.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/application.js
new file mode 100644
index 00000000..07b29344
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/application.js
@@ -0,0 +1,3 @@
+define(['ember', 'text!App/HtmlTemplates/application.html'], function (ember, applicationTemplate) {
+ ember.TEMPLATES['application'] = ember.Handlebars.compile(applicationTemplate);
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/input.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/input.js
new file mode 100644
index 00000000..36edb34c
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/input.js
@@ -0,0 +1,3 @@
+define(['ember', 'text!App/HtmlTemplates/input.html'], function (ember, inputTemplate) {
+ ember.TEMPLATES['input'] = ember.Handlebars.compile(inputTemplate);
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/navigation.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/navigation.js
new file mode 100644
index 00000000..20270c3d
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/navigation.js
@@ -0,0 +1,3 @@
+define(['ember', 'text!App/HtmlTemplates/navigation.html'], function (ember, navigationTemplate) {
+ ember.TEMPLATES['navigation'] = ember.Handlebars.compile(navigationTemplate);
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/result.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/result.js
new file mode 100644
index 00000000..5ddd698f
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/result.js
@@ -0,0 +1,3 @@
+define(['ember', 'text!App/HtmlTemplates/result.html'], function (ember, resultTemplate) {
+ ember.TEMPLATES['result'] = ember.Handlebars.compile(resultTemplate);
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/validation.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/validation.js
new file mode 100644
index 00000000..de336e0b
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/validation.js
@@ -0,0 +1,3 @@
+define(['ember', 'text!App/HtmlTemplates/validation.html'], function (ember, validationTemplate) {
+ ember.TEMPLATES['validation'] = ember.Handlebars.compile(validationTemplate);
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/vssver2.scc b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/vssver2.scc
new file mode 100644
index 00000000..5ba66f97
Binary files /dev/null and b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/Templates/vssver2.scc differ
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/router.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/router.js
new file mode 100644
index 00000000..e7d3c8fc
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/router.js
@@ -0,0 +1,25 @@
+define([
+ 'ember',
+ 'application',
+
+ // load the application template and controller
+ // the application route gets created by the CLX framework
+ 'App/Controllers/application',
+ 'App/Templates/application',
+
+ // load the navigation template
+ 'App/Templates/navigation',
+
+ // load all the routes
+ 'App/Routes/index',
+ 'App/Routes/input',
+ 'App/Routes/validation',
+ 'App/Routes/result'
+], function (ember, app) {
+ app.Router = ember.Router.extend();
+ app.Router.map(function () {
+ this.route('input');
+ this.route('validation');
+ this.route('result');
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/App/vssver2.scc b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/vssver2.scc
new file mode 100644
index 00000000..d74dc1f2
Binary files /dev/null and b/test/apps/EmberApps/AnmeldedetailsEinlesen/App/vssver2.scc differ
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/index.html b/test/apps/EmberApps/AnmeldedetailsEinlesen/index.html
new file mode 100644
index 00000000..29c3677f
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/index.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/main.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/main.js
new file mode 100644
index 00000000..dbe90696
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/main.js
@@ -0,0 +1,50 @@
+(function (root) {
+ require(['../config', '../customConfig'], function (mainConfig, customConfig) {
+ requirejs.config(mainConfig);
+ requirejs.config(customConfig);
+ require([
+ 'application',
+ 'ember',
+ 'framework',
+ 'applicationHelpers',
+ 'api',
+ 'locale/de-CH',
+ 'locale/Custom/de-CH',
+ 'locale/fr-CH',
+ 'locale/Custom/fr-CH',
+ 'appConfig',
+ 'storage',
+ 'App/router'
+ ], function (app, ember, framework, applicationHelpers, api, de_CH, custom_de_CH, fr_CH, custom_fr_CH, appConfig, storage) {
+
+ // templates are all loaded when the application starts
+ // so this is not needed
+ app.automaticTemplateLoading = false;
+
+ // change api a little so the callback gets called
+ api.ember.getEvent = function (id) {
+ return api.getEmber('Events/' + id, true);
+ };
+
+ api.ember.getSubscriptionByIdEventPerson = function (idEvent, idPerson) {
+ return api.getEmber('Subscriptions/?filter.EventId==' + idEvent + '&filter.PersonId==' + idPerson, true);
+ };
+
+ api.ember.SubscriptionDetailsByIdSubscription = function (idSubscription) {
+ return api.getEmber('Subscriptions/'+ idSubscription +'/SubscriptionDetails', true);
+ };
+
+ // patch langauge files
+ ember.$.extend(de_CH, custom_de_CH);
+ ember.$.extend(fr_CH, custom_fr_CH);
+
+ // start application
+ applicationHelpers.initializeApplication(function () {
+ var appName = 'ClxApp';
+ var application = ember.Application.create(app);
+ root[appName] = application;
+ applicationHelpers.afterStartApplication();
+ });
+ });
+ });
+})(this);
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/settings.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/settings.js
new file mode 100644
index 00000000..0005307d
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/settings.js
@@ -0,0 +1,12 @@
+define([
+ 'ember',
+ 'main_settings'
+], function (ember, mainSettings) {
+ var settingsClass = ember.Object.extend(mainSettings);
+ var settings = {
+ // module specific settings
+ applicationScope: 'NG',//constants.applicationScope.tutoring,
+ };
+ var inheritedSettingsClass = settingsClass.extend(settings);
+ return inheritedSettingsClass.create();
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/uiSettings.js b/test/apps/EmberApps/AnmeldedetailsEinlesen/uiSettings.js
new file mode 100644
index 00000000..8e466558
--- /dev/null
+++ b/test/apps/EmberApps/AnmeldedetailsEinlesen/uiSettings.js
@@ -0,0 +1,11 @@
+define([
+ 'ember',
+ 'main_uiSettings'
+], function (ember, mainUiSettings) {
+ var uiSettingsClass = ember.Object.extend(mainUiSettings);
+ var uiSettings = {
+ // module specific settings
+ };
+ var inheritedUiSettingsClass = uiSettingsClass.extend(uiSettings);
+ return inheritedUiSettingsClass.create();
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/AnmeldedetailsEinlesen/vssver2.scc b/test/apps/EmberApps/AnmeldedetailsEinlesen/vssver2.scc
new file mode 100644
index 00000000..5977d905
Binary files /dev/null and b/test/apps/EmberApps/AnmeldedetailsEinlesen/vssver2.scc differ
diff --git a/test/apps/EmberApps/App/Components/accordionNavigationComponent.js b/test/apps/EmberApps/App/Components/accordionNavigationComponent.js
new file mode 100644
index 00000000..ba259edd
--- /dev/null
+++ b/test/apps/EmberApps/App/Components/accordionNavigationComponent.js
@@ -0,0 +1,86 @@
+define([
+ 'application',
+ 'jquery',
+ 'ember',
+ 'uiSettings'
+], function (app, $, ember, uiSettings) {
+ app.AccordionNavigationComponent = ember.Component.extend({
+ tagName: 'div',
+ classNames: ['accordionNavigation'],
+
+ didInsertElement: function () {
+ var element = this.$();
+ var that = this;
+ ember.run.scheduleOnce('afterRender',
+ function() {
+ // init click behavior
+ element.find('a')
+ .click(function(e) {
+ var link = $(this);
+ if (!that.get('forceOpenActive'))
+ that.openNewNavigation(link);
+
+ // in any case prevent the link-to action, because the content of the navigation might be something else already
+ if (link.hasClass('active')) {
+ e.preventDefault();
+ return false;
+ }
+ });
+
+ // open active link on page visit
+ element.find('a.active').next('.navigationContent').slideDown(uiSettings.defaultAnimationSpeed);
+ });
+ },
+
+ forceOpenActiveTriggered: ember.observer('forceOpenActive',
+ function () {
+ var that = this;
+ ember.run.scheduleOnce('afterRender',
+ function() {
+ var link = that.$().find('a.active');
+ if (link.length === 1)
+ that.openNewNavigation(link, true);
+ });
+ }),
+
+ openNewNavigation: function (link, forced) {
+ var container = link.next('.navigationContent');
+ // first close all open containers (should always be just one) and then open a new one
+ var isVisible = container.is(':visible');
+ var isActive = link.hasClass('active');
+ var openContainers = $('.navigationContent:visible').toArray();
+ var time = 1;
+ if (openContainers.length > 0) {
+ $.each(openContainers,
+ function () {
+ if (this !== container[0]) {
+ time = uiSettings.defaultAnimationSpeed;
+ $(this).slideUp(uiSettings.defaultAnimationSpeed);
+ }
+ });
+ }
+
+ if (!isVisible || !isActive || forced) {
+ setTimeout(function() {
+ container.slideDown(uiSettings.defaultAnimationSpeed);
+ }, time);
+ }
+ },
+
+ openNavigationTriggered: ember.observer('openNavigation',
+ function () {
+ var that = this;
+ ember.run.scheduleOnce('afterRender',
+ function() {
+ var index = that.get('openNavigation');
+ if (index > -1) {
+ var link = that.$().find('a').eq(index);
+ if (!link.hasClass('active')) {
+ that.openNewNavigation(link);
+ }
+ }
+ });
+
+ }).on('init')
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/App/Components/adaptableTextBoxComponent.js b/test/apps/EmberApps/App/Components/adaptableTextBoxComponent.js
new file mode 100644
index 00000000..04b91ca8
--- /dev/null
+++ b/test/apps/EmberApps/App/Components/adaptableTextBoxComponent.js
@@ -0,0 +1,22 @@
+define([
+ 'application',
+ 'jquery',
+ 'ember',
+ 'guiHelpers'
+],
+ function(app, $, ember, guiHelpers) {
+ app.AdaptableTextBoxComponent = ember.Component.extend($.extend({},
+ guiHelpers.adaptableTextBoxComponentLogic,
+ {
+ tagName: 'div',
+ classNames: ['textBox'],
+ didInsertElement: function() {
+ var element = this.$();
+ this.initializeInputControl(element);
+ },
+ updateControlModel: function(value) {
+ this.set('value', (value || '').trim());
+ }
+ }
+ ));
+ });
\ No newline at end of file
diff --git a/test/apps/EmberApps/App/Components/addressLabelComponent.js b/test/apps/EmberApps/App/Components/addressLabelComponent.js
new file mode 100644
index 00000000..2a462db8
--- /dev/null
+++ b/test/apps/EmberApps/App/Components/addressLabelComponent.js
@@ -0,0 +1,39 @@
+define([
+ 'jquery',
+ 'ember'
+], function ($, ember) {
+ ClxApp.AddressLabelComponent = ember.Component.extend({
+ tagName: 'pre',
+ classNames: ['address'],
+
+ didInsertElement: function () {
+ this.$().text(this.getText());
+ },
+
+ getText: function() {
+ var address = this.get('address');
+ var text = '';
+ if (address.Company) {
+ text += address.Company;
+ text += '\r\n';
+ }
+ if (address.Department) {
+ text += address.Department;
+ text += '\r\n';
+ }
+ text += address.FormOfAddress + ' ' + (address.FirstName ? address.FirstName + ' ': '') + address.LastName;
+ text += '\r\n';
+ text += address.AddressLine1;
+ text += '\r\n';
+ if (address.AddressLine2) {
+ text += address.AddressLine2;
+ text += '\r\n';
+ }
+ text += address.Zip + ' ' + address.Location;
+ text += '\r\n';
+ text += address.Country;
+
+ return text;
+ }
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/App/Components/autoScrollContainerComponent.js b/test/apps/EmberApps/App/Components/autoScrollContainerComponent.js
new file mode 100644
index 00000000..e9057e0c
--- /dev/null
+++ b/test/apps/EmberApps/App/Components/autoScrollContainerComponent.js
@@ -0,0 +1,76 @@
+define([
+ 'jquery',
+ 'ember',
+ 'uiSettings'
+], function ($, ember, uiSettings) {
+ ClxApp.AutoScrollContainerComponent = ember.Component.extend({
+ tagName: 'div',
+ classNames: ['autoScrollContainer'],
+
+ dataCollectionChanged: ember.observer('dataCollection',
+ function() {
+ var dataCollection = this.get('dataCollection');
+ if (dataCollection && (dataCollection.length === undefined || typeof dataCollection === 'string'))
+ console
+ .error('auto-save-controller: if "dataCollection" is set, it must be an Array or an equivalent ember object');
+ }).on('init'),
+
+ underlyingDataCollectionChanged: ember.observer('dataCollection.@each',
+ function() {
+ var that = this;
+ ember.run.scheduleOnce('afterRender',
+ function () {
+ if (that.get('waitForOtherAnimation'))
+ setTimeout(function() {
+ that.checkScrolling();
+ }, uiSettings.defaultAnimationSpeed + 100);
+ else
+ that.checkScrolling();
+ });
+ }),
+
+ activeElementIdChanged: ember.observer('activeElementId', function() {
+ var that = this;
+ ember.run.scheduleOnce('afterRender', function () {
+ if (that.get('waitForOtherAnimation'))
+ setTimeout(function() {
+ that.scrollTo(that.get('activeElementId'));
+ }, uiSettings.defaultAnimationSpeed + 100);
+ else
+ that.scrollTo(that.get('activeElementId'));
+ });
+ }),
+
+ checkScrolling: function() {
+ var element = this.$();
+ var last = element.children().last();
+ var height = element.outerHeight();
+ var top = last.offset().top;
+ var bottom = top + last.outerHeight();
+ var elementTop = element.offset().top;
+ if (bottom > height) // hangs out lower bound
+ this.scrollTo(undefined, top);
+ if (top < elementTop) // hangs out upper bound
+ this.scrollTo(undefined, top);
+ },
+
+ scrollTo: function (idElement, elementTop) {
+ var container = this.$();
+ var containerTop = container.offset().top;
+ if (elementTop === undefined) {
+ if (!idElement)
+ return;
+ var el = $('#' + idElement);
+ if (el.length > 0)
+ elementTop = el.eq(0).offset().top;
+ }
+
+ var scrollTop = container.scrollTop() - containerTop + elementTop;
+ container
+ .animate({
+ scrollTop: scrollTop
+ },
+ uiSettings.defaultAnimationSpeed);
+ }
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/App/Components/blendElementComponent.js b/test/apps/EmberApps/App/Components/blendElementComponent.js
new file mode 100644
index 00000000..ae503023
--- /dev/null
+++ b/test/apps/EmberApps/App/Components/blendElementComponent.js
@@ -0,0 +1,90 @@
+define([
+ 'application',
+ 'jquery',
+ 'ember',
+ 'uiSettings'
+], function (app, $, ember, uiSettings) {
+ app.BlendElementComponent = ember.Component.extend({
+ tagName: 'div',
+ classNames: ['blendElement'],
+ classNameBindings: ['blendType'],
+
+ validBlendTypes: ['fadeIn', 'slideIn', 'slideDown'],
+
+ initialize: function() {
+ var blendType = this.get('blendType');
+ if (!blendType) {
+ blendType = uiSettings.defaultBlendType;
+ if (!blendType)
+ blendType = 'fadeIn';
+ this.set('blendType', blendType);
+ }
+ if ($.inArray(blendType, this.validBlendTypes) === -1) {
+ console.error('blend-element: blend type "' + blendType + '" is not valid. Valid are: ' + this.validBlendTypes.join(', '));
+ }
+ }.on('init'),
+
+ didInsertElement: function () {
+ var that = this;
+ var element = this.$();
+
+ if (this.get('waitForOtherAnimation'))
+ element.hide();
+ ember.run.scheduleOnce('afterRender', function () {
+ if (that.get('waitForOtherAnimation')) {
+ setTimeout(function() {
+ element.show();
+ setTimeout(function() {
+ that.applyEffect(element, true);
+ },
+ 10);
+ },
+ uiSettings.defaultAnimationSpeed);
+ } else
+ setTimeout(function () {
+ if (!that.get('isDestroying') && ! that.get('isDestroyed'))
+ that.applyEffect(element, true);
+ }, 10);
+ });
+ },
+
+ willDestroyElement: function () {
+ var that = this;
+ var element = this.$();
+ var clone = element.clone();
+ var prev = element.prev().eq(0);
+ var parent = element.parent();
+
+ this.$().hide();
+ if (prev.length === 0)
+ parent.append(clone);
+ else
+ prev.after(clone);
+
+ setTimeout(function () {
+ that.applyEffect(clone, false);
+ }, 10);
+ setTimeout(function () {
+ clone.remove();
+ }, uiSettings.defaultAnimationSpeed);
+ },
+
+ applyEffect: function (element, show) {
+ var blendType = this.get('blendType');
+ switch (blendType) {
+ case 'slideDown':
+ if (show)
+ element.slideDown(uiSettings.defaultAnimationSpeed);
+ else
+ element.slideUp(uiSettings.defaultAnimationSpeed);
+ break;
+ default:
+ if (show)
+ element.removeClass(blendType);
+ else
+ element.addClass(blendType);
+ break;
+ }
+ }
+ });
+});
\ No newline at end of file
diff --git a/test/apps/EmberApps/App/Components/comboBoxComponent.js b/test/apps/EmberApps/App/Components/comboBoxComponent.js
new file mode 100644
index 00000000..9e408135
--- /dev/null
+++ b/test/apps/EmberApps/App/Components/comboBoxComponent.js
@@ -0,0 +1,549 @@
+define([
+ 'application',
+ 'jquery',
+ 'ember',
+ 'keyboard',
+ 'actionStack',
+ 'icons',
+ 'translate',
+ 'guiHelpers',
+ 'regexHelpers',
+ 'uiSettings'
+], function (app, $, ember, keyboard, actionStack, icons, translate, guiHelpers, regexHelpers, uiSettings) {
+ app.ComboBoxComponent = ember.Component.extend($.extend({}, guiHelpers.adaptableTextBoxComponentLogic, {
+ tagName: 'div',
+ classNames: ['combobox'],
+
+ didInsertElement: function() {
+ this.set('lastValidSearch', '');
+ this.set('lastConfirmedValue', '');
+ this.set('lastFilteredSource', undefined);
+ this.set('isPopupVisible', false);
+ this.set('didActionHappen', false);
+ this.set('keepPopupOpen', false);
+ this.set('isAllVisible', false);
+
+ // run in another thread to save performance and let ember finish rendering the view
+ var that = this;
+ var element = this.$();
+ element.prop('autocomplete', false);
+ setTimeout(function () {
+ try {
+ var source = that.get('source');
+ if (source)
+ that.fillBySource(source);
+
+ var dataSource = that.get('dataSource');
+
+ var inputControl = that.get('adaptAs')
+ ? that.createAdaptableControl()
+ : that.createInputControl();
+ element.append(inputControl);
+
+ var value = that.get('value');
+ if (value) {
+ var item = dataSource.find(function(el) {
+ return el.Key === value;
+ });
+ if (item || that.get('allowArbitraryText')) {
+ if (item)
+ value = item.Value;
+ that.setInputControlContent(value);
+ that.set('lastValidSearch', value);
+ that.set('lastConfirmedValue', value);
+ }
+ }
+
+ if (!dataSource) {
+ console.error('combobox ' + that.$().attr('id') + ' cannot evaluate data source');
+ return;
+ }
+
+ element.append(that.createShowAllButton());
+ var popup = that.buildPopup(dataSource);
+ popup.hide();
+ element.append(popup);
+ } catch (ex) {
+ // don't throw the error when the object is beeing destroyed
+ if (!that.get('isDestroyed') && !that.get('isDestroying'))
+ throw ex;
+ }
+ }, 1);
+ },
+
+ fillBySource: function(source) {
+ var keyName = this.get('keyName');
+ var valueName = this.get('valueName');
+ if (!keyName || !valueName) {
+ console.error('when using "source" on combobox, "keyName" and "valueName" must be set as well');
+ return;
+ }
+ var dataSource = [];
+ $.each(source,
+ function() {
+ dataSource.push({
+ Key: this[keyName],
+ Value: this[valueName]
+ });
+ });
+ this.set('dataSource', dataSource);
+ },
+
+ createInputControl: function() {
+ var input = guiHelpers.getTextbox(undefined, this.get('id'), true);
+ input.attr('autocomplete', 'off');
+ if (this.get('inputMaxlength'))
+ input.prop('maxlength', this.get('inputMaxlength'));
+
+ guiHelpers.addLongtextTooltip(input);
+
+ return this.initInputControl(input);
+ },
+
+ createAdaptableControl: function() {
+ var editDiv = guiHelpers.getDiv();
+
+ this.initializeInputControl(editDiv, true);
+ return this.initInputControl(editDiv);
+ },
+
+ initInputControl: function(control) {
+ control.prop('disabled', this.get('disabled'));
+ control.prop('name', this.get('name'));
+
+ var that = this;
+ control.on('keyup',
+ function(e) {
+ that.handleKeyUp(e);
+ })
+ .on('keydown',
+ function(e) {
+ that.handleKeyDown(e);
+ })
+ .on('blur',
+ function() {
+ that.handleBlur();
+ });
+
+ this.set('inputControl', control);
+ return control;
+ },
+
+ getAdaptation: function() {
+ return this.get(this.adaptationAttribute);
+ },
+
+ endsWith: function (text, suffix) {
+ return text.indexOf(suffix, text.length - suffix.length) > -1;
+ },
+
+ getInputControlContent: function() {
+ var inputCtrl = this.get('inputControl');
+ return this.isAdaptableControl() // empty innerText returns '\n'
+ ? inputCtrl.prop('innerText').replace(/\n$/, this.endsWith(inputCtrl.html(), ' ') ? ' ' : '')
+ : inputCtrl.val();
+ },
+
+ setInputControlContent: function(content) {
+ var inputCtrl = this.get('inputControl');
+ this.isAdaptableControl()
+ ? inputCtrl.prop('innerText', (this.isAdaptableControl('text') ? this.textControlContentTransformer(content) : content))
+ : inputCtrl.val(content);
+ },
+
+ updateControlModel: function(valueKey) {
+ if (this.get('value') === valueKey) return;
+ if (valueKey && valueKey.trim)
+ valueKey = valueKey.trim();
+ this.set('value', valueKey);
+ },
+
+ createShowAllButton: function() {
+ var that = this;
+ var button = guiHelpers.getButton(icons.dropdown, null, guiHelpers.classes.controls.inputButton)
+ .attr('tabIndex', -1)
+ .attr('title', translate.getString('showAll'))
+ .prop('disabled', this.get('disabled'))
+ .on('mousedown',
+ function() {
+ if (!that.get('isAllVisible')) {
+ that.set('isAllVisible', true);
+ that.set('keepPopupOpen', true);
+ var searchText = that.getInputControlContent();
+ var regex = searchText
+ ? new RegExp(regexHelpers
+ .escapeRegExp(searchText),
+ 'gi')
+ : undefined;
+ that.rebuildPopup(that.get('dataSource'), regex);
+ } else {
+ that.hidePopup();
+ }
+ })
+ .on('blur',
+ function () {
+ that.set('keepPopupOpen', false);
+ that.handleBlur();
+ });
+ this.set('showAllButton', button);
+ return button;
+ },
+
+ buildPopup: function(dataSource, searchRegex) {
+ var that = this;
+ var popup = $('