From a40ecad00829bc2bf188cc14047b601f20b07b7f Mon Sep 17 00:00:00 2001 From: David Baldwynn Date: Sun, 5 Nov 2017 19:40:28 -0800 Subject: [PATCH] added aggregating for form analytics --- app/controllers/forms.server.controller.js | 134 +- app/models/form.server.model.js | 105 +- app/routes/forms.server.routes.js | 2 + app/sockets/analytics_service.js | 1 - package-lock.json | 670 +-- package.json | 3 +- public/dist/application.js | 4418 ++++++++++++++++- public/dist/application.min.js | 7 +- .../edit-submissions-form.client.directive.js | 56 +- .../edit-submissions-form.client.view.html | 45 +- 10 files changed, 4527 insertions(+), 914 deletions(-) diff --git a/app/controllers/forms.server.controller.js b/app/controllers/forms.server.controller.js index dc28ed73..ea148387 100644 --- a/app/controllers/forms.server.controller.js +++ b/app/controllers/forms.server.controller.js @@ -83,7 +83,7 @@ exports.listSubmissions = function(req, res) { FormSubmission.find({ form: _form._id }).sort('created').lean().exec(function(err, _submissions) { if (err) { console.error(err); - res.status(500).send({ + return res.status(500).send({ message: errorHandler.getErrorMessage(err) }); } @@ -91,6 +91,135 @@ exports.listSubmissions = function(req, res) { }); }; +/** + * Get Visitor Analytics Data for a given Form + */ +exports.getVisitorData = function(req, res) { + Form.aggregate([ + { + $match: { + _id: mongoose.Types.ObjectId(req.params.formIdNoMiddleware), + admin: mongoose.Types.ObjectId(req.user.id) + } + }, + { + $facet: { + "deviceStatistics": [ + { + $unwind: '$analytics.visitors' + }, + { + $project: { + _id: 0, + deviceType: '$analytics.visitors.deviceType', + SubmittedTimeElapsed: { + $cond: [ + { + $eq: ['$analytics.visitors.isSubmitted', true] + }, + '$analytics.visitors.timeElapsed', + 0 + ] + }, + SubmittedResponses: { + $cond: [ + { + $eq: ['$analytics.visitors.isSubmitted', true] + }, + 1, + 0 + ] + } + } + }, + { + $group: { + _id: "$deviceType", + total_time: { $sum: "$SubmittedTimeElapsed" }, + responses: { $sum: "$SubmittedResponses" }, + visits: { $sum: 1 } + } + }, + { + $project: { + total_time: "$total_time", + responses: "$responses", + visits: "$visits", + average_time: { + $divide : ["$total_time", "$responses"] + }, + conversion_rate: { + $divide : ["$responses", "$visits"] + } + } + } + ], + "globalStatistics": [ + { + $unwind: '$analytics.visitors' + }, + { + $project: { + _id: 0, + deviceType: '$analytics.visitors.deviceType', + SubmittedTimeElapsed: { + $cond: [ + { + $eq: ['$analytics.visitors.isSubmitted', true] + }, + '$analytics.visitors.timeElapsed', + 0 + ] + }, + SubmittedResponses: { + $cond: [ + { + $eq: ['$analytics.visitors.isSubmitted', true] + }, + 1, + 0 + ] + } + } + }, + { + $group: { + _id: null, + total_time: { $sum: "$SubmittedTimeElapsed" }, + responses: { $sum: "$SubmittedResponses" }, + visits: { $sum: 1 } + } + }, + { + $project: { + _id: 0, + total_time: "$total_time", + responses: "$responses", + visits: "$visits", + average_time: { + $divide : ["$total_time", "$responses"] + }, + conversion_rate: { + $divide : ["$responses", "$visits"] + } + } + } + ], + } + } + ], function(err, results){ + if (err) { + console.error(err); + return res.status(500).send({ + message: errorHandler.getErrorMessage(err) + }); + } + + return res.json(results); + }); +}; + + /** * Create a new form */ @@ -132,7 +261,7 @@ exports.read = function(req, res) { var newForm = req.form.toJSON(); - if(newForm.admin._id === req.user._id){ + if(newForm.admin === req.user._id){ return res.json(newForm); } @@ -274,6 +403,7 @@ exports.formByID = function(req, res, next, id) { } Form.findById(id) + .select('admin title language form_fields startPage endPage hideFooter isLive design analytics.gaCode') .populate('admin') .exec(function(err, form) { if (err) { diff --git a/app/models/form.server.model.js b/app/models/form.server.model.js index 5f8321a0..45248df1 100644 --- a/app/models/form.server.model.js +++ b/app/models/form.server.model.js @@ -7,12 +7,7 @@ var mongoose = require('mongoose'), Schema = mongoose.Schema, _ = require('lodash'), timeStampPlugin = require('../libs/timestamp.server.plugin'), - async = require('async'), - Random = require('random-js'), - mt = Random.engines.mt19937(); - - -mt.autoSeed(); + async = require('async'); //Mongoose Models var FieldSchema = require('./form_field.server.model.js'); @@ -47,8 +42,8 @@ var VisitorDataSchema = new Schema({ referrer: { type: String }, - lastActiveField: { - type: Schema.Types.ObjectId + filledOutFields: { + type: [Schema.Types.ObjectId] }, timeElapsed: { type: Number @@ -110,7 +105,7 @@ var FormSchema = new Schema({ type: Schema.Types.ObjectId, ref: 'FormSubmission' }], - dfeault: [] + default: [] }, admin: { type: Schema.Types.ObjectId, @@ -196,98 +191,6 @@ var FormSchema = new Schema({ } }, formSchemaOptions); -/* -** In-Form Analytics Virtual Attributes - */ -FormSchema.virtual('analytics.views').get(function () { - if(this.analytics && this.analytics.visitors && this.analytics.visitors.length > 0){ - return this.analytics.visitors.length; - } else { - return 0; - } -}); - -FormSchema.virtual('analytics.submissions').get(function () { - return this.submissions.length; -}); - -FormSchema.virtual('analytics.conversionRate').get(function () { - if(this.analytics && this.analytics.visitors && this.analytics.visitors.length > 0){ - return this.submissions.length/this.analytics.visitors.length*100; - } else { - return 0; - } -}); - -FormSchema.virtual('analytics.fields').get(function () { - var fieldDropoffs = []; - var visitors = this.analytics.visitors; - var that = this; - - if(!this.form_fields || this.form_fields.length === 0) { - return null; - } - - for(var i=0; i i){ - return sum + 1; - } - return sum; - }, 0); - } else { - continueViews = _.reduce(visitors, function(sum, visitorObj){ - if(visitorObj.lastActiveField+'' === field._id+'' && visitorObj.isSubmitted){ - return sum + 1; - } - return sum; - }, 0); - - } - - var totalViews = dropoffViews+continueViews; - var continueRate = 0; - var dropoffRate = 0; - - if(totalViews > 0){ - continueRate = (continueViews/totalViews*100).toFixed(0); - dropoffRate = (dropoffViews/totalViews*100).toFixed(0); - } - - fieldDropoffs[i] = { - dropoffViews: dropoffViews, - responses: continueViews, - totalViews: totalViews, - continueRate: continueRate, - dropoffRate: dropoffRate, - field: field - }; - - } - } - - return fieldDropoffs; -}); - FormSchema.plugin(timeStampPlugin, { createdPath: 'created', modifiedPath: 'lastModified', diff --git a/app/routes/forms.server.routes.js b/app/routes/forms.server.routes.js index 6798e525..123ce644 100644 --- a/app/routes/forms.server.routes.js +++ b/app/routes/forms.server.routes.js @@ -47,6 +47,8 @@ module.exports = function(app) { .get(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.listSubmissions) .delete(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.deleteSubmissions); + app.route('/forms/:formIdNoMiddleware([a-zA-Z0-9]+)/visitors') + .get(auth.isAuthenticatedOrApiKey, forms.getVisitorData); // Slower formId middleware app.param('formId', forms.formByID); diff --git a/app/sockets/analytics_service.js b/app/sockets/analytics_service.js index 993a0468..0021f60f 100644 --- a/app/sockets/analytics_service.js +++ b/app/sockets/analytics_service.js @@ -22,7 +22,6 @@ module.exports = function (io, socket) { var newVisitor = { socketId: data.socketId, referrer: data.referrer, - lastActiveField: data.lastActiveField, timeElapsed: data.timeElapsed, isSubmitted: data.isSubmitted, language: data.language, diff --git a/package-lock.json b/package-lock.json index ad6d9b12..25d97ec8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -442,22 +442,6 @@ "integrity": "sha1-PzQU84AyF0O/wQQvmoP/HVgk1GQ=", "dev": true }, - "bcrypt": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-0.8.7.tgz", - "integrity": "sha1-vDh1qa/Qp7LNIxpqfyGKXOFWsJM=", - "requires": { - "bindings": "1.2.1", - "nan": "2.3.5" - }, - "dependencies": { - "nan": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.3.5.tgz", - "integrity": "sha1-gioNwmYpDOTNOhIoLKPn42Rmigg=" - } - } - }, "bcrypt-pbkdf": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", @@ -485,11 +469,6 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=" }, - "bindings": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", - "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" - }, "bl": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", @@ -2731,16 +2710,16 @@ "resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz", "integrity": "sha1-5louPHUH/63kEJvHV1p25Q+NqRU=" }, + "buffer-shims": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", + "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=" + }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" }, - "byt": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/byt/-/byt-0.1.0.tgz", - "integrity": "sha1-tyVTl3uMTciQ0mL9XiDzMAOdFUs=" - }, "bytes": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.2.0.tgz", @@ -4086,6 +4065,36 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=" }, + "envfile": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/envfile/-/envfile-2.1.1.tgz", + "integrity": "sha1-jlrmacHyRZ5YkJWdn4obHAXmj50=", + "requires": { + "ambi": "2.5.0", + "eachr": "3.2.0", + "editions": "1.3.3", + "typechecker": "4.4.1" + }, + "dependencies": { + "eachr": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eachr/-/eachr-3.2.0.tgz", + "integrity": "sha1-LDXkPqCGUW95l8+At6pk1VpKRIQ=", + "requires": { + "editions": "1.3.3", + "typechecker": "4.4.1" + } + }, + "typechecker": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/typechecker/-/typechecker-4.4.1.tgz", + "integrity": "sha1-+XuV9RsDhBchLWd9RaNz7nvO1+Y=", + "requires": { + "editions": "1.3.3" + } + } + } + }, "error-ex": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", @@ -6260,14 +6269,6 @@ "jade": "1.11.0" } }, - "grunt-i18nlint": { - "version": "github:jwarby/grunt-i18nlint#6b398f775c8ca881a16f45a3cb94c5b79ee11e20", - "dev": true, - "requires": { - "chalk": "1.1.3", - "i18n-lint": "git://github.com/jwarby/i18n-lint.git#0a06373c0d880047ad680239c103d60ed414efc1" - } - }, "grunt-karma": { "version": "0.12.2", "resolved": "https://registry.npmjs.org/grunt-karma/-/grunt-karma-0.12.2.tgz", @@ -6700,9 +6701,9 @@ "integrity": "sha1-D1kbGzRL3LPfWXc/Yvu6+Fv0Aos=" }, "hooks-fixed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-1.1.0.tgz", - "integrity": "sha1-DowVM2cI5mERhf45C0RofdUjDbs=" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.2.tgz", + "integrity": "sha512-YurCM4gQSetcrhwEtpQHhQ4M7Zo7poNGqY4kQGeBS6eZtOcT3tnNs01ThFa0jYBByAiYt1MjMjP/YApG0EnAvQ==" }, "hosted-git-info": { "version": "2.5.0", @@ -6888,48 +6889,6 @@ "sprintf-js": "1.1.1" } }, - "i18n-lint": { - "version": "git://github.com/jwarby/i18n-lint.git#0a06373c0d880047ad680239c103d60ed414efc1", - "dev": true, - "requires": { - "chalk": "1.1.3", - "commander": "2.8.1", - "glob": "5.0.15", - "htmlparser2": "3.8.3", - "lodash": "3.10.1", - "node.extend": "1.1.6" - }, - "dependencies": { - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.8" - } - } - } - }, "iconv-lite": { "version": "0.2.11", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", @@ -7114,12 +7073,6 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.5.tgz", "integrity": "sha1-X6eM8wG4JceKvDBC2BJyMEnqI8c=" }, - "is": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is/-/is-3.2.1.tgz", - "integrity": "sha1-0Kwq1V63sL7JJqUmb2xmKqqD3KU=", - "dev": true - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -7662,6 +7615,7 @@ "version": "2.4.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "dev": true, "requires": { "graceful-fs": "4.1.11" }, @@ -7670,6 +7624,7 @@ "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true, "optional": true } } @@ -7707,9 +7662,9 @@ } }, "kareem": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.0.1.tgz", - "integrity": "sha1-eAXSFbtTIU7Dr5aaHQsfF+PnuVw=" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz", + "integrity": "sha1-4+QQHZ3P3imXadr0tNtk2JXRdEg=" }, "karma": { "version": "0.13.22", @@ -8207,6 +8162,11 @@ } } }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, "lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", @@ -8268,11 +8228,6 @@ "integrity": "sha1-euTsJXMC/XkNVXyxDJcQDYV7AFY=", "dev": true }, - "log-rotate": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/log-rotate/-/log-rotate-0.2.7.tgz", - "integrity": "sha1-qttkJZ7qSeVmiEI0LIZNCxQujC0=" - }, "log4js": { "version": "0.6.38", "resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz", @@ -8315,27 +8270,6 @@ } } }, - "logrotate-stream": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/logrotate-stream/-/logrotate-stream-0.2.5.tgz", - "integrity": "sha1-/dQueYKn7i64a1X18j3L/8ZuaZA=", - "requires": { - "byt": "0.1.0", - "log-rotate": "0.2.7", - "optimist": "0.6.1" - }, - "dependencies": { - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.2" - } - } - } - }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", @@ -8902,70 +8836,79 @@ } }, "mongoose": { - "version": "4.4.20", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.4.20.tgz", - "integrity": "sha1-6XT/tq6MUPQJgBqEl6mOnztR8t0=", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.13.0.tgz", + "integrity": "sha512-PVUEQ4eS1Bh0Q4IqWRph+li8VMwBxHetdJ1O/P/vE8DktOtBOM1G1G0QOrtQSW1FDrLFSVYkzK4IfI7vJeihQg==", "requires": { - "async": "1.5.2", - "bson": "0.4.23", - "hooks-fixed": "1.1.0", - "kareem": "1.0.1", - "mongodb": "2.1.18", - "mpath": "0.2.1", + "async": "2.1.4", + "bson": "1.0.4", + "hooks-fixed": "2.0.2", + "kareem": "1.5.0", + "lodash.get": "4.4.2", + "mongodb": "2.2.33", + "mpath": "0.3.0", "mpromise": "0.5.5", - "mquery": "1.11.0", - "ms": "0.7.1", - "muri": "1.1.0", + "mquery": "2.3.2", + "ms": "2.0.0", + "muri": "1.3.0", "regexp-clone": "0.0.1", "sliced": "1.0.1" }, "dependencies": { + "async": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz", + "integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=", + "requires": { + "lodash": "4.17.4" + } + }, "bluebird": { - "version": "2.10.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz", - "integrity": "sha1-AkpVFylTCIV/FPkfEQb8O1VfRGs=" + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" + }, + "bson": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz", + "integrity": "sha1-k8ENOeqltYQVy8QFLz5T5WKwtyw=" }, "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { - "ms": "0.7.1" + "ms": "2.0.0" } }, "es6-promise": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", - "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=" - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz", + "integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q=" }, "mongodb": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.1.18.tgz", - "integrity": "sha1-KNQLUVsr5NWmn/3UxTXw30MuQJc=", + "version": "2.2.33", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.33.tgz", + "integrity": "sha1-tTfEcdNKZlG0jzb9vyl1A0Dgi1A=", "requires": { - "es6-promise": "3.0.2", - "mongodb-core": "1.3.18", - "readable-stream": "1.0.31" + "es6-promise": "3.2.1", + "mongodb-core": "2.1.17", + "readable-stream": "2.2.7" } }, "mongodb-core": { - "version": "1.3.18", - "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.18.tgz", - "integrity": "sha1-kGhLO3xzVtZa41Y5HTCw8kiATHo=", + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.17.tgz", + "integrity": "sha1-pBizN6FKFJkPtRC5I97mqBMXPfg=", "requires": { - "bson": "0.4.23", + "bson": "1.0.4", "require_optional": "1.0.1" } }, "mpath": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.2.1.tgz", - "integrity": "sha1-Ok6Ck1mAHeljCcJ6ay4QLon56W4=" + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.3.0.tgz", + "integrity": "sha1-elj3iem1/TyUUgY0FXlg8mvV70Q=" }, "mpromise": { "version": "0.5.5", @@ -8973,12 +8916,12 @@ "integrity": "sha1-9bJCWddjrMIlewoMjG2Gb9UXMuY=" }, "mquery": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-1.11.0.tgz", - "integrity": "sha1-4MZd7bEDftv2z7iCYud3/uI1Udk=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.2.tgz", + "integrity": "sha512-KXWMypZSvhCuqRtza+HMQZdYw7PfFBjBTFvP31NNAq0OX0/NTIgpcDpkWQ2uTxk6vGQtwQ2elhwhs+ZvCA8OaA==", "requires": { - "bluebird": "2.10.2", - "debug": "2.2.0", + "bluebird": "3.5.1", + "debug": "2.6.9", "regexp-clone": "0.0.1", "sliced": "0.0.5" }, @@ -8990,31 +8933,29 @@ } } }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=" + "muri": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/muri/-/muri-1.3.0.tgz", + "integrity": "sha512-FiaFwKl864onHFFUV/a2szAl7X0fxVlSKNdhTf+BM8i8goEgYut8u5P9MqQqIYwvaMxjzVESsoEm/2kfkFH1rg==" }, "readable-stream": { - "version": "1.0.31", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.31.tgz", - "integrity": "sha1-jyUC4LyeOw2huUUgqrtOJgPsr64=", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz", + "integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=", "requires": { + "buffer-shims": "1.0.0", "core-util-is": "1.0.2", "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "sliced": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" } } }, @@ -9253,15 +9194,6 @@ "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.0.0.tgz", "integrity": "sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA=" }, - "node.extend": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-1.1.6.tgz", - "integrity": "sha1-p7iCyC1sk6SGOlUEvV3o7IYli5Y=", - "dev": true, - "requires": { - "is": "3.2.1" - } - }, "nodemailer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.0.1.tgz", @@ -9789,283 +9721,6 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, - "phantomjs": { - "version": "1.9.20", - "resolved": "https://registry.npmjs.org/phantomjs/-/phantomjs-1.9.20.tgz", - "integrity": "sha1-RCSsog4U0lXAsIia9va4lz2hDg0=", - "dev": true, - "requires": { - "extract-zip": "1.5.0", - "fs-extra": "0.26.7", - "hasha": "2.2.0", - "kew": "0.7.0", - "progress": "1.1.8", - "request": "2.67.0", - "request-progress": "2.0.1", - "which": "1.2.14" - }, - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true - }, - "async": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", - "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true - }, - "bl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz", - "integrity": "sha1-/FQhoo/UImA2w7OJGmaiW8ZNIm4=", - "dev": true, - "requires": { - "readable-stream": "2.0.6" - } - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "dev": true - }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "dev": true - }, - "concat-stream": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz", - "integrity": "sha1-U/fUPFHF5D+ByP3QMyHGMb5o1hE=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.0.6", - "typedarray": "0.0.6" - } - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "debug": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", - "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=", - "dev": true - }, - "extract-zip": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.5.0.tgz", - "integrity": "sha1-ksz22B73Cp+kwXRxFMzvbYaIpsQ=", - "dev": true, - "requires": { - "concat-stream": "1.5.0", - "debug": "0.7.4", - "mkdirp": "0.5.0", - "yauzl": "2.4.1" - } - }, - "form-data": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz", - "integrity": "sha1-rjFduaSQf6BlUCMEpm13M0de43w=", - "dev": true, - "requires": { - "async": "2.5.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "fs-extra": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", - "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "2.4.0", - "klaw": "1.3.1", - "path-is-absolute": "1.0.1", - "rimraf": "2.2.8" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "commander": "2.11.0", - "is-my-json-valid": "2.16.1", - "pinkie-promise": "2.0.1" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } - }, - "mkdirp": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", - "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", - "dev": true - }, - "qs": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-5.2.1.tgz", - "integrity": "sha1-gB/uAw4LlFDWOFrcSKTMVbRK7fw=", - "dev": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.67.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.67.0.tgz", - "integrity": "sha1-ivdHgOK/EeoK6aqWXBHxGv0nJ0I=", - "dev": true, - "requires": { - "aws-sign2": "0.6.0", - "bl": "1.0.3", - "caseless": "0.11.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "1.0.1", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "node-uuid": "1.4.8", - "oauth-sign": "0.8.2", - "qs": "5.2.1", - "stringstream": "0.0.5", - "tough-cookie": "2.2.2", - "tunnel-agent": "0.4.3" - } - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "tough-cookie": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz", - "integrity": "sha1-yDoYMPTl7wuT7yo0iOck+N4Basc=", - "dev": true - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true - }, - "which": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", - "dev": true, - "requires": { - "isexe": "2.0.0" - } - } - } - }, "phantomjs-prebuilt": { "version": "2.1.15", "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.15.tgz", @@ -11928,66 +11583,6 @@ "upper-case": "1.1.3" } }, - "swig": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/swig/-/swig-1.4.2.tgz", - "integrity": "sha1-QIXKBFM2kQS11IPihBs5t64aq6U=", - "requires": { - "optimist": "0.6.1", - "uglify-js": "2.4.24" - }, - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=" - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.2" - } - }, - "source-map": { - "version": "0.1.34", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", - "integrity": "sha1-p8/omux7FoLDsZjQrPtH19CQVms=", - "requires": { - "amdefine": "1.0.1" - } - }, - "uglify-js": { - "version": "2.4.24", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz", - "integrity": "sha1-+tV1XB4Vd2WLsG/5q25UjJW+vW4=", - "requires": { - "async": "0.2.10", - "source-map": "0.1.34", - "uglify-to-browserify": "1.0.2", - "yargs": "3.5.4" - } - }, - "yargs": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz", - "integrity": "sha1-2K/49mXpTDS9JZvevRv68N3TU2E=", - "requires": { - "camelcase": "1.2.1", - "decamelize": "1.2.0", - "window-size": "0.1.0", - "wordwrap": "0.0.2" - } - } - } - }, "taskgroup": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/taskgroup/-/taskgroup-4.3.1.tgz", @@ -12566,9 +12161,6 @@ "string-width": "1.0.2" } }, - "wildcard-subdomains": { - "version": "github:tellform/wildcard-subdomains#af33bb3e040da9f11387f920f3801c4d080c0423" - }, "window-size": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", @@ -12599,40 +12191,6 @@ } } }, - "winston-logrotate": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/winston-logrotate/-/winston-logrotate-1.3.0.tgz", - "integrity": "sha1-Sy9cLqrvSS9bdcccpzd48wXI/4A=", - "requires": { - "bluebird": "2.9.30", - "fs-extra": "0.24.0", - "logrotate-stream": "0.2.5", - "winston": "2.4.0" - }, - "dependencies": { - "bluebird": { - "version": "2.9.30", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.30.tgz", - "integrity": "sha1-7dqHXsmq0fKc8fVtboL7qysN9VY=" - }, - "fs-extra": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.24.0.tgz", - "integrity": "sha1-1OQ0KpZnXLeEZjOmCZJJMytTmVI=", - "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "2.4.0", - "path-is-absolute": "1.0.1", - "rimraf": "2.2.8" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - } - } - }, "wiredep": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/wiredep/-/wiredep-4.0.0.tgz", diff --git a/package.json b/package.json index d8bb61d2..343b7053 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "deep-diff": "^0.3.4", "dotenv": "^2.0.0", "email-verification": "github:tellform/node-email-verification", + "envfile": "^2.1.1", "express": "~4.13.3", "express-session": "~1.12.1", "glob": "^7.0.3", @@ -57,7 +58,7 @@ "main-bower-files": "~2.9.0", "method-override": "~2.3.0", "mkdirp": "^0.5.1", - "mongoose": "~4.4.19", + "mongoose": "^4.13.0", "morgan": "~1.8.1", "nodemailer": "~4.0.0", "passport": "~0.3.0", diff --git a/public/dist/application.js b/public/dist/application.js index 92f6ce4e..3d3aa5ae 100644 --- a/public/dist/application.js +++ b/public/dist/application.js @@ -1,5 +1,29 @@ 'use strict'; +// Init the application configuration module for AngularJS application +var ApplicationConfiguration = (function() { + // Init module configuration options + var applicationModuleName = 'TellForm'; + var applicationModuleVendorDependencies = ['duScroll', 'ui.select', 'ngSanitize', 'vButton', 'ngResource', 'TellForm.templates', 'ui.router', 'ui.bootstrap', 'ui.utils', 'pascalprecht.translate', 'view-form']; + + // Add a new vertical module + var registerModule = function(moduleName, dependencies) { + // Create angular module + angular.module(moduleName, dependencies || []); + + // Add the module to the AngularJS configuration file + angular.module(applicationModuleName).requires.push(moduleName); + }; + + return { + applicationModuleName: applicationModuleName, + applicationModuleVendorDependencies: applicationModuleVendorDependencies, + registerModule: registerModule + }; +})(); + +'use strict'; + //Start by defining the main module and adding the module dependencies angular.module(ApplicationConfiguration.applicationModuleName, ApplicationConfiguration.applicationModuleVendorDependencies); @@ -36,29 +60,4217 @@ angular.element(document).ready(function() { angular.bootstrap(document, [ApplicationConfiguration.applicationModuleName]); }); +angular.module('TellForm.templates', []).run(["$templateCache", function ($templateCache) { + "use strict"; + $templateCache.put("modules/core/views/header.client.view.html", + "
"); + $templateCache.put("modules/forms/admin/views/admin-form.client.view.html", + "

{{ 'TELLFORM_URL' | translate }}
{{ 'COPY_AND_PASTE' | translate }}
{{ 'BACKGROUND_COLOR' | translate }}
{{ 'QUESTION_TEXT_COLOR' | translate }}
{{ 'ANSWER_TEXT_COLOR' | translate }}
{{ 'BTN_BACKGROUND_COLOR' | translate }}
{{ 'BTN_TEXT_COLOR' | translate }}
"); + $templateCache.put("modules/forms/admin/views/list-forms.client.view.html", + "

{{ 'CREATE_A_NEW_FORM' | translate }}
{{ 'NAME' | translate }}
{{ 'LANGUAGE' | translate }}

{{ form.numberOfResponses }} {{ 'RESPONSES' | translate }}

{{ 'FORM_PAUSED' | translate }}
"); + $templateCache.put("modules/forms/admin/views/adminTabs/analyze.html", + ""); + $templateCache.put("modules/forms/admin/views/adminTabs/configure.html", + ""); + $templateCache.put("modules/forms/admin/views/adminTabs/create.html", + ""); + $templateCache.put("modules/forms/admin/views/directiveViews/form/configure-form.client.view.html", + "
{{ 'FORM_NAME' | translate }}
{{ 'FORM_STATUS' | translate }}
{{ 'LANGUAGE' | translate }}
* {{ 'REQUIRED_FIELD' | translate }}
{{ 'GA_TRACKING_CODE' | translate }}
{{ 'DISPLAY_FOOTER' | translate }}
{{ 'DISPLAY_START_PAGE' | translate }}
{{ 'DISPLAY_END_PAGE' | translate }}
"); + $templateCache.put("modules/forms/admin/views/directiveViews/form/edit-form.client.view.html", + "

{{ 'ADD_FIELD_LG' | translate }}

{{ 'ADD_FIELD_MD' | translate }}

{{ 'ADD_FIELD_SM' | translate }}

{{ 'WELCOME_SCREEN' | translate }}


{{field.title}} *

{{ 'CLICK_FIELDS_FOOTER' | translate }}


{{ 'END_SCREEN' | translate }}

"); + $templateCache.put("modules/forms/admin/views/directiveViews/form/edit-submissions-form.client.view.html", + "
{{ 'TOTAL_VIEWS' | translate }}
{{ 'RESPONSES' | translate }}
{{ 'COMPLETION_RATE' | translate }}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{analyticsData.globalStatistics.visits}}
{{analyticsData.globalStatistics.responses | number:0}}
{{analyticsData.globalStatistics.conversion_rate | number:0}}
{{ analyticsData.globalStatistics.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'DESKTOP_AND_LAPTOP' | translate }}
{{ 'TABLETS' | translate }}
{{ 'PHONES' | translate }}
{{ 'OTHER' | translate }}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.desktop.visits}}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.tablet.visits}}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.tablet.visits}}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.other.visits}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.desktop.responses}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.tablet.responses}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.phone.responses}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.other.responses}}
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.desktop.completion}}%
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.tablet.completion}}%
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.phone.completion}}%
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.other.completion}}%
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.desktop.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.tablet.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.phone.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.other.average_time | secondsToDateTime | date:'mm:ss'}}

