560 lines
No EOL
16 KiB
JavaScript
560 lines
No EOL
16 KiB
JavaScript
// app initialization
|
|
Ember.MODEL_FACTORY_INJECTIONS = true;
|
|
|
|
window.App = Ember.Application.create({
|
|
LOG_TRANSITIONS: true,
|
|
|
|
ready: function(){
|
|
this.register('encryption:current', App.Encryption, {singleton: true});
|
|
this.inject('controller:poll', 'encryption', 'encryption:current');
|
|
this.inject('route:create', 'encryption', 'encryption:current');
|
|
this.inject('model', 'encryption', 'encryption:current');
|
|
}
|
|
});
|
|
|
|
// adapter initialization
|
|
App.ApplicationAdapter = DS.RESTAdapter.extend({
|
|
// set namespace to api.php in same subdirectory
|
|
namespace: window.location.pathname
|
|
// remove index.html if it's there
|
|
.replace(/index.html$/, '')
|
|
// remove leading and trailing slash
|
|
.replace(/\/$/, '')
|
|
// add api.php
|
|
.concat('/api.php?')
|
|
// remove leading slash
|
|
.replace(/^\//g, '')
|
|
});
|
|
|
|
// serializer initialization
|
|
App.ApplicationSerializer = DS.EmbeddedSerializer.extend();
|
|
|
|
// adding support for attribut data-option-id to input fields
|
|
Ember.TextField.reopen({
|
|
attributeBindings: ['data-option']
|
|
});
|
|
|
|
// decrypt / encrypt computed property helper
|
|
Ember.computed.encrypted = function(encryptedField, dataType) {
|
|
return Ember.computed(encryptedField, function(key, decryptedValue) {
|
|
var encryptKey = this.get('encryption.key'),
|
|
encryptedValue;
|
|
|
|
// check if encryptKey is set
|
|
if (typeof encryptKey === 'undefined') {
|
|
return;
|
|
}
|
|
|
|
// setter
|
|
if (arguments.length === 2) {
|
|
var decryptedJSON = JSON.stringify(decryptedValue);
|
|
|
|
encryptedValue = Ember.isNone(decryptedValue) ? null : String(sjcl.encrypt(encryptKey , decryptedJSON));
|
|
this.set(encryptedField, encryptedValue);
|
|
}
|
|
|
|
// get value of field to decrypt
|
|
encryptedJSON = this.get(encryptedField);
|
|
|
|
// check if encryptedField is defined and not null
|
|
if (typeof encryptedJSON === 'undefined' ||
|
|
encryptedJSON === null) {
|
|
return null;
|
|
}
|
|
|
|
// try to decrypt value
|
|
try {
|
|
decryptedJSON = sjcl.decrypt(encryptKey, encryptedJSON);
|
|
decryptedValue = JSON.parse(decryptedJSON);
|
|
} catch (e) {
|
|
console.log('Error on decrypting ' + encryptedField);
|
|
console.log(e);
|
|
console.log('Perhaps a wrong encryption key?');
|
|
decryptedValue = '';
|
|
}
|
|
|
|
switch (dataType) {
|
|
case 'array':
|
|
return Ember.isNone(decryptedValue) ? null : decryptedValue;
|
|
break;
|
|
|
|
case 'date':
|
|
return Ember.isNone(decryptedValue) ? null : Date(decryptedValue);
|
|
break;
|
|
|
|
case 'string':
|
|
return Ember.isNone(decryptedValue) ? null : String(decryptedValue);
|
|
break;
|
|
}
|
|
});
|
|
};
|
|
|
|
/*
|
|
* models
|
|
*/
|
|
|
|
// poll model
|
|
App.Poll = DS.Model.extend({
|
|
encryptedTitle : DS.attr('string'),
|
|
title : Ember.computed.encrypted('encryptedTitle', 'string'),
|
|
encryptedDescription : DS.attr('string'),
|
|
description: Ember.computed.encrypted('encryptedDescription', 'string'),
|
|
encryptedPollType : DS.attr('string'),
|
|
pollType : Ember.computed.encrypted('encryptedPollType', 'string'),
|
|
encryptedAnswerType: DS.attr('string'),
|
|
answerType : Ember.computed.encrypted('encryptedAnswerType', 'string'),
|
|
encryptedAnswers : DS.attr('string'),
|
|
answers : Ember.computed.encrypted('encryptedAnswers', 'array'),
|
|
encryptedOptions : DS.attr('string'),
|
|
options : Ember.computed.encrypted('encryptedOptions', 'array'),
|
|
users : DS.hasMany('user', {async: true}),
|
|
encryptedCreationDate : DS.attr('string'),
|
|
creationDate : Ember.computed.encrypted('encryptedCreationDate', 'date'),
|
|
|
|
isFindADate: function() {
|
|
return this.get('pollType') === 'FindADate';
|
|
}.property('pollType'),
|
|
isFreeText: function() {
|
|
return this.get('answerType') === 'FreeText';
|
|
}.property('answerType'),
|
|
isMakeAPoll: function() {
|
|
return this.get('pollType') === 'MakeAPoll';
|
|
}.property('pollType')
|
|
});
|
|
|
|
// user model
|
|
// used by poll model
|
|
App.User = DS.Model.extend({
|
|
poll : DS.belongsTo('poll', {async: true}),
|
|
encryptedName : DS.attr('string'),
|
|
name : Ember.computed.encrypted('encryptedName', 'string'),
|
|
encryptedSelections : DS.attr('string'),
|
|
selections : Ember.computed.encrypted('encryptedSelections', 'array'),
|
|
encryptedCreationDate : DS.attr('string'),
|
|
creationDate : Ember.computed.encrypted('encryptedCreationDate', 'date')
|
|
});
|
|
|
|
App.Encryption = Ember.Object.extend({
|
|
key : '',
|
|
isSet: false
|
|
});
|
|
|
|
App.PollTypes = [
|
|
Ember.Object.create({
|
|
id : "FindADate",
|
|
label : "Find a date"
|
|
}),
|
|
Ember.Object.create({
|
|
id : "MakeAPoll",
|
|
label : "Make a poll"
|
|
})
|
|
];
|
|
|
|
App.AnswerTypes = [
|
|
Ember.Object.create({
|
|
id : "YesNo",
|
|
label : "yes, no",
|
|
answers : [
|
|
{label: "yes"},
|
|
{label: "no"}
|
|
]
|
|
}),
|
|
Ember.Object.create({
|
|
id : "YesNoMaybe",
|
|
label : "yes, no, maybe",
|
|
answers : [
|
|
{label: "yes"},
|
|
{label: "no"},
|
|
{label: "maybe"}
|
|
]
|
|
}),
|
|
Ember.Object.create({
|
|
id : "FreeText",
|
|
label : "free text",
|
|
answers : []
|
|
})
|
|
];
|
|
|
|
/*
|
|
* Serializer
|
|
*/
|
|
App.PollSerializer = App.ApplicationSerializer.extend({
|
|
attrs: {
|
|
users: {embedded: 'load'}
|
|
}
|
|
});
|
|
|
|
/*
|
|
* routes
|
|
*/
|
|
|
|
// defining routes of app
|
|
App.Router.map(function(){
|
|
this.route('poll', { path: '/poll/:poll_id' });
|
|
this.resource('create', function(){
|
|
this.route('meta');
|
|
this.route('options');
|
|
this.route('settings');
|
|
});
|
|
});
|
|
|
|
App.CreateRoute = Ember.Route.extend({
|
|
beforeModel: function(){
|
|
// generate encryptionKey
|
|
var encryptionKeyLength = 40;
|
|
var encryptionKeyChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
|
|
var encryptionKey = '';
|
|
var list = encryptionKeyChars.split('');
|
|
var len = list.length, i = 0;
|
|
do {
|
|
i++;
|
|
var index = Math.floor(Math.random() * len);
|
|
encryptionKey += list[index];
|
|
} while(i < encryptionKeyLength);
|
|
|
|
// set encryption key
|
|
this.set('encryption.key', encryptionKey);
|
|
},
|
|
|
|
model: function(){
|
|
// create empty poll
|
|
return this.store.createRecord('poll', {
|
|
creationDate : new Date(),
|
|
options : [{title: ''}, {title: ''}]
|
|
});
|
|
}
|
|
});
|
|
|
|
App.CreateIndexRoute = Ember.Route.extend({
|
|
model: function(){
|
|
return this.modelFor('create');
|
|
}
|
|
});
|
|
|
|
App.CreateMetaRoute = Ember.Route.extend({
|
|
model: function(){
|
|
return this.modelFor('create');
|
|
},
|
|
|
|
// redirect to create/index if poll type is not set
|
|
afterModel: function(create){
|
|
if (create.get('pollType') === null) {
|
|
this.transitionTo('create.index');
|
|
}
|
|
}
|
|
});
|
|
|
|
App.CreateOptionsRoute = Ember.Route.extend({
|
|
model: function(){
|
|
return this.modelFor('create');
|
|
},
|
|
|
|
// redirect to create/meta if title is not set
|
|
afterModel: function(create){
|
|
if (create.get('title') === null) {
|
|
this.transitionTo('create.meta');
|
|
}
|
|
}
|
|
});
|
|
|
|
App.CreateSettingsRoute = Ember.Route.extend({
|
|
model: function(){
|
|
return this.modelFor('create');
|
|
},
|
|
|
|
// redirect to create/options if less then two options are defined
|
|
afterModel: function(create){
|
|
if (create.get('options.length') < 2) {
|
|
this.transitionTo('create.options');
|
|
}
|
|
}
|
|
});
|
|
|
|
App.PollRoute = Ember.Route.extend({
|
|
model: function(params){
|
|
return this.store.find('poll', params.poll_id).then(function(poll) {
|
|
return poll;
|
|
});
|
|
}
|
|
});
|
|
|
|
/*
|
|
* controller
|
|
*/
|
|
App.CreateIndexController = Ember.ObjectController.extend({
|
|
actions: {
|
|
submit: function(){
|
|
// redirect to CreateMeta
|
|
this.transitionToRoute('create.meta');
|
|
}
|
|
}
|
|
});
|
|
|
|
App.CreateMetaController = Ember.ObjectController.extend({
|
|
actions: {
|
|
submit: function(){
|
|
// redirect to CreateOptions
|
|
this.transitionToRoute('create.options');
|
|
}
|
|
}
|
|
});
|
|
|
|
App.CreateOptionsController = Ember.ObjectController.extend({
|
|
actions: {
|
|
submitMakeAPoll: function() {
|
|
var options = this.get('model.options'),
|
|
newOptions = [];
|
|
|
|
// remove options without value
|
|
options.forEach(function(option) {
|
|
if (option.title !== '') {
|
|
newOptions.pushObject(option);
|
|
}
|
|
});
|
|
|
|
// set updated options
|
|
//
|
|
// we have to hardly set new options even if they wasn't changed to
|
|
// trigger computed property; push on array doesn't trigger computed
|
|
// property to recalculate
|
|
this.set('model.options', newOptions);
|
|
|
|
// tricker save action
|
|
this.send('save');
|
|
},
|
|
|
|
submitFindADate: function() {
|
|
var options = $('.datepicker').datepicker('getDates'),
|
|
newOptions = [];
|
|
|
|
// sort dates
|
|
options.sort(function(a,b){
|
|
return new Date(a) - new Date(b);
|
|
});
|
|
|
|
// get array in correct form
|
|
options.forEach(function(option) {
|
|
newOptions.pushObject({title: option});
|
|
});
|
|
|
|
// set options
|
|
this.set('model.options', newOptions);
|
|
|
|
// tricker save action
|
|
this.send('save');
|
|
},
|
|
|
|
save: function(){
|
|
// redirect to CreateSettings
|
|
this.transitionToRoute('create.settings');
|
|
}
|
|
}
|
|
});
|
|
|
|
App.CreateSettingsController = Ember.ObjectController.extend({
|
|
actions: {
|
|
submit: function(){
|
|
// save poll
|
|
var self = this;
|
|
this.get('model').save().then(function(model){
|
|
// reload as workaround for bug: duplicated records after save
|
|
model.reload().then(function(model){
|
|
// redirect to new poll
|
|
self.transitionToRoute('poll', model, {queryParams: {encryptionKey: self.get('encryption.key')}});
|
|
});
|
|
});
|
|
}
|
|
},
|
|
|
|
updateAnswers: function(){
|
|
var selectedAnswer = this.get('model.answerType'),
|
|
answers = [];
|
|
|
|
if (selectedAnswer !== null) {
|
|
for (var i=0; i<App.AnswerTypes.length; i++) {
|
|
if (App.AnswerTypes[i].id === selectedAnswer) {
|
|
answers = App.AnswerTypes[i].answers;
|
|
}
|
|
}
|
|
|
|
this.set('answers', answers);
|
|
}
|
|
}.observes('answerType')
|
|
});
|
|
|
|
App.PollController = Ember.ObjectController.extend({
|
|
queryParams: ['encryptionKey'],
|
|
encryptionKey: '',
|
|
|
|
actions: {
|
|
saveNewUser: function(user){
|
|
var self = this;
|
|
|
|
// create new user record in store
|
|
var newUser = this.store.createRecord('user', {
|
|
name: user.name,
|
|
creationDate: new Date(),
|
|
poll: this.get('model'),
|
|
selections: user.selections
|
|
});
|
|
|
|
// save new user
|
|
newUser.save().then(function(){
|
|
self.get('model.users').then(function(users){
|
|
// assign new user to poll
|
|
users.pushObject(newUser);
|
|
});
|
|
// reload as workaround for bug: duplicated records after save
|
|
self.get('model').reload();
|
|
});
|
|
}
|
|
},
|
|
|
|
colspan: function(){
|
|
var colspan = this.get('options.length') + 2;
|
|
return colspan;
|
|
}.property('options.@each'),
|
|
|
|
evaluation: function() {
|
|
var evaluation = [],
|
|
lookup = [];
|
|
|
|
// init evalutation object
|
|
// create object for every possible answer
|
|
this.get('answers').forEach(function(answer){
|
|
evaluation.push({
|
|
id: answer.label,
|
|
label: answer.label,
|
|
options: []
|
|
});
|
|
});
|
|
// create object for no answer
|
|
evaluation.push({
|
|
id: null,
|
|
label: 'no answer',
|
|
options: []
|
|
});
|
|
|
|
// create lookup array
|
|
evaluation.forEach(function(value, index){
|
|
lookup[value.id] = index;
|
|
});
|
|
|
|
// loop over all users
|
|
this.get('users').forEach(function(user){
|
|
|
|
// loop over all selections of the user
|
|
user.get('selections').forEach(function(selection, optionindex){
|
|
|
|
// get answer index by lookup array
|
|
if (typeof selection.value === 'undefined') {
|
|
answerindex = lookup[null];
|
|
}
|
|
else {
|
|
answerindex = lookup[selection.value];
|
|
}
|
|
|
|
// set value for this index to one if it's not set yet
|
|
// or increment value if it's allreay set
|
|
if (typeof evaluation[answerindex]['options'][optionindex] === 'undefined') {
|
|
evaluation[answerindex]['options'][optionindex] = 1;
|
|
}
|
|
else {
|
|
evaluation[answerindex]['options'][optionindex] = evaluation[answerindex]['options'][optionindex] + 1;
|
|
}
|
|
});
|
|
});
|
|
|
|
return evaluation;
|
|
}.property('users.@each'),
|
|
|
|
pollUrl: function() {
|
|
return window.location.href;
|
|
}.property('currentPath', 'encryptionKey'),
|
|
|
|
updateEncryptionKey: function() {
|
|
// update encryption key
|
|
this.set('encryption.key', this.get('encryptionKey'));
|
|
|
|
// reload content to recalculate computed properties
|
|
// if encryption key was set before
|
|
if (this.get('encryption.isSet') === true) {
|
|
this.get('content').reload();
|
|
}
|
|
|
|
this.set('encryption.isSet', true);
|
|
}.observes('encryptionKey')
|
|
});
|
|
|
|
/*
|
|
* views
|
|
*/
|
|
App.CreateOptionsView = Ember.View.extend({
|
|
title: '',
|
|
|
|
actions: {
|
|
moreOptions: function(){
|
|
// create new Option
|
|
this.get('controller.model.options').pushObject({title: ''});
|
|
}
|
|
}
|
|
});
|
|
|
|
App.PollView = Ember.View.extend({
|
|
newUserName: '',
|
|
newUserSelections: [],
|
|
|
|
actions: {
|
|
addNewUser: function(){
|
|
var newUser = {
|
|
name: this.get('newUserName'),
|
|
selections: this.get('newUserSelections')
|
|
};
|
|
|
|
this.get('controller').send('saveNewUser', newUser);
|
|
|
|
// clear input fields
|
|
this.set('newUserName', '');
|
|
this.get('newUserSelections').forEach(function(selection){
|
|
selection.set('value', '');
|
|
});
|
|
}
|
|
},
|
|
|
|
// generate selection array for new user
|
|
willInsertElement: function(){
|
|
var newUserSelections = Ember.A(),
|
|
self = this,
|
|
options = this.get('controller.model.options');
|
|
|
|
options.forEach(function(){
|
|
newSelection = Ember.Object.create({value: ''});
|
|
newUserSelections.pushObject(newSelection);
|
|
});
|
|
self.set('newUserSelections', newUserSelections);
|
|
}
|
|
});
|
|
|
|
App.Datepicker = Em.View.extend({
|
|
classNames: ['datepicker'],
|
|
|
|
didInsertElement: function() {
|
|
this._super();
|
|
|
|
$('.datepicker').datepicker({
|
|
format: "yyyy-mm-dd hh:mm:ss",
|
|
startDate: "today",
|
|
multidate: true,
|
|
multidateSeparator: ";",
|
|
calendarWeeks: true
|
|
});
|
|
}
|
|
});
|
|
|
|
/*
|
|
* Handlebars helper
|
|
*/
|
|
Ember.Handlebars.registerBoundHelper('formattedDate', function(date, format) {
|
|
return moment(date).format(format);
|
|
}); |