#{{value.title}}{{ 'PERCENTAGE_COMPLETE' | translate }}{{ 'TIME_ELAPSED' | translate }}{{ 'DEVICE' | translate }}{{ 'LOCATION' | translate }}{{ 'IP_ADDRESS' | translate }}{{ 'DATE_SUBMITTED' | translate }} (UTC)
{{$index+1}}{{field.fieldValue}}{{row.percentageComplete}}%{{row.timeElapsed | secondsToDateTime | date:'mm:ss'}}{{row.device.name}}, {{row.device.type}}{{row.geoLocation.City}}, {{row.geoLocation.Country}}{{row.ipAddr}}{{row.created | date:'yyyy-MM-dd HH:mm:ss'}}
"); + $templateCache.put("modules/users/views/authentication/access-denied.client.view.html", + "

{{ 'ACCESS_DENIED_TEXT' | translate }}

"); + $templateCache.put("modules/users/views/authentication/signin.client.view.html", + "
{{ 'ERROR' | translate }}:
"); + $templateCache.put("modules/users/views/authentication/signup-success.client.view.html", + "

{{ 'SUCCESS_HEADER' | translate }}

{{ 'SUCCESS_TEXT' | translate }}

{{ 'NOT_ACTIVATED_YET' | translate }}



{{ 'BEFORE_YOU_CONTINUE' | translate }} team@tellform.com

"); + $templateCache.put("modules/users/views/authentication/signup.client.view.html", + "
{{'SIGNUP_ERROR_TEXT' | translate}}:
"); + $templateCache.put("modules/users/views/password/forgot-password.client.view.html", + "
{{ 'ERROR' | translate }}: {{error}}
{{success}}
"); + $templateCache.put("modules/users/views/password/reset-password-invalid.client.view.html", + "

{{ 'PASSWORD_RESET_INVALID' | translate }}

"); + $templateCache.put("modules/users/views/password/reset-password-success.client.view.html", + "

{{ 'PASSWORD_RESET_SUCCESS' | translate }}

"); + $templateCache.put("modules/users/views/password/reset-password.client.view.html", + "

{{ 'RESET_PASSWORD' | translate }}

{{error}}
{{success}}
"); + $templateCache.put("modules/users/views/settings/change-password.client.view.html", + "

{{ 'CHANGE_PASSWORD' | translate }}


{{ 'PASSWORD_CHANGE_SUCCESS' | translate }}
"); + $templateCache.put("modules/users/views/settings/edit-profile.client.view.html", + "

{{ 'EDIT_PROFILE' | translate }}

{{ 'PROFILE_SAVE_SUCCESS' | translate }}
{{ 'PROFILE_SAVE_ERROR' | translate }}
{{ 'FIRST_NAME_LABEL' | translate }}
{{ 'LAST_NAME_LABEL' | translate }}

{{ 'LANGUAGE_LABEL' | translate }}
{{ 'USERNAME_LABEL' | translate }}
{{ 'EMAIL_LABEL' | translate }}
"); + $templateCache.put("modules/users/views/settings/social-accounts.client.view.html", + "

{{ 'CONNECTED_SOCIAL_ACCOUNTS' | translate }}:

{{ 'CONNECT_OTHER_SOCIAL_ACCOUNTS' | translate }}

"); + $templateCache.put("modules/users/views/verify/resend-verify-email.client.view.html", + "
{{error}}

{{ 'VERIFICATION_EMAIL_SENT' | translate }}

{{ 'VERIFICATION_EMAIL_SENT_TO' | translate }} {{username}}.
{{ 'NOT_ACTIVATED_YET' | translate }}

{{ 'CHECK_YOUR_EMAIL' | translate }} polydaic@gmail.com

"); + $templateCache.put("modules/users/views/verify/verify-account.client.view.html", + "

{{ 'VERIFY_SUCCESS' | translate }}

"); + $templateCache.put("form_modules/forms/base/views/directiveViews/entryPage/startPage.html", + "

{{pageData.introTitle}}

{{pageData.introParagraph}}

"); + $templateCache.put("form_modules/forms/base/views/directiveViews/field/date.html", + "

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{field.description}}

"); + $templateCache.put("form_modules/forms/base/views/directiveViews/field/dropdown.html", + "
0\">

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{field.description}}


"); + $templateCache.put("form_modules/forms/base/views/directiveViews/field/hidden.html", + ""); + $templateCache.put("form_modules/forms/base/views/directiveViews/field/legal.html", + "

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}


{{field.description}}


"); + $templateCache.put("form_modules/forms/base/views/directiveViews/field/radio.html", + "
0\">

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{field.description}}


"); + $templateCache.put("form_modules/forms/base/views/directiveViews/field/rating.html", + "

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{field.description}}

"); + $templateCache.put("form_modules/forms/base/views/directiveViews/field/statement.html", + "

{{field.title}}

{{field.description}}

{{field.description}}


"); + $templateCache.put("form_modules/forms/base/views/directiveViews/field/textarea.html", + "

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{ 'NEWLINE' | translate }}

{{field.description}}

{{ 'ADD_NEW_LINE_INSTR' | translate }}
{{ 'ENTER' | translate }}
"); + $templateCache.put("form_modules/forms/base/views/directiveViews/field/textfield.html", + "

{{index+1}} {{field.title}} ({{ 'OPTIONAL' | translate }})

{{field.description}}

{{ 'ENTER' | translate }}
"); + $templateCache.put("form_modules/forms/base/views/directiveViews/field/yes_no.html", + "

{{index+1}} {{field.title}} {{ 'OPTIONAL' | translate }}

{{field.description}}


"); + $templateCache.put("form_modules/forms/base/views/directiveViews/form/submit-form.client.view.html", + "
{{ 'COMPLETING_NEEDED' | translate:translateAdvancementData }}
{{ 'ENTER' | translate }}

{{ 'ADVANCEMENT' | translate:translateAdvancementData }}

"); + $templateCache.put("form_modules/forms/base/views/form-not-found.client.view.html", + "

{{ 'FORM_404_HEADER' | translate }}

{{ 'FORM_404_BODY' | translate }}
"); + $templateCache.put("form_modules/forms/base/views/form-unauthorized.client.view.html", + "

{{ 'FORM_UNAUTHORIZED_HEADER' | translate }}

{{ 'FORM_UNAUTHORIZED_BODY1' | translate }}
{{ 'FORM_UNAUTHORIZED_BODY2' | translate }}
"); + $templateCache.put("form_modules/forms/base/views/submit-form.client.view.html", + "
"); +}]); + 'use strict'; -// Init the application configuration module for AngularJS application -var ApplicationConfiguration = (function() { - // Init module configuration options - var applicationModuleName = 'TellForm'; - var applicationModuleVendorDependencies = ['duScroll', 'ui.select', 'ngSanitize', 'vButton', 'ngResource', 'TellForm.templates', 'ui.router', 'ui.bootstrap', 'ui.utils', 'pascalprecht.translate', 'view-form']; +// Use Application configuration module to register a new module +ApplicationConfiguration.registerModule('core', ['users']); - // Add a new vertical module - var registerModule = function(moduleName, dependencies) { - // Create angular module - angular.module(moduleName, dependencies || []); +'use strict'; - // Add the module to the AngularJS configuration file - angular.module(applicationModuleName).requires.push(moduleName); +// Use Application configuration module to register a new module +ApplicationConfiguration.registerModule('forms', [ + 'ngFileUpload', 'ui.date', 'ui.sortable', + 'angular-input-stars', 'users', 'ngclipboard' +]);//, 'colorpicker.module' @TODO reactivate this module + +'use strict'; + +// Use Application configuration module to register a new module +ApplicationConfiguration.registerModule('users'); +'use strict'; + +// Setting up route +angular.module('core').config(["$stateProvider", "$urlRouterProvider", "Authorization", function($stateProvider, $urlRouterProvider, Authorization) { + // Redirect to home view when route not found + $urlRouterProvider.otherwise('/forms'); + } +]); + +angular.module(ApplicationConfiguration.applicationModuleName).run(["$rootScope", "Auth", "$state", "$stateParams", function($rootScope, Auth, $state, $stateParams) { + + $rootScope.$state = $state; + $rootScope.$stateParams = $stateParams; + + // add previous state property + $rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) { + $state.previous = { + state: fromState, + params: fromParams + } + + var statesToIgnore = ['', 'home', 'signin', 'resendVerifyEmail', 'verify', 'signup', 'signup-success', 'forgot', 'reset-invalid', 'reset', 'reset-success']; + + //Redirect to listForms if user is authenticated + if(statesToIgnore.indexOf(toState.name) > 0){ + if(Auth.isAuthenticated()){ + event.preventDefault(); // stop current execution + $state.go('listForms'); // go to listForms page + } + } + //Redirect to 'signup' route if user is not authenticated + else if(toState.name !== 'access_denied' && !Auth.isAuthenticated() && toState.name !== 'submitForm'){ + event.preventDefault(); // stop current execution + $state.go('listForms'); // go to listForms page + } + + }); + + } +]); + +//Page access/authorization logic +angular.module(ApplicationConfiguration.applicationModuleName).run(["$rootScope", "Auth", "User", "Authorizer", "$state", "$stateParams", function($rootScope, Auth, User, Authorizer, $state, $stateParams) { + $rootScope.$on('$stateChangeStart', function(event, next) { + var authenticator, permissions, user; + permissions = next && next.data && next.data.permissions ? next.data.permissions : null; + + Auth.ensureHasCurrentUser(User); + user = Auth.currentUser; + + if(user){ + authenticator = new Authorizer(user); + + if( (permissions !== null) ){ + if( !authenticator.canAccess(permissions) ){ + event.preventDefault(); + $state.go('access_denied'); + } + } + } + }); + }]); + +'use strict'; + +angular.module('core').controller('HeaderController', ["$rootScope", "$scope", "Menus", "$state", "Auth", "User", "$window", "$translate", function ($rootScope, $scope, Menus, $state, Auth, User, $window, $translate) { + + $rootScope.signupDisabled = $window.signupDisabled; + + $scope.user = $rootScope.user = Auth.ensureHasCurrentUser(User); + + $scope.authentication = $rootScope.authentication = Auth; + + $rootScope.languages = $scope.languages = ['en', 'fr', 'es', 'it', 'de']; + + //Set global app language + $rootScope.language = $scope.user.language; + $translate.use($scope.user.language); + + $scope.isCollapsed = false; + $rootScope.hideNav = false; + $scope.menu = Menus.getMenu('topbar'); + + $scope.signout = function() { + var promise = User.logout(); + promise.then(function() { + Auth.logout(); + Auth.ensureHasCurrentUser(User); + $scope.user = $rootScope.user = null; + $state.go('listForms'); + + //Refresh view + $state.reload(); + }, + function(reason) { + console.error('Logout Failed: ' + reason); + }); + }; + + $scope.toggleCollapsibleMenu = function() { + $scope.isCollapsed = !$scope.isCollapsed; + }; + + // Collapsing the menu after navigation + $scope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) { + $scope.isCollapsed = false; + $rootScope.hideNav = false; + if ( angular.isDefined( toState.data ) ) { + + if ( angular.isDefined( toState.data.hideNav ) ) { + $rootScope.hideNav = toState.data.hideNav; + } + } + }); + + } +]); + +'use strict'; + +//Menu service used for managing menus +angular.module('core').service('Menus', function() { + // Define a set of default roles + this.defaultRoles = ['*']; + + // Define the menus object + this.menus = {}; + + // A private function for rendering decision + var shouldRender = function(user) { + if (user) { + if (~this.roles.indexOf('*')) { + return true; + } + for (var userRoleIndex in user.roles) { + for (var roleIndex in this.roles) { + if (this.roles[roleIndex] === user.roles[userRoleIndex]) { + return true; + } + } + } + return false; + + } + return this.isPublic; + }; + + // Validate menu existance + this.validateMenuExistance = function(menuId) { + if (menuId && menuId.length) { + if (this.menus[menuId]) { + return true; + } else { + throw new Error('Menu does not exists'); + } + } else { + throw new Error('MenuId was not provided'); + } + }; + + // Get the menu object by menu id + this.getMenu = function(menuId) { + // Validate that the menu exists + this.validateMenuExistance(menuId); + + // Return the menu object + return this.menus[menuId]; + }; + + // Add new menu object by menu id + this.addMenu = function(menuId, isPublic, roles) { + // Create the new menu + this.menus[menuId] = { + isPublic: isPublic || false, + roles: roles || this.defaultRoles, + items: [], + shouldRender: shouldRender + }; + + // Return the menu object + return this.menus[menuId]; + }; + + // Remove existing menu object by menu id + this.removeMenu = function(menuId) { + // Validate that the menu exists + this.validateMenuExistance(menuId); + + // Return the menu object + delete this.menus[menuId]; + }; + + // Add menu item object + this.addMenuItem = function(menuId, menuItemTitle, menuItemURL, menuItemType, menuItemUIRoute, isPublic, roles, position) { + // Validate that the menu exists + this.validateMenuExistance(menuId); + + // Push new menu item + this.menus[menuId].items.push({ + title: menuItemTitle, + link: menuItemURL, + menuItemType: menuItemType || 'item', + menuItemClass: menuItemType, + uiRoute: menuItemUIRoute || ('/' + menuItemURL), + isPublic: ((isPublic === null || typeof isPublic === 'undefined') ? this.menus[menuId].isPublic : isPublic), + roles: ((roles === null || typeof roles === 'undefined') ? this.menus[menuId].roles : roles), + position: position || 0, + items: [], + shouldRender: shouldRender + }); + + // Return the menu object + return this.menus[menuId]; + }; + + // Add submenu item object + this.addSubMenuItem = function(menuId, rootMenuItemURL, menuItemTitle, menuItemURL, menuItemUIRoute, isPublic, roles, position) { + // Validate that the menu exists + this.validateMenuExistance(menuId); + + // Search for menu item + for (var itemIndex in this.menus[menuId].items) { + if (this.menus[menuId].items[itemIndex].link === rootMenuItemURL) { + // Push new submenu item + this.menus[menuId].items[itemIndex].items.push({ + title: menuItemTitle, + link: menuItemURL, + uiRoute: menuItemUIRoute || ('/' + menuItemURL), + isPublic: ((isPublic === null || typeof isPublic === 'undefined') ? this.menus[menuId].items[itemIndex].isPublic : isPublic), + roles: ((roles === null || typeof roles === 'undefined') ? this.menus[menuId].items[itemIndex].roles : roles), + position: position || 0, + shouldRender: shouldRender + }); + } + } + + // Return the menu object + return this.menus[menuId]; + }; + + // Remove existing menu object by menu id + this.removeMenuItem = function(menuId, menuItemURL) { + // Validate that the menu exists + this.validateMenuExistance(menuId); + + // Search for menu item to remove + for (var itemIndex in this.menus[menuId].items) { + if (this.menus[menuId].items[itemIndex].link === menuItemURL) { + this.menus[menuId].items.splice(itemIndex, 1); + } + } + + // Return the menu object + return this.menus[menuId]; + }; + + // Remove existing menu object by menu id + this.removeSubMenuItem = function(menuId, submenuItemURL) { + // Validate that the menu exists + this.validateMenuExistance(menuId); + + // Search for menu item to remove + for (var itemIndex in this.menus[menuId].items) { + for (var subitemIndex in this.menus[menuId].items[itemIndex].items) { + if (this.menus[menuId].items[itemIndex].items[subitemIndex].link === submenuItemURL) { + this.menus[menuId].items[itemIndex].items.splice(subitemIndex, 1); + } + } + } + + // Return the menu object + return this.menus[menuId]; + }; + + //Adding the topbar menu + this.addMenu('topbar', false, ['*']); + + //Adding the bottombar menu for the Form-Footer view + this.addMenu('bottombar', false, ['*']); + }); + +'use strict'; + +angular.module('core').factory('subdomain', ["$location", function ($location) { + var host = $location.host(); + if (host.indexOf('.') < 0) { + return null; + } + return host.split('.')[0]; +}]); + +'use strict'; + +// Configuring the Forms drop-down menus +angular.module('forms').run(["Menus", function(Menus) { + // Set top bar menu items + Menus.addMenuItem('topbar', 'My Forms', 'forms', '', '/forms', false); + } +]).filter('secondsToDateTime', function() { + return function(seconds) { + return new Date(1970, 0, 1).setSeconds(seconds); }; +}).filter('formValidity', function(){ + return function(formObj){ + if(formObj && formObj.form_fields && formObj.visible_form_fields){ + //get keys + var formKeys = Object.keys(formObj); + + //we only care about things that don't start with $ + var fieldKeys = formKeys.filter(function(key){ + return key[0] !== '$'; + }); + + var fields = formObj.form_fields; + + var valid_count = fields.filter(function(field){ + if(typeof field === 'object' && field.fieldType !== 'statement' && field.fieldType !== 'rating'){ + return !!(field.fieldValue); + } else if(field.fieldType === 'rating'){ + return true; + } + + }).length; + return valid_count - (formObj.form_fields.length - formObj.visible_form_fields.length); + } + return 0; + }; +}).filter('trustSrc', ["$sce", function($sce){ + return function(formUrl){ + return $sce.trustAsResourceUrl(formUrl); + }; +}]).config(["$provide", function ($provide){ + $provide.decorator('accordionDirective', ["$delegate", function($delegate) { + var directive = $delegate[0]; + directive.replace = true; + return $delegate; + }]); +}]); + +'use strict'; + +// Setting up route +angular.module('forms').config(["$stateProvider", function($stateProvider) { + // Forms state routing + $stateProvider. + state('listForms', { + url: '/forms', + templateUrl: 'modules/forms/admin/views/list-forms.client.view.html', + resolve: { + Forms: 'GetForms', + myForms: ["GetForms", "$q", function (GetForms, $q) { + var deferred = $q.defer(); + + GetForms.query(function(forms){ + deferred.resolve(forms); + }); + + return deferred.promise; + }] + }, + controller: 'ListFormsController', + controllerAs: 'ctrl' + }).state('submitForm', { + url: '/forms/:formId', + templateUrl: '/static/form_modules/forms/base/views/submit-form.client.view.html', + data: { + hideNav: true + }, + resolve: { + Forms: 'GetForms', + myForm: ["GetForms", "$stateParams", "$q", function (GetForms, $stateParams, $q) { + var deferred = $q.defer(); + GetForms.get({formId: $stateParams.formId}, function(resolvedForm){ + deferred.resolve(resolvedForm); + }); + + return deferred.promise; + }] + }, + controller: 'SubmitFormController', + controllerAs: 'ctrl' + }).state('viewForm', { + url: '/forms/:formId/admin', + templateUrl: 'modules/forms/admin/views/admin-form.client.view.html', + data: { + permissions: [ 'editForm' ] + }, + resolve: { + GetForms: 'GetForms', + myForm: ["GetForms", "$stateParams", "$q", function (GetForms, $stateParams, $q) { + var deferred = $q.defer(); + GetForms.get({formId: $stateParams.formId}, function(resolvedForm){ + deferred.resolve(resolvedForm); + }); + + return deferred.promise; + }], + formId: ["$stateParams", function ($stateParams) { + return $stateParams.formId; + }] + }, + controller: 'AdminFormController' + }).state('viewForm.configure', { + url: '/configure', + templateUrl: 'modules/forms/admin/views/adminTabs/configure.html' + }).state('viewForm.design', { + url: '/design', + templateUrl: 'modules/forms/admin/views/adminTabs/design.html' + }).state('viewForm.analyze', { + url: '/analyze', + templateUrl: 'modules/forms/admin/views/adminTabs/analyze.html' + }).state('viewForm.create', { + url: '/create', + templateUrl: 'modules/forms/admin/views/adminTabs/create.html' + }); + } +]); + +'use strict'; + +//Forms service used for communicating with the forms REST endpoints +angular.module('forms').factory('GetForms', ["$resource", "FORM_URL", function($resource, FORM_URL) { + return $resource(FORM_URL, { + formId: '@_id' + }, { + 'query' : { + method: 'GET', + url: '/forms', + isArray: true + }, + 'get' : { + method: 'GET', + transformResponse: function(data, header) { + var form = angular.fromJson(data); + + form.visible_form_fields = _.filter(form.form_fields, function(field){ + return (field.deletePreserved === false); + }); + return form; + } + }, + 'update': { + method: 'PUT' + }, + 'save': { + method: 'POST' + } + }); + } +]); + +'use strict'; + +// Config HTTP Error Handling +angular.module('users').config(["$httpProvider", function($httpProvider) { + $httpProvider.interceptors.push(["$q", "$location", function($q, $location) { + return { + responseError: function(response) { + if( $location.path() !== '/users/me' && response.config){ + if(response.config.url !== '/users/me'){ + if (response.status === 401) { + // save the current location so that login can redirect back + $location.nextAfterLogin = $location.path(); + $location.path('/signin'); + }else if(response.status === 403){ + $location.path('/access_denied'); + } + } + + } + return $q.reject(response); + } + }; + }]); +}]); + +'use strict'; + +// Setting up route +angular.module('users').config(["$stateProvider", function($stateProvider) { + + var checkLoggedin = function($q, $timeout, $state, User, Auth) { + var deferred = $q.defer(); + + if (Auth.currentUser && Auth.currentUser.email) { + $timeout(deferred.resolve); + } + else { + Auth.currentUser = User.getCurrent( + function() { + Auth.login(); + $timeout(deferred.resolve()); + }, + function() { + Auth.logout(); + $timeout(deferred.reject()); + $state.go('signin', {reload: true}); + }); + } + + return deferred.promise; + }; + checkLoggedin.$inject = ["$q", "$timeout", "$state", "User", "Auth"]; + + var checkSignupDisabled = function($window, $timeout, $q) { + var deferred = $q.defer(); + if($window.signupDisabled) { + $timeout(deferred.reject()); + } else { + $timeout(deferred.resolve()); + } + return deferred.promise; + }; + checkSignupDisabled.$inject = ["$window", "$timeout", "$q"]; + + // Users state routing + $stateProvider. + state('profile', { + resolve: { + loggedin: checkLoggedin + }, + url: '/settings/profile', + templateUrl: 'modules/users/views/settings/edit-profile.client.view.html' + }). + state('password', { + resolve: { + loggedin: checkLoggedin + }, + url: '/settings/password', + templateUrl: 'modules/users/views/settings/change-password.client.view.html' + }). + state('accounts', { + resolve: { + loggedin: checkLoggedin + }, + url: '/settings/accounts', + templateUrl: 'modules/users/views/settings/social-accounts.client.view.html' + }). + state('signup', { + resolve: { + isDisabled: checkSignupDisabled + }, + url: '/signup', + templateUrl: 'modules/users/views/authentication/signup.client.view.html' + }). + state('signup-success', { + resolve: { + isDisabled: checkSignupDisabled + }, + url: '/signup-success', + templateUrl: 'modules/users/views/authentication/signup-success.client.view.html' + }). + state('signin', { + url: '/signin', + templateUrl: 'modules/users/views/authentication/signin.client.view.html' + }). + state('access_denied', { + url: '/access_denied', + templateUrl: 'modules/users/views/authentication/access-denied.client.view.html' + }). + state('verify', { + resolve: { + isDisabled: checkSignupDisabled + }, + url: '/verify/:token', + templateUrl: 'modules/users/views/verify/verify-account.client.view.html' + }). + state('resendVerifyEmail', { + resolve: { + isDisabled: checkSignupDisabled + }, + url: '/verify', + templateUrl: 'modules/users/views/verify/resend-verify-email.client.view.html' + }). + state('forgot', { + url: '/password/forgot', + templateUrl: 'modules/users/views/password/forgot-password.client.view.html' + }). + state('reset-invalid', { + url: '/password/reset/invalid', + templateUrl: 'modules/users/views/password/reset-password-invalid.client.view.html' + }). + state('reset-success', { + url: '/password/reset/success', + templateUrl: 'modules/users/views/password/reset-password-success.client.view.html' + }). + state('reset', { + url: '/password/reset/:token', + templateUrl: 'modules/users/views/password/reset-password.client.view.html' + }); + } +]); + +'use strict'; + +angular.module('users').controller('AuthenticationController', ["$scope", "$location", "$state", "$rootScope", "User", "Auth", "$translate", "$window", function($scope, $location, $state, $rootScope, User, Auth, $translate, $window) { + + $scope = $rootScope; + $scope.credentials = {}; + $scope.error = ''; + $scope.forms = {}; + + var statesToIgnore = ['', 'home', 'signin', 'resendVerifyEmail', 'verify', 'signup', 'signup-success', 'forgot', 'reset-invalid', 'reset', 'reset-success']; + + $scope.signin = function() { + if($scope.forms && $scope.forms.hasOwnProperty('siginForm') && !$scope.forms.signinForm.$invalid){ + User.login($scope.credentials).then( + function(response) { + Auth.login(response); + $scope.user = $rootScope.user = Auth.ensureHasCurrentUser(User); + + if(statesToIgnore.indexOf($state.previous.state.name) === -1) { + $state.go($state.previous.state.name, $state.previous.params); + } else { + $state.go('listForms'); + } + }, + function(error) { + $rootScope.user = Auth.ensureHasCurrentUser(User); + $scope.user = $rootScope.user; + + $scope.error = error; + console.error('loginError: '+error); + } + ); + } + }; + + $scope.signup = function() { + if($scope.credentials === 'admin'){ + $scope.error = 'Username cannot be \'admin\'. Please pick another username.'; + return; + } + + if(!$scope.forms.signupForm.$invalid){ + User.signup($scope.credentials).then( + function(response) { + $state.go('signup-success'); + }, + function(error) { + console.error(error); + if(error) { + $scope.error = error; + console.error(error); + } else { + console.error('No response received'); + } + } + ); + } + }; + + } +]); + +'use strict'; + +angular.module('users').controller('PasswordController', ["$scope", "$stateParams", "$state", "User", "$translate", "$window", function($scope, $stateParams, $state, User, $translate, $window) { + $translate.use($window.locale); + + $scope.error = ''; + $scope.forms = {}; + + // Submit forgotten password account id + $scope.askForPasswordReset = function() { + User.askForPasswordReset($scope.credentials).then( + function(response){ + $scope.success = response.message; + $scope.error = null; + $scope.credentials = null; + }, + function(error){ + $scope.error = error; + $scope.success = null; + $scope.credentials = null; + } + ); + }; + + // Change user password + $scope.resetUserPassword = function() { + if(!$scope.forms.resetPasswordForm.$invalid){ + $scope.success = $scope.error = null; + User.resetPassword($scope.passwordDetails, $stateParams.token).then( + function(response){ + // If successful show success message and clear form + $scope.success = response.message; + $scope.error = null; + $scope.passwordDetails = null; + + // And redirect to the index page + $state.go('reset-success'); + }, + function(error){ + $scope.error = error.message || error; + $scope.success = null; + $scope.passwordDetails = null; + } + ); + } + }; + } +]); + +'use strict'; + +angular.module('users').controller('SettingsController', ["$scope", "$rootScope", "$http", "$state", "Users", "Auth", function($scope, $rootScope, $http, $state, Users, Auth) { + + $scope.user = Auth.currentUser; + + // Check if there are additional accounts + $scope.hasConnectedAdditionalSocialAccounts = function(provider) { + for (var i in $scope.user.additionalProvidersData) { + return true; + } + return false; + }; + + $scope.cancel = function(){ + $scope.user = Auth.currentUser; + }; + + // Check if provider is already in use with current user + $scope.isConnectedSocialAccount = function(provider) { + return $scope.user.provider === provider || ($scope.user.additionalProvidersData && $scope.user.additionalProvidersData[provider]); + }; + + // Remove a user social account + $scope.removeUserSocialAccount = function(provider) { + $scope.success = $scope.error = null; + + $http.delete('/users/accounts', { + params: { + provider: provider + } + }).success(function(response) { + // If successful show success message and clear form + $scope.success = true; + $scope.error = null; + $scope.user = response; + }).error(function(response) { + $scope.success = null; + $scope.error = response.message; + }); + }; + + // Update a user profile + $scope.updateUserProfile = function(isValid) { + if (isValid) { + $scope.success = $scope.error = null; + var user = new Users($scope.user); + + user.$update(function(response) { + $scope.success = true; + $scope.error = null; + $scope.user = response; + }, function(response) { + $scope.success = null; + $scope.error = response.data.message; + }); + } else { + $scope.submitted = true; + } + }; + + // Change user password + $scope.changeUserPassword = function() { + $scope.success = $scope.error = null; + + $http.post('/users/password', $scope.passwordDetails).success(function(response) { + // If successful show success message and clear form + $scope.success = true; + $scope.error = null; + $scope.passwordDetails = null; + }).error(function(response) { + $scope.success = null; + $scope.error = response.message; + }); + }; + + } +]); + +'use strict'; + +angular.module('users').controller('VerifyController', ["$scope", "$state", "$rootScope", "User", "Auth", "$stateParams", "$translate", "$window", function($scope, $state, $rootScope, User, Auth, $stateParams, $translate, $window) { + $translate.use($window.locale); + + $scope.isResetSent = false; + $scope.credentials = {}; + $scope.error = ''; + + // Submit forgotten password account id + $scope.resendVerifyEmail = function() { + User.resendVerifyEmail($scope.credentials.email).then( + function(response){ + $scope.success = response.message; + $scope.error = null; + $scope.credentials = null; + $scope.isResetSent = true; + }, + function(error){ + $scope.error = error; + $scope.success = null; + $scope.credentials.email = null; + $scope.isResetSent = false; + } + ); + }; + + //Validate Verification Token + $scope.validateVerifyToken = function() { + if($stateParams.token){ + console.log($stateParams.token); + User.validateVerifyToken($stateParams.token).then( + function(response){ + $scope.success = response.message; + $scope.error = null; + $scope.isResetSent = true; + $scope.credentials.email = null; + }, + function(error){ + $scope.isResetSent = false; + $scope.success = null; + $scope.error = error; + $scope.credentials.email = null; + } + ); + } + }; + } +]); +'use strict'; + +angular.module('users').factory('Auth', ["$window", function($window) { + + var userState = { + isLoggedIn: false + }; + + var service = { + _currentUser: null, + get currentUser(){ + return this._currentUser; + }, + + // Note: we can't make the User a dependency of Auth + // because that would create a circular dependency + // Auth <- $http <- $resource <- LoopBackResource <- User <- Auth + ensureHasCurrentUser: function(User) { + if (service._currentUser && service._currentUser.username) { + return service._currentUser; + } else if ($window.user){ + service._currentUser = $window.user; + return service._currentUser; + } else { + User.getCurrent().then(function(user) { + // success + service._currentUser = user; + userState.isLoggedIn = true; + $window.user = service._currentUser; + return service._currentUser; + }, + function(response) { + userState.isLoggedIn = false; + service._currentUser = null; + $window.user = null; + return null; + }); + } + }, + + isAuthenticated: function() { + return !!service._currentUser; + }, + + getUserState: function() { + return userState; + }, + + login: function(new_user) { + userState.isLoggedIn = true; + service._currentUser = new_user; + }, + + logout: function() { + $window.user = null; + userState.isLoggedIn = false; + service._currentUser = null; + } + }; + return service; + + } +]); + +'use strict'; + +angular.module('users').service('Authorizer', ["APP_PERMISSIONS", "USER_ROLES", function(APP_PERMISSIONS, USER_ROLES) { + return function(user) { + return { + canAccess: function(permissions) { + var i, len, permission; + if (!angular.isArray(permissions)) { + permissions = [permissions]; + } + for (i = 0, len = permissions.length; i < len; i++) { + permission = permissions[i]; + if (APP_PERMISSIONS[permission] === null) { + throw 'Bad permission value'; + } + if (user && user.roles) { + switch (permission) { + case APP_PERMISSIONS.viewAdminSettings: + case APP_PERMISSIONS.editAdminSettings: + return user.roles.indexOf(USER_ROLES.admin) > -1; + case APP_PERMISSIONS.viewPrivateForm: + case APP_PERMISSIONS.editForm: + return user.roles.indexOf(USER_ROLES.admin) > -1 || user.roles.indexOf(USER_ROLES.normal) > -1; + } + } else { + return false; + } + } + + return false; + } + }; + }; +}]); +'use strict'; + +angular.module('users').factory('User', ["$window", "$q", "$timeout", "$http", "$state", function($window, $q, $timeout, $http, $state) { + + var userService = { + getCurrent: function() { + var deferred = $q.defer(); + + $http.get('/users/me') + .success(function(response) { + deferred.resolve(response); + }) + .error(function() { + deferred.reject('User\'s session has expired'); + }); + + return deferred.promise; + }, + login: function(credentials) { + + var deferred = $q.defer(); + $http.post('/auth/signin', credentials).then(function(response) { + deferred.resolve(response.data); + }, function(error) { + deferred.reject(error.data.message || error.data); + }); + + return deferred.promise; + }, + logout: function() { + + var deferred = $q.defer(); + $http.get('/auth/signout').then(function(response) { + deferred.resolve(null); + }, function(error) { + deferred.reject(error.data.message || error.data); + }); + + return deferred.promise; + }, + signup: function(credentials) { + + var deferred = $q.defer(); + $http.post('/auth/signup', credentials).then(function(response) { + // If successful we assign the response to the global user model + deferred.resolve(response.data); + }, function(error) { + deferred.reject(error.data.message || error.data); + }); + + return deferred.promise; + }, + + resendVerifyEmail: function(_email) { + + var deferred = $q.defer(); + $http.post('/auth/verify', {email: _email}).then(function(response) { + deferred.resolve(response.data); + }, function(error) { + deferred.reject(error.data.message || error.data); + }); + + return deferred.promise; + }, + + validateVerifyToken: function(token) { + + //DAVID: TODO: The valid length of a token should somehow be linked to server config values + //DAVID: TODO: SEMI-URGENT: Should we even be doing this? + var validTokenRe = /^([A-Za-z0-9]{48})$/g; + if( !validTokenRe.test(token) ) throw new Error('Error token: '+token+' is not a valid verification token'); + + var deferred = $q.defer(); + $http.get('/auth/verify/'+token).then(function(response) { + deferred.resolve(response.data); + }, function(error) { + deferred.reject(error.data); + }); + + return deferred.promise; + }, + + resetPassword: function(passwordDetails, token) { + + var deferred = $q.defer(); + $http.post('/auth/reset/'+token, passwordDetails).then(function(response) { + deferred.resolve(response); + }, function(error) { + deferred.reject(error.data.message || error.data); + }); + + return deferred.promise; + }, + + // Submit forgotten password account id + askForPasswordReset: function(credentials) { + + var deferred = $q.defer(); + $http.post('/auth/forgot', credentials).then(function(response) { + // Show user success message and clear form + deferred.resolve(response.data); + }, function(error) { + // Show user error message + deferred.reject(error.data.message || error.data); + }); + + return deferred.promise; + } + + }; + + return userService; + + } +]); + +'use strict'; + +// Users service used for communicating with the users REST endpoint +angular.module('users').factory('Users', ["$resource", function($resource) { + return $resource('users', {}, { + update: { + method: 'PUT' + } + }); + } +]); +'use strict'; + +angular.module('core').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('en', { + MENU: 'MENU', + SIGNUP_TAB: 'Sign Up', + SIGNIN_TAB: 'Sign In', + SIGNOUT_TAB: 'Signout', + EDIT_PROFILE: 'Edit Profile', + MY_SETTINGS: 'My Settings', + CHANGE_PASSWORD: 'Change Password', + TOGGLE_NAVIGATION: 'Toggle navigation' + }); + + $translateProvider.preferredLanguage('en') + .fallbackLanguage('en') + .useSanitizeValueStrategy('escape'); + +}]); + +'use strict'; + +angular.module('core').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('fr', { + MENU: 'MENU', + SIGNUP_TAB: 'Créer un Compte', + SIGNIN_TAB: 'Connexion', + SIGNOUT_TAB: 'Créer un compte', + EDIT_PROFILE: 'Modifier Mon Profil', + MY_SETTINGS: 'Mes Paramètres', + CHANGE_PASSWORD: 'Changer mon Mot de Pass', + TOGGLE_NAVIGATION: 'Basculer la navigation', + }); +}]); + +'use strict'; + +angular.module('core').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('de', { + MENU: 'MENÜ', + SIGNUP_TAB: 'Anmelden', + SIGNIN_TAB: 'Anmeldung', + SIGNOUT_TAB: 'Abmelden', + EDIT_PROFILE: 'Profil bearbeiten', + MY_SETTINGS: 'Meine Einstellungen', + CHANGE_PASSWORD: 'Passwort ändern', + TOGGLE_NAVIGATION: 'Navigation umschalten' + }); +}]); + +'use strict'; + +angular.module('core').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('it', { + MENU: 'MENÜ', + SIGNUP_TAB: 'Vi Phrasal', + SIGNIN_TAB: 'Accedi', + SIGNOUT_TAB: 'Esci', + EDIT_PROFILE: 'Modifica Profilo', + MY_SETTINGS: 'Mie Impostazioni', + CHANGE_PASSWORD: 'Cambia la password', + TOGGLE_NAVIGATION: 'Attiva la navigazione' + }); +}]); + +'use strict'; + +angular.module('core').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('es', { + MENU: 'MENU', + SIGNUP_TAB: 'Registrarse', + SIGNIN_TAB: 'Entrar', + SIGNOUT_TAB: 'Salir', + EDIT_PROFILE: 'Editar Perfil', + MY_SETTINGS: 'Mis configuraciones', + CHANGE_PASSWORD: 'Cambiar contraseña', + TOGGLE_NAVIGATION: 'Navegación de palanca' + }); + +}]); + +'use strict'; + +// Forms controller +angular.module('forms').controller('AdminFormController', ["$rootScope", "$window", "$scope", "$stateParams", "$state", "Forms", "CurrentForm", "$http", "$uibModal", "myForm", "$filter", "$translate", function($rootScope, $window, $scope, $stateParams, $state, Forms, CurrentForm, $http, $uibModal, myForm, $filter, $translate) { + + //Set active tab to Create + $scope.activePill = 0; + + $scope.copied = false; + $scope.onCopySuccess = function (e) { + $scope.copied = true; + }; + + $scope = $rootScope; + $scope.animationsEnabled = true; + $scope.myform = myForm; + $rootScope.saveInProgress = false; + $scope.oldForm = _.cloneDeep($scope.myform); + + CurrentForm.setForm($scope.myform); + + $scope.formURL = '/#!/forms/' + $scope.myform._id; + + if ($scope.myform.isLive) { + if ($window.subdomainsDisabled === true) { + $scope.actualFormURL = window.location.protocol + '//' + window.location.host + '/view' + $scope.formURL; + } else { + if (window.location.host.split('.').length < 3) { + $scope.actualFormURL = window.location.protocol + '//' + $scope.myform.admin.username + '.' + window.location.host + $scope.formURL; + } else { + $scope.actualFormURL = window.location.protocol + '//' + $scope.myform.admin.username + '.' + window.location.host.split('.').slice(1, 3).join('.') + $scope.formURL; + } + } + } else { + $scope.actualFormURL = window.location.protocol + '//' + window.location.host + $scope.formURL; + } + + + var refreshFrame = $scope.refreshFrame = function(){ + if(document.getElementById('iframe')) { + document.getElementById('iframe').contentWindow.location.reload(); + } + }; + + $scope.tabData = [ + { + heading: $filter('translate')('CONFIGURE_TAB'), + templateName: 'configure' + } + ]; + + $scope.designTabActive = false + + $scope.deactivateDesignTab = function(){ + $scope.designTabActive = false + } + + $scope.activateDesignTab = function(){ + $scope.designTabActive = true + } + + $scope.setForm = function(form){ + $scope.myform = form; + }; + + $rootScope.resetForm = function(){ + $scope.myform = Forms.get({ + formId: $stateParams.formId + }); + }; + + /* + ** DeleteModal Functions + */ + $scope.openDeleteModal = function(){ + $scope.deleteModal = $uibModal.open({ + animation: $scope.animationsEnabled, + templateUrl: 'formDeleteModal.html', + controller: 'AdminFormController', + resolve: { + myForm: function(){ + return $scope.myform; + } + } + }); + $scope.deleteModal.result.then(function (selectedItem) { + $scope.selected = selectedItem; + }); + }; + + $scope.cancelDeleteModal = function(){ + if($scope.deleteModal){ + $scope.deleteModal.dismiss('cancel'); + } + }; + + // Remove existing Form + $scope.removeCurrentForm = function() { + if($scope.deleteModal && $scope.deleteModal.opened){ + + $scope.deleteModal.close(); + + var form_id = $scope.myform._id; + if(!form_id) throw new Error('Error - removeCurrentForm(): $scope.myform._id does not exist'); + + $http.delete('/forms/'+form_id) + .then(function(response){ + $state.go('listForms', {}, {reload: true}) + }, function(error){ + console.error(error); + }); + } + }; + + $scope.updateDesign = function(updateImmediately, data, shouldDiff, refreshAfterUpdate){ + $scope.update(updateImmediately, data, shouldDiff, refreshAfterUpdate, function(){ + refreshFrame(); + }); + } + + // Update existing Form + $scope.update = $rootScope.update = function(updateImmediately, data, shouldDiff, refreshAfterUpdate, cb){ + var continueUpdate = true; + if(!updateImmediately){ + continueUpdate = !$rootScope.saveInProgress; + } + + //Update form **if we are not in the middle of an update** or if **shouldUpdateNow flag is set** + if(continueUpdate) { + var err = null; + + if (!updateImmediately) { + $rootScope.saveInProgress = true; + } + + if (shouldDiff) { + //Do this so we can create duplicate fields + var checkForValidId = new RegExp('^[0-9a-fA-F]{24}$'); + for(var i=0; i < $scope.myform.form_fields.length; i++){ + var field = $scope.myform.form_fields[i]; + if(!checkForValidId.exec(field._id+'')){ + delete $scope.myform.form_fields[i]._id; + delete $scope.myform.form_fields[i].id; + } + } + + var data = DeepDiff.diff($scope.oldForm, $scope.myform); + + $scope.updatePromise = $http.put('/forms/' + $scope.myform._id, {changes: data}) + .then(function (response) { + if (refreshAfterUpdate) { + $rootScope.myform = $scope.myform = response.data; + $scope.oldForm = _.cloneDeep($scope.myform); + } + }).catch(function (response) { + err = response.data; + console.error(err); + }).finally(function () { + if (!updateImmediately) { + $rootScope.saveInProgress = false; + } + + if ((typeof cb) === 'function') { + return cb(err); + } + }); + } else { + var dataToSend = data; + if(dataToSend.analytics && dataToSend.analytics.visitors){ + delete dataToSend.analytics.visitors; + } + if(dataToSend.submissions){ + delete dataToSend.submissions; + } + + if(dataToSend.visible_form_fields){ + delete dataToSend.visible_form_fields; + } + + if(dataToSend.analytics){ + delete dataToSend.analytics.visitors; + delete dataToSend.analytics.fields; + delete dataToSend.analytics.submissions; + delete dataToSend.analytics.views; + delete dataToSend.analytics.conversionRate; + } + + delete dataToSend.created; + delete dataToSend.lastModified; + delete dataToSend.__v; + + $scope.updatePromise = $http.put('/forms/' + $scope.myform._id, {form: dataToSend}) + .then(function (response) { + if (refreshAfterUpdate) { + $rootScope.myform = $scope.myform = response.data; + } + + }).catch(function (response) { + err = response.data; + console.error(err); + }).finally(function () { + if (!updateImmediately) { + $rootScope.saveInProgress = false; + } + + if ((typeof cb) === 'function') { + return cb(err); + } + }); + } + } + }; + + + } +]); +'use strict'; + +// Forms controller +angular.module('forms').controller('ListFormsController', ["$rootScope", "$scope", "$stateParams", "$state", "GetForms", "CurrentForm", "$http", "$uibModal", "myForms", function($rootScope, $scope, $stateParams, $state, GetForms, CurrentForm, $http, $uibModal, myForms) { + + $scope = $rootScope; + $scope.forms = {}; + $scope.showCreateModal = false; + $scope.myforms = myForms + + $rootScope.languageRegExp = { + regExp: /[@!#$%^&*()\-+={}\[\]|\\/'";:`.,~№?<>]+/i, + test: function(val) { + return !this.regExp.test(val); + } + }; + + /* + ** DeleteModal Functions + */ + $scope.openDeleteModal = function(index){ + $scope.deleteModal = $uibModal.open({ + animation: $scope.animationsEnabled, + templateUrl: 'deleteModalListForms.html', + controller: ["$uibModalInstance", "items", "$scope", function($uibModalInstance, items, $scope) { + $scope.content = items; + + $scope.cancel = $scope.cancelDeleteModal; + + $scope.deleteForm = function() { + $scope.$parent.removeForm(items.formIndex); + }; + }], + resolve: { + items: function() { + return { + currFormTitle: $scope.myforms[index].title, + formIndex: index + }; + } + } + }); + }; + + + $scope.cancelDeleteModal = function(){ + if($scope.deleteModal){ + $scope.deleteModal.dismiss('cancel'); + } + }; + + //Modal functions + $scope.openCreateModal = function(){ + if(!$scope.showCreateModal){ + $scope.showCreateModal = true; + } + }; + $scope.closeCreateModal = function(){ + if($scope.showCreateModal){ + $scope.showCreateModal = false; + } + }; + + $scope.setForm = function (form) { + $scope.myform = form; + }; + $scope.goToWithId = function(route, id) { + $state.go(route, {'formId': id}, {reload: true}); + }; + + $scope.duplicateForm = function(form_index){ + var form = _.cloneDeep($scope.myforms[form_index]); + delete form._id; + + $http.post('/forms', {form: form}) + .success(function(data, status, headers){ + $scope.myforms.splice(form_index+1, 0, data); + }).error(function(errorResponse){ + console.error(errorResponse); + if(errorResponse === null){ + $scope.error = errorResponse.data.message; + } + }); + }; + + // Create new Form + $scope.createNewForm = function(){ + + var form = {}; + form.title = $scope.forms.createForm.title.$modelValue; + form.language = $scope.forms.createForm.language.$modelValue; + + if($scope.forms.createForm.$valid && $scope.forms.createForm.$dirty){ + $http.post('/forms', {form: form}) + .success(function(data, status, headers){ + // Redirect after save + $scope.goToWithId('viewForm.create', data._id+''); + }).error(function(errorResponse){ + console.error(errorResponse); + $scope.error = errorResponse.data.message; + }); + } + }; + + $scope.removeForm = function(form_index) { + if(form_index >= $scope.myforms.length || form_index < 0){ + throw new Error('Error: form_index in removeForm() must be between 0 and '+$scope.myforms.length-1); + } + + $http.delete('/forms/'+$scope.myforms[form_index]._id) + .success(function(data, status, headers){ + $scope.myforms.splice(form_index, 1); + $scope.cancelDeleteModal(); + }).error(function(error){ + console.error(error); + }); + }; + } +]); + +'use strict'; + +angular.module('forms').directive('configureFormDirective', ["$rootScope", "$http", "Upload", "CurrentForm", function ($rootScope, $http, Upload, CurrentForm) { + return { + templateUrl: 'modules/forms/admin/views/directiveViews/form/configure-form.client.view.html', + restrict: 'E', + scope: { + myform:'=', + user:'=', + pdfFields:'@', + formFields:'@' + }, + controller: ["$scope", function($scope){ + $scope.log = ''; + $scope.languages = $rootScope.languages; + + $scope.resetForm = $rootScope.resetForm; + $scope.update = $rootScope.update; + + }] + }; + } +]); + + +'use strict'; + +angular.module('forms').directive('editFormDirective', ["$rootScope", "FormFields", "$uibModal", function ($rootScope, FormFields, $uibModal) { + return { + templateUrl: 'modules/forms/admin/views/directiveViews/form/edit-form.client.view.html', + restrict: 'E', + transclude: true, + scope: { + myform:'=' + }, + controller: ["$scope", function($scope){ + + /* + ** Initialize scope with variables + */ + var newField; + + //Setup UI-Sortable + $scope.sortableOptions = { + appendTo: '.dropzone', + //helper: 'clone', + forceHelperSize: true, + forcePlaceholderSize: true, + update: function(e, ui) { + $scope.update(false, $scope.myform, true, false, function(err){ + }); + }, + }; + + /* + ** EditModal Functions + */ + $scope.openEditModal = function(curr_field, isEdit, field_index){ + $scope.editFieldModal = $uibModal.open({ + animation: true, + templateUrl: 'editFieldModal.html', + windowClass: 'edit-modal-window', + controller: ["$uibModalInstance", "$scope", function($uibModalInstance, $scope) { + $scope.field = curr_field; + $scope.showLogicJump = false; + + $scope.isEdit = isEdit; + + // decides whether field options block will be shown (true for dropdown and radio fields) + $scope.showAddOptions = function (field){ + if($scope.field.fieldType === 'dropdown' || $scope.field.fieldType === 'checkbox' || $scope.field.fieldType === 'radio'){ + return true; + } else { + return false; + } + }; + + $scope.validShapes = [ + 'Heart', + 'Star', + 'thumbs-up', + 'thumbs-down', + 'Circle', + 'Square', + 'Check Circle', + 'Smile Outlined', + 'Hourglass', + 'bell', + 'Paper Plane', + 'Comment', + 'Trash' + ]; + + // add new option to the field + $scope.addOption = function(){ + if($scope.field.fieldType === 'checkbox' || $scope.field.fieldType === 'dropdown' || $scope.field.fieldType === 'radio'){ + if(!$scope.field.fieldOptions){ + $scope.field.fieldOptions = []; + } + + var lastOptionID = $scope.field.fieldOptions.length+1; + + // new option's id + + var newOption = { + 'option_id' : Math.floor(100000*Math.random()), + 'option_title' : 'Option '+lastOptionID, + 'option_value' : 'Option ' +lastOptionID + }; + + // put new option into fieldOptions array + $scope.field.fieldOptions.push(newOption); + } + }; + + // delete particular option + $scope.deleteOption = function (option){ + if($scope.field.fieldType === 'checkbox' || $scope.field.fieldType === 'dropdown' || $scope.field.fieldType === 'radio'){ + for(var i = 0; i < $scope.field.fieldOptions.length; i++){ + if($scope.field.fieldOptions[i].option_id === option.option_id){ + + $scope.field.fieldOptions.splice(i, 1); + break; + } + } + } + }; + + //Populate Name to Font-awesomeName Conversion Map + $scope.select2FA = { + 'Heart': 'Heart', + 'Star': 'Star', + 'thumbs-up': 'Thumbs Up', + 'thumbs-down':'Thumbs Down', + 'Circle': 'Circle', + 'Square':'Square', + 'Check Circle': 'Checkmark', + 'Smile Outlined': 'Smile', + 'Hourglass': 'Hourglass', + 'bell': 'Bell', + 'Paper Plane': 'Paper Plane', + 'Comment': 'Chat Bubble', + 'Trash': 'Trash Can' + }; + + // decides whether field options block will be shown (true for dropdown and radio fields) + $scope.showRatingOptions = function (){ + if($scope.field.fieldType === 'rating'){ + return true; + } else { + return false; + } + }; + + $scope.saveField = function(){ + if($scope.isEdit){ + $scope.myform.form_fields[field_index] = $scope.field; + } else { + $scope.myform.form_fields.push(curr_field); + } + + $scope.$parent.update(false, $scope.$parent.myform, true, true, function(){ + $uibModalInstance.close(); + }); + + }; + $scope.cancel = function(){ + $uibModalInstance.close(); + }; + }] + }); + }; + + /* + ** EditStartPageModal Functions + */ + $scope.openEditStartPageModal = function(){ + $scope.editStartPageModal = $uibModal.open({ + animation: true, + templateUrl: 'editStartPageModal.html', + windowClass: 'edit-modal-window', + controller: ["$uibModalInstance", "$scope", function($uibModalInstance, $scope) { + + /* + ** startPage Button Methods + */ + + $scope.showButtons = false; + $scope.lastButtonID = 0; + + // add new Button to the startPage + $scope.addButton = function(){ + + var newButton = {}; + newButton.bgColor = '#ddd'; + newButton.color = '#ffffff'; + newButton.text = 'Button'; + newButton._id = Math.floor(100000*Math.random()); + + $scope.myform.startPage.buttons.push(newButton); + }; + + // delete particular Button from startPage + $scope.deleteButton = function(button){ + var currID; + for(var i = 0; i < $scope.myform.startPage.buttons.length; i++){ + + currID = $scope.myform.startPage.buttons[i]._id; + + if(currID === button._id){ + $scope.myform.startPage.buttons.splice(i, 1); + break; + } + } + }; + + $scope.saveStartPage = function(){ + $scope.$parent.update(false, $scope.$parent.myform, true, true, function(){ + $uibModalInstance.close(); + }); + }; + $scope.cancel = function(){ + $uibModalInstance.close(); + }; + }] + }); + }; + + /* + ** EditEndPageModal Functions + */ + $scope.openEditEndPageModal = function(){ + $scope.editEndPageModal = $uibModal.open({ + animation: true, + templateUrl: 'editEndPageModal.html', + windowClass: 'edit-modal-window', + controller: ["$uibModalInstance", "$scope", function($uibModalInstance, $scope) { + + /* + ** startPage Button Methods + */ + + $scope.showButtons = false; + $scope.lastButtonID = 0; + + // add new Button to the startPage + $scope.addButton = function(){ + + var newButton = {}; + newButton.bgColor = '#ddd'; + newButton.color = '#ffffff'; + newButton.text = 'Button'; + newButton._id = Math.floor(100000*Math.random()); + + $scope.myform.endPage.buttons.push(newButton); + }; + + // delete particular Button from startPage + $scope.deleteButton = function(button){ + var currID; + for(var i = 0; i < $scope.myform.endPage.buttons.length; i++){ + + currID = $scope.myform.endPage.buttons[i]._id; + + if(currID === button._id){ + $scope.myform.endPage.buttons.splice(i, 1); + break; + } + } + }; + + $scope.saveEndPage = function(){ + $scope.$parent.update(false, $scope.$parent.myform, true, true, function(){ + $uibModalInstance.close(); + }); + }; + $scope.cancel = function(){ + $uibModalInstance.close(); + }; + }] + }); + }; + + + //Populate local scope with rootScope methods/variables + $scope.update = $rootScope.update; + + /* + ** FormFields (ui-sortable) drag-and-drop configuration + */ + $scope.dropzone = { + handle: '.handle', + containment: '.dropzoneContainer', + cursor: 'grabbing' + }; + + /* + ** Field CRUD Methods + */ + // Add a new field + $scope.addNewField = function(fieldType){ + // increment lastAddedID counter + $scope.addField.lastAddedID++; + var fieldTitle = fieldType; + + for(var i = 0; i < $scope.addField.types.length; i++){ + if($scope.addField.types[i].name === fieldType){ + $scope.addField.types[i].lastAddedID++; + fieldTitle = $scope.addField.types[i].value+$scope.addField.types[i].lastAddedID; + break; + } + } + newField = { + title: fieldTitle, + fieldType: fieldType, + fieldValue: '', + required: true, + disabled: false, + deletePreserved: false, + logicJump: {} + }; + + if(fieldType === 'rating'){ + newField.ratingOptions = { + steps: 5, + shape: 'Heart' + }; + newField.fieldValue = 0; + } + + if($scope.showAddOptions(newField)){ + newField.fieldOptions = []; + newField.fieldOptions.push({ + 'option_id' : Math.floor(100000*Math.random()), //Generate pseudo-random option id + 'option_title' : 'Option 0', + 'option_value' : 'Option 0' + }); + } + + $scope.openEditModal(newField, false, $scope.myform.form_fields.length); + }; + + // decides whether field options block will be shown (true for dropdown and radio fields) + $scope.showAddOptions = function (field){ + if(field.fieldType === 'dropdown' || field.fieldType === 'checkbox' || field.fieldType === 'radio'){ + return true; + } else { + return false; + } + }; + + // decides whether field options block will be shown (true for dropdown and radio fields) + $scope.showRatingOptions = function (field){ + if(field.fieldType === 'rating'){ + return true; + } else { + return false; + } + }; + + // Delete particular field on button click + $scope.deleteField = function (field_index) { + $scope.myform.form_fields.splice(field_index, 1); + $scope.update(false, $scope.myform, false, true, null); + }; + + $scope.duplicateField = function(field_index){ + var currField = angular.copy($scope.myform.form_fields[field_index]); + currField._id = 'cloned'+_.uniqueId(); + currField.title += ' copy'; + + //Insert field at selected index + $scope.myform.form_fields.push(currField); + $scope.update(false, $scope.myform, false, true, null); + }; + + //Populate AddField with all available form field types + $scope.addField = {}; + $scope.addField.types = FormFields.types; + + $scope.addField.types.forEach(function(type){ + type.lastAddedID = 1; + return type; + }); + + }] + }; + } +]); + +'use strict'; + +angular.module('forms').directive('editSubmissionsFormDirective', ["$rootScope", "$http", "Forms", "$stateParams", "$interval", function ($rootScope, $http, Forms, $stateParams, $interval) { + return { + templateUrl: 'modules/forms/admin/views/directiveViews/form/edit-submissions-form.client.view.html', + restrict: 'E', + scope: { + user:'=', + myform: '=' + }, + controller: ["$scope", function($scope){ + $scope.table = { + masterChecker: false, + rows: [] + }; + $scope.analyticsData = { + deviceStatistics: [], + globalStatistics: [] + }; + + $scope.deletionInProgress = false; + $scope.waitingForDeletion = false; + + //Waits until deletionInProgress is false before running getSubmissions + $scope.$watch("deletionInProgress",function(newVal, oldVal){ + if(newVal === oldVal) return; + + if(newVal === false && $scope.waitingForDeletion) { + $scope.getSubmissions(); + $scope.waitingForDeletion = false; + } + }); + + $scope.handleSubmissionsRefresh = function(){ + if(!$scope.deletionInProgress) { + $scope.getSubmissions(); + } else { + $scope.waitingForDeletion = true; + } + }; + + $scope.getSubmissions = function(cb){ + $http({ + method: 'GET', + url: '/forms/'+$scope.myform._id+'/submissions' + }).then(function successCallback(response) { + var defaultFormFields = _.cloneDeep($scope.myform.form_fields); + + var submissions = response.data || []; + + //Iterate through form's submissions + for(var i = 0; i < submissions.length; i++){ + for(var x = 0; x < submissions[i].form_fields.length; x++){ + if(submissions[i].form_fields[x].fieldType === 'dropdown'){ + submissions[i].form_fields[x].fieldValue = submissions[i].form_fields[x].fieldValue.option_value; + } + } + submissions[i].selected = false; + } + + $scope.table.rows = submissions; + + if(cb && typeof cb === 'function'){ + cb(); + } + }, function errorCallback(err){ + console.error(err); + if(cb && typeof cb === 'function'){ + cb(err); + } + }); + }; + + $scope.getVisitors = function(){ + $http({ + method: 'GET', + url: '/forms/'+$scope.myform._id+'/visitors' + }).then(function successCallback(response) { + var data = response.data || []; + + $scope.analyticsData = data[0]; + $scope.analyticsData.globalStatistics = $scope.analyticsData.globalStatistics[0]; + $scope.analyticsData.deviceStatistics = $scope.analyticsData.deviceStatistics[0]; + }); + }; + + $scope.handleSubmissionsRefresh(); + $scope.getVisitors(); + + //Fetch submissions and visitor data every 1.67 min + var updateSubmissions = $interval($scope.handleSubmissionsRefresh, 100000); + var updateVisitors = $interval($scope.getVisitors, 1000000); + + //Prevent $intervals from running after directive is destroyed + $scope.$on('$destroy', function() { + if (updateSubmissions) { + $interval.cancel($scope.updateSubmissions); + } + + if (updateVisitors) { + $interval.cancel($scope.updateVisitors); + } + }); + + /* + ** Analytics Functions + */ + $scope.DeviceStatistics = (function(){ + var newStatItem = function(){ + return { + visits: 0, + responses: 0, + conversion_rate: 0, + average_time: 0, + total_time: 0 + }; + }; + + var stats = { + desktop: newStatItem(), + tablet: newStatItem(), + phone: newStatItem(), + other: newStatItem() + }; + + if($scope.analyticsData.deviceStatistics.length){ + for(var i=0; i<$scope.analyticsData.deviceStatistics.length; i++){ + var currDevice = $scope.analyticsData.deviceStatistics[i]; + if(stats[currDevice._id]){ + stats[currDevice._id] = currDevice; + } + } + } + return stats; + })(); + + /* + ** Table Functions + */ + $scope.isAtLeastOneChecked = function(){ + for(var i=0; i<$scope.table.rows.length; i++){ + if($scope.table.rows[i].selected) return true; + } + return false; + }; + $scope.toggleAllCheckers = function(){ + for(var i=0; i<$scope.table.rows.length; i++){ + $scope.table.rows[i].selected = $scope.table.masterChecker; + } + }; + $scope.toggleObjSelection = function($event) { + $event.stopPropagation(); + }; + $scope.rowClicked = function(row_index) { + $scope.table.rows[row_index].selected = !$scope.table.rows[row_index].selected; + }; + + /* + * Form Submission Methods + */ + + //Delete selected submissions of Form + $scope.deleteSelectedSubmissions = function(){ + + $scope.deletionInProgress = true; + var delete_ids = _.chain($scope.table.rows).filter(function(row){ + return !!row.selected; + }).pluck('_id').value(); + + return $http({ url: '/forms/'+$scope.myform._id+'/submissions', + method: 'DELETE', + data: {deleted_submissions: delete_ids}, + headers: {'Content-Type': 'application/json;charset=utf-8'} + }).success(function(data, status){ + $scope.deletionInProgress = true; + //Remove deleted ids from table + $scope.table.rows = $scope.table.rows.filter(function(field){ + return !field.selected; + }); + }) + .error(function(err){ + $scope.deletionInProgress = true; + console.error(err); + }); + }; + + //Export selected submissions of Form + $scope.exportSubmissions = function(type){ + angular.element('#table-submission-data').tableExport({type: type, escape:false, ignoreColumn: [0]}); + }; + + }] + }; + } +]); + + +'use strict'; + +//TODO: DAVID: URGENT: Make this a $resource that fetches valid field types from server +angular.module('forms').service('FormFields', ["$rootScope", "$translate", "Auth", function($rootScope, $translate, Auth) { + + var language = Auth.ensureHasCurrentUser().language; + $translate.use(language); + + this.types = [ + { + name : 'textfield', + value : $translate.instant('SHORT_TEXT'), + }, + { + name : 'email', + value : $translate.instant('EMAIL'), + }, + { + name : 'radio', + value : $translate.instant('MULTIPLE_CHOICE'), + }, + { + name : 'dropdown', + value : $translate.instant('DROPDOWN'), + }, + { + name : 'date', + value : $translate.instant('DATE'), + }, + { + name : 'textarea', + value : $translate.instant('PARAGRAPH_FIELD'), + }, + { + name : 'yes_no', + value : $translate.instant('YES_NO'), + }, + { + name : 'legal', + value : $translate.instant('LEGAL'), + }, + // { + // name : 'sig', + // value : $translate.instant('SIGNATURE'), + // }, + // { + // name : 'file', + // value : $translate.instant('FILE_UPLOAD'), + // }, + { + name : 'rating', + value : $translate.instant('RATING'), + }, + { + name : 'link', + value : $translate.instant('LINK'), + }, + { + name : 'number', + value : $translate.instant('NUMBERS'), + }, + // { + // name : 'scale', + // value : $translate.instant('OPINION SCALE'), + // }, + // { + // name : 'stripe', + // value : $translate.instant('PAYMENT'), + // }, + { + name : 'statement', + value : $translate.instant('STATEMENT') + } + ]; + } + +]); + +'use strict'; + +//Submissions service used for communicating with the forms REST endpoints +angular.module('forms').factory('Submissions', ["$resource", function($resource) { + return $resource('forms/:formID/submissions/:submissionId', { + submissionId: '@_id', + formId: '@_id' + }, { + 'query' : { + method: 'GET', + isArray: true, + }, + 'update': { + method: 'PUT' + }, + 'save': { + method: 'POST' + } + }); + } +]); + +'use strict'; + +angular.module('users').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('en', { + ACCESS_DENIED_TEXT: 'You need to be logged in to access this page', + USERNAME_OR_EMAIL_LABEL: 'Username or Email', + USERNAME_LABEL: 'Username', + PASSWORD_LABEL: 'Password', + CURRENT_PASSWORD_LABEL: 'Current Password', + NEW_PASSWORD_LABEL: 'New Password', + VERIFY_PASSWORD_LABEL: 'Verify Password', + UPDATE_PASSWORD_LABEL: 'Update Password', + FIRST_NAME_LABEL: 'First Name', + LAST_NAME_LABEL: 'Last Name', + LANGUAGE_LABEL: 'Language', + EMAIL_LABEL: 'Email', + + SIGNUP_ACCOUNT_LINK: 'Don\'t have an account? Sign up here', + SIGN_IN_ACCOUNT_LINK: 'Already have an account? Sign in here', + SIGNUP_HEADER_TEXT: 'Sign up', + SIGNIN_HEADER_TEXT: 'Sign in', + + SIGNUP_ERROR_TEXT: 'Couldn\'t complete registration due to errors', + ENTER_ACCOUNT_EMAIL: 'Enter your account email.', + RESEND_VERIFICATION_EMAIL: 'Resend Verification Email', + SAVE_CHANGES: 'Save Changes', + CANCEL_BTN: 'Cancel', + + EDIT_PROFILE: 'Edit your profile', + UPDATE_PROFILE_BTN: 'Update Profile', + PROFILE_SAVE_SUCCESS: 'Profile saved successfully', + PROFILE_SAVE_ERROR: 'Could\'t Save Your Profile.', + CONNECTED_SOCIAL_ACCOUNTS: 'Connected social accounts', + CONNECT_OTHER_SOCIAL_ACCOUNTS: 'Connect other social accounts', + + FORGOT_PASSWORD_LINK: 'Forgot your password?', + REVERIFY_ACCOUNT_LINK: 'Resend your verification email', + + SIGNIN_BTN: 'Sign in', + SIGNUP_BTN: 'Sign up', + SAVE_PASSWORD_BTN: 'Save Password', + + SUCCESS_HEADER: 'Signup Successful', + SUCCESS_TEXT: 'You’ve successfully registered an account at TellForm.', + VERIFICATION_EMAIL_SENT: 'Verification Email has been Sent', + VERIFICATION_EMAIL_SENT_TO: 'A verification email has been sent to', + NOT_ACTIVATED_YET: 'But your account is not activated yet', + BEFORE_YOU_CONTINUE: 'Before you continue, make sure to check your email for our verification. If you don’t receive it within 24h drop us a line at ', + CHECK_YOUR_EMAIL: 'Check your email and click on the activation link to activate your account. If you have any questions drop us a line at', + CONTINUE: 'Continue', + + PASSWORD_RESTORE_HEADER: 'Restore your password', + ENTER_YOUR_EMAIL: 'Enter your account email.', + SUBMIT_BTN: 'Submit', + + ASK_FOR_NEW_PASSWORD: 'Ask for new password reset', + PASSWORD_RESET_INVALID: 'Password reset link is invalid', + PASSWORD_RESET_SUCCESS: 'Password successfully reset', + PASSWORD_CHANGE_SUCCESS: 'Password successfully changed', + RESET_PASSWORD: 'Reset your password', + CHANGE_PASSWORD: 'Change your password', + + CONTINUE_TO_LOGIN: 'Continue to login page', + + VERIFY_SUCCESS: 'Account successfully activated', + VERIFY_ERROR: 'Verification link is invalid or has expired', + ERROR: 'Error' + }); + + $translateProvider.preferredLanguage('en') + .fallbackLanguage('en') + .useSanitizeValueStrategy('escape'); + +}]); + +'use strict'; + +angular.module('users').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('fr', { + ACCESS_DENIED_TEXT: 'Vouz n’êtes pas autorisé à accéder à cette page.', + USERNAME_LABEL: 'Nom d’utilisateur', + PASSWORD_LABEL: 'Mot de Passe', + CURRENT_PASSWORD_LABEL: 'Mot de passe actuel', + NEW_PASSWORD_LABEL: 'Nouveau Mot de Passe', + VERIFY_PASSWORD_LABEL: 'Vérifier le mot de passe', + UPDATE_PASSWORD_LABEL: 'Mettre à jour le mot de passe', + FIRST_NAME_LABEL: 'Prénom', + LAST_NAME_LABEL: 'Nom', + LANGUAGE_LABEL: 'Langue', + EMAIL_LABEL: 'Email', + + UPDATE_PROFILE_BTN: 'Modifier le Profil', + PROFILE_SAVE_SUCCESS: 'Profil enregistré avec succès', + PROFILE_SAVE_ERROR: 'Erreur: impossible d’enregistrer votre Profile.', + + FORGOT_PASSWORD_LINK: 'Mot de passe oublié ?', + REVERIFY_ACCOUNT_LINK: 'Re-envoyez un email de vérification', + + SIGNIN_BTN: 'Connexion', + SIGNUP_BTN: 'Créer un compte', + SAVE_PASSWORD_BTN: 'Enregistrer votre nouveau Mot de Passe', + + SUCCESS_HEADER: 'Votre Compte a été enregistré !', + SUCCESS_TEXT: 'Votre compte Tellform a été crée avec succès.', + VERIFICATION_EMAIL_SENT: 'Un email de verification a été envoyer à', + NOT_ACTIVATED_YET: 'Mais votre compte n\'est pas activé', + BEFORE_YOU_CONTINUE: 'Avant de continuer, vous devez valider votre adresse mail. Merci de vérifier votre boite mail. Si vous ne l’avez pas reçu dans les prochaines 24h, contactez-nous a ', + CHECK_YOUR_EMAIL: 'Vérifiez vos emails, et cliquez sur le lien de validation pour activer votre compte. Si vous avez une question contactez-nous à', + + PASSWORD_RESTORE_HEADER: 'Mot de passe perdu', + ENTER_YOUR_EMAIL: 'Entrer votre email', + SUBMIT_BTN: 'Enregistrer', + + ASK_FOR_NEW_PASSWORD: 'Demander un nouveau mot de pass ', + PASSWORD_RESET_INVALID: 'Ce lien de réinitialisation de mot de passe a déjà expiré', + PASSWORD_RESET_SUCCESS: 'Mot de passe réinitialisé avec succès', + PASSWORD_CHANGE_SUCCESS: 'Mot de passe enregistré avec succès', + + CONTINUE_TO_LOGIN: 'Allez à la page de connexion', + + VERIFY_SUCCESS: 'Votre compte est activé !', + VERIFY_ERROR: 'Le lien de vérification est invalide ou à expiré', + ERROR: 'Erreur' + }); + +}]); + +'use strict'; + +angular.module('users').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('de', { + ACCESS_DENIED_TEXT: 'Sie müssen eingeloggt sein, um auf diese Seite zugreifen zu können', + USERNAME_OR_EMAIL_LABEL: 'Benutzername oder E-Mail', + USERNAME_LABEL: 'Benutzername', + PASSWORD_LABEL: 'Passwort', + CURRENT_PASSWORD_LABEL: 'Aktuelles Passwort', + NEW_PASSWORD_LABEL: 'Neues Passwort', + VERIFY_PASSWORD_LABEL: 'Passwort bestätigen', + UPDATE_PASSWORD_LABEL: 'Passwort aktualisieren', + FIRST_NAME_LABEL: 'Vorname', + LAST_NAME_LABEL: 'Nachname', + LANGUAGE_LABEL: 'Sprache', + EMAIL_LABEL: 'Email', + + SIGNUP_ACCOUNT_LINK: 'Haben Sie kein Konto? Hier registrieren', + SIGN_IN_ACCOUNT_LINK: 'Haben Sie bereits ein Konto? Hier anmelden', + SIGNUP_HEADER_TEXT: 'Registrieren', + SIGNIN_HEADER_TEXT: 'Anmelden', + + SIGNUP_ERROR_TEXT: 'Konnte die Registrierung aufgrund von Fehlern nicht abschließen', + ENTER_ACCOUNT_EMAIL: 'Geben Sie Ihre Konto-E-Mail ein.', + RESEND_VERIFICATION_EMAIL: 'Bestätigungs-E-Mail erneut senden', + SAVE_CHANGES: 'Änderungen speichern', + CANCEL_BTN: 'Abbrechen', + + EDIT_PROFILE: 'Bearbeiten Sie Ihr Profil', + UPDATE_PROFILE_BTN: 'Profil aktualisieren', + PROFILE_SAVE_SUCCESS: 'Profil wurde erfolgreich gespeichert', + PROFILE_SAVE_ERROR: 'Könnte Ihr Profil nicht speichern.', + CONNECTED_SOCIAL_ACCOUNTS: 'Verbundene Sozialkonten', + CONNECT_OTHER_SOCIAL_ACCOUNTS: 'Andere soziale Konten verbinden', + + FORGOT_PASSWORD_LINK: 'Passwort vergessen?', + REVERIFY_ACCOUNT_LINK: 'Bestätige deine Bestätigungs-E-Mail erneut', + + SIGNIN_BTN: "Anmelden", + SIGNUP_BTN: 'Registrieren', + SAVE_PASSWORD_BTN: 'Passwort speichern', + + SUCCESS_HEADER: 'Anmeldung erfolgreich', + SUCCESS_TEXT: 'Sie haben ein Konto erfolgreich bei TellForm registriert.', + VERIFICATION_EMAIL_SENT: 'Bestätigungs-E-Mail wurde gesendet', + VERIFICATION_EMAIL_SENT_TO: 'Es wurde eine Bestätigungs-E-Mail gesendet.', + NOT_ACTIVATED_YET: 'Dein Account ist noch nicht aktiviert', + BEFORE_YOU_CONTINUE: 'Bevor Sie fortfahren, überprüfen Sie bitte Ihre E-Mail-Adresse auf Überprüfung. Wenn Sie nicht innerhalb von 24 Stunden erhalten Sie uns eine Zeile bei ', + CHECK_YOUR_EMAIL: 'Überprüfe deine E-Mail und klicke auf den Aktivierungslink, um deinen Account zu aktivieren. Wenn Sie irgendwelche Fragen haben, lassen Sie uns eine Zeile bei ', + WEITER: 'Weiter', + + PASSWORD_RESTORE_HEADER: 'Wiederherstellen Ihres Passworts', + ENTER_YOUR_EMAIL: 'Geben Sie Ihre E-Mail-Adresse ein.', + SUBMIT_BTN: 'Senden', + + ASK_FOR_NEW_PASSWORD: 'Neues Passwort zurücksetzen', + PASSWORD_RESET_INVALID: 'Dieser Link zum Zurücksetzen des Passworts ist bereits abgelaufen', + PASSWORD_RESET_SUCCESS: 'Passport erfolgreich zurückgesetzt', + PASSWORD_CHANGE_SUCCESS: 'Pass wurde erfolgreich geändert', + RESET_PASSWORD: 'Passwort zurücksetzen', + CHANGE_PASSWORD: 'Ändern Sie Ihr Passwort', + + CONTINUE_TO_LOGIN: 'Weiter zur Anmeldeseite', + + VERIFY_SUCCESS: 'Konto erfolgreich aktiviert', + VERIFY_ERROR: 'Überprüfungslink ist ungültig oder abgelaufen', + ERROR: 'Fehler' + }); +}]); + +'use strict'; + +angular.module('users').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('it', { + ACCESS_DENIED_TEXT: 'Devi aver effettuato l\'accesso per accedere a questa pagina', + USERNAME_OR_EMAIL_LABEL: 'Nome utente o posta elettronica', + USERNAME_LABEL: 'Nome utente', + PASSWORD_LABEL: 'Password', + CURRENT_PASSWORD_LABEL: 'Current Password', + NEW_PASSWORD_LABEL: 'Nuova password', + VERIFY_PASSWORD_LABEL: 'Verifica password', + UPDATE_PASSWORD_LABEL: 'Aggiorna password', + FIRST_NAME_LABEL: 'Nome', + LAST_NAME_LABEL: 'Cognome', + LANGUAGE_LABEL: 'Lingua', + EMAIL_LABEL: 'Email', + + SIGNUP_ACCOUNT_LINK: 'Non hai un account? Iscriviti qui ', + SIGN_IN_ACCOUNT_LINK: 'Hai già un account? Accedi qui ', + SIGNUP_HEADER_TEXT: 'Iscriviti', + SIGNIN_HEADER_TEXT: 'Accedi', + + SIGNUP_ERROR_TEXT: 'Impossibile completare la registrazione a causa di errori', + ENTER_ACCOUNT_EMAIL: "Inserisci l'email del tuo account.", + RESEND_VERIFICATION_EMAIL: 'Ripeti l\'email di verifica', + SAVE_CHANGES: 'Salva modifiche', + CANCEL_BTN: 'Annulla', + + EDIT_PROFILE: 'Modifica il tuo profilo', + UPDATE_PROFILE_BTN: 'Aggiorna profilo', + PROFILE_SAVE_SUCCESS: 'Profilo salvato con successo', + PROFILE_SAVE_ERROR: 'Impossibile salvare il tuo profilo.', + CONNECTED_SOCIAL_ACCOUNTS: 'Conti sociali connessi', + CONNECT_OTHER_SOCIAL_ACCOUNTS: 'Connetti altri account sociali', + + FORGOT_PASSWORD_LINK: 'Hai dimenticato la password?', + REVERIFY_ACCOUNT_LINK: 'Ripeti la tua email di verifica', + + SIGNIN_BTN: 'Accedi', + SIGNUP_BTN: 'Iscriviti', + SAVE_PASSWORD_BTN: 'Salva password', + + SUCCESS_HEADER: 'Registra il successo', + SUCCESS_TEXT: 'Hai registrato un account con TellForm.', + VERIFICATION_EMAIL_SENT: 'L\'email di verifica è stata inviata', + VERIFICATION_EMAIL_SENT_TO: 'E\' stata inviata un\'email di verifica a ', + NOT_ACTIVATED_YET: 'Ma il tuo account non è ancora attivato', + BEFORE_YOU_CONTINUE: 'Prima di continuare, assicurati di controllare la tua email per la nostra verifica. Se non lo ricevi entro 24 ore ci cali una linea a ', + CHECK_YOUR_EMAIL: 'Controlla la tua email e fai clic sul link di attivazione per attivare il tuo account. Se hai domande, fai una linea a ', + CONTINUA: 'Continua', + + PASSWORD_RESTORE_HEADER: 'Ripristina password', + ENTER_YOUR_EMAIL: 'Inserisci l\'email del tuo account', + SUBMIT_BTN: 'Invia', + + ASK_FOR_NEW_PASSWORD: 'Richiedi nuova password reimpostata', + PASSWORD_RESET_INVALID: 'Questo collegamento per la reimpostazione della password è già scaduto', + PASSWORD_RESET_SUCCESS: 'Passaporto resettato con successo', + PASSWORD_CHANGE_SUCCESS: 'Passaporto modificato con successo', + RESET_PASSWORD: 'Ripristina la tua password', + CHANGE_PASSWORD: 'Modifica password', + + CONTINUE_TO_LOGIN: 'Continua alla pagina di login', + + VERIFY_SUCCESS: 'Account attivato correttamente', + VERIFY_ERROR: 'Il collegamento di verifica non è valido o è scaduto', + ERROR: 'Errore' + }); +}]); + +'use strict'; + +angular.module('users').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('es', { + ACCESS_DENIED_TEXT: 'Tenés que estar logueado para acceder a esta página', + USERNAME_OR_EMAIL_LABEL: 'Usuario o Email', + USERNAME_LABEL: 'Usuario', + PASSWORD_LABEL: 'Contraseña', + CURRENT_PASSWORD_LABEL: 'Contraseña actual', + NEW_PASSWORD_LABEL: 'Nueva contraseña', + VERIFY_PASSWORD_LABEL: 'Verificar contraseña', + UPDATE_PASSWORD_LABEL: 'Actualizar contraseña', + FIRST_NAME_LABEL: 'Nombre', + LAST_NAME_LABEL: 'Apellido', + LANGUAGE_LABEL: 'Idioma', + EMAIL_LABEL: 'Email', + + SIGNUP_ACCOUNT_LINK: '¿No tenés cuenta? Resgistrate acá', + SIGN_IN_ACCOUNT_LINK: '¿Ya tenés cuenta? Entra acá', + SIGNUP_HEADER_TEXT: 'Registrar', + SIGNIN_HEADER_TEXT: 'Entrar', + + SIGNUP_ERROR_TEXT: 'No se pudo terminar la registración por errores', + ENTER_ACCOUNT_EMAIL: 'Ingresá tu correo electrónico.', + RESEND_VERIFICATION_EMAIL: 'Reenviar email de verificación', + SAVE_CHANGES: 'Grabar cambios', + CANCEL_BTN: 'Cancelar', + + EDIT_PROFILE: 'Editar perfil', + UPDATE_PROFILE_BTN: 'Actualizar perfil', + PROFILE_SAVE_SUCCESS: 'Perfil actualizado satisfactoriamente', + PROFILE_SAVE_ERROR: 'No se pudo grabar el perfil.', + CONNECTED_SOCIAL_ACCOUNTS: 'Redes sociales conectadas', + CONNECT_OTHER_SOCIAL_ACCOUNTS: 'Conectar otras redes sociales', + + FORGOT_PASSWORD_LINK: '¿Olvidaste la contraseña?', + REVERIFY_ACCOUNT_LINK: 'Reenviar email de verificación', + + SIGNIN_BTN: 'Entrar', + SIGNUP_BTN: 'Registrarse', + SAVE_PASSWORD_BTN: 'Grabar contraseña', + + SUCCESS_HEADER: 'Ingresaste exitosamente', + SUCCESS_TEXT: 'Registraste exitosamente una cuenta en TellForm.', + VERIFICATION_EMAIL_SENT: 'El email de verificación fue enviado exitosamente', + VERIFICATION_EMAIL_SENT_TO: 'Un email de verificación fue enviado a', + NOT_ACTIVATED_YET: 'Tu cuenta aún no está activa', + BEFORE_YOU_CONTINUE: 'Antes de continuar asegurate de leer el email de verificación que te enviamos. Si no lo recibís en 24hs escribinos a ', + CHECK_YOUR_EMAIL: 'Leé el email y hacé click en el link de activación para activar la cuenta. Si tenés alguna pregunta escribinos a ', + CONTINUE: 'Continuar', + + PASSWORD_RESTORE_HEADER: 'Restaurar la contraseña', + ENTER_YOUR_EMAIL: 'Ingresá el email de tu cuenta.', + SUBMIT_BTN: 'Enviar', + + ASK_FOR_NEW_PASSWORD: 'Pedir reseteo de contraseña', + PASSWORD_RESET_INVALID: 'Este enlace de restablecimiento de contraseña ya ha caducado', + PASSWORD_RESET_SUCCESS: 'Contraseña exitosamente reseteada', + PASSWORD_CHANGE_SUCCESS: 'Contraseña exitosamente cambiada', + RESET_PASSWORD: 'Resetear contraseña', + CHANGE_PASSWORD: 'Cambiar contraseña', + + CONTINUE_TO_LOGIN: 'Ir a la página de ingreso', + + VERIFY_SUCCESS: 'Cuenta activada exitosamente', + VERIFY_ERROR: 'El link de verificación es inválido o inexistente', + ERROR: 'Error' + }); +}]); + +'use strict'; + +angular.module('forms').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('en', { + + //Configure Form Tab View + ADVANCED_SETTINGS: 'Advanced Settings', + FORM_NAME: 'Form Name', + FORM_STATUS: 'Form Status', + PUBLIC: 'Public', + PRIVATE: 'Private', + GA_TRACKING_CODE: 'Google Analytics Tracking Code', + DISPLAY_FOOTER: 'Display Form Footer?', + SAVE_CHANGES: 'Save Changes', + CANCEL: 'Cancel', + DISPLAY_START_PAGE: 'Display Start Page?', + DISPLAY_END_PAGE: 'Display Custom End Page?', + + //List Forms View + CREATE_A_NEW_FORM: 'Create a new form', + CREATE_FORM: 'Create form', + CREATED_ON: 'Created on', + MY_FORMS: 'My forms', + NAME: 'Name', + LANGUAGE: 'Language', + FORM_PAUSED: 'Form paused', + + //Edit Field Modal + EDIT_FIELD: 'Edit this Field', + SAVE_FIELD: 'Save', + ON: 'ON', + OFF: 'OFF', + REQUIRED_FIELD: 'Required', + LOGIC_JUMP: 'Logic Jump', + SHOW_BUTTONS: 'Additional Buttons', + SAVE_START_PAGE: 'Save', + + //Admin Form View + ARE_YOU_SURE: 'Are you ABSOLUTELY sure?', + READ_WARNING: 'Unexpected bad things will happen if you don’t read this!', + DELETE_WARNING1: 'This action CANNOT be undone. This will permanently delete the "', + DELETE_WARNING2: '" form and remove all associated form submissions.', + DELETE_CONFIRM: 'Please type in the name of the form to confirm.', + I_UNDERSTAND: 'I understand the consequences, delete this form.', + DELETE_FORM_SM: 'Delete', + DELETE_FORM_MD: 'Delete Form', + DELETE: 'Delete', + FORM: 'Form', + VIEW: 'View', + LIVE: 'Live', + PREVIEW: 'Preview', + COPY: 'Copy', + COPY_AND_PASTE: 'Copy and Paste this to add your TellForm to your website', + CHANGE_WIDTH_AND_HEIGHT: 'Change the width and height values to suit you best', + POWERED_BY: 'Powered by', + TELLFORM_URL: 'Your TellForm is permanently at this URL', + + //Edit Form View + DISABLED: 'Disabled', + YES: 'YES', + NO: 'NO', + ADD_LOGIC_JUMP: 'Add Logic Jump', + ADD_FIELD_LG: 'Click to Add New Field', + ADD_FIELD_MD: 'Add New Field', + ADD_FIELD_SM: 'Add Field', + EDIT_START_PAGE: 'Edit Start Page', + EDIT_END_PAGE: 'Edit End Page', + WELCOME_SCREEN: 'Start Page', + END_SCREEN: 'End Page', + INTRO_TITLE: 'Title', + INTRO_PARAGRAPH: 'Paragraph', + INTRO_BTN: 'Start Button', + TITLE: 'Title', + PARAGRAPH: 'Paragraph', + BTN_TEXT: 'Go Back Button', + BUTTONS: 'Buttons', + BUTTON_TEXT: 'Text', + BUTTON_LINK: 'Link', + ADD_BUTTON: 'Add Button', + PREVIEW_FIELD: 'Preview Question', + QUESTION_TITLE: 'Title', + QUESTION_DESCRIPTION: 'Description', + OPTIONS: 'Options', + ADD_OPTION: 'Add Option', + NUM_OF_STEPS: 'Number of Steps', + CLICK_FIELDS_FOOTER: 'Click on fields to add them here', + SHAPE: 'Shape', + IF_THIS_FIELD: 'If this field', + IS_EQUAL_TO: 'is equal to', + IS_NOT_EQUAL_TO: 'is not equal to', + IS_GREATER_THAN: 'is greater than', + IS_GREATER_OR_EQUAL_THAN: 'is greater or equal than', + IS_SMALLER_THAN: 'is smaller than', + IS_SMALLER_OR_EQUAL_THAN: 'is smaller or equal than', + CONTAINS: 'contains', + DOES_NOT_CONTAINS: 'does not contain', + ENDS_WITH: 'ends with', + DOES_NOT_END_WITH: 'does not end with', + STARTS_WITH: 'starts with', + DOES_NOT_START_WITH: 'does not start with', + THEN_JUMP_TO: 'then jump to', + + //Edit Submissions View + TOTAL_VIEWS: 'total unique visits', + RESPONSES: 'responses', + COMPLETION_RATE: 'completion rate', + AVERAGE_TIME_TO_COMPLETE: 'avg. completion time', + + DESKTOP_AND_LAPTOP: 'Desktops', + TABLETS: 'Tablets', + PHONES: 'Phones', + OTHER: 'Other', + UNIQUE_VISITS: 'Unique Visits', + + FIELD_TITLE: 'Field Title', + FIELD_VIEWS: 'Field Views', + FIELD_DROPOFF: 'Field Completion', + FIELD_RESPONSES: 'Field Responses', + DELETE_SELECTED: 'Delete Selected', + EXPORT_TO_EXCEL: 'Export to Excel', + EXPORT_TO_CSV: 'Export to CSV', + EXPORT_TO_JSON: 'Export to JSON', + PERCENTAGE_COMPLETE: 'Percentage Complete', + TIME_ELAPSED: 'Time Elapsed', + DEVICE: 'Device', + LOCATION: 'Location', + IP_ADDRESS: 'IP Address', + DATE_SUBMITTED: 'Date Submitted', + + //Design View + BACKGROUND_COLOR: 'Background Color', + DESIGN_HEADER: 'Change how your Form Looks', + QUESTION_TEXT_COLOR: 'Question Text Color', + ANSWER_TEXT_COLOR: 'Answer Text Color', + BTN_BACKGROUND_COLOR: 'Button Background Color', + BTN_TEXT_COLOR: 'Button Text Color', + + //Share View + EMBED_YOUR_FORM: 'Embed your form', + SHARE_YOUR_FORM: 'Share your form', + + //Admin Tabs + CREATE_TAB: 'Create', + DESIGN_TAB: 'Design', + CONFIGURE_TAB: 'Configure', + ANALYZE_TAB: 'Analyze', + SHARE_TAB: 'Share', + + //Field Types + SHORT_TEXT: 'Short Text', + EMAIL: 'Email', + MULTIPLE_CHOICE: 'Multiple Choice', + DROPDOWN: 'Dropdown', + DATE: 'Date', + PARAGRAPH_FIELD: 'Paragraph', + YES_NO: 'Yes/No', + LEGAL: 'Legal', + RATING: 'Rating', + NUMBERS: 'Numbers', + SIGNATURE: 'Signature', + FILE_UPLOAD: 'File upload', + OPTION_SCALE: 'Option Scale', + PAYMENT: 'Payment', + STATEMENT: 'Statement', + LINK: 'Link', + + //Form Preview + FORM_SUCCESS: 'Form entry successfully submitted!', + REVIEW: 'Review', + BACK_TO_FORM: 'Go back to Form', + EDIT_FORM: 'Edit this TellForm', + ADVANCEMENT: '{{done}} out of {{total}} answered', + CONTINUE_FORM: 'Continue to Form', + REQUIRED: 'required', + COMPLETING_NEEDED: '{{answers_not_completed}} answer(s) need completing', + OPTIONAL: 'optional', + ERROR_EMAIL_INVALID: 'Please enter a valid email address', + ERROR_NOT_A_NUMBER: 'Please enter valid numbers only', + ERROR_URL_INVALID: 'Please a valid url', + OK: 'OK', + ENTER: 'press ENTER', + NEWLINE: 'press SHIFT+ENTER to create a newline', + CONTINUE: 'Continue', + LEGAL_ACCEPT: 'I accept', + LEGAL_NO_ACCEPT: 'I don’t accept', + SUBMIT: 'Submit', + UPLOAD_FILE: 'Upload your File' + }); +}]); + +'use strict'; + +angular.module('forms').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('fr', { + // Configurer la vue de l'onglet Formulaire + ADVANCED_SETTINGS: 'Paramètres avancés', + FORM_NAME: "Nom du formulaire", + FORM_STATUS: 'Statut du formulaire', + PUBLIC: 'Public', + PRIVATE: "Privé", + GA_TRACKING_CODE: "Code de suivi Google Analytics", + DISPLAY_FOOTER: "Afficher le pied de formulaire?", + SAVE_CHANGES: 'Enregistrer les modifications', + CANCEL: 'Annuler', + DISPLAY_START_PAGE: "Afficher la page de démarrage?", + DISPLAY_END_PAGE: "Afficher la page de fin personnalisée?", + + // Afficher les formulaires + CREATE_A_NEW_FORM: "Créer un nouveau formulaire", + CREATE_FORM: "Créer un formulaire", + CREATED_ON: 'Créé le', + MY_FORMS: 'Mes formes', + NAME: "Nom", + LANGUE: 'Langue', + FORM_PAUSED: 'Formulaire en pause', + + // Modifier le modal de champ + EDIT_FIELD: "Modifier ce champ", + SAVE_FIELD: 'Enregistrer', + ON: 'ON', + OFF: "OFF", + REQUIRED_FIELD: "Obligatoire", + LOGIC_JUMP: 'Saut logique', + SHOW_BUTTONS: 'Boutons supplémentaires', + SAVE_START_PAGE: "Enregistrer", + + // Affichage du formulaire d'administration + ARE_YOU_SURE: 'Es-tu ABSOLUMENT sûr?', + READ_WARNING: "De mauvaises choses inattendues se produiront si vous ne lisez pas ceci!", + DELETE_WARNING1: 'Cette action NE PEUT PAS être annulée. Cela supprimera définitivement le "', + DELETE_WARNING2: '" forme et supprime toutes les soumissions de formulaire associées. ', + DELETE_CONFIRM: "Veuillez taper le nom du formulaire pour confirmer.", + I_UNDERSTAND: 'Je comprends les conséquences, efface ce formulaire.', + DELETE_FORM_SM: 'Supprimer', + DELETE_FORM_MD: "Supprimer le formulaire", + DELETE: "Supprimer", + FORM: 'Formulaire', + VIEW: "Afficher", + LIVE: "Live", + PREVIEW: 'Aperçu', + COPY: "Copier", + COPY_AND_PASTE: "Copiez et collez ceci pour ajouter votre TellForm à votre site Web", + CHANGE_WIDTH_AND_HEIGHT: "Changez les valeurs de largeur et de hauteur pour mieux vous convenir", + POWERED_BY: "Alimenté par", + TELLFORM_URL: "Votre TellForm est en permanence sur cette URL", + + // Modifier la vue de formulaire + DISABLED: "Désactivé", + OUI: 'OUI', + NO: 'NON', + ADD_LOGIC_JUMP: 'Ajouter un saut de logique', + ADD_FIELD_LG: "Cliquez pour ajouter un nouveau champ", + ADD_FIELD_MD: "Ajouter un nouveau champ", + ADD_FIELD_SM: "Ajouter un champ", + EDIT_START_PAGE: "Modifier la page de démarrage", + EDIT_END_PAGE: "Modifier la page de fin", + WELCOME_SCREEN: 'Page de démarrage', + END_SCREEN: 'Fin de page', + INTRO_TITLE: "Titre", + INTRO_PARAGRAPH: 'Paragraphe', + INTRO_BTN: 'Bouton de démarrage', + TITLE: "Titre", + PARAGRAPHE: 'Paragraphe', + BTN_TEXT: "Bouton Retour", + BOUTONS: 'Boutons', + BUTTON_TEXT: "Texte", + BUTTON_LINK: "Lien", + ADD_BUTTON: 'Ajouter un bouton', + PREVIEW_FIELD: 'Question d\'aperçu', + QUESTION_TITLE: "Titre", + QUESTION_DESCRIPTION: 'Description', + OPTIONS: 'Options', + ADD_OPTION: 'Ajouter une option', + NUM_OF_STEPS: "Nombre d'étapes", + CLICK_FIELDS_FOOTER: 'Cliquez sur les champs pour les ajouter ici', + SHAPE: 'Forme', + IF_THIS_FIELD: "Si ce champ", + IS_EQUAL_TO: 'est égal à', + IS_NOT_EQUAL_TO: 'n\'est pas égal à', + IS_GREATER_THAN: 'est supérieur à', + IS_GREATER_OR_EQUAL_THAN: 'est supérieur ou égal à', + IS_SMALLER_THAN: 'est plus petit que', + IS_SMALLER_OR_EQUAL_THAN: 'est plus petit ou égal à', + CONTAINS: 'contient', + DOES_NOT_CONTAINS: 'ne contient pas', + ENDS_WITH: "se termine par", + DOES_NOT_END_WITH: "ne finit pas avec", + STARTS_WITH: 'commence par', + DOES_NOT_START_WITH: "ne commence pas par", + THEN_JUMP_TO: 'alors saute à', + + // Modifier la vue des soumissions + TOTAL_VIEWS: 'total des visites uniques', + RESPONSES: "réponses", + COMPLETION_RATE: "taux d'achèvement", + AVERAGE_TIME_TO_COMPLETE: 'moy. le temps d\'achèvement', + + DESKTOP_AND_LAPTOP: 'Desktops', + TABLETS: 'Tablettes', + PHONES: 'Téléphones', + OTHER: 'Autre', + UNIQUE_VISITS: 'Visites uniques', + + FIELD_TITLE: 'Titre du champ', + FIELD_VIEWS: 'Vues de champ', + FIELD_DROPOFF: "Achèvement du champ", + FIELD_RESPONSES: 'Réponses sur le terrain', + DELETE_SELECTED: 'Supprimer la sélection', + EXPORT_TO_EXCEL: 'Exporter vers Excel', + EXPORT_TO_CSV: 'Export vers CSV', + EXPORT_TO_JSON: "Exporter vers JSON", + PERCENTAGE_COMPLETE: 'Pourcentage terminé', + TIME_ELAPSED: 'Temps écoulé', + DEVICE: "Dispositif", + LOCATION: "Emplacement", + IP_ADDRESS: 'Adresse IP', + DATE_SUBMITTED: 'Date de soumission', + + // Vue de conception + BACKGROUND_COLOR: "Couleur d'arrière-plan", + DESIGN_HEADER: "Changez l'apparence de votre formulaire", + QUESTION_TEXT_COLOR: "Couleur du texte de la question", + ANSWER_TEXT_COLOR: "Couleur du texte de la réponse", + BTN_BACKGROUND_COLOR: "Couleur d'arrière-plan du bouton", + BTN_TEXT_COLOR: "Couleur du texte du bouton", + + // Vue de partage + EMBED_YOUR_FORM: "Intégrez votre formulaire", + SHARE_YOUR_FORM: "Partager votre formulaire", + + // Onglets d'administration + CREATE_TAB: "Créer", + DESIGN_TAB: 'Design', + CONFIGURE_TAB: 'Configurer', + ANALYZE_TAB: "Analyser", + SHARE_TAB: "Partager", + + // Types de champs + SHORT_TEXT: "Texte court", + EMAIL: "E-mail", + MULTIPLE_CHOICE: 'Choix multiple', + DROPDOWN: 'Menu Déroulant', + DATE: 'Date', + PARAGRAPH_FIELD: "Paragraphe", + OUI_NON: 'Oui / Non', + LEGAL: 'Légal', + RATING: "Évaluation", + NUMBERS: "Chiffres", + SIGNATURE: 'Signature', + FILE_UPLOAD: 'Téléchargement de fichier', + OPTION_SCALE: 'Option Scale', + PAYMENT: 'Paiement', + STATEMENT: 'Déclaration', + LINK: "Lien", + + // Aperçu du formulaire + FORM_SUCCESS: 'Entrée de formulaire soumise avec succès!', + REVIEW: 'Réviser', + BACK_TO_FORM: "Revenir au formulaire", + EDIT_FORM: "Modifier ce TellForm", + ADVANCEMENT: '{{done}} sur {{total}} a répondu', + CONTINUE_FORM: "Continuer à se former", + REQUIRED: 'requis', + COMPLETING_NEEDED: '{{answers_not_completed}} réponse (s) doivent être complétées', + OPTIONAL: 'optionnel', + ERROR_EMAIL_INVALID: "Veuillez entrer une adresse email valide", + ERROR_NOT_A_NUMBER: "Veuillez entrer uniquement des numéros valides", + ERROR_URL_INVALID: "S'il vous plaît une adresse valide", + OK: 'OK', + ENTER: 'appuyez sur ENTRER', + NEWLINE: 'appuyez sur MAJ + ENTRÉE pour créer une nouvelle ligne', + CONTINUE: "Continuer", + LEGAL_ACCEPT: 'J\'accepte', + LEGAL_NO_ACCEPT: "Je n'accepte pas", + SUBMIT: "Soumettre", + UPLOAD_FILE: "Télécharger votre fichier" + }); +}]); + +'use strict'; + +angular.module('forms').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('de', { + // Konfigurieren der Formularregisterkarte + ADVANCED_SETTINGS: 'Erweiterte Einstellungen', + FORM_NAME: 'Formularname', + FORM_STATUS: 'Formularstatus', + PUBLIC: 'Öffentlich', + PRIVATE: 'Privat', + GA_TRACKING_CODE: 'Google Analytics Tracking-Code', + DISPLAY_FOOTER: 'Formularfußzeile anzeigen?', + SAVE_CHANGES: 'Änderungen speichern', + CANCEL: 'Abbrechen', + DISPLAY_START_PAGE: 'Startseite anzeigen?', + DISPLAY_END_PAGE: 'Benutzerdefinierte Endseite anzeigen?', + + // Listenformularansicht + CREATE_A_NEW_FORM: 'Erstelle ein neues Formular', + CREATE_FORM: 'Formular erstellen', + CREATED_ON: 'Erstellt am', + MY_FORMS: 'Meine Formulare', + NAME: 'Name', + SPRACHE: 'Sprache', + FORM_PAUSED: 'Formular pausiert', + + // Feld Modal bearbeiten + EDIT_FIELD: 'Dieses Feld bearbeiten', + SAVE_FIELD: 'Speichern', + ON: 'ON', + AUS: 'AUS', + REQUIRED_FIELD: 'Erforderlich', + LOGIC_JUMP: 'Logischer Sprung', + SHOW_BUTTONS: 'Zusätzliche Schaltflächen', + SAVE_START_PAGE: 'Speichern', + + // Admin-Formularansicht + ARE_YOU_SURE: "Bist du ABSOLUT sicher?", + READ_WARNING: 'Unerwartete schlimme Dinge werden passieren, wenn Sie das nicht lesen!', + DELETE_WARNING1: 'Diese Aktion kann NICHT rückgängig gemacht werden. Dies wird dauerhaft die "', + DELETE_WARNING2: '"Formular und entferne alle verknüpften Formulareinreichungen.', + DELETE_CONFIRM: 'Bitte geben Sie den Namen des zu bestätigenden Formulars ein.', + I_UNDERSTAND: "Ich verstehe die Konsequenzen, lösche dieses Formular.", + DELETE_FORM_SM: 'Löschen', + DELETE_FORM_MD: 'Formular löschen', + DELETE: 'Löschen', + FORM: 'Formular', + VIEW: 'Ansicht', + LIVE: 'Leben', + PREVIEW: 'Vorschau', + COPY: 'Kopieren', + COPY_AND_PASTE: 'Kopieren und einfügen, um Ihre TellForm auf Ihrer Website hinzuzufügen', + CHANGE_WIDTH_AND_HEIGHT: 'Ändern Sie die Werte für Breite und Höhe, um Ihnen am besten zu entsprechen', + POWERED_BY: 'Unterstützt von', + TELLFORM_URL: "Ihr TellForm ist dauerhaft unter dieser URL", + + // Formularansicht bearbeiten + DISABLED: 'Deaktiviert', + JA: 'JA', + NO: 'NEIN', + ADD_LOGIC_JUMP: 'Logic Jump hinzufügen', + ADD_FIELD_LG: 'Klicken Sie auf Neues Feld hinzufügen', + ADD_FIELD_MD: 'Neues Feld hinzufügen', + ADD_FIELD_SM: 'Feld hinzufügen', + EDIT_START_PAGE: 'Startseite bearbeiten', + EDIT_END_PAGE: 'Endseite bearbeiten', + WELCOME_SCREEN: 'Startseite', + END_SCREEN: 'Ende Seite', + INTRO_TITLE: 'Titel', + INTRO_PARAGRAPH: "Absatz", + INTRO_BTN: 'Start Knopf', + TITLE: "Titel", + PARAGRAPH: "Absatz", + BTN_TEXT: 'Zurück Button', + TASTEN: 'Knöpfe', + BUTTON_TEXT: 'Text', + BUTTON_LINK: 'Link', + ADD_BUTTON: 'Schaltfläche hinzufügen', + PREVIEW_FIELD: 'Vorschaufrage', + QUESTION_TITLE: 'Titel', + QUESTION_DESCRIPTION: 'Beschreibung', + OPTIONS: 'Optionen', + ADD_OPTION: 'Option hinzufügen', + NUM_OF_STEPS: 'Anzahl der Schritte', + CLICK_FIELDS_FOOTER: 'Klicken Sie auf Felder, um sie hier hinzuzufügen', + IF_THIS_FIELD: 'Wenn dieses Feld', + IS_EQUAL_TO: 'ist gleich', + IS_NOT_EQUAL_TO: 'ist nicht gleich', + IS_GREATER_THAN: 'ist größer als', + IS_GREATER_OR_EQUAL_THAN: 'ist größer oder gleich', + IS_SMALLER_THAN: 'ist kleiner als', + IS_SMALLER_OR_EQUAL_THAN: 'ist kleiner oder gleich', + CONTAINS: 'enthält', + DOES_NOT_CONTAINS: 'enthält nicht', + ENDS_WITH: 'endet mit', + DOES_NOT_END_WITH: 'endet nicht mit', + STARTS_WITH: 'beginnt mit', + DOES_NOT_START_WITH: 'beginnt nicht mit', + THEN_JUMP_TO: 'Springe dann zu', + + // Bearbeiten der Einreichungsansicht + TOTAL_VIEWS: 'Gesamtzahl eindeutiger Besuche', + RESPONSES: 'Antworten', + COMPLETION_RATE: 'Abschlussrate', + AVERAGE_TIME_TO_COMPLETE: 'avg. Fertigstellungszeit', + + DESKTOP_AND_LAPTOP: 'Desktops', + TABLETS: "Tabletten", + PHONES: 'Telefone', + OTHER: 'Andere', + UNIQUE_VISITS: 'Eindeutige Besuche', + + FIELD_TITLE: 'Feldtitel', + FIELD_VIEWS: 'Feld Ansichten', + FIELD_DROPOFF: 'Feldabschluss', + FIELD_RESPONSES: 'Feldantworten', + DELETE_SELECTED: 'Ausgewählte löschen', + EXPORT_TO_EXCEL: 'Export nach Excel', + EXPORT_TO_CSV: 'In CSV exportieren', + EXPORT_TO_JSON: 'Export nach JSON', + PERCENTAGE_COMPLETE: 'Prozent abgeschlossen', + TIME_ELAPSED: 'Zeit verstrichen', + DEVICE: 'Gerät', + LOCATION: 'Ort', + IP_ADDRESS: 'IP-Adresse', + DATE_SUBMITTED: 'Eingereichtes Datum', + + // Entwurfsansicht + BACKGROUND_COLOR: 'Hintergrundfarbe', + DESIGN_HEADER: 'Ändern Sie, wie Ihr Formular aussieht', + QUESTION_TEXT_COLOR: 'Fragetextfarbe', + ANSWER_TEXT_COLOR: 'Textfarbe beantworten', + BTN_BACKGROUND_COLOR: 'Schaltfläche Hintergrundfarbe', + BTN_TEXT_COLOR: 'Schaltfläche Textfarbe', + + // Freigabeansicht + EMBED_YOUR_FORM: 'Einbetten Ihres Formulars', + SHARE_YOUR_FORM: 'Teilen Sie Ihr Formular', + + // Admin-Registerkarten + CREATE_TAB: 'Erstellen', + DESIGN_TAB: 'Entwurf', + CONFIGURE_TAB: 'Konfigurieren', + ANALYZE_TAB: 'Analysieren', + SHARE_TAB: 'Freigeben', + + // Feldtypen + SHORT_TEXT: 'Kurztext', + EMAIL: 'Email', + MULTIPLE_CHOICE: 'Mehrfachauswahl', + DROPDOWN: 'Dropdown-Liste', + DATE: 'Datum', + PARAGRAPH_FIELD: "Absatz", + YES_NO: 'Ja / Nein', + LEGAL: "Rechtliche", + RATING: 'Bewertung', + NUMBERS: 'Zahlen', + SIGNATURE: "Unterschrift", + FILE_UPLOAD: 'Datei-Upload', + OPTION_SCALE: 'Optionsskala', + ZAHLUNG: "Zahlung", + STATEMENT: 'Anweisung', + LINK: 'Link', + + // Formularvorschau + FORM_SUCCESS: 'Formulareintrag erfolgreich gesendet!', + REVIEW: 'Überprüfung', + BACK_TO_FORM: 'Gehe zurück zu Formular', + EDIT_FORM: 'Bearbeiten Sie diese TellForm', + ADVANCEMENT: '{{done}} von {{total}} wurde beantwortet', + CONTINUE_FORM: 'Weiter zum Formular', + REQUIRED: 'erforderlich', + COMPLETING_NEEDED: '{{answers_not_completed}} Antwort (en) müssen ausgefüllt werden', + OPTIONAL: 'optional', + ERROR_EMAIL_INVALID: 'Geben Sie eine gültige E-Mail-Adresse ein', + ERROR_NOT_A_NUMBER: 'Bitte nur gültige Nummern eingeben', + ERROR_URL_INVALID: 'Bitte eine gültige URL', + OK: 'OK', + ENTER: 'ENTER drücken', + NEWLINE: 'Drücken Sie UMSCHALT + EINGABETASTE, um eine neue Zeile zu erstellen', + CONTINUE: 'Weiter', + LEGAL_ACCEPT: "Ich akzeptiere", + LEGAL_NO_ACCEPT: "Ich akzeptiere nicht", + SUBMIT: 'Senden', + UPLOAD_FILE: 'Hochladen Ihrer Datei' + }); + +}]); + +'use strict'; + +angular.module('forms').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('it', { + // Configura la visualizzazione scheda modulo + ADVANCED_SETTINGS: 'Impostazioni avanzate', + FORM_NAME: 'Nome modulo', + FORM_STATUS: 'Stato modulo', + PUBLIC: 'pubblico', + PRIVATE: 'Privato', + GA_TRACKING_CODE: 'Codice di monitoraggio di Google Analytics', + DISPLAY_FOOTER: 'Visualizza piè di pagina?', + SAVE_CHANGES: 'Salva modifiche', + CANCEL: 'Annulla', + DISPLAY_START_PAGE: 'Visualizza pagina iniziale?', + DISPLAY_END_PAGE: 'Mostra pagina finale personalizzata?', + + // Visualizzazione dei moduli di elenco + CREATE_A_NEW_FORM: 'Crea un nuovo modulo', + CREATE_FORM: 'Crea modulo', + CREATED_ON: 'Creato su', + MY_FORMS: 'Le mie forme', + NAME: 'Nome', + LINGUA: 'Lingua', + FORM_PAUSED: 'Forme in pausa', + + // Modifica campo modale + EDIT_FIELD: 'Modifica questo campo', + SAVE_FIELD: 'Salva', + ON: 'ON', + OFF: 'OFF', + REQUIRED_FIELD: 'Obbligatorio', + LOGIC_JUMP: 'Jump Logic', + SHOW_BUTTONS: 'Pulsanti aggiuntivi', + SAVE_START_PAGE: 'Salva', + + // Visualizzazione modulo di amministrazione + ARE_YOU_SURE: 'Sei ASSOLUTAMENTE sicuro?', + READ_WARNING: 'Le cose cattive impreviste avverranno se non lo leggi!', + DELETE_WARNING1: 'Questa azione NON può essere annullata. Ciò eliminerà in modo permanente il "', + DELETE_WARNING2: '" forma e rimuovi tutti i moduli di modulo associati. ', + DELETE_CONFIRM: 'Inserisci il nome del modulo per confermare', + I_UNDERSTAND: "Capisco le conseguenze, elimina questa forma", + DELETE_FORM_SM: 'Elimina', + DELETE_FORM_MD: 'Elimina modulo', + DELETE: 'Elimina', + FORM: 'Forma', + VIEW: 'Visualizza', + LIVE: 'Live', + PREVIEW: 'Anteprima', + COPY: 'Copia', + COPY_AND_PASTE: 'Copia e incolla questo per aggiungere il tuo TellForm al tuo sito web', + CHANGE_WIDTH_AND_HEIGHT: 'Modifica i valori di larghezza e di altezza per adattarti al meglio', + POWERED_BY: 'Offerto da', + TELLFORM_URL: 'Il tuo TellForm è permanente in questo URL', + + // Modifica vista modulo + DISABLED: 'disabilitato', + YES: 'SI', + NO: 'NO', + ADD_LOGIC_JUMP: 'Aggiungi logico salto', + ADD_FIELD_LG: 'Clicca per aggiungere nuovo campo', + ADD_FIELD_MD: 'Aggiungi nuovo campo', + ADD_FIELD_SM: 'Aggiungi campo', + EDIT_START_PAGE: 'Modifica pagina iniziale', + EDIT_END_PAGE: 'Modifica pagina finale', + WELCOME_SCREEN: 'Pagina iniziale', + END_SCREEN: 'Fine pagina', + INTRO_TITLE: 'Titolo', + INTRO_PARAGRAPH: 'Paragrafo', + INTRO_BTN: 'Pulsante Start', + TITLE: 'Titolo', + PARAGRAFO: 'Paragrafo', + BTN_TEXT: 'Tornare indietro', + TASTI: 'Pulsanti', + BUTTON_TEXT: 'Testo', + BUTTON_LINK: 'Link', + ADD_BUTTON: 'Aggiungi pulsante', + PREVIEW_FIELD: 'Anteprima domanda', + QUESTION_TITLE: 'Titolo', + QUESTION_DESCRIPTION: 'Descrizione', + OPTIONS: 'Opzioni', + ADD_OPTION: 'Aggiungi opzione', + NUM_OF_STEPS: 'Numero di passi', + CLICK_FIELDS_FOOTER: 'Clicca sui campi per aggiungerli qui', + FORMA: 'Forma', + IF_THIS_FIELD: 'Se questo campo', + IS_EQUAL_TO: 'è uguale a', + IS_NOT_EQUAL_TO: 'non è uguale a', + IS_GREATER_THAN: 'è maggiore di', + IS_GREATER_OR_EQUAL_THAN: 'è maggiore o uguale a', + IS_SMALLER_THAN: 'è inferiore a', + IS_SMALLER_OR_EQUAL_THAN: 'è più piccolo o uguale a quello', + CONTAINS: 'contiene', + DOES_NOT_CONTAINS: 'non contiene', + ENDS_WITH: 'finisce con', + DOES_NOT_END_WITH: 'non finisce con', + STARTS_WITH: 'inizia con', + DOES_NOT_START_WITH: 'non inizia con', + THEN_JUMP_TO: 'poi salta a', + + // Modifica visualizzazione presentazioni + TOTAL_VIEWS: 'visite totali totali', + RESPONSES: 'risposte', + COMPLETION_RATE: 'tasso di completamento', + AVERAGE_TIME_TO_COMPLETE: 'avg. tempo di completamento', + + DESKTOP_AND_LAPTOP: 'Desktop', + TABLETS: 'compresse', + PHONES: 'Telefoni', + OTHER: 'Altro', + UNIQUE_VISITS: 'Visite Uniche', + + FIELD_TITLE: 'Titolo del campo', + FIELD_VIEWS: 'Viste sul campo', + FIELD_DROPOFF: 'Completamento del campo', + FIELD_RESPONSES: 'Risposte sul campo', + DELETE_SELECTED: 'Elimina selezionata', + EXPORT_TO_EXCEL: 'Esporta in Excel', + EXPORT_TO_CSV: 'Esporta in CSV', + EXPORT_TO_JSON: 'Esporta in JSON', + PERCENTAGE_COMPLETE: 'Percentuale completa', + TIME_ELAPSED: 'Tempo trascorso', + DEVICE: 'Dispositivo', + LOCATION: 'Posizione', + IP_ADDRESS: 'Indirizzo IP', + DATE_SUBMITTED: 'Data trasmessa', + + // Vista di progettazione + BACKGROUND_COLOR: 'Colore di sfondo', + DESIGN_HEADER: 'Modifica il tuo aspetto forma', + QUESTION_TEXT_COLOR: 'Colore del testo di domanda', + ANSWER_TEXT_COLOR: 'Rispondere al colore del testo', + BTN_BACKGROUND_COLOR: 'Colore di sfondo del pulsante', + BTN_TEXT_COLOR: 'Colore del testo pulsante', + + // Vista condivisione + EMBED_YOUR_FORM: 'Inserisci il tuo modulo', + SHARE_YOUR_FORM: 'Condividi il tuo modulo', + + // Schede amministratore + CREATE_TAB: 'Crea', + DESIGN_TAB: 'Design', + CONFIGURE_TAB: 'Configura', + ANALYZE_TAB: 'Analizza', + SHARE_TAB: 'Condividi', + + // Tipi di campo + SHORT_TEXT: 'Testo corto', + EMAIL: 'E-mail', + MULTIPLE_CHOICE: 'Scelta multipla', + DROPDOWN: 'Dropdown', + DATE: 'Data', + PARAGRAPH_FIELD: 'Paragrafo', + YES_NO: 'Sì / no', + LEGAL: 'Legale', + RATING: 'Valutazione', + NUMBERS: 'Numeri', + SIGNATURE: 'Firma', + FILE_UPLOAD: 'Caricamento file', + OPTION_SCALE: 'Scala opzione', + PAGAMENTO: 'Pagamento', + STATEMENT: 'Dichiarazione', + LINK: 'Link', + + // Anteprima del modulo + FORM_SUCCESS: 'Inserimento modulo con successo presentato!', + REVIEW: 'Recensione', + BACK_TO_FORM: 'Torna alla scheda', + EDIT_FORM: 'Modifica questo TellForm', + ADVANCEMENT: '{{done}} su {{total}} ha risposto', + CONTINUE_FORM: "Continua a formare", + REQUIRED: 'richiesta', + COMPLETING_NEEDED: '{{answers_not_completed}} answer (s) need completing', + OPTIONAL: 'facoltativo', + ERROR_EMAIL_INVALID: 'Inserisci un indirizzo e-mail valido', + ERROR_NOT_A_NUMBER: 'Inserisci solo numeri validi', + ERROR_URL_INVALID: 'Per favore un url valido', + OK: 'OK', + ENTER: 'premere INVIO', + NEWLINE: 'premere SHIFT + INVIO per creare una nuova riga', + CONTINUE: 'Continua', + LEGAL_ACCEPT: 'accetto', + LEGAL_NO_ACCEPT: 'Non accetto', + SUBMIT: 'Invia', + UPLOAD_FILE: 'Carica il tuo file' + }); + +}]); + +'use strict'; + +angular.module('forms').config(["$translateProvider", function ($translateProvider) { + + $translateProvider.translations('es', { + + //Configure Form Tab View + ADVANCED_SETTINGS: 'Configuraciones avanzadas', + FORM_NAME: 'Nombre del formulario', + FORM_STATUS: 'Estado del formulario', + PUBLIC: 'Público', + PRIVATE: 'Privado', + GA_TRACKING_CODE: 'Código de Google Analytics', + DISPLAY_FOOTER: '¿Mostrar pie de página?', + SAVE_CHANGES: 'Grabar', + CANCEL: 'Cancelar', + DISPLAY_START_PAGE: '¿Mostrar página de inicio?', + DISPLAY_END_PAGE: '¿Mostrar paǵina de fin?', + + //List Forms View + CREATE_A_NEW_FORM: 'Crear formulario', + CREATE_FORM: 'Crear formulario', + CREATED_ON: 'Creado en', + MY_FORMS: 'Mis formularios', + NAME: 'Nombre', + LANGUAGE: 'Idioma', + FORM_PAUSED: 'Formulario pausado', + + //Edit Field Modal + EDIT_FIELD: 'Editar este campo', + SAVE_FIELD: 'Grabar', + ON: 'EN', + OFF: 'APAGADO', + REQUIRED_FIELD: 'Requerido', + LOGIC_JUMP: 'Salto lógico', + SHOW_BUTTONS: 'Botones adicionales', + SAVE_START_PAGE: 'Grabar', + + //Admin Form View + ARE_YOU_SURE: '¿Estás absolutamente seguro?', + READ_WARNING: '¡Algo malo ocurrirá si no lees esto!', + DELETE_WARNING1: 'Esta acción no tiene vuelta atrás. Esto borrará permanentemente el "', + DELETE_WARNING2: '" formulario y todos los datos asociados.', + DELETE_CONFIRM: 'Por favor escribí el nombre del formulario para confirmar.', + I_UNDERSTAND: 'Entiendo las consecuencias y quiero borrarlo.', + DELETE_FORM_SM: 'Borrar', + DELETE_FORM_MD: 'Borrar formulario', + DELETE: 'Borrar', + FORM: 'Formulario', + VIEW: 'Vista', + LIVE: 'Online', + PREVIEW: 'Vista previa', + COPY: 'Copiar', + COPY_AND_PASTE: 'Copiar y pegar esto para agregar su TellForm a su sitio web', + CHANGE_WIDTH_AND_HEIGHT: 'Cambie los valores de ancho y altura para adaptar el formulario a sus necesidades', + POWERED_BY: 'Con la tecnlogía de', + TELLFORM_URL: 'Tu TellForm está en esta URL permanente', + + //Edit Form View + DISABLED: 'Deshabilitado', + YES: 'SI', + NO: 'NO', + ADD_LOGIC_JUMP: 'Agregar salto lógico', + ADD_FIELD_LG: 'Click para agregar campo', + ADD_FIELD_MD: 'Agregar nuevo campo', + ADD_FIELD_SM: 'Agregar campo', + EDIT_START_PAGE: 'Editar paǵina de inicio', + EDIT_END_PAGE: 'Editar página de finalización', + WELCOME_SCREEN: 'Comienzo', + END_SCREEN: 'Fin', + INTRO_TITLE: 'Título', + INTRO_PARAGRAPH: 'Parágrafo', + INTRO_BTN: 'Botón de comienzo', + TITLE: 'Título', + PARAGRAPH: 'Paragrafo', + BTN_TEXT: 'Botón para volver atrás', + BUTTONS: 'Botones', + BUTTON_TEXT: 'Texto', + BUTTON_LINK: 'Link', + ADD_BUTTON: 'Agregar Botón', + PREVIEW_FIELD: 'Vista previa Pregunta', + QUESTION_TITLE: 'Título', + QUESTION_DESCRIPTION: 'Descripción', + OPTIONS: 'Opciones', + ADD_OPTION: 'Agregar Opciones', + NUM_OF_STEPS: 'Cantidad de pasos', + CLICK_FIELDS_FOOTER: 'Click en los campos para agregar', + SHAPE: 'Forma', + IF_THIS_FIELD: 'Si este campo', + IS_EQUAL_TO: 'es igual a', + IS_NOT_EQUAL_TO: 'no es igual a', + IS_GREATER_THAN: 'es mayor que', + IS_GREATER_OR_EQUAL_THAN: 'es mayor o igual que', + IS_SMALLER_THAN: 'es menor que', + IS_SMALLER_OR_EQUAL_THAN: 'is menor o igual que', + CONTAINS: 'contiene', + DOES_NOT_CONTAINS: 'no contiene', + ENDS_WITH: 'termina con', + DOES_NOT_END_WITH: 'no termina con', + STARTS_WITH: 'comienza con', + DOES_NOT_START_WITH: 'no comienza con', + THEN_JUMP_TO: 'luego salta a', + + //Edit Submissions View + TOTAL_VIEWS: 'Total de visitas únicas', + RESPONSES: 'respuestas', + COMPLETION_RATE: 'Taza de terminación', + AVERAGE_TIME_TO_COMPLETE: 'Promedio de tiempo de rellenado', + + DESKTOP_AND_LAPTOP: 'Computadora', + TABLETS: 'Tablets', + PHONES: 'Móviles', + OTHER: 'Otros', + UNIQUE_VISITS: 'Visitas únicas', + + FIELD_TITLE: 'Título de campo', + FIELD_VIEWS: 'Vistas de campo', + FIELD_DROPOFF: 'Finalización de campo', + FIELD_RESPONSES: 'Respuestas de campo', + DELETE_SELECTED: 'Borrar selección', + EXPORT_TO_EXCEL: 'Exportar a Excel', + EXPORT_TO_CSV: 'Exportar a CSV', + EXPORT_TO_JSON: 'Exportar a JSON', + PERCENTAGE_COMPLETE: 'Porcentaje de completitud', + TIME_ELAPSED: 'Tiempo usado', + DEVICE: 'Dispositivo', + LOCATION: 'Lugar', + IP_ADDRESS: 'Dirección IP', + DATE_SUBMITTED: 'Fecha de envío', + + //Design View + BACKGROUND_COLOR: 'Color de fondo', + DESIGN_HEADER: 'Cambiar diseño de formulario', + QUESTION_TEXT_COLOR: 'Color de la pregunta', + ANSWER_TEXT_COLOR: 'Color de la respuesta', + BTN_BACKGROUND_COLOR: 'Color de fondo del botón', + BTN_TEXT_COLOR: 'Color del texto del botón', + + //Share View + EMBED_YOUR_FORM: 'Pone tu formulario', + SHARE_YOUR_FORM: 'Compartí tu formulario', + + //Admin Tabs + CREATE_TAB: 'Crear', + DESIGN_TAB: 'Diseño', + CONFIGURE_TAB: 'Configuración', + ANALYZE_TAB: 'Análisis', + SHARE_TAB: 'Compartir', + + //Field Types + SHORT_TEXT: 'Texto corto', + EMAIL: 'Email', + MULTIPLE_CHOICE: 'Opciones múltiples', + DROPDOWN: 'Desplegable', + DATE: 'Fecha', + PARAGRAPH_FIELD: 'Párrafo', + YES_NO: 'Si/No', + LEGAL: 'Legal', + RATING: 'Puntaje', + NUMBERS: 'Números', + SIGNATURE: 'Firma', + FILE_UPLOAD: 'Subir archivo', + OPTION_SCALE: 'Escala', + PAYMENT: 'Pago', + STATEMENT: 'Declaración', + LINK: 'Enlace', + + FORM_SUCCESS: '¡El formulario ha sido enviado con éxito!', + REVIEW: 'Revisar', + BACK_TO_FORM: 'Regresar al formulario', + ADVANCEMENT: '{{done}} de {{total}} contestadas', + CONTINUE_FORM: 'Continuar al formulario', + REQUIRED: 'Información requerida', + COMPLETING_NEEDED: '{{answers_not_completed}} respuesta(s) necesita(n) ser completada(s)', + OPTIONAL: 'Opcional', + ERROR_EMAIL_INVALID: 'Favor de proporcionar un correo electrónico válido', + ERROR_NOT_A_NUMBER: 'Por favor, introduzca sólo números válidos', + ERROR_URL_INVALID: 'Favor de proporcionar un url válido', + OK: 'OK', + ENTER: 'pulse INTRO', + NEWLINE: 'presione SHIFT+INTRO para crear una nueva línea', + CONTINUE: 'Continuar', + LEGAL_ACCEPT: 'Yo acepto', + LEGAL_NO_ACCEPT: 'Yo no acepto', + SUBMIT: 'Registrar', + UPLOAD_FILE: 'Cargar el archivo', + Y: 'S', + N: 'N' + }); +}]); + +'use strict'; + +// Use Application configuration module to register a new module +ApplicationConfiguration.registerModule('view-form', [ + 'ngFileUpload', 'ui.date', 'angular-input-stars' +]); + +(function () { + 'use strict'; + + // Create the SendVisitorData service + angular + .module('view-form') + .factory('SendVisitorData', SendVisitorData); + + SendVisitorData.$inject = ["Socket", "$state"]; + + function SendVisitorData(Socket, $state) { + + // Create a controller method for sending visitor data + function send(form, lastActiveIndex, timeElapsed) { + + var lang = window.navigator.userLanguage || window.navigator.language; + lang = lang.slice(0,2); + + var userAgentString = navigator.userAgent; + var md = new MobileDetect(userAgentString); + var deviceType = 'other'; + + if (md.tablet()){ + deviceType = 'tablet'; + } else if (md.mobile()) { + deviceType = 'mobile'; + } else if (!md.is('bot')) { + deviceType = 'desktop'; + } + + // Create a new message object + var visitorData = { + referrer: document.referrer, + isSubmitted: form.submitted, + formId: form._id, + lastActiveField: form.form_fields[lastActiveIndex]._id, + timeElapsed: timeElapsed, + language: lang, + deviceType: deviceType, + ipAddr: null, + geoLocation: null + }; + + Socket.emit('form-visitor-data', visitorData); + } + + function init(){ + // Make sure the Socket is connected + if (!Socket.socket) { + Socket.connect(); + } + + Socket.on('disconnect', function(){ + Socket.connect(); + }); + } + + var service = { + send: send + }; + + init(); + return service; + + } +}()); + + +'use strict'; + +angular.module('view-form').directive('keyToOption', function(){ return { - applicationModuleName: applicationModuleName, - applicationModuleVendorDependencies: applicationModuleVendorDependencies, - registerModule: registerModule + restrict: 'A', + scope: { + field: '=' + }, + link: function($scope, $element, $attrs, $select) { + $element.bind('keydown keypress', function(event) { + + var keyCode = event.which || event.keyCode; + var index = parseInt(String.fromCharCode(keyCode))-1; + + if (index < $scope.field.fieldOptions.length) { + event.preventDefault(); + $scope.$apply(function () { + $scope.field.fieldValue = $scope.field.fieldOptions[index].option_value; + }); + } + + }); + } }; -})(); +}); + +'use strict'; + +angular.module('view-form').directive('keyToTruthy', ["$rootScope", function($rootScope){ + return { + restrict: 'A', + scope: { + field: '=', + nextField: '&' + }, + link: function($scope, $element, $attrs) { + $element.bind('keydown keypress', function(event) { + var keyCode = event.which || event.keyCode; + var truthyKeyCode = $attrs.keyCharTruthy.charCodeAt(0) - 32; + var falseyKeyCode = $attrs.keyCharFalsey.charCodeAt(0) - 32; + + if(keyCode === truthyKeyCode ) { + event.preventDefault(); + $scope.$apply(function() { + $scope.field.fieldValue = 'true'; + if($attrs.onValidKey){ + $scope.$root.$eval($attrs.onValidKey); + } + }); + }else if(keyCode === falseyKeyCode){ + event.preventDefault(); + $scope.$apply(function() { + $scope.field.fieldValue = 'false'; + if($attrs.onValidKey){ + $scope.$root.$eval($attrs.onValidKey); + } + }); + } + }); + } + }; +}]); + 'use strict'; @@ -1171,179 +5383,3 @@ angular.module('view-form').service('TimeCounter', function(){ }; }); - -'use strict'; - -// Setting up route -angular.module('view-form').config(["$stateProvider", function($stateProvider) { - // Forms state routing - $stateProvider. - state('submitForm', { - url: '/forms/:formId', - templateUrl: '/static/form_modules/forms/base/views/submit-form.client.view.html', - resolve: { - Forms: 'Forms', - myForm: ["Forms", "$q", "$state", "$stateParams", function (Forms, $q, $state, $stateParams) { - var deferred = $q.defer(); - - Forms.get({formId: $stateParams.formId}).$promise.then(function(data) { - deferred.resolve(data); - }, function(reason) { - $state.go('unauthorizedFormAccess'); - deferred.reject({redirectTo: 'unauthorizedFormAccess'}); - }); - return deferred.promise; - }] - }, - controller: 'SubmitFormController', - controllerAs: 'ctrl' - }). - state('unauthorizedFormAccess', { - url: '/forms/unauthorized', - templateUrl: '/static/form_modules/forms/base/views/form-unauthorized.client.view.html' - }) - .state('formNotFound', { - url: '*path', - templateUrl: '/static/form_modules/forms/base/views/form-not-found.client.view.html' - }); - } -]); - -(function () { - 'use strict'; - - // Create the SendVisitorData service - angular - .module('view-form') - .factory('SendVisitorData', SendVisitorData); - - SendVisitorData.$inject = ["Socket", "$state"]; - - function SendVisitorData(Socket, $state) { - - // Create a controller method for sending visitor data - function send(form, lastActiveIndex, timeElapsed) { - - var lang = window.navigator.userLanguage || window.navigator.language; - lang = lang.slice(0,2); - - var userAgentString = navigator.userAgent; - var md = new MobileDetect(userAgentString); - var deviceType = 'other'; - - if (md.tablet()){ - deviceType = 'tablet'; - } else if (md.mobile()) { - deviceType = 'mobile'; - } else if (!md.is('bot')) { - deviceType = 'desktop'; - } - - // Create a new message object - var visitorData = { - referrer: document.referrer, - isSubmitted: form.submitted, - formId: form._id, - lastActiveField: form.form_fields[lastActiveIndex]._id, - timeElapsed: timeElapsed, - language: lang, - deviceType: deviceType, - ipAddr: null, - geoLocation: null - }; - - Socket.emit('form-visitor-data', visitorData); - } - - function init(){ - // Make sure the Socket is connected - if (!Socket.socket) { - Socket.connect(); - } - - Socket.on('disconnect', function(){ - Socket.connect(); - }); - } - - var service = { - send: send - }; - - init(); - return service; - - } -}()); - - -'use strict'; - -angular.module('view-form').directive('keyToOption', function(){ - return { - restrict: 'A', - scope: { - field: '=' - }, - link: function($scope, $element, $attrs, $select) { - $element.bind('keydown keypress', function(event) { - - var keyCode = event.which || event.keyCode; - var index = parseInt(String.fromCharCode(keyCode))-1; - - if (index < $scope.field.fieldOptions.length) { - event.preventDefault(); - $scope.$apply(function () { - $scope.field.fieldValue = $scope.field.fieldOptions[index].option_value; - }); - } - - }); - } - }; -}); - -'use strict'; - -angular.module('view-form').directive('keyToTruthy', ["$rootScope", function($rootScope){ - return { - restrict: 'A', - scope: { - field: '=', - nextField: '&' - }, - link: function($scope, $element, $attrs) { - $element.bind('keydown keypress', function(event) { - var keyCode = event.which || event.keyCode; - var truthyKeyCode = $attrs.keyCharTruthy.charCodeAt(0) - 32; - var falseyKeyCode = $attrs.keyCharFalsey.charCodeAt(0) - 32; - - if(keyCode === truthyKeyCode ) { - event.preventDefault(); - $scope.$apply(function() { - $scope.field.fieldValue = 'true'; - if($attrs.onValidKey){ - $scope.$root.$eval($attrs.onValidKey); - } - }); - }else if(keyCode === falseyKeyCode){ - event.preventDefault(); - $scope.$apply(function() { - $scope.field.fieldValue = 'false'; - if($attrs.onValidKey){ - $scope.$root.$eval($attrs.onValidKey); - } - }); - } - }); - } - }; -}]); - - -'use strict'; - -// Use Application configuration module to register a new module -ApplicationConfiguration.registerModule('view-form', [ - 'ngFileUpload', 'ui.date', 'angular-input-stars' -]); diff --git a/public/dist/application.min.js b/public/dist/application.min.js index 26eeac55..b9aa0702 100644 --- a/public/dist/application.min.js +++ b/public/dist/application.min.js @@ -1 +1,6 @@ -"use strict";angular.module(ApplicationConfiguration.applicationModuleName,ApplicationConfiguration.applicationModuleVendorDependencies),angular.module(ApplicationConfiguration.applicationModuleName).config(["$locationProvider",function(a){a.hashPrefix("!")}]),angular.module(ApplicationConfiguration.applicationModuleName).constant("APP_PERMISSIONS",{viewAdminSettings:"viewAdminSettings",editAdminSettings:"editAdminSettings",editForm:"editForm",viewPrivateForm:"viewPrivateForm"}),angular.module(ApplicationConfiguration.applicationModuleName).constant("USER_ROLES",{admin:"admin",normal:"user",superuser:"superuser"}),angular.module(ApplicationConfiguration.applicationModuleName).constant("FORM_URL","/forms/:formId"),angular.element(document).ready(function(){"#_=_"===window.location.hash&&(window.location.hash="#!"),angular.bootstrap(document,[ApplicationConfiguration.applicationModuleName])});var ApplicationConfiguration=function(){var a="TellForm",b=["duScroll","ui.select","ngSanitize","vButton","ngResource","TellForm.templates","ui.router","ui.bootstrap","ui.utils","pascalprecht.translate","view-form"],c=function(b,c){angular.module(b,c||[]),angular.module(a).requires.push(b)};return{applicationModuleName:a,applicationModuleVendorDependencies:b,registerModule:c}}();angular.module("view-form").filter("formValidity",function(){return function(a){if(a&&a.form_fields&&a.visible_form_fields){var b=Object.keys(a),c=(b.filter(function(a){return"$"!==a[0]}),a.form_fields),d=c.filter(function(a){return"object"==typeof a&&"rating"!==a.fieldType&&"statement"!==a.fieldType?!!a.fieldValue:"rating"===a.fieldType||void 0}).length;return d-(a.form_fields.length-a.visible_form_fields.length)}return 0}}),angular.module("view-form").value("supportedFields",["textfield","textarea","date","dropdown","hidden","password","radio","legal","statement","rating","yes_no","number","natural"]),angular.module("view-form").constant("VIEW_FORM_URL","/forms/:formId/render"),angular.module("view-form").config(["$translateProvider",function(a){a.translations("en",{FORM_SUCCESS:"Form entry successfully submitted!",REVIEW:"Review",BACK_TO_FORM:"Go back to Form",EDIT_FORM:"Edit this TellForm",CREATE_FORM:"Create this TellForm",ADVANCEMENT:"{{done}} out of {{total}} answered",CONTINUE_FORM:"Continue to Form",REQUIRED:"required",COMPLETING_NEEDED:"{{answers_not_completed}} answer(s) need completing",OPTIONAL:"optional",ERROR_EMAIL_INVALID:"Please enter a valid email address",ERROR_NOT_A_NUMBER:"Please enter valid numbers only",ERROR_URL_INVALID:"Please a valid url",OK:"OK",ENTER:"press ENTER",YES:"Yes",NO:"No",NEWLINE:"press SHIFT+ENTER to create a newline",CONTINUE:"Continue",LEGAL_ACCEPT:"I accept",LEGAL_NO_ACCEPT:"I don’t accept",DELETE:"Delete",CANCEL:"Cancel",SUBMIT:"Submit",UPLOAD_FILE:"Upload your File",Y:"Y",N:"N",OPTION_PLACEHOLDER:"Type or select an option",ADD_NEW_LINE_INSTR:"Press SHIFT+ENTER to add a newline",ERROR:"Error",FORM_404_HEADER:"404 - Form Does Not Exist",FORM_404_BODY:"The form you are trying to access does not exist. Sorry about that!",FORM_UNAUTHORIZED_HEADER:"Not Authorized to Access Form",FORM_UNAUTHORIZED_BODY1:"The form you are trying to access is currently private and not accesible publically.",FORM_UNAUTHORIZED_BODY2:'If you are the owner of the form, you can set it to "Public" in the "Configuration" panel in the form admin.'}),a.preferredLanguage("en").fallbackLanguage("en").useSanitizeValueStrategy("escape")}]),angular.module("view-form").config(["$translateProvider",function(a){a.translations("fr",{FORM_SUCCESS:"Votre formulaire a été enregistré!",REVIEW:"Incomplet",BACK_TO_FORM:"Retourner au formulaire",EDIT_FORM:"Éditer le Tellform",CREATE_FORM:"Créer un TellForm",ADVANCEMENT:"{{done}} complétés sur {{total}}",CONTINUE_FORM:"Aller au formulaire",REQUIRED:"obligatoire",COMPLETING_NEEDED:"{{answers_not_completed}} réponse(s) doive(nt) être complétée(s)",OPTIONAL:"facultatif",ERROR_EMAIL_INVALID:"Merci de rentrer une adresse mail valide",ERROR_NOT_A_NUMBER:"Merce de ne rentrer que des nombres",ERROR_URL_INVALID:"Merci de rentrer une url valide",OK:"OK",ENTER:"presser ENTRÉE",YES:"Oui",NO:"Non",NEWLINE:"presser SHIFT+ENTER pour créer une nouvelle ligne",CONTINUE:"Continuer",LEGAL_ACCEPT:"J’accepte",LEGAL_NO_ACCEPT:"Je n’accepte pas",DELETE:"Supprimer",CANCEL:"Réinitialiser",SUBMIT:"Enregistrer",UPLOAD_FILE:"Envoyer un fichier",Y:"O",N:"N",OPTION_PLACEHOLDER:"Tapez ou sélectionnez une option",ADD_NEW_LINE_INSTR:"Appuyez sur MAJ + ENTRÉE pour ajouter une nouvelle ligne",ERROR:"Erreur",FORM_404_HEADER:"404 - Le formulaire n'existe pas",FORM_404_BODY:"Le formulaire auquel vous essayez d'accéder n'existe pas. Désolé pour ça!",FORM_UNAUTHORIZED_HEADER:"Non autorisé à accéder au formulaire",FORM_UNAUTHORIZED_BODY1:"Le formulaire auquel vous essayez d'accéder est actuellement privé et inaccessible publiquement.",FORM_UNAUTHORIZED_BODY2:'Si vous êtes le propriétaire du formulaire, vous pouvez le définir sur "Public" dans le panneau "Configuration" du formulaire admin.'})}]),angular.module("view-form").config(["$translateProvider",function(a){a.translations("de",{FORM_SUCCESS:"Ihre Angaben wurden gespeichert.",REVIEW:"Unvollständig",BACK_TO_FORM:"Zurück zum Formular",EDIT_FORM:"Bearbeiten Sie diese TellForm",CREATE_FORM:"Dieses TellForm erstellen",ADVANCEMENT:"{{done}} von {{total}} beantwortet",CONTINUE_FORM:"Zum Formular",REQUIRED:"verpflichtend",COMPLETING_NEEDED:"Es fehlen/fehtl noch {{answers_not_completed}} Antwort(en)",OPTIONAL:"fakultativ",ERROR_EMAIL_INVALID:"Bitte gültige Mailadresse eingeben",ERROR_NOT_A_NUMBER:"Bitte nur Zahlen eingeben",ERROR_URL_INVALID:"Bitte eine gültige URL eingeben",OK:"Okay",ENTER:"Eingabetaste drücken",YES:"Ja",NO:"Nein",NEWLINE:"Für eine neue Zeile SHIFT+ENTER drücken",CONTINUE:"Weiter",LEGAL_ACCEPT:"I accept",LEGAL_NO_ACCEPT:"I don’t accept",DELETE:"Entfernen",CANCEL:"Canceln",SUBMIT:"Speichern",UPLOAD_FILE:"Datei versenden",Y:"J",N:"N",OPTION_PLACEHOLDER:"Geben oder wählen Sie eine Option aus",ADD_NEW_LINE_INSTR:"Drücken Sie UMSCHALT + EINGABETASTE, um eine neue Zeile hinzuzufügen",ERROR:"Fehler",FORM_404_HEADER:"404 - Formular existiert nicht",FORM_404_BODY:"Das Formular, auf das Sie zugreifen möchten, existiert nicht. Das tut mir leid!",FORM_UNAUTHORIZED_HEADER:"Nicht zum Zugriffsformular berechtigt' ",FORM_UNAUTHORIZED_BODY1:"Das Formular, auf das Sie zugreifen möchten, ist derzeit privat und nicht öffentlich zugänglich.",FORM_UNAUTHORIZED_BODY2:'Wenn Sie der Eigentümer des Formulars sind, können Sie es im Fenster "Konfiguration" im Formular admin auf "Öffentlich" setzen.'})}]),angular.module("view-form").config(["$translateProvider",function(a){a.translations("it",{FORM_SUCCESS:"Il formulario è stato inviato con successo!",REVIEW:"Incompleto",BACK_TO_FORM:"Ritorna al formulario",EDIT_FORM:"Modifica questo TellForm",CREATE_FORM:"Crea questo TellForm",ADVANCEMENT:"{{done}} su {{total}} completate",CONTINUE_FORM:"Vai al formulario",REQUIRED:"obbligatorio",COMPLETING_NEEDED:"{{answers_not_completed}} risposta/e deve/ono essere completata/e",OPTIONAL:"opzionale",ERROR_EMAIL_INVALID:"Si prega di inserire un indirizzo email valido",ERROR_NOT_A_NUMBER:"Si prega di inserire solo numeri",ERROR_URL_INVALID:"Grazie per inserire un URL valido",OK:"OK",ENTER:"premere INVIO",YES:"Sì",NO:"No",NEWLINE:"premere SHIFT+INVIO per creare una nuova linea",CONTINUE:"Continua",LEGAL_ACCEPT:"I accept",LEGAL_NO_ACCEPT:"I don’t accept",DELETE:"Cancella",CANCEL:"Reset",SUBMIT:"Registra",UPLOAD_FILE:"Invia un file",Y:"S",N:"N",OPTION_PLACEHOLDER:"Digitare o selezionare un'opzione",ADD_NEW_LINE_INSTR:"Premere SHIFT + INVIO per aggiungere una nuova riga",ERROR:"Errore",FORM_404_HEADER:"404 - Il modulo non esiste",FORM_404_BODY:"La forma che stai cercando di accedere non esiste. Ci dispiace!",FORM_UNAUTHORIZED_HEADER:"Non autorizzato per accedere al modulo",FORM_UNAUTHORIZED_BODY1:"Il modulo che si sta tentando di accedere è attualmente privato e non accessibile in pubblico.",FORM_UNAUTHORIZED_BODY2:'Se sei il proprietario del modulo, puoi impostarlo su "Pubblico" nel pannello "Configurazione" nell\'amministratore di moduli.'})}]),angular.module("view-form").config(["$translateProvider",function(a){a.translations("es",{FORM_SUCCESS:"¡El formulario ha sido enviado con éxito!",REVIEW:"Revisar",BACK_TO_FORM:"Regresar al formulario",EDIT_FORM:"Editar este TellForm",CREATE_FORM:"Crear este TellForm",ADVANCEMENT:"{{done}} de {{total}} contestadas",CONTINUE_FORM:"Continuar al formulario",REQUIRED:"Información requerida",COMPLETING_NEEDED:"{{answers_not_completed}} respuesta(s) necesita(n) ser completada(s)",OPTIONAL:"Opcional",ERROR_EMAIL_INVALID:"Favor de proporcionar un correo electrónico válido",ERROR_NOT_A_NUMBER:"Por favor, introduzca sólo números válidos",ERROR_URL_INVALID:"Favor de proporcionar un url válido",OK:"OK",ENTER:"pulse INTRO",YES:"Si",NO:"No",NEWLINE:"presione SHIFT+INTRO para crear una nueva línea",CONTINUE:"Continuar",LEGAL_ACCEPT:"Yo acepto",LEGAL_NO_ACCEPT:"Yo no acepto",DELETE:"Eliminar",CANCEL:"Cancelar",SUBMIT:"Registrar",UPLOAD_FILE:"Cargar el archivo",Y:"S",N:"N",OPTION_PLACEHOLDER:"Escriba o seleccione una opción",ADD_NEW_LINE_INSTR:"Presione MAYÚS + ENTRAR para agregar una nueva línea",ERROR:"Error",FORM_404_HEADER:"404 - La forma no existe",FORM_404_BODY:"El formulario al que intenta acceder no existe. ¡Lo siento por eso!",FORM_UNAUTHORIZED_HEADER:"Non autorizzato per accedere al modulo",FORM_UNAUTHORIZED_BODY1:"Il modulo che si sta tentando di accedere è attualmente privato e non accessibile in pubblico.",FORM_UNAUTHORIZED_BODY2:'Se sei il proprietario del modulo, puoi impostarlo su "Pubblico" nel pannello "Configurazione" nell\'amministratore di moduli.'})}]),angular.module("view-form").controller("SubmitFormController",["$scope","$rootScope","$state","$translate","myForm",function(a,b,c,d,e){a.myform=e,$(".loader").fadeOut("slow"),document.body.style.background=e.design.colors.backgroundColor,d.use(e.language)}]),angular.module("view-form").directive("fieldIconDirective",function(){return{template:'',restrict:"E",scope:{typeName:"@"},controller:["$scope",function(a){var b={textfield:"fa fa-pencil-square-o",dropdown:"fa fa-th-list",date:"fa fa-calendar",checkbox:"fa fa-check-square-o",radio:"fa fa-dot-circle-o",email:"fa fa-envelope-o",textarea:"fa fa-pencil-square",legal:"fa fa-legal",file:"fa fa-cloud-upload",rating:"fa fa-star-half-o",link:"fa fa-link",scale:"fa fa-sliders",stripe:"fa fa-credit-card",statement:"fa fa-quote-left",yes_no:"fa fa-toggle-on",number:"fa fa-slack"};a.typeIcon=b[a.typeName]}]}});var __indexOf=[].indexOf||function(a){for(var b=0,c=this.length;b=0&&(c=c+b+".html"),d.get(c)};return{template:"
{{field.title}}
",restrict:"E",scope:{field:"=",required:"&",design:"=",index:"=",forms:"="},link:function(a,d){c.chooseDefaultOption=a.chooseDefaultOption=function(b){"yes_no"===b?a.field.fieldValue="true":"rating"===b?a.field.fieldValue=0:"radio"===a.field.fieldType?a.field.fieldValue=a.field.fieldOptions[0].option_value:"legal"===b&&(a.field.fieldValue="true",c.nextField())},a.nextField=c.nextField,a.setActiveField=c.setActiveField,"date"===a.field.fieldType&&(a.dateOptions={changeYear:!0,changeMonth:!0,altFormat:"mm/dd/yyyy",yearRange:"1900:-0",defaultDate:0});var e=a.field.fieldType;if("number"===a.field.fieldType||"textfield"===a.field.fieldType||"email"===a.field.fieldType||"link"===a.field.fieldType){switch(a.field.fieldType){case"textfield":a.input_type="text";break;case"email":a.input_type="email",a.placeholder="joesmith@example.com";break;case"number":a.input_type="text",a.validateRegex=/^-?\d+$/;break;default:a.input_type="url",a.placeholder="http://example.com"}e="textfield"}var g=f(e);d.html(g).show();b(d.contents())(a)}}}]),angular.module("view-form").directive("onEnterKey",["$rootScope",function(a){return{restrict:"A",link:function(b,c,d){c.bind("keydown keypress",function(b){var c=b.which||b.keyCode,e=!1;null!==d.onEnterKeyDisabled&&(e=d.onEnterKeyDisabled),13!==c||b.shiftKey||e||(b.preventDefault(),a.$apply(function(){a.$eval(d.onEnterKey)}))})}}}]).directive("onTabKey",["$rootScope",function(a){return{restrict:"A",link:function(b,c,d){c.bind("keyup keypress",function(b){var c=b.which||b.keyCode;9!==c||b.shiftKey||(b.preventDefault(),a.$apply(function(){a.$eval(d.onTabKey)}))})}}}]).directive("onEnterOrTabKey",["$rootScope",function(a){return{restrict:"A",link:function(b,c,d){c.bind("keydown keypress",function(b){var c=b.which||b.keyCode;13!==c&&9!==c||b.shiftKey||(b.preventDefault(),a.$apply(function(){a.$eval(d.onEnterOrTabKey)}))})}}}]).directive("onTabAndShiftKey",["$rootScope",function(a){return{restrict:"A",link:function(b,c,d){c.bind("keydown keypress",function(b){var c=b.which||b.keyCode;9===c&&b.shiftKey&&(console.log("onTabAndShiftKey"),b.preventDefault(),a.$apply(function(){a.$eval(d.onTabAndShiftKey)}))})}}}]),angular.module("view-form").directive("onFinishRender",["$rootScope","$timeout",function(a,b){return{restrict:"A",link:function(b,c,d){if(c.attr("ng-repeat")||c.attr("data-ng-repeat")){var e=d.onFinishRender||"ngRepeat";b.$first&&!b.$last?b.$evalAsync(function(){a.$broadcast(e+" Started")}):b.$last&&b.$evalAsync(function(){a.$broadcast(e+" Finished")})}}}}]),jsep.addBinaryOp("contains",10),jsep.addBinaryOp("!contains",10),jsep.addBinaryOp("begins",10),jsep.addBinaryOp("!begins",10),jsep.addBinaryOp("ends",10),jsep.addBinaryOp("!ends",10),angular.module("view-form").directive("submitFormDirective",["$http","TimeCounter","$filter","$rootScope","SendVisitorData","$translate","$timeout",function(a,b,c,d,e,f,g){return{templateUrl:"form_modules/forms/base/views/directiveViews/form/submit-form.client.view.html",restrict:"E",scope:{myform:"=",ispreview:"="},controller:["$document","$window","$scope",function(f,g,h){var i=!1,j="submit_field";h.forms={},h.ispreview&&b.restartClock();var k=h.myform.visible_form_fields.filter(function(a){return"statement"!==a.fieldType}).length,l=c("formValidity")(h.myform);h.translateAdvancementData={done:l,total:k,answers_not_completed:k-l},h.reloadForm=function(){h.myform.submitted=!1,h.myform.form_fields=_.chain(h.myform.visible_form_fields).map(function(a){return a.fieldValue="",a}).value(),h.loading=!1,h.error="",h.selected={_id:"",index:0},h.setActiveField(h.myform.visible_form_fields[0]._id,0,!1),b.restartClock()};var m=function(a){var b=a.logicJump;if(b.enabled&&b.expressionString&&b.valueB&&a.fieldValue){var c,d,e=jsep(b.expressionString);if("field"===e.left.name?(c=a.fieldValue,d=b.valueB):(c=b.valueB,d=a.fieldValue),"number"===a.fieldType||"scale"===a.fieldType||"rating"===a.fieldType)switch(e.operator){case"==":return parseInt(c)===parseInt(d);case"!==":return parseInt(c)!==parseInt(d);case">":return parseInt(c)>parseInt(d);case">=":return parseInt(c)>parseInt(d);case"<":return parseInt(c)-1;case"!contains":return!(c.indexOf(d)>-1);case"begins":return c.startsWith(d);case"!begins":return!c.startsWith(d);case"ends":return c.endsWith(d);case"!ends":return c.endsWith(d);default:return!1}}},n=function(){if(null===h.selected)throw console.error("current active field is null"),new Error("current active field is null");return h.selected._id===j?h.myform.form_fields.length-1:h.selected.index};h.isActiveField=function(a){return h.selected._id===a._id},h.setActiveField=d.setActiveField=function(a,b,d){if(null!==h.selected&&(a||null!==b)){if(a){if(null===b){b=h.myform.visible_form_fields.length;for(var e=0;e .field-directive:nth-of-type("+String(h.myform.visible_form_fields.length-1)+")"),m=$(l).height(),n=k-g-1.2*m,o=.9;h.selected.index===h.myform.visible_form_fields.length?bn?(a=h.selected.index+1,h.setActiveField(j,a,!1)):ef*o&&(a=h.selected.index-1,h.setActiveField(null,a,!1))}h.$apply()},d.nextField=h.nextField=function(){if(h.selected&&h.selected.index>-1)if(h.selected._id!==j){var a=h.myform.visible_form_fields[h.selected.index];a.logicJump&&a.logicJump.jumpTo&&m(a)?h.setActiveField(a.logicJump.jumpTo,null,!0):h.selected.index0&&h.setActiveField(null,a,!0)},d.goToInvalid=h.goToInvalid=function(){var a=$(".row.field-directive .ng-invalid.focusOn, .row.field-directive .ng-untouched.focusOn:not(.ng-valid)").first().parents(".row.field-directive").first().attr("data-id");h.setActiveField(a,null,!0)},h.exitStartPage=function(){h.myform.startPage.showStart=!1,h.myform.visible_form_fields.length>0&&(h.selected._id=h.myform.visible_form_fields[0]._id)};var o=function(){var a=new MobileDetect(window.navigator.userAgent),b="other";return a.tablet()?b="tablet":a.mobile()?b="mobile":a.is("bot")||(b="desktop"),{type:b,name:window.navigator.platform}},p=function(){$.ajaxSetup({async:!1});var a=$.getJSON("https://freegeoip.net/json/").responseJSON;return $.ajaxSetup({async:!0}),a&&a.ip||(a={ip:"Adblocker"}),{ipAddr:a.ip,geoLocation:{City:a.city,Country:a.country_name}}};d.submitForm=h.submitForm=function(){if(h.forms.myForm.$invalid)return void h.goToInvalid();var d=b.stopClock();h.loading=!0;var f=_.cloneDeep(h.myform),g=o();f.device=g;var i=p();f.ipAddr=i.ipAddr,f.geoLocation=i.geoLocation,f.timeElapsed=d,f.percentageComplete=c("formValidity")(h.myform)/h.myform.visible_form_fields.length*100,delete f.endPage,delete f.isLive,delete f.provider,delete f.startPage,delete f.visible_form_fields,delete f.analytics,delete f.design,delete f.submissions,delete f.submitted;for(var j=0;j"),a.put("modules/forms/admin/views/admin-form.client.view.html",'
{{ \'TELLFORM_URL\' | translate }}
{{ \'COPY_AND_PASTE\' | translate }}
{{ \'BACKGROUND_COLOR\' | translate }}
{{ \'QUESTION_TEXT_COLOR\' | translate }}
{{ \'ANSWER_TEXT_COLOR\' | translate }}
{{ \'BTN_BACKGROUND_COLOR\' | translate }}
{{ \'BTN_TEXT_COLOR\' | translate }}
'),a.put("modules/forms/admin/views/list-forms.client.view.html",'

{{ \'CREATE_A_NEW_FORM\' | translate }}
{{ \'NAME\' | translate }}
{{ \'LANGUAGE\' | translate }}

{{ form.numberOfResponses }} {{ \'RESPONSES\' | translate }}

{{ \'FORM_PAUSED\' | translate }}
'),a.put("modules/forms/admin/views/adminTabs/analyze.html",""),a.put("modules/forms/admin/views/adminTabs/configure.html",""),a.put("modules/forms/admin/views/adminTabs/create.html",""),a.put("modules/forms/admin/views/directiveViews/form/configure-form.client.view.html",'
{{ \'FORM_NAME\' | translate }}
{{ \'FORM_STATUS\' | translate }}
{{ \'LANGUAGE\' | translate }}
* {{ \'REQUIRED_FIELD\' | translate }}
{{ \'GA_TRACKING_CODE\' | translate }}
{{ \'DISPLAY_FOOTER\' | translate }}
{{ \'DISPLAY_START_PAGE\' | translate }}
{{ \'DISPLAY_END_PAGE\' | translate }}
'),a.put("modules/forms/admin/views/directiveViews/form/edit-form.client.view.html",'

{{ \'WELCOME_SCREEN\' | translate }}


{{field.title}} *

{{ \'CLICK_FIELDS_FOOTER\' | translate }}


{{ \'END_SCREEN\' | translate }}

'), +a.put("modules/forms/admin/views/directiveViews/form/edit-submissions-form.client.view.html","
{{ 'TOTAL_VIEWS' | translate }}
{{ 'RESPONSES' | translate }}
{{ 'COMPLETION_RATE' | translate }}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{analyticsData.globalStatistics.visits}}
{{analyticsData.globalStatistics.responses | number:0}}
{{analyticsData.globalStatistics.conversion_rate | number:0}}
{{ analyticsData.globalStatistics.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'DESKTOP_AND_LAPTOP' | translate }}
{{ 'TABLETS' | translate }}
{{ 'PHONES' | translate }}
{{ 'OTHER' | translate }}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.desktop.visits}}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.tablet.visits}}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.tablet.visits}}
{{ 'UNIQUE_VISITS' | translate }}
{{DeviceStatistics.other.visits}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.desktop.responses}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.tablet.responses}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.phone.responses}}
{{ 'RESPONSES' | translate }}
{{DeviceStatistics.other.responses}}
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.desktop.completion}}%
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.tablet.completion}}%
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.phone.completion}}%
{{ 'COMPLETION_RATE' | translate }}
{{DeviceStatistics.other.completion}}%
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.desktop.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.tablet.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.phone.average_time | secondsToDateTime | date:'mm:ss'}}
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
{{DeviceStatistics.other.average_time | secondsToDateTime | date:'mm:ss'}}

#{{value.title}}{{ 'PERCENTAGE_COMPLETE' | translate }}{{ 'TIME_ELAPSED' | translate }}{{ 'DEVICE' | translate }}{{ 'LOCATION' | translate }}{{ 'IP_ADDRESS' | translate }}{{ 'DATE_SUBMITTED' | translate }} (UTC)
{{$index+1}}{{field.fieldValue}}{{row.percentageComplete}}%{{row.timeElapsed | secondsToDateTime | date:'mm:ss'}}{{row.device.name}}, {{row.device.type}}{{row.geoLocation.City}}, {{row.geoLocation.Country}}{{row.ipAddr}}{{row.created | date:'yyyy-MM-dd HH:mm:ss'}}
"),a.put("modules/users/views/authentication/access-denied.client.view.html",'

{{ \'ACCESS_DENIED_TEXT\' | translate }}

'),a.put("modules/users/views/authentication/signin.client.view.html",'
'),a.put("modules/users/views/authentication/signup-success.client.view.html",''),a.put("modules/users/views/authentication/signup.client.view.html",''),a.put("modules/users/views/password/forgot-password.client.view.html",'
{{ \'ERROR\' | translate }}: {{error}}
{{success}}
'),a.put("modules/users/views/password/reset-password-invalid.client.view.html",'

{{ \'PASSWORD_RESET_INVALID\' | translate }}

'),a.put("modules/users/views/password/reset-password-success.client.view.html",'

{{ \'PASSWORD_RESET_SUCCESS\' | translate }}

'),a.put("modules/users/views/password/reset-password.client.view.html",'

{{ \'RESET_PASSWORD\' | translate }}

'),a.put("modules/users/views/settings/change-password.client.view.html",'

{{ \'CHANGE_PASSWORD\' | translate }}

'),a.put("modules/users/views/settings/edit-profile.client.view.html",'

{{ \'EDIT_PROFILE\' | translate }}

'),a.put("modules/users/views/settings/social-accounts.client.view.html",'

{{ \'CONNECTED_SOCIAL_ACCOUNTS\' | translate }}:

{{ \'CONNECT_OTHER_SOCIAL_ACCOUNTS\' | translate }}

'),a.put("modules/users/views/verify/resend-verify-email.client.view.html",'
{{error}}

{{ \'VERIFICATION_EMAIL_SENT\' | translate }}

{{ \'VERIFICATION_EMAIL_SENT_TO\' | translate }} {{username}}.
{{ \'NOT_ACTIVATED_YET\' | translate }}

{{ \'CHECK_YOUR_EMAIL\' | translate }} polydaic@gmail.com

'),a.put("modules/users/views/verify/verify-account.client.view.html",'

{{ \'VERIFY_SUCCESS\' | translate }}

'),a.put("form_modules/forms/base/views/directiveViews/entryPage/startPage.html",'

{{pageData.introTitle}}

{{pageData.introParagraph}}

'),a.put("form_modules/forms/base/views/directiveViews/field/date.html",'

{{index+1}} {{field.title}} {{ \'OPTIONAL\' | translate }}

{{field.description}}

'),a.put("form_modules/forms/base/views/directiveViews/field/dropdown.html",'
'),a.put("form_modules/forms/base/views/directiveViews/field/hidden.html",""),a.put("form_modules/forms/base/views/directiveViews/field/legal.html",'
'),a.put("form_modules/forms/base/views/directiveViews/field/radio.html",'

{{index+1}} {{field.title}} {{ \'OPTIONAL\' | translate }}

{{field.description}}


'),a.put("form_modules/forms/base/views/directiveViews/field/rating.html",'

{{index+1}} {{field.title}} {{ \'OPTIONAL\' | translate }}

{{field.description}}

'),a.put("form_modules/forms/base/views/directiveViews/field/statement.html",'

{{field.title}}

{{field.description}}

{{field.description}}


'),a.put("form_modules/forms/base/views/directiveViews/field/textarea.html",'

{{index+1}} {{field.title}} {{ \'OPTIONAL\' | translate }}

{{ \'NEWLINE\' | translate }}

{{field.description}}

{{ \'ADD_NEW_LINE_INSTR\' | translate }}
{{ \'ENTER\' | translate }}
'), +a.put("form_modules/forms/base/views/directiveViews/field/textfield.html",'

{{index+1}} {{field.title}} ({{ \'OPTIONAL\' | translate }})

{{field.description}}

{{ \'ENTER\' | translate }}
'),a.put("form_modules/forms/base/views/directiveViews/field/yes_no.html",'

{{index+1}} {{field.title}} {{ \'OPTIONAL\' | translate }}

{{field.description}}


'),a.put("form_modules/forms/base/views/directiveViews/form/submit-form.client.view.html",'
{{ \'COMPLETING_NEEDED\' | translate:translateAdvancementData }}
'),a.put("form_modules/forms/base/views/form-not-found.client.view.html",'

{{ \'FORM_404_HEADER\' | translate }}

{{ \'FORM_404_BODY\' | translate }}
'),a.put("form_modules/forms/base/views/form-unauthorized.client.view.html",'

{{ \'FORM_UNAUTHORIZED_HEADER\' | translate }}

{{ \'FORM_UNAUTHORIZED_BODY1\' | translate }}
{{ \'FORM_UNAUTHORIZED_BODY2\' | translate }}
'),a.put("form_modules/forms/base/views/submit-form.client.view.html","
")}]),ApplicationConfiguration.registerModule("core",["users"]),ApplicationConfiguration.registerModule("forms",["ngFileUpload","ui.date","ui.sortable","angular-input-stars","users","ngclipboard"]),ApplicationConfiguration.registerModule("users"),angular.module("core").config(["$stateProvider","$urlRouterProvider","Authorization",function(a,b,c){b.otherwise("/forms")}]),angular.module(ApplicationConfiguration.applicationModuleName).run(["$rootScope","Auth","$state","$stateParams",function(a,b,c,d){a.$state=c,a.$stateParams=d,a.$on("$stateChangeSuccess",function(a,d,e,f,g){c.previous={state:f,params:g};var h=["","home","signin","resendVerifyEmail","verify","signup","signup-success","forgot","reset-invalid","reset","reset-success"];h.indexOf(d.name)>0?b.isAuthenticated()&&(a.preventDefault(),c.go("listForms")):"access_denied"===d.name||b.isAuthenticated()||"submitForm"===d.name||(a.preventDefault(),c.go("listForms"))})}]),angular.module(ApplicationConfiguration.applicationModuleName).run(["$rootScope","Auth","User","Authorizer","$state","$stateParams",function(a,b,c,d,e,f){a.$on("$stateChangeStart",function(a,f){var g,h,i;h=f&&f.data&&f.data.permissions?f.data.permissions:null,b.ensureHasCurrentUser(c),i=b.currentUser,i&&(g=new d(i),null!==h&&(g.canAccess(h)||(a.preventDefault(),e.go("access_denied"))))})}]),angular.module("core").controller("HeaderController",["$rootScope","$scope","Menus","$state","Auth","User","$window","$translate",function(a,b,c,d,e,f,g,h){a.signupDisabled=g.signupDisabled,b.user=a.user=e.ensureHasCurrentUser(f),b.authentication=a.authentication=e,a.languages=b.languages=["en","fr","es","it","de"],a.language=b.user.language,h.use(b.user.language),b.isCollapsed=!1,a.hideNav=!1,b.menu=c.getMenu("topbar"),b.signout=function(){var c=f.logout();c.then(function(){e.logout(),e.ensureHasCurrentUser(f),b.user=a.user=null,d.go("listForms"),d.reload()},function(a){console.error("Logout Failed: "+a)})},b.toggleCollapsibleMenu=function(){b.isCollapsed=!b.isCollapsed},b.$on("$stateChangeSuccess",function(c,d,e,f,g){b.isCollapsed=!1,a.hideNav=!1,angular.isDefined(d.data)&&angular.isDefined(d.data.hideNav)&&(a.hideNav=d.data.hideNav)})}]),angular.module("core").service("Menus",function(){this.defaultRoles=["*"],this.menus={};var a=function(a){if(a){if(~this.roles.indexOf("*"))return!0;for(var b in a.roles)for(var c in this.roles)if(this.roles[c]===a.roles[b])return!0;return!1}return this.isPublic};this.validateMenuExistance=function(a){if(a&&a.length){if(this.menus[a])return!0;throw new Error("Menu does not exists")}throw new Error("MenuId was not provided")},this.getMenu=function(a){return this.validateMenuExistance(a),this.menus[a]},this.addMenu=function(b,c,d){return this.menus[b]={isPublic:c||!1,roles:d||this.defaultRoles,items:[],shouldRender:a},this.menus[b]},this.removeMenu=function(a){this.validateMenuExistance(a),delete this.menus[a]},this.addMenuItem=function(b,c,d,e,f,g,h,i){return this.validateMenuExistance(b),this.menus[b].items.push({title:c,link:d,menuItemType:e||"item",menuItemClass:e,uiRoute:f||"/"+d,isPublic:null===g||"undefined"==typeof g?this.menus[b].isPublic:g,roles:null===h||"undefined"==typeof h?this.menus[b].roles:h,position:i||0,items:[],shouldRender:a}),this.menus[b]},this.addSubMenuItem=function(b,c,d,e,f,g,h,i){this.validateMenuExistance(b);for(var j in this.menus[b].items)this.menus[b].items[j].link===c&&this.menus[b].items[j].items.push({title:d,link:e,uiRoute:f||"/"+e,isPublic:null===g||"undefined"==typeof g?this.menus[b].items[j].isPublic:g,roles:null===h||"undefined"==typeof h?this.menus[b].items[j].roles:h,position:i||0,shouldRender:a});return this.menus[b]},this.removeMenuItem=function(a,b){this.validateMenuExistance(a);for(var c in this.menus[a].items)this.menus[a].items[c].link===b&&this.menus[a].items.splice(c,1);return this.menus[a]},this.removeSubMenuItem=function(a,b){this.validateMenuExistance(a);for(var c in this.menus[a].items)for(var d in this.menus[a].items[c].items)this.menus[a].items[c].items[d].link===b&&this.menus[a].items[c].items.splice(d,1);return this.menus[a]},this.addMenu("topbar",!1,["*"]),this.addMenu("bottombar",!1,["*"])}),angular.module("core").factory("subdomain",["$location",function(a){var b=a.host();return b.indexOf(".")<0?null:b.split(".")[0]}]),angular.module("forms").run(["Menus",function(a){a.addMenuItem("topbar","My Forms","forms","","/forms",!1)}]).filter("secondsToDateTime",function(){return function(a){return new Date(1970,0,1).setSeconds(a)}}).filter("formValidity",function(){return function(a){if(a&&a.form_fields&&a.visible_form_fields){var b=Object.keys(a),c=(b.filter(function(a){return"$"!==a[0]}),a.form_fields),d=c.filter(function(a){return"object"==typeof a&&"statement"!==a.fieldType&&"rating"!==a.fieldType?!!a.fieldValue:"rating"===a.fieldType||void 0}).length;return d-(a.form_fields.length-a.visible_form_fields.length)}return 0}}).filter("trustSrc",["$sce",function(a){return function(b){return a.trustAsResourceUrl(b)}}]).config(["$provide",function(a){a.decorator("accordionDirective",["$delegate",function(a){var b=a[0];return b.replace=!0,a}])}]),angular.module("forms").config(["$stateProvider",function(a){a.state("listForms",{url:"/forms",templateUrl:"modules/forms/admin/views/list-forms.client.view.html",resolve:{Forms:"GetForms",myForms:["GetForms","$q",function(a,b){var c=b.defer();return a.query(function(a){c.resolve(a)}),c.promise}]},controller:"ListFormsController",controllerAs:"ctrl"}).state("submitForm",{url:"/forms/:formId",templateUrl:"/static/form_modules/forms/base/views/submit-form.client.view.html",data:{hideNav:!0},resolve:{Forms:"GetForms",myForm:["GetForms","$stateParams","$q",function(a,b,c){var d=c.defer();return a.get({formId:b.formId},function(a){d.resolve(a)}),d.promise}]},controller:"SubmitFormController",controllerAs:"ctrl"}).state("viewForm",{url:"/forms/:formId/admin",templateUrl:"modules/forms/admin/views/admin-form.client.view.html",data:{permissions:["editForm"]},resolve:{GetForms:"GetForms",myForm:["GetForms","$stateParams","$q",function(a,b,c){var d=c.defer();return a.get({formId:b.formId},function(a){d.resolve(a)}),d.promise}],formId:["$stateParams",function(a){return a.formId}]},controller:"AdminFormController"}).state("viewForm.configure",{url:"/configure",templateUrl:"modules/forms/admin/views/adminTabs/configure.html"}).state("viewForm.design",{url:"/design",templateUrl:"modules/forms/admin/views/adminTabs/design.html"}).state("viewForm.analyze",{url:"/analyze",templateUrl:"modules/forms/admin/views/adminTabs/analyze.html"}).state("viewForm.create",{url:"/create",templateUrl:"modules/forms/admin/views/adminTabs/create.html"})}]),angular.module("forms").factory("GetForms",["$resource","FORM_URL",function(a,b){return a(b,{formId:"@_id"},{query:{method:"GET",url:"/forms",isArray:!0},get:{method:"GET",transformResponse:function(a,b){var c=angular.fromJson(a);return c.visible_form_fields=_.filter(c.form_fields,function(a){return a.deletePreserved===!1}),c}},update:{method:"PUT"},save:{method:"POST"}})}]),angular.module("users").config(["$httpProvider",function(a){a.interceptors.push(["$q","$location",function(a,b){return{responseError:function(c){return"/users/me"!==b.path()&&c.config&&"/users/me"!==c.config.url&&(401===c.status?(b.nextAfterLogin=b.path(),b.path("/signin")):403===c.status&&b.path("/access_denied")),a.reject(c)}}}])}]),angular.module("users").config(["$stateProvider",function(a){var b=function(a,b,c,d,e){var f=a.defer();return e.currentUser&&e.currentUser.email?b(f.resolve):e.currentUser=d.getCurrent(function(){e.login(),b(f.resolve())},function(){e.logout(),b(f.reject()),c.go("signin",{reload:!0})}),f.promise};b.$inject=["$q","$timeout","$state","User","Auth"];var c=function(a,b,c){var d=c.defer();return b(a.signupDisabled?d.reject():d.resolve()),d.promise};c.$inject=["$window","$timeout","$q"],a.state("profile",{resolve:{loggedin:b},url:"/settings/profile",templateUrl:"modules/users/views/settings/edit-profile.client.view.html"}).state("password",{resolve:{loggedin:b},url:"/settings/password",templateUrl:"modules/users/views/settings/change-password.client.view.html"}).state("accounts",{resolve:{loggedin:b},url:"/settings/accounts",templateUrl:"modules/users/views/settings/social-accounts.client.view.html"}).state("signup",{resolve:{isDisabled:c},url:"/signup",templateUrl:"modules/users/views/authentication/signup.client.view.html"}).state("signup-success",{resolve:{isDisabled:c},url:"/signup-success",templateUrl:"modules/users/views/authentication/signup-success.client.view.html"}).state("signin",{url:"/signin",templateUrl:"modules/users/views/authentication/signin.client.view.html"}).state("access_denied",{url:"/access_denied",templateUrl:"modules/users/views/authentication/access-denied.client.view.html"}).state("verify",{resolve:{isDisabled:c},url:"/verify/:token",templateUrl:"modules/users/views/verify/verify-account.client.view.html"}).state("resendVerifyEmail",{resolve:{isDisabled:c},url:"/verify",templateUrl:"modules/users/views/verify/resend-verify-email.client.view.html"}).state("forgot",{url:"/password/forgot",templateUrl:"modules/users/views/password/forgot-password.client.view.html"}).state("reset-invalid",{url:"/password/reset/invalid",templateUrl:"modules/users/views/password/reset-password-invalid.client.view.html"}).state("reset-success",{url:"/password/reset/success",templateUrl:"modules/users/views/password/reset-password-success.client.view.html"}).state("reset",{url:"/password/reset/:token",templateUrl:"modules/users/views/password/reset-password.client.view.html"})}]),angular.module("users").controller("AuthenticationController",["$scope","$location","$state","$rootScope","User","Auth","$translate","$window",function(a,b,c,d,e,f,g,h){a=d,a.credentials={},a.error="",a.forms={};var i=["","home","signin","resendVerifyEmail","verify","signup","signup-success","forgot","reset-invalid","reset","reset-success"];a.signin=function(){a.forms&&a.forms.hasOwnProperty("siginForm")&&!a.forms.signinForm.$invalid&&e.login(a.credentials).then(function(b){f.login(b),a.user=d.user=f.ensureHasCurrentUser(e),i.indexOf(c.previous.state.name)===-1?c.go(c.previous.state.name,c.previous.params):c.go("listForms")},function(b){d.user=f.ensureHasCurrentUser(e),a.user=d.user,a.error=b,console.error("loginError: "+b)})},a.signup=function(){return"admin"===a.credentials?void(a.error="Username cannot be 'admin'. Please pick another username."):void(a.forms.signupForm.$invalid||e.signup(a.credentials).then(function(a){c.go("signup-success")},function(b){console.error(b),b?(a.error=b,console.error(b)):console.error("No response received")}))}}]),angular.module("users").controller("PasswordController",["$scope","$stateParams","$state","User","$translate","$window",function(a,b,c,d,e,f){e.use(f.locale),a.error="",a.forms={},a.askForPasswordReset=function(){d.askForPasswordReset(a.credentials).then(function(b){a.success=b.message,a.error=null,a.credentials=null},function(b){a.error=b,a.success=null,a.credentials=null})},a.resetUserPassword=function(){a.forms.resetPasswordForm.$invalid||(a.success=a.error=null,d.resetPassword(a.passwordDetails,b.token).then(function(b){a.success=b.message,a.error=null,a.passwordDetails=null,c.go("reset-success")},function(b){a.error=b.message||b,a.success=null,a.passwordDetails=null}))}}]),angular.module("users").controller("SettingsController",["$scope","$rootScope","$http","$state","Users","Auth",function(a,b,c,d,e,f){a.user=f.currentUser,a.hasConnectedAdditionalSocialAccounts=function(b){for(var c in a.user.additionalProvidersData)return!0;return!1},a.cancel=function(){a.user=f.currentUser},a.isConnectedSocialAccount=function(b){return a.user.provider===b||a.user.additionalProvidersData&&a.user.additionalProvidersData[b]},a.removeUserSocialAccount=function(b){a.success=a.error=null,c["delete"]("/users/accounts",{params:{provider:b}}).success(function(b){a.success=!0,a.error=null,a.user=b}).error(function(b){a.success=null,a.error=b.message})},a.updateUserProfile=function(b){if(b){a.success=a.error=null;var c=new e(a.user);c.$update(function(b){a.success=!0,a.error=null,a.user=b},function(b){a.success=null,a.error=b.data.message})}else a.submitted=!0},a.changeUserPassword=function(){a.success=a.error=null,c.post("/users/password",a.passwordDetails).success(function(b){a.success=!0,a.error=null,a.passwordDetails=null}).error(function(b){a.success=null,a.error=b.message})}}]),angular.module("users").controller("VerifyController",["$scope","$state","$rootScope","User","Auth","$stateParams","$translate","$window",function(a,b,c,d,e,f,g,h){g.use(h.locale),a.isResetSent=!1,a.credentials={},a.error="",a.resendVerifyEmail=function(){d.resendVerifyEmail(a.credentials.email).then(function(b){a.success=b.message,a.error=null,a.credentials=null,a.isResetSent=!0},function(b){a.error=b,a.success=null,a.credentials.email=null,a.isResetSent=!1})},a.validateVerifyToken=function(){f.token&&(console.log(f.token),d.validateVerifyToken(f.token).then(function(b){a.success=b.message,a.error=null,a.isResetSent=!0,a.credentials.email=null},function(b){a.isResetSent=!1,a.success=null,a.error=b,a.credentials.email=null}))}}]),angular.module("users").factory("Auth",["$window",function(a){var b={isLoggedIn:!1},c={_currentUser:null,get currentUser(){return this._currentUser},ensureHasCurrentUser:function(d){return c._currentUser&&c._currentUser.username?c._currentUser:a.user?(c._currentUser=a.user,c._currentUser):void d.getCurrent().then(function(d){return c._currentUser=d,b.isLoggedIn=!0,a.user=c._currentUser,c._currentUser},function(d){return b.isLoggedIn=!1,c._currentUser=null,a.user=null,null})},isAuthenticated:function(){return!!c._currentUser},getUserState:function(){return b},login:function(a){b.isLoggedIn=!0,c._currentUser=a},logout:function(){a.user=null,b.isLoggedIn=!1,c._currentUser=null}};return c}]),angular.module("users").service("Authorizer",["APP_PERMISSIONS","USER_ROLES",function(a,b){return function(c){return{canAccess:function(d){var e,f,g;for(angular.isArray(d)||(d=[d]),e=0,f=d.length;e-1;case a.viewPrivateForm:case a.editForm:return c.roles.indexOf(b.admin)>-1||c.roles.indexOf(b.normal)>-1}}return!1}}}}]),angular.module("users").factory("User",["$window","$q","$timeout","$http","$state",function(a,b,c,d,e){var f={getCurrent:function(){var a=b.defer();return d.get("/users/me").success(function(b){a.resolve(b)}).error(function(){a.reject("User's session has expired")}),a.promise},login:function(a){var c=b.defer();return d.post("/auth/signin",a).then(function(a){c.resolve(a.data)},function(a){c.reject(a.data.message||a.data)}),c.promise},logout:function(){var a=b.defer();return d.get("/auth/signout").then(function(b){a.resolve(null)},function(b){a.reject(b.data.message||b.data)}),a.promise},signup:function(a){var c=b.defer();return d.post("/auth/signup",a).then(function(a){c.resolve(a.data)},function(a){c.reject(a.data.message||a.data)}),c.promise},resendVerifyEmail:function(a){var c=b.defer();return d.post("/auth/verify",{email:a}).then(function(a){c.resolve(a.data)},function(a){c.reject(a.data.message||a.data)}),c.promise},validateVerifyToken:function(a){var c=/^([A-Za-z0-9]{48})$/g;if(!c.test(a))throw new Error("Error token: "+a+" is not a valid verification token");var e=b.defer();return d.get("/auth/verify/"+a).then(function(a){e.resolve(a.data)},function(a){e.reject(a.data)}),e.promise},resetPassword:function(a,c){var e=b.defer();return d.post("/auth/reset/"+c,a).then(function(a){e.resolve(a)},function(a){e.reject(a.data.message||a.data)}),e.promise},askForPasswordReset:function(a){var c=b.defer();return d.post("/auth/forgot",a).then(function(a){c.resolve(a.data)},function(a){c.reject(a.data.message||a.data)}),c.promise}};return f}]),angular.module("users").factory("Users",["$resource",function(a){return a("users",{},{update:{method:"PUT"}})}]),angular.module("core").config(["$translateProvider",function(a){a.translations("en",{MENU:"MENU",SIGNUP_TAB:"Sign Up",SIGNIN_TAB:"Sign In",SIGNOUT_TAB:"Signout",EDIT_PROFILE:"Edit Profile",MY_SETTINGS:"My Settings",CHANGE_PASSWORD:"Change Password",TOGGLE_NAVIGATION:"Toggle navigation"}),a.preferredLanguage("en").fallbackLanguage("en").useSanitizeValueStrategy("escape")}]),angular.module("core").config(["$translateProvider",function(a){a.translations("fr",{MENU:"MENU",SIGNUP_TAB:"Créer un Compte",SIGNIN_TAB:"Connexion",SIGNOUT_TAB:"Créer un compte",EDIT_PROFILE:"Modifier Mon Profil",MY_SETTINGS:"Mes Paramètres",CHANGE_PASSWORD:"Changer mon Mot de Pass",TOGGLE_NAVIGATION:"Basculer la navigation"})}]),angular.module("core").config(["$translateProvider",function(a){a.translations("de",{MENU:"MENÜ",SIGNUP_TAB:"Anmelden",SIGNIN_TAB:"Anmeldung",SIGNOUT_TAB:"Abmelden",EDIT_PROFILE:"Profil bearbeiten",MY_SETTINGS:"Meine Einstellungen",CHANGE_PASSWORD:"Passwort ändern",TOGGLE_NAVIGATION:"Navigation umschalten"})}]),angular.module("core").config(["$translateProvider",function(a){a.translations("it",{MENU:"MENÜ",SIGNUP_TAB:"Vi Phrasal",SIGNIN_TAB:"Accedi",SIGNOUT_TAB:"Esci",EDIT_PROFILE:"Modifica Profilo",MY_SETTINGS:"Mie Impostazioni",CHANGE_PASSWORD:"Cambia la password",TOGGLE_NAVIGATION:"Attiva la navigazione"})}]),angular.module("core").config(["$translateProvider",function(a){a.translations("es",{MENU:"MENU",SIGNUP_TAB:"Registrarse",SIGNIN_TAB:"Entrar",SIGNOUT_TAB:"Salir",EDIT_PROFILE:"Editar Perfil",MY_SETTINGS:"Mis configuraciones",CHANGE_PASSWORD:"Cambiar contraseña",TOGGLE_NAVIGATION:"Navegación de palanca"})}]),angular.module("forms").controller("AdminFormController",["$rootScope","$window","$scope","$stateParams","$state","Forms","CurrentForm","$http","$uibModal","myForm","$filter","$translate",function(a,b,c,d,e,f,g,h,i,j,k,l){c.activePill=0,c.copied=!1,c.onCopySuccess=function(a){c.copied=!0},c=a,c.animationsEnabled=!0,c.myform=j,a.saveInProgress=!1,c.oldForm=_.cloneDeep(c.myform),g.setForm(c.myform),c.formURL="/#!/forms/"+c.myform._id,c.myform.isLive?b.subdomainsDisabled===!0?c.actualFormURL=window.location.protocol+"//"+window.location.host+"/view"+c.formURL:window.location.host.split(".").length<3?c.actualFormURL=window.location.protocol+"//"+c.myform.admin.username+"."+window.location.host+c.formURL:c.actualFormURL=window.location.protocol+"//"+c.myform.admin.username+"."+window.location.host.split(".").slice(1,3).join(".")+c.formURL:c.actualFormURL=window.location.protocol+"//"+window.location.host+c.formURL;var m=c.refreshFrame=function(){document.getElementById("iframe")&&document.getElementById("iframe").contentWindow.location.reload()};c.tabData=[{heading:k("translate")("CONFIGURE_TAB"),templateName:"configure"}],c.designTabActive=!1,c.deactivateDesignTab=function(){c.designTabActive=!1},c.activateDesignTab=function(){c.designTabActive=!0},c.setForm=function(a){c.myform=a},a.resetForm=function(){c.myform=f.get({formId:d.formId})},c.openDeleteModal=function(){c.deleteModal=i.open({animation:c.animationsEnabled,templateUrl:"formDeleteModal.html",controller:"AdminFormController",resolve:{myForm:function(){return c.myform}}}),c.deleteModal.result.then(function(a){ +c.selected=a})},c.cancelDeleteModal=function(){c.deleteModal&&c.deleteModal.dismiss("cancel")},c.removeCurrentForm=function(){if(c.deleteModal&&c.deleteModal.opened){c.deleteModal.close();var a=c.myform._id;if(!a)throw new Error("Error - removeCurrentForm(): $scope.myform._id does not exist");h["delete"]("/forms/"+a).then(function(a){e.go("listForms",{},{reload:!0})},function(a){console.error(a)})}},c.updateDesign=function(a,b,d,e){c.update(a,b,d,e,function(){m()})},c.update=a.update=function(b,d,e,f,g){var i=!0;if(b||(i=!a.saveInProgress),i){var j=null;if(b||(a.saveInProgress=!0),e){for(var k=new RegExp("^[0-9a-fA-F]{24}$"),l=0;l]+/i,test:function(a){return!this.regExp.test(a)}},b.openDeleteModal=function(a){b.deleteModal=h.open({animation:b.animationsEnabled,templateUrl:"deleteModalListForms.html",controller:["$uibModalInstance","items","$scope",function(a,b,c){c.content=b,c.cancel=c.cancelDeleteModal,c.deleteForm=function(){c.$parent.removeForm(b.formIndex)}}],resolve:{items:function(){return{currFormTitle:b.myforms[a].title,formIndex:a}}}})},b.cancelDeleteModal=function(){b.deleteModal&&b.deleteModal.dismiss("cancel")},b.openCreateModal=function(){b.showCreateModal||(b.showCreateModal=!0)},b.closeCreateModal=function(){b.showCreateModal&&(b.showCreateModal=!1)},b.setForm=function(a){b.myform=a},b.goToWithId=function(a,b){d.go(a,{formId:b},{reload:!0})},b.duplicateForm=function(a){var c=_.cloneDeep(b.myforms[a]);delete c._id,g.post("/forms",{form:c}).success(function(c,d,e){b.myforms.splice(a+1,0,c)}).error(function(a){console.error(a),null===a&&(b.error=a.data.message)})},b.createNewForm=function(){var a={};a.title=b.forms.createForm.title.$modelValue,a.language=b.forms.createForm.language.$modelValue,b.forms.createForm.$valid&&b.forms.createForm.$dirty&&g.post("/forms",{form:a}).success(function(a,c,d){b.goToWithId("viewForm.create",a._id+"")}).error(function(a){console.error(a),b.error=a.data.message})},b.removeForm=function(a){if(a>=b.myforms.length||a<0)throw new Error("Error: form_index in removeForm() must be between 0 and "+b.myforms.length-1);g["delete"]("/forms/"+b.myforms[a]._id).success(function(c,d,e){b.myforms.splice(a,1),b.cancelDeleteModal()}).error(function(a){console.error(a)})}}]),angular.module("forms").directive("configureFormDirective",["$rootScope","$http","Upload","CurrentForm",function(a,b,c,d){return{templateUrl:"modules/forms/admin/views/directiveViews/form/configure-form.client.view.html",restrict:"E",scope:{myform:"=",user:"=",pdfFields:"@",formFields:"@"},controller:["$scope",function(b){b.log="",b.languages=a.languages,b.resetForm=a.resetForm,b.update=a.update}]}}]),angular.module("forms").directive("editFormDirective",["$rootScope","FormFields","$uibModal",function(a,b,c){return{templateUrl:"modules/forms/admin/views/directiveViews/form/edit-form.client.view.html",restrict:"E",transclude:!0,scope:{myform:"="},controller:["$scope",function(d){var e;d.sortableOptions={appendTo:".dropzone",forceHelperSize:!0,forcePlaceholderSize:!0,update:function(a,b){d.update(!1,d.myform,!0,!1,function(a){})}},d.openEditModal=function(a,b,e){d.editFieldModal=c.open({animation:!0,templateUrl:"editFieldModal.html",windowClass:"edit-modal-window",controller:["$uibModalInstance","$scope",function(c,d){d.field=a,d.showLogicJump=!1,d.isEdit=b,d.showAddOptions=function(a){return"dropdown"===d.field.fieldType||"checkbox"===d.field.fieldType||"radio"===d.field.fieldType},d.validShapes=["Heart","Star","thumbs-up","thumbs-down","Circle","Square","Check Circle","Smile Outlined","Hourglass","bell","Paper Plane","Comment","Trash"],d.addOption=function(){if("checkbox"===d.field.fieldType||"dropdown"===d.field.fieldType||"radio"===d.field.fieldType){d.field.fieldOptions||(d.field.fieldOptions=[]);var a=d.field.fieldOptions.length+1,b={option_id:Math.floor(1e5*Math.random()),option_title:"Option "+a,option_value:"Option "+a};d.field.fieldOptions.push(b)}},d.deleteOption=function(a){if("checkbox"===d.field.fieldType||"dropdown"===d.field.fieldType||"radio"===d.field.fieldType)for(var b=0;b',restrict:"E",scope:{typeName:"@"},controller:["$scope",function(a){var b={textfield:"fa fa-pencil-square-o",dropdown:"fa fa-th-list",date:"fa fa-calendar",checkbox:"fa fa-check-square-o",radio:"fa fa-dot-circle-o",email:"fa fa-envelope-o",textarea:"fa fa-pencil-square",legal:"fa fa-legal",file:"fa fa-cloud-upload",rating:"fa fa-star-half-o",link:"fa fa-link",scale:"fa fa-sliders",stripe:"fa fa-credit-card",statement:"fa fa-quote-left",yes_no:"fa fa-toggle-on",number:"fa fa-slack"};a.typeIcon=b[a.typeName]}]}});var __indexOf=[].indexOf||function(a){for(var b=0,c=this.length;b=0&&(c=c+b+".html"),d.get(c)};return{template:"
{{field.title}}
",restrict:"E",scope:{field:"=",required:"&",design:"=",index:"=",forms:"="},link:function(a,d){c.chooseDefaultOption=a.chooseDefaultOption=function(b){"yes_no"===b?a.field.fieldValue="true":"rating"===b?a.field.fieldValue=0:"radio"===a.field.fieldType?a.field.fieldValue=a.field.fieldOptions[0].option_value:"legal"===b&&(a.field.fieldValue="true",c.nextField())},a.nextField=c.nextField,a.setActiveField=c.setActiveField,"date"===a.field.fieldType&&(a.dateOptions={changeYear:!0,changeMonth:!0,altFormat:"mm/dd/yyyy",yearRange:"1900:-0",defaultDate:0});var e=a.field.fieldType;if("number"===a.field.fieldType||"textfield"===a.field.fieldType||"email"===a.field.fieldType||"link"===a.field.fieldType){switch(a.field.fieldType){case"textfield":a.input_type="text";break;case"email":a.input_type="email",a.placeholder="joesmith@example.com";break;case"number":a.input_type="text",a.validateRegex=/^-?\d+$/;break;default:a.input_type="url",a.placeholder="http://example.com"}e="textfield"}var g=f(e);d.html(g).show();b(d.contents())(a)}}}]),angular.module("view-form").directive("onEnterKey",["$rootScope",function(a){return{restrict:"A",link:function(b,c,d){c.bind("keydown keypress",function(b){var c=b.which||b.keyCode,e=!1;null!==d.onEnterKeyDisabled&&(e=d.onEnterKeyDisabled),13!==c||b.shiftKey||e||(b.preventDefault(),a.$apply(function(){a.$eval(d.onEnterKey); +}))})}}}]).directive("onTabKey",["$rootScope",function(a){return{restrict:"A",link:function(b,c,d){c.bind("keyup keypress",function(b){var c=b.which||b.keyCode;9!==c||b.shiftKey||(b.preventDefault(),a.$apply(function(){a.$eval(d.onTabKey)}))})}}}]).directive("onEnterOrTabKey",["$rootScope",function(a){return{restrict:"A",link:function(b,c,d){c.bind("keydown keypress",function(b){var c=b.which||b.keyCode;13!==c&&9!==c||b.shiftKey||(b.preventDefault(),a.$apply(function(){a.$eval(d.onEnterOrTabKey)}))})}}}]).directive("onTabAndShiftKey",["$rootScope",function(a){return{restrict:"A",link:function(b,c,d){c.bind("keydown keypress",function(b){var c=b.which||b.keyCode;9===c&&b.shiftKey&&(console.log("onTabAndShiftKey"),b.preventDefault(),a.$apply(function(){a.$eval(d.onTabAndShiftKey)}))})}}}]),angular.module("view-form").directive("onFinishRender",["$rootScope","$timeout",function(a,b){return{restrict:"A",link:function(b,c,d){if(c.attr("ng-repeat")||c.attr("data-ng-repeat")){var e=d.onFinishRender||"ngRepeat";b.$first&&!b.$last?b.$evalAsync(function(){a.$broadcast(e+" Started")}):b.$last&&b.$evalAsync(function(){a.$broadcast(e+" Finished")})}}}}]),jsep.addBinaryOp("contains",10),jsep.addBinaryOp("!contains",10),jsep.addBinaryOp("begins",10),jsep.addBinaryOp("!begins",10),jsep.addBinaryOp("ends",10),jsep.addBinaryOp("!ends",10),angular.module("view-form").directive("submitFormDirective",["$http","TimeCounter","$filter","$rootScope","SendVisitorData","$translate","$timeout",function(a,b,c,d,e,f,g){return{templateUrl:"form_modules/forms/base/views/directiveViews/form/submit-form.client.view.html",restrict:"E",scope:{myform:"=",ispreview:"="},controller:["$document","$window","$scope",function(f,g,h){var i=!1,j="submit_field";h.forms={},h.ispreview&&b.restartClock();var k=h.myform.visible_form_fields.filter(function(a){return"statement"!==a.fieldType}).length,l=c("formValidity")(h.myform);h.translateAdvancementData={done:l,total:k,answers_not_completed:k-l},h.reloadForm=function(){h.myform.submitted=!1,h.myform.form_fields=_.chain(h.myform.visible_form_fields).map(function(a){return a.fieldValue="",a}).value(),h.loading=!1,h.error="",h.selected={_id:"",index:0},h.setActiveField(h.myform.visible_form_fields[0]._id,0,!1),b.restartClock()};var m=function(a){var b=a.logicJump;if(b.enabled&&b.expressionString&&b.valueB&&a.fieldValue){var c,d,e=jsep(b.expressionString);if("field"===e.left.name?(c=a.fieldValue,d=b.valueB):(c=b.valueB,d=a.fieldValue),"number"===a.fieldType||"scale"===a.fieldType||"rating"===a.fieldType)switch(e.operator){case"==":return parseInt(c)===parseInt(d);case"!==":return parseInt(c)!==parseInt(d);case">":return parseInt(c)>parseInt(d);case">=":return parseInt(c)>parseInt(d);case"<":return parseInt(c)-1;case"!contains":return!(c.indexOf(d)>-1);case"begins":return c.startsWith(d);case"!begins":return!c.startsWith(d);case"ends":return c.endsWith(d);case"!ends":return c.endsWith(d);default:return!1}}},n=function(){if(null===h.selected)throw console.error("current active field is null"),new Error("current active field is null");return h.selected._id===j?h.myform.form_fields.length-1:h.selected.index};h.isActiveField=function(a){return h.selected._id===a._id},h.setActiveField=d.setActiveField=function(a,b,d){if(null!==h.selected&&(a||null!==b)){if(a){if(null===b){b=h.myform.visible_form_fields.length;for(var e=0;e .field-directive:nth-of-type("+String(h.myform.visible_form_fields.length-1)+")"),m=$(l).height(),n=k-g-1.2*m,o=.9;h.selected.index===h.myform.visible_form_fields.length?bn?(a=h.selected.index+1,h.setActiveField(j,a,!1)):ef*o&&(a=h.selected.index-1,h.setActiveField(null,a,!1))}h.$apply()},d.nextField=h.nextField=function(){if(h.selected&&h.selected.index>-1)if(h.selected._id!==j){var a=h.myform.visible_form_fields[h.selected.index];a.logicJump&&a.logicJump.jumpTo&&m(a)?h.setActiveField(a.logicJump.jumpTo,null,!0):h.selected.index0&&h.setActiveField(null,a,!0)},d.goToInvalid=h.goToInvalid=function(){var a=$(".row.field-directive .ng-invalid.focusOn, .row.field-directive .ng-untouched.focusOn:not(.ng-valid)").first().parents(".row.field-directive").first().attr("data-id");h.setActiveField(a,null,!0)},h.exitStartPage=function(){h.myform.startPage.showStart=!1,h.myform.visible_form_fields.length>0&&(h.selected._id=h.myform.visible_form_fields[0]._id)};var o=function(){var a=new MobileDetect(window.navigator.userAgent),b="other";return a.tablet()?b="tablet":a.mobile()?b="mobile":a.is("bot")||(b="desktop"),{type:b,name:window.navigator.platform}},p=function(){$.ajaxSetup({async:!1});var a=$.getJSON("https://freegeoip.net/json/").responseJSON;return $.ajaxSetup({async:!0}),a&&a.ip||(a={ip:"Adblocker"}),{ipAddr:a.ip,geoLocation:{City:a.city,Country:a.country_name}}};d.submitForm=h.submitForm=function(){if(h.forms.myForm.$invalid)return void h.goToInvalid();var d=b.stopClock();h.loading=!0;var f=_.cloneDeep(h.myform),g=o();f.device=g;var i=p();f.ipAddr=i.ipAddr,f.geoLocation=i.geoLocation,f.timeElapsed=d,f.percentageComplete=c("formValidity")(h.myform)/h.myform.visible_form_fields.length*100,delete f.endPage,delete f.isLive,delete f.provider,delete f.startPage,delete f.visible_form_fields,delete f.analytics,delete f.design,delete f.submissions,delete f.submitted;for(var j=0;j
- {{myform.analytics.visitors.length}} + {{analyticsData.globalStatistics.visits | number:0}}
- {{myform.analytics.submissions}} + {{analyticsData.globalStatistics.responses | number:0}}
- {{myform.analytics.conversionRate | number:0}}% + {{analyticsData.globalStatistics.conversion_rate | number:2}}
- {{ AverageTimeElapsed | secondsToDateTime | date:'mm:ss'}} + {{analyticsData.globalStatistics.average_time | secondsToDateTime | date:'mm:ss'}}
@@ -58,7 +58,7 @@ {{ 'UNIQUE_VISITS' | translate }}
- {{DeviceStatistics.desktop.visits}} + {{analyticsData.deviceStatistics.desktop.visits | number:0}}
@@ -67,7 +67,7 @@ {{ 'UNIQUE_VISITS' | translate }}
- {{DeviceStatistics.tablet.visits}} + {{analyticsData.deviceStatistics.tablet.visits | number:0}}
@@ -76,7 +76,7 @@ {{ 'UNIQUE_VISITS' | translate }}
- {{DeviceStatistics.tablet.visits}} + {{analyticsData.deviceStatistics.tablet.visits | number:0}}
@@ -85,7 +85,7 @@ {{ 'UNIQUE_VISITS' | translate }}
- {{DeviceStatistics.other.visits}} + {{analyticsData.deviceStatistics.other.visits | number:0}}
@@ -96,7 +96,7 @@ {{ 'RESPONSES' | translate }}
- {{DeviceStatistics.desktop.responses}} + {{analyticsData.deviceStatistics.desktop.responses | number:0}}
@@ -105,7 +105,7 @@ {{ 'RESPONSES' | translate }}
- {{DeviceStatistics.tablet.responses}} + {{analyticsData.deviceStatistics.tablet.responses | number:0}}
@@ -114,7 +114,7 @@ {{ 'RESPONSES' | translate }}
- {{DeviceStatistics.phone.responses}} + {{analyticsData.deviceStatistics.phone.responses | number:0}}
@@ -123,7 +123,7 @@ {{ 'RESPONSES' | translate }}
- {{DeviceStatistics.other.responses}} + {{analyticsData.deviceStatistics.other.responses | number:0}}
@@ -134,7 +134,7 @@ {{ 'COMPLETION_RATE' | translate }}
- {{DeviceStatistics.desktop.completion}}% + {{analyticsData.deviceStatistics.desktop.conversion_rate | number:2}}%
@@ -143,7 +143,7 @@ {{ 'COMPLETION_RATE' | translate }}
- {{DeviceStatistics.tablet.completion}}% + {{analyticsData.deviceStatistics.tablet.conversion_rate | number:2}}%
@@ -152,7 +152,7 @@ {{ 'COMPLETION_RATE' | translate }}
- {{DeviceStatistics.phone.completion}}% + {{analyticsData.deviceStatistics.phone.conversion_rate | number:2}}%
@@ -161,7 +161,7 @@ {{ 'COMPLETION_RATE' | translate }}
- {{DeviceStatistics.other.completion}}% + {{analyticsData.deviceStatistics.other.conversion_rate | number:2}}%
@@ -172,7 +172,7 @@ {{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
- {{DeviceStatistics.desktop.average_time | secondsToDateTime | date:'mm:ss'}} + {{analyticsData.deviceStatistics.desktop.average_time | secondsToDateTime | date:'mm:ss'}}
@@ -181,7 +181,7 @@ {{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
- {{DeviceStatistics.tablet.average_time | secondsToDateTime | date:'mm:ss'}} + {{analyticsData.deviceStatistics.tablet.average_time | secondsToDateTime | date:'mm:ss'}}
@@ -190,7 +190,7 @@ {{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
- {{DeviceStatistics.phone.average_time | secondsToDateTime | date:'mm:ss'}} + {{analyticsData.deviceStatistics.phone.average_time | secondsToDateTime | date:'mm:ss'}}
@@ -199,11 +199,12 @@ {{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
- {{DeviceStatistics.other.average_time | secondsToDateTime | date:'mm:ss'}} + {{analyticsData.deviceStatistics.other.average_time | secondsToDateTime | date:'mm:ss'}}
+ + +