', {
'class': 'cp-icons-element',
@@ -2661,6 +2661,18 @@ define([
$creation.focus();
};
+ Messages.restrictedLoginPrompt = "You are not authorized to access this document. Log in if you think your account should be able to access it."; // XXX
+ UIElements.loginErrorScreenContent = function (common) {
+ var msg = Pages.setHTML(h('span'), Messages.restrictedLoginPrompt);
+ $(msg).find('a').attr({
+ href: '/login/',
+ }).click(function (ev) {
+ ev.preventDefault();
+ common.setLoginRedirect('login');
+ });
+ return msg;
+ };
+
var autoStoreModal = {};
UIElements.onServerError = function (common, err, toolbar, cb) {
//if (["EDELETED", "EEXPIRED", "ERESTRICTED"].indexOf(err.type) === -1) { return; }
@@ -2708,6 +2720,10 @@ define([
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
} else if (err.type === 'ERESTRICTED') {
msg = Messages.restrictedError;
+ if (!common.isLoggedIn()) {
+ msg = UIElements.loginErrorScreenContent(common);
+ }
+
if (toolbar && typeof toolbar.failed === "function") { toolbar.failed(true); }
} else if (err.type === 'HASH_NOT_FOUND' && priv.isHistoryVersion) {
msg = Messages.oo_deletedVersion;
@@ -2868,7 +2884,13 @@ define([
UIElements.displayStorePadPopup = function (common, data) {
if (storePopupState) { return; }
storePopupState = true;
- if (data && data.stored) { return; } // We won't display the popup for dropped files
+ // We won't display the popup for dropped files or already stored pads
+ if (data && data.stored) {
+ if (!data.inMyDrive) {
+ $('.cp-toolbar-storeindrive').show();
+ }
+ return;
+ }
var priv = common.getMetadataMgr().getPrivateData();
// This pad will be deleted automatically, it shouldn't be stored
diff --git a/www/common/inner/share.js b/www/common/inner/share.js
index 5db88b672..9ba75f867 100644
--- a/www/common/inner/share.js
+++ b/www/common/inner/share.js
@@ -502,9 +502,6 @@ define([
var auditor;
if (isForm) {
- Messages.share_formEdit = "Author"; // XXX
- Messages.share_formView = "Participant"; // XXX
- Messages.share_formAuditor = "Auditor"; // XXX
labelEdit = Messages.share_formEdit;
labelView = Messages.share_formView;
auditor = UI.createRadio('accessRights', 'cp-share-form', Messages.share_formAuditor, false, {
diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js
index d13cb09fb..66e1967cd 100644
--- a/www/common/outer/async-store.js
+++ b/www/common/outer/async-store.js
@@ -1228,7 +1228,7 @@ define([
});
// Add the pad if it does not exist in our drive
- if (!contains) { // || (ownedByMe && !inMyDrive)) {
+ if (!contains || (data.forceSave && !inMyDrive)) {
var autoStore = Util.find(store.proxy, ['settings', 'general', 'autostore']);
if (autoStore !== 1 && !data.forceSave && !data.path) {
// send event to inner to display the corner popup
@@ -1269,7 +1269,8 @@ define([
});
// Let inner know that dropped files shouldn't trigger the popup
postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", {
- stored: true
+ stored: true,
+ inMyDrive: inMyDrive
});
nThen(function (waitFor) {
sendTo.forEach(function (teamId) {
diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js
index e99cc4187..869f079d3 100644
--- a/www/common/sframe-app-framework.js
+++ b/www/common/sframe-app-framework.js
@@ -572,7 +572,9 @@ define([
if (!readOnly) { onLocal(); }
evOnReady.fire(newPad);
- common.openPadChat(onLocal);
+ // In forms, only editors can see the chat
+ if (!readOnly || type !== 'form') { common.openPadChat(onLocal); }
+
if (!readOnly && cursorGetter) {
common.openCursorChannel(onLocal);
cursor = common.createCursor(onLocal);
diff --git a/www/common/sframe-common-codemirror.js b/www/common/sframe-common-codemirror.js
index 970272124..58475bbed 100644
--- a/www/common/sframe-common-codemirror.js
+++ b/www/common/sframe-common-codemirror.js
@@ -73,6 +73,8 @@ define([
else {
editor.setSelection(posToCursor(selects[0], remoteDoc), posToCursor(selects[1], remoteDoc));
}
+
+ editor.scrollTo(scroll.left, scroll.top);
};
module.handleImagePaste = function (editor) {
diff --git a/www/common/toolbar.js b/www/common/toolbar.js
index 26ff870f6..785d1bc7e 100644
--- a/www/common/toolbar.js
+++ b/www/common/toolbar.js
@@ -166,6 +166,7 @@ MessengerUI, Messages, Pages) {
var showColors = false;
var updateUserList = function (toolbar, config, forceOffline) {
if (!config.displayed || config.displayed.indexOf('userlist') === -1) { return; }
+ if (toolbar.isAlone) { return; }
// Make sure the elements are displayed
var $userButtons = toolbar.userlist;
var $userlistContent = toolbar.userlistContent;
@@ -1216,6 +1217,7 @@ MessengerUI, Messages, Pages) {
if (!config.metadataMgr) { return; }
var metadataMgr = config.metadataMgr;
var notify = function(type, name, oldname) {
+ if (toolbar.isAlone) { return; }
// type : 1 (+1 user), 0 (rename existing user), -1 (-1 user)
if (typeof name === "undefined") { return; }
name = name || Messages.anonymous;
@@ -1515,6 +1517,15 @@ MessengerUI, Messages, Pages) {
}
};
+ // disable notification, userlist and chat
+ toolbar.alone = function () {
+ toolbar.userlist.hide();
+ toolbar.chat.hide();
+ $('.cp-toolbar-userlist-drawer').remove();
+ $('.cp-toolbar-chat-drawer').remove();
+ toolbar.isAlone = true;
+ };
+
// On log out, remove permanently the realtime elements of the toolbar
Common.onLogout(function () {
failed();
diff --git a/www/common/translations/messages.json b/www/common/translations/messages.json
index 24b393bf6..68df0a718 100644
--- a/www/common/translations/messages.json
+++ b/www/common/translations/messages.json
@@ -910,7 +910,7 @@
"access_main": "Access",
"access_allow": "List",
"accessButton": "Access",
- "restrictedError": "You are not authorised to access this document",
+ "restrictedError": "You are not authorized to access this document",
"contacts": "Contacts",
"access_noContact": "No other contact to add",
"allow_checkbox": "Enable access list",
@@ -918,7 +918,7 @@
"allow_disabled": "disabled",
"allow_label": "Access list: {0}",
"access_muteRequests": "Mute access requests for this pad",
- "owner_text": "The owner(s) of a pad are the only users authorised to: add/remove owners, restrict access to the pad with an access list, or delete the pad.",
+ "owner_text": "The owner(s) of a pad are the only users authorized to: add/remove owners, restrict access to the pad with an access list, or delete the pad.",
"logoutEverywhere": "Log out everywhere",
"allow_text": "Using an access list means that only selected users and owners will be able to access this document.",
"teams": "Teams",
@@ -1117,7 +1117,7 @@
"whatis_model": "Business model",
"whatis_model_info": "CryptPad has been supported since 2016 by French and European research grants such as BPI France, NLNet Foundation, NGI Trust, Mozilla Open Source Support, as well as donations and subscriptions to cryptpad.fr. We believe that public money should fund public code, so the service is fully open source. This means anyone can use, host, and modify the software.
CryptPad does not profit from user data. This is part of a vision for online services that respect privacy. Unlike the big platforms that pretend to be \"free\" while making profits from personal information, CryptPad aims to build a sustainable model funded willingly by users.
We offer CryptPad's functionality for free because we believe everyone deserves personal privacy, not just people with disposable income. If you are in a position to support the project, you will contribute new features, improvements and maintenance that benefit all users.
Now that the feasibility of the project has been established, the next goal is to make it financially sustainable through user funding. If you would like to support CryptPad and help make it a sustainable alternative to the big platforms, please consider making a one-time or recurring donation.
",
"whatis_xwiki": "Made at XWiki",
- "whatis_xwiki_info": "CryptPad is made at XWiki, a company based in Paris, France that has been making open-source software for over 15 years. We have extensive experience making collaborative software to organise information. Our track record shows we are committed to the long-term development and maintenance of CryptPad.
",
+ "whatis_xwiki_info": "CryptPad is made at XWiki, a company based in Paris, France that has been making open-source software for over 15 years. We have extensive experience making collaborative software to organize information. Our track record shows we are committed to the long-term development and maintenance of CryptPad.
",
"creation_expiresIn": "Expires in",
"creation_helperText": "Open in documentation",
"docs_link": "Documentation",
@@ -1238,5 +1238,92 @@
"admin_supportInitGenerate": "Generate support keys",
"admin_supportPrivHint": "Display the private key that other admins will need to view support tickets. A form to enter this key will be displayed on their admin panel.",
"admin_supportPrivButton": "Show key",
- "admin_emailButton": "Update"
+ "admin_emailButton": "Update",
+ "share_formEdit": "Author",
+ "share_formAuditor": "Auditor",
+ "share_formView": "Participant",
+ "button_newform": "New Form",
+ "form_invalid": "Invalid form",
+ "form_editBlock": "Edit",
+ "form_editMax": "Maximum selectable options",
+ "form_editMaxLength": "Maximum characters",
+ "form_editType": "Option type",
+ "form_poll_text": "Text",
+ "form_poll_day": "Day",
+ "form_poll_time": "Time",
+ "form_poll_switch": "Swap axes",
+ "form_pollTotal": "Total",
+ "form_pollYourAnswers": "Your answers",
+ "form_textType": "Text type",
+ "form_text_text": "Text",
+ "form_text_url": "Link",
+ "form_text_email": "Email",
+ "form_text_number": "Number",
+ "form_default": "Your question here?",
+ "form_type_input": "Text",
+ "form_type_textarea": "Paragraph",
+ "form_type_radio": "Choice",
+ "form_type_multiradio": "Choice Grid",
+ "form_type_checkbox": "Checkbox",
+ "form_type_multicheck": "Checkbox Grid",
+ "form_type_poll": "Poll",
+ "form_type_sort": "Ordered list",
+ "form_sort_hint": "Please drag these items from most (1) to least ({0}) preferred.",
+ "form_type_md": "Description",
+ "form_type_page": "Page break",
+ "form_description_default": "Your text here",
+ "form_duplicates": "Duplicate entries have been removed",
+ "form_maxOptions": "maximum {0} answer(s)",
+ "form_maxLength": "Character limit: {0}/{1}",
+ "form_submit": "Submit",
+ "form_update": "Update",
+ "form_reset": "Reset",
+ "form_sent": "Sent",
+ "form_delete": "Delete",
+ "form_submitWarning": "Submit anyway",
+ "form_updateWarning": "Update anyway",
+ "form_cantFindAnswers": "Unable to retrieve your existing answers for this form.",
+ "form_answered": "You have already answered this form",
+ "form_results": "Responses",
+ "form_results_empty": "There are no responses",
+ "form_editor": "Editor",
+ "form_form": "Form",
+ "form_viewResults": "Go to responses",
+ "form_showIndividual": "Show individual answers",
+ "form_showSummary": "Show summary",
+ "form_answerAnonymous": "Anonymous answer on {0}",
+ "form_viewButton": "View",
+ "form_backButton": "Back",
+ "form_answerName": "Answer from {0} on {1}",
+ "form_answerWarning": "Unconfirmed identity",
+ "form_notAnswered": "{0} empty answers",
+ "form_input_ph_email": "email@example.com",
+ "form_input_ph_url": "https://example.com",
+ "form_invalidWarning": "There are errors in some answers:",
+ "form_invalidQuestion": "Question {0}",
+ "form_makePublic": "Publish responses",
+ "form_makePublicWarning": "Are you sure you want to make responses to this form public? This cannot be undone.",
+ "form_isPublic": "Responses are public",
+ "form_isPrivate": "Responses are private",
+ "form_open": "Open",
+ "form_setEnd": "Set closing date",
+ "form_removeEnd": "Remove closing date",
+ "form_isOpen": "This form is open",
+ "form_isClosed": "This form was closed on {0}",
+ "form_willClose": "This form will close on {0}",
+ "form_anonymous": "Anonymous answers",
+ "form_anonymous_on": "Allowed",
+ "form_anonymous_off": "Blocked",
+ "form_defaultOption": "Option {0}",
+ "form_defaultItem": "Item {0}",
+ "form_newOption": "New option",
+ "form_newItem": "New item",
+ "form_add_option": "Add option",
+ "form_add_item": "Add item",
+ "form_anonymous_blocked": "Anonymous responses are blocked for this form. You must log in or register to submit answers.",
+ "form_addMultiple": "Add all",
+ "form_addMultipleHint": "Add multiple dates and times",
+ "form_clear": "Clear",
+ "form_page": "Page {0}/{1}",
+ "form_anonymousBox": "Answer anonymously"
}
diff --git a/www/form/app-form.less b/www/form/app-form.less
index 278e1d1db..dcbe5758a 100644
--- a/www/form/app-form.less
+++ b/www/form/app-form.less
@@ -32,6 +32,9 @@
div.cp-form-creator-results, .cp-app-form-button-creator {
display: none !important;
}
+ .cp-form-block {
+ .tools_unselectable();
+ }
}
#cp-app-form-container {
@@ -67,6 +70,9 @@
.cp-form-creator-settings {
padding: 30px;
+ .cp-form-actions {
+ margin-top: 5px;
+ }
& > div:not(:last-child) {
margin-bottom: 20px;
}
@@ -90,6 +96,7 @@
div.cp-form-creator-content, div.cp-form-creator-results {
max-width: 1000px;
min-width: 300px;
+ margin-top: 10px;
padding: 10px;
display: flex;
flex-flow: column;
@@ -193,6 +200,24 @@
.cp-form-page + .cp-form-send-container {
margin-top: 10px;
}
+ .cp-form-send-container {
+ text-align: center;
+ margin: 50px auto 100px auto;
+ button {
+ &:not(:last-child) {
+ margin-right: 10px;
+ }
+ }
+ .cp-form-invalid-warning {
+ color: @cp_form-invalid;
+ ul {
+ list-style-type: disclosure-closed;
+ }
+ li {
+ text-align: left;
+ }
+ }
+ }
.cp-form-page-container {
display: flex;
@@ -206,16 +231,16 @@
justify-content: center;
}
button {
- &.cp-next {
- .fa {
- margin-right: 0;
- margin-left: 5px;
- }
+ display: flex;
+ height: 38px;
+ align-items: center;
+ i {
+ margin-right: 0;
+ font-size: 25px;
}
}
}
.cp-form-block {
- .tools_unselectable();
background: @cp_form-bg1;
padding: 10px;
&:not(:last-child) {
@@ -240,12 +265,22 @@
&.sortable-ghost { visibility: hidden; }
&.sortable-drag { opacity: 0.9 !important; }
+ .flatpickr-calendar.inline {
+ box-shadow: unset;
+ border: 1px solid @cryptpad_text_col;
+ }
+
.cp-form-block-question {
margin-bottom: 5px;
+ .cp-form-block-question-number {
+ font-weight: bold;
+ margin-right: 10px;
+ }
}
.cp-form-block-content {
overflow-x: auto;
.cp-form-page-break-edit {
+ font-size: 20px;
text-align: center;
padding: 10px;
i {
@@ -258,7 +293,13 @@
justify-content: space-between;
}
input:invalid {
- border: 1px solid @cp_form-invalid;
+ border: 2px solid @cp_form-invalid;
+ color: @cp_form-invalid;
+ }
+ media-tag {
+ & > * {
+ max-width: 100%;
+ }
}
}
.cp-form-input-block {
@@ -312,6 +353,12 @@
margin-left: 10px;
}
}
+ .cp-form-multiple-picker {
+ margin: 10px 0px 0px 0px;
+ button {
+ margin: 10px 10px 0px 0px;
+ }
+ }
}
}
.cp-form-edit-max-options {
@@ -357,7 +404,7 @@
}
}
.cp-form-edit-block-input {
- margin-bottom: 5px; // XXX DB margin bug
+ margin-bottom: 5px;
&.sortable-ghost { visibility: hidden; }
&.sortable-drag { opacity: 0.9 !important; }
display: flex;
@@ -409,9 +456,9 @@
}
.cp-form-results-type-textarea-data {
white-space: pre-wrap;
- font-size: 14px;
- border: 1px solid @cp_profile-hint;
- padding: 0 5px;
+ padding: 5px 10px;
+ background: @cp_form-bg2;
+ &:not(:last-child) { margin-bottom: 1px; }
}
.cp-form-results-type-radio {
display: table;
@@ -475,48 +522,125 @@
}
}
}
+ .cp-form-sort-hint {
+ margin-bottom: 10px;
+ }
.cp-form-type-sort {
cursor: grab;
- padding: 2px;
+ padding: 5px;
.cp-form-handle {
margin-right: 5px;
}
.cp-form-sort-order {
- border: 1px solid @cp_profile-hint;
+ border: 1px solid @cryptpad_text_col;
padding: 0 5px;
margin-right: 5px;
}
+ &:hover {
+ background-color: @cp_app-bg;
+ color: @cryptpad_color_link;
+ .cp-form-sort-order {
+ border-color: @cryptpad_color_link;
+ }
+ }
}
+ .cp-form-type-poll-container {
+ overflow: auto;
+ }
.cp-form-type-poll {
display: inline-flex;
flex-flow: column;
& > div {
display: flex;
}
+ .cp-form-poll-body {
+ flex-flow: column;
+ max-height: 225px;
+ overflow: auto;
+
+ & > div {
+ display: flex;
+ }
+ }
.cp-poll-cell {
width: 100px;
- height: 40px;
+ height: 35px;
display: inline-flex;
align-items: center;
justify-content: center;
+ margin-top:5px;
+ margin-left:5px;
&:first-child {
width: 200px;
}
button {
width: 100%;
+ border-top: 0px;
+ border-left: 0px;
+ background-color: @cp_form-bg1;
+ .fa{
+ margin-left:10px;
+ transform: rotate(-45deg);
+ }
+ }
+ .cp-form-total-yes {
+ margin-right: 5px;
+ }
+ &.cp-poll-best {
+ font-weight: bold;
+ }
+ }
+ .cp-poll-answer-name {
+ padding: 0 5px;
+ display: flex;
+ :last-child {
+ flex: 1;
+ min-width: 0;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ .cp-avatar {
+ .avatar_main(30px);
+ margin-right: 10px;
}
}
.cp-poll-time-day {
flex-basis: 100px;
- border-right: 1px solid @cryptpad_text_col;
- border-left: 1px solid @cryptpad_text_col;
- border-top: 1px solid @cryptpad_text_col;
+ border-bottom: 1px solid @cryptpad_text_col;
+ }
+ &:not(.cp-form-poll-switch) {
+ & > div {
+ &:last-child {
+ margin-bottom: 5px;
+ }
+ }
+ .cp-form-poll-body {
+ &::-webkit-scrollbar {
+ display: none;
+ }
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+ }
}
&.cp-form-poll-switch {
+ display: flex;
flex-flow: row;
+ .cp-avatar {
+ .avatar_main(20px);
+ }
& > div {
flex-flow: column;
+ &.cp-form-poll-body {
+ flex-flow: row;
+ max-width: 550px;
+ max-height: unset;
+ scroll-snap-type: x mandatory;
+ & > div {
+ flex-flow: column;
+ }
+ }
}
.cp-poll-cell:not(.cp-poll-switch) {
&:first-child {
@@ -529,9 +653,8 @@
.cp-poll-time-day {
flex-basis: 40px;
border-right: none;
- border-bottom: 1px solid @cryptpad_text_col;
- border-left: 1px solid @cryptpad_text_col;
- border-top: 1px solid @cryptpad_text_col;
+ border-right: 1px solid @cryptpad_text_col;
+ border-bottom: 0px;
}
}
.cp-form-poll-choice, .cp-form-poll-answer {
@@ -539,6 +662,7 @@
display: none;
}
color: @cp_form-poll-color;
+ font-size: 25px;
&[data-value="0"] {
background: @cp_form-poll-no;
.cp-no { display: inline; }
@@ -555,7 +679,9 @@
div.cp-form-poll-choice {
cursor: pointer;
padding: 5px;
- border: 5px double @cp_form-bg1;
+ //margin: 10px 0px 0px 10px;
+ //border: 1px solid @cryptpad_text_col;
+ color: @cryptpad_text_col;
}
div.cp-form-poll-answer {
color: @cp_form-poll-yes-color;
diff --git a/www/form/inner.js b/www/form/inner.js
index fcf1cc349..2e6564465 100644
--- a/www/form/inner.js
+++ b/www/form/inner.js
@@ -27,7 +27,7 @@ define([
'/bower_components/sortablejs/Sortable.min.js',
'cm/addon/display/placeholder',
- 'cm/mode/markdown/markdown',
+ 'cm/mode/gfm/gfm',
'css!cm/lib/codemirror.css',
'css!/bower_components/codemirror/lib/codemirror.css',
@@ -76,98 +76,6 @@ define([
timeFormat = "h:i K";
}
- Messages.button_newform = "New Form"; // XXX
- Messages.form_invalid = "Invalid form";
- Messages.form_editBlock = "Edit";
- Messages.form_editMax = "Max selectable options";
- Messages.form_editMaxLength = "Maximum characters";
- Messages.form_editType = "Options type";
-
- Messages.form_poll_text = "Text";
- Messages.form_poll_day = "Day";
- Messages.form_poll_time = "Time";
-
-
- Messages.form_textType = "Text type";
- Messages.form_text_text = "Text";
- Messages.form_text_url = "URL";
- Messages.form_text_email = "Email";
- Messages.form_text_number = "Number";
-
- Messages.form_default = "Your question here?";
- Messages.form_type_input = "Text"; // XXX
- Messages.form_type_textarea = "Textarea"; // XXX
- Messages.form_type_radio = "Radio"; // XXX
- Messages.form_type_multiradio = "Multiline Radio"; // XXX
- Messages.form_type_checkbox = "Checkbox"; // XXX
- Messages.form_type_multicheck = "Multiline Checkbox"; // XXX
- Messages.form_type_poll = "Poll"; // XXX
- Messages.form_type_sort = "Ordered list"; // XXX
-
- Messages.form_type_md = "Description"; // XXX
- Messages.form_type_page = "Page break"; // XXX
-
- Messages.form_description_default = "Your text here";
-
- Messages.form_duplicates = "Duplicate entries have been removed";
- Messages.form_maxOptions = "{0} answer(s) max";
-
- Messages.form_submit = "Submit";
- Messages.form_update = "Update";
- Messages.form_reset = "Reset";
- Messages.form_sent = "Sent";
- Messages.form_delete = "Delete";
-
- Messages.form_cantFindAnswers = "Unable to retrieve your existing answers for this form.";
- Messages.form_answered = "You already answered this form";
-
- Messages.form_results = "Responses";
- Messages.form_editor = "Editor";
- Messages.form_form = "Form";
- Messages.form_viewResults = "Go to responses";
- Messages.form_viewCreator = "Go to form creator";
- Messages.form_showIndividual = "Show individual answers";
- Messages.form_showSummary = "Show summary";
- Messages.form_answerAnonymous = "Anonymous answer on {0}";
- Messages.form_viewButton = "View";
- Messages.form_backButton = "Back";
- Messages.form_answerName = "Answer from {0} on {1}";
- Messages.form_answerWarning = "Unconfirmed identity";
-
- Messages.form_notAnswered = "And {0} empty answers";
-
- Messages.form_makePublic = "Publish results";
- Messages.form_makePublicWarning = "Are you sure you want to make the results of this form public? This can't be undone.";
- Messages.form_isPublic = "Results are public";
- Messages.form_isPrivate = "Results are private";
-
- Messages.form_open = "Open";
- Messages.form_setEnd = "Set closing date";
- Messages.form_removeEnd = "Remove closing date";
- Messages.form_isOpen = "This form is open";
- Messages.form_isClosed = "This form was closed on {0}";
- Messages.form_willClose = "This form will close on {0}";
-
- Messages.form_anonymous = "Anonymous answers";
- Messages.form_anonymous_on = "Allowed";
- Messages.form_anonymous_off = "Blocked";
- Messages.form_anonymous_blocked = "Anonymous responses are blocked for this form. You must log in or register to submit answers.";
-
- Messages.form_defaultOption = "Option {0}";
- Messages.form_defaultItem = "Item {0}";
- Messages.form_newOption = "New option";
- Messages.form_newItem = "New item";
- Messages.form_add_option = "Add option";
- Messages.form_add_item = "Add item";
- Messages.form_addMultiple = "Add all";
- Messages.form_clear = "Clear";
-
- Messages.form_page_prev = "Previous";
- Messages.form_page = "Page {0}/{1}";
- Messages.form_page_next = "Next";
-
- Messages.form_anonymousBox = "Answer anonymously";
-
var MAX_OPTIONS = 15; // XXX
var MAX_ITEMS = 10; // XXX
@@ -440,13 +348,13 @@ define([
// Calendar time
if (v.type) {
- var multipleInput = h('input');
+ var multipleInput = h('input', {placeholder: Messages.form_addMultipleHint});
var multipleClearButton = h('button.btn', Messages.form_clear);
var addMultipleButton = h('button.btn', [
h('i.fa.fa-plus'),
h('span', Messages.form_addMultiple)
]);
- addMultiple = h('div', { style: "display: none;" }, [
+ addMultiple = h('div.cp-form-multiple-picker', { style: "display: none;" }, [
multipleInput,
addMultipleButton,
multipleClearButton
@@ -487,6 +395,7 @@ define([
$(addMultiple).hide();
}
} else {
+ $(addMultiple).hide();
$calendar.show();
$container.hide();
}
@@ -500,6 +409,7 @@ define([
refreshView();
if (val !== "text") {
$container.find('.cp-form-edit-block-input').remove();
+ $(add).click();
return;
}
$container.find('input').each(function (i, input) {
@@ -598,12 +508,13 @@ define([
val = +f.selectedDates[0];
}
}
- if (values.indexOf(val) === -1) { values.push(val); }
+ if (val && values.indexOf(val) === -1) { values.push(val); }
else { duplicates = true; }
});
}
+ values = values.filter(Boolean); // Block empty or undeinfed options
if (!values.length) {
- return void UI.warn(Messages.error); // XXX error message: no values
+ return void UI.warn(Messages.error);
}
var res = { values: values };
@@ -672,10 +583,13 @@ define([
var _dateT = new Date(data);
data = Flatpickr.formatDate(_dateT, timeFormat);
}
- return h('div.cp-poll-cell.cp-form-poll-option', data);
+ return h('div.cp-poll-cell.cp-form-poll-option', {
+ title: Util.fixHTML(data)
+ }, data);
});
// Insert axis switch button
var switchAxis = h('button.btn.btn-default', [
+ Messages.form_poll_switch,
h('i.fa.fa-exchange'),
]);
els.unshift(h('div.cp-poll-cell.cp-poll-switch', switchAxis));
@@ -700,10 +614,14 @@ define([
}
// Add answers
+ var bodyEls = [];
if (Array.isArray(answers)) {
- answers.forEach(function (answer) {
- if (!answer.name || !answer.values) { return; }
- var _name = answer.name;
+ answers.forEach(function (answerObj) {
+ var answer = answerObj.results;
+ if (!answer || !answer.values) { return; }
+ var name = Util.find(answerObj, ['user', 'name']) || answer.name || Messages.anonymous;
+ var avatar = h('span.cp-avatar');
+ APP.common.displayAvatar($(avatar), Util.find(answerObj, ['user', 'avatar']), name);
var values = answer.values || {};
var els = opts.values.map(function (data) {
var res = values[data] || 0;
@@ -713,10 +631,17 @@ define([
}, v);
return cell;
});
- els.unshift(h('div.cp-poll-cell.cp-poll-answer-name', _name));
- lines.push(h('div', els));
+ els.unshift(h('div.cp-poll-cell.cp-poll-answer-name', {
+ title: Util.fixHTML(name)
+ }, [
+ avatar,
+ h('span', name)
+ ]));
+ bodyEls.push(h('div', els));
});
}
+ var body = h('div.cp-form-poll-body', bodyEls);
+ lines.push(body);
var $s = $(switchAxis).click(function () {
$s.closest('.cp-form-type-poll').toggleClass('cp-form-poll-switch');
@@ -724,6 +649,111 @@ define([
return lines;
};
+ var makePollTotal = function (answers, opts, myLine, evOnChange) {
+ if (!Array.isArray(answers)) { return; }
+ var totals = {};
+ var myTotals = {};
+ var updateMyTotals = function () {
+ if (!myLine) { return; }
+ opts.values.forEach(function (data) {
+ myLine.some(function (el) {
+ if ($(el).data('option') !== data) { return; }
+ var res = Number($(el).attr('data-value')) || 0;
+ if (res === 1) {
+ myTotals[data] = {
+ y: 1,
+ m: 0
+ };
+ }
+ else if (res === 2) {
+ myTotals[data] = {
+ y: 0,
+ m: 1
+ };
+ } else {
+ delete myTotals[data];
+ }
+ return true;
+ });
+
+ });
+ };
+ var totalEls = opts.values.map(function (data) {
+ var y = 0; // Yes
+ var m = 0; // Maybe
+ answers.forEach(function (answerObj) {
+ var answer = answerObj.results;
+ if (!answer || !answer.values) { return; }
+ var values = answer.values || {};
+ var res = Number(values[data]) || 0;
+ if (res === 1) { y++; }
+ else if (res === 2) { m++; }
+ });
+ totals[data] = {
+ y: y,
+ m: m
+ };
+
+ return h('div.cp-poll-cell', {
+ 'data-id': data
+ }, [
+ h('span.cp-form-total-yes', y),
+ h('span.cp-form-total-maybe', '('+m+')'),
+ ]);
+ });
+
+ totalEls.unshift(h('div.cp-poll-cell', Messages.form_pollTotal));
+ var total = h('div.cp-poll-total', totalEls);
+ var $total = $(total);
+ var refreshBest = function () {
+ var totalMax = {
+ value: 0,
+ data: []
+ };
+ Object.keys(totals).forEach(function (k) {
+ var obj = Util.clone(totals[k]);
+ if (myTotals[k]) {
+ obj.y += myTotals[k].y || 0;
+ obj.m += myTotals[k].m || 0;
+ }
+ if (obj.y === totalMax.value) {
+ totalMax.data.push(k);
+ } else if (obj.y > totalMax.value) {
+ totalMax.value = obj.y;
+ totalMax.data = [k];
+ }
+ });
+ if (totalMax.value) {
+ $total.find('[data-id]').removeClass('cp-poll-best');
+ totalMax.data.forEach(function (k) {
+ $total.find('[data-id="'+k+'"]').addClass('cp-poll-best');
+ });
+ }
+ };
+ refreshBest();
+
+ if (myLine && evOnChange) {
+ var updateValues = function () {
+ totalEls.forEach(function (cell) {
+ var $c = $(cell);
+ var data = $c.attr('data-id');
+ if (!data) { return; }
+ var y = totals[data].y + ((myTotals[data] || {}).y || 0);
+ var m = totals[data].m + ((myTotals[data] || {}).m || 0);
+ $c.find('.cp-form-total-yes').text(y);
+ $c.find('.cp-form-total-maybe').text('('+m+')');
+ });
+ };
+ evOnChange.reg(function () {
+ updateMyTotals();
+ updateValues();
+ refreshBest();
+ });
+ }
+
+
+ return total;
+ };
var getEmpty = function (empty) {
if (empty) {
@@ -744,10 +774,14 @@ define([
};
var getBlockAnswers = function (answers, uid, filterCurve) {
+ if (!answers) { return; }
return Object.keys(answers || {}).map(function (user) {
if (filterCurve && user === filterCurve) { return; }
try {
- return answers[user].msg[uid];
+ return {
+ user: answers[user].msg._userdata,
+ results: answers[user].msg[uid]
+ };
} catch (e) { console.error(e); }
}).filter(Boolean);
};
@@ -877,7 +911,8 @@ define([
get: function (opts, a, n, evOnChange) {
if (!opts) { opts = TYPES.input.defaultOpts; }
var tag = h('input', {
- type: opts.type
+ type: opts.type,
+ placeholder: Messages['form_input_ph_'+opts.type] || ''
});
var $tag = $(tag);
$tag.on('change keypress', Util.throttle(function () {
@@ -922,23 +957,49 @@ define([
},
get: function (opts, a, n, evOnChange) {
if (!opts) { opts = TYPES.textarea.defaultOpts; }
- var tag = h('textarea', {maxlength: opts.maxLength});
- var $tag = $(tag);
- $tag.on('change keypress', Util.throttle(function () {
+ var text = h('textarea', {maxlength: opts.maxLength});
+ var $text = $(text);
+ var charCount = h('div.cp-form-type-textarea-charcount');
+ var updateChar = function () {
+ var l = $text.val().length;
+ if (l > opts.maxLength) {
+ $text.val($text.val().slice(0, opts.maxLength));
+ l = $text.val().length;
+ }
+ $(charCount).text(Messages._getKey('form_maxLength', [
+ $text.val().length,
+ opts.maxLength
+ ]));
+ };
+ updateChar();
+ var tag = h('div.cp-form-type-textarea', [
+ text,
+ charCount
+ ]);
+
+ var evChange = Util.throttle(function () {
evOnChange.fire();
- }, 500));
+ }, 500);
+
+ $text.on('change keypress keyup keydown', function () {
+ setTimeout(updateChar);
+ evChange();
+ });
var cursorGetter;
var setCursorGetter = function (f) { cursorGetter = f; };
return {
tag: tag,
- getValue: function () { return $tag.val(); },
- setValue: function (val) { $tag.val(val); },
+ getValue: function () { return $text.val().slice(0, opts.maxLength); },
+ setValue: function (val) {
+ $text.val(val);
+ updateChar();
+ },
edit: function (cb, tmp) {
var v = Util.clone(opts);
return editTextOptions(v, setCursorGetter, cb, tmp);
},
getCursor: function () { return cursorGetter(); },
- reset: function () { $tag.val(''); }
+ reset: function () { $text.val(''); }
};
},
printResults: function (answers, uid) {
@@ -1108,6 +1169,7 @@ define([
printResults: function (answers, uid, form) {
var structure = form[uid];
if (!structure) { return; }
+ var opts = structure.opts || TYPES.multiradio.defaultOpts;
var results = [];
var empty = 0;
var count = {};
@@ -1125,7 +1187,6 @@ define([
});
});
Object.keys(count).forEach(function (q_uid) {
- var opts = structure.opts || TYPES.multiradio.defaultOpts;
var q = findItem(opts.items, q_uid);
var c = count[q_uid];
var values = Object.keys(c).map(function (res) {
@@ -1321,6 +1382,7 @@ define([
printResults: function (answers, uid, form) {
var structure = form[uid];
if (!structure) { return; }
+ var opts = structure.opts || TYPES.multicheck.defaultOpts;
var results = [];
var empty = 0;
var count = {};
@@ -1339,7 +1401,7 @@ define([
});
});
Object.keys(count).forEach(function (q_uid) {
- var q = findItem(structure.opts.items, q_uid);
+ var q = findItem(opts.items, q_uid);
var c = count[q_uid];
var values = Object.keys(c).map(function (res) {
return h('div.cp-form-results-type-radio-data', [
@@ -1358,94 +1420,6 @@ define([
},
icon: h('i.cptools.cptools-form-grid-check')
},
- poll: {
- defaultOpts: {
- type: 'text', // Text or Days or Time
- values: [1, 2, 3].map(function (i) {
- return Messages._getKey('form_defaultOption', [i]);
- })
- },
- get: function (opts, answers, username, evOnChange) {
- if (!opts) { opts = TYPES.poll.defaultOpts; }
- if (!Array.isArray(opts.values)) { return; }
-
- var lines = makePollTable(answers, opts);
-
- // Add form
- var addLine = opts.values.map(function (data) {
- var cell = h('div.cp-poll-cell.cp-form-poll-choice', [
- h('i.fa.fa-times.cp-no'),
- h('i.fa.fa-check.cp-yes'),
- h('i.fa.fa-question.cp-maybe'),
- ]);
- var $c = $(cell);
- $c.data('option', data);
- var val = 0;
- $c.attr('data-value', val);
- $c.click(function () {
- val = (val+1)%3;
- $c.attr('data-value', val);
- evOnChange.fire();
- });
- cell._setValue = function (v) {
- val = v;
- $c.attr('data-value', val);
- };
- return cell;
- });
- // Name input
- var nameInput = h('input', { value: username || Messages.anonymous });
- addLine.unshift(h('div.cp-poll-cell', nameInput));
- lines.push(h('div', addLine));
-
- var tag = h('div.cp-form-type-poll', lines);
- var $tag = $(tag);
-
- var cursorGetter;
- var setCursorGetter = function (f) { cursorGetter = f; };
- return {
- tag: tag,
- getValue: function () {
- var res = {};
- var name = $(nameInput).val().trim() || Messages.anonymous;
- $tag.find('.cp-form-poll-choice').each(function (i, el) {
- var $el = $(el);
- res[$el.data('option')] = $el.attr('data-value');
- });
- return {
- name: name,
- values: res
- };
- },
- reset: function () {
- $tag.find('.cp-form-poll-choice').attr('data-value', 0);
- },
- edit: function (cb, tmp) {
- var v = Util.clone(opts);
- return editOptions(v, setCursorGetter, cb, tmp);
- },
- getCursor: function () { return cursorGetter(); },
- setValue: function (res) {
- this.reset();
- if (!res || !res.values || !res.name) { return; }
- var val = res.values;
- $(nameInput).val(res.name);
- $tag.find('.cp-form-poll-choice').each(function (i, el) {
- if (!el._setValue) { return; }
- var $el = $(el);
- el._setValue(val[$el.data('option')] || 0);
- });
- }
- };
-
- },
- printResults: function (answers, uid, form) {
- var _answers = getBlockAnswers(answers, uid);
- var lines = makePollTable(_answers, form[uid].opts);
- return h('div.cp-form-type-poll', lines);
- },
- icon: h('i.cptools.cptools-form-poll')
- },
sort: {
defaultOpts: {
values: [1,2].map(function (i) {
@@ -1453,7 +1427,7 @@ define([
})
},
get: function (opts, a, n, evOnChange) {
- if (!opts) { opts = TYPES.radio.defaultOpts; }
+ if (!opts) { opts = TYPES.sort.defaultOpts; }
if (!Array.isArray(opts.values)) { return; }
var map = {};
var invMap = {};
@@ -1472,7 +1446,10 @@ define([
$(div).data('val', data);
return div;
});
- var tag = h('div.cp-form-type-sort-container', els);
+ var tag = h('div.cp-form-type-sort-container', [
+ h('div.cp-form-sort-hint', Messages._getKey('form_sort_hint', [els.length])),
+ els
+ ]);
var $tag = $(tag);
var reorder = function () {
$tag.find('.cp-form-type-sort').each(function (i, el) {
@@ -1527,7 +1504,7 @@ define([
},
printResults: function (answers, uid, form) {
- var opts = form[uid].opts || TYPES.radio.defaultOpts;
+ var opts = form[uid].opts || TYPES.sort.defaultOpts;
var l = (opts.values || []).length;
var results = [];
var empty = 0;
@@ -1556,15 +1533,116 @@ define([
},
icon: h('i.cptools.cptools-form-list-ordered')
},
+ poll: {
+ defaultOpts: {
+ type: 'text', // Text or Days or Time
+ values: [1, 2, 3].map(function (i) {
+ return Messages._getKey('form_defaultOption', [i]);
+ })
+ },
+ get: function (opts, answers, username, evOnChange) {
+ if (!opts) { opts = TYPES.poll.defaultOpts; }
+ if (!Array.isArray(opts.values)) { return; }
+
+ var lines = makePollTable(answers, opts);
+
+ // Add form
+ var addLine = opts.values.map(function (data) {
+ var cell = h('div.cp-poll-cell.cp-form-poll-choice', [
+ h('i.fa.fa-times.cp-no'),
+ h('i.fa.fa-check.cp-yes'),
+ h('i.fa.fa-question.cp-maybe'),
+ ]);
+ var $c = $(cell);
+ $c.data('option', data);
+ var val = 0;
+ $c.attr('data-value', val);
+ $c.click(function () {
+ val = (val+1)%3;
+ $c.attr('data-value', val);
+ evOnChange.fire();
+ });
+ cell._setValue = function (v) {
+ val = v;
+ $c.attr('data-value', val);
+ };
+ return cell;
+ });
+ // Name input
+ //var nameInput = h('input', { value: username || Messages.anonymous });
+ var nameInput = h('span.cp-poll-your-answers', Messages.form_pollYourAnswers)
+ addLine.unshift(h('div.cp-poll-cell', nameInput));
+ lines.push(h('div', addLine));
+
+ var total = makePollTotal(answers, opts, addLine, evOnChange);
+ if (total) { lines.push(h('div', total)); }
+
+ var tag = h('div.cp-form-type-poll-container', h('div.cp-form-type-poll', lines));
+ var $tag = $(tag);
+
+ var cursorGetter;
+ var setCursorGetter = function (f) { cursorGetter = f; };
+ return {
+ tag: tag,
+ getValue: function () {
+ var res = {};
+ $tag.find('.cp-form-poll-choice').each(function (i, el) {
+ var $el = $(el);
+ res[$el.data('option')] = $el.attr('data-value');
+ });
+ return {
+ values: res
+ };
+ },
+ reset: function () {
+ $tag.find('.cp-form-poll-choice').attr('data-value', 0);
+ },
+ edit: function (cb, tmp) {
+ var v = Util.clone(opts);
+ return editOptions(v, setCursorGetter, cb, tmp);
+ },
+ getCursor: function () { return cursorGetter(); },
+ setValue: function (res) {
+ this.reset();
+ if (!res || !res.values) { return; }
+ var val = res.values;
+ $tag.find('.cp-form-poll-choice').each(function (i, el) {
+ if (!el._setValue) { return; }
+ var $el = $(el);
+ el._setValue(val[$el.data('option')] || 0);
+ });
+ }
+ };
+
+ },
+ printResults: function (answers, uid, form) {
+ var opts = form[uid].opts || TYPES.poll.defaultOpts;
+ var _answers = getBlockAnswers(answers, uid);
+ var lines = makePollTable(_answers, opts);
+
+ var total = makePollTotal(_answers, opts);
+ if (total) { lines.push(h('div', total)); }
+
+ return h('div.cp-form-type-poll', lines);
+ },
+ icon: h('i.cptools.cptools-form-poll')
+ },
};
var renderResults = function (content, answers) {
var $container = $('div.cp-form-creator-results').empty();
+
+ if (!Object.keys(answers || {}).length) {
+ $container.append(h('div.alert.alert-info', Messages.form_results_empty));
+ return;
+ }
+
var controls = h('div.cp-form-creator-results-controls');
var $controls = $(controls).appendTo($container);
var results = h('div.cp-form-creator-results-content');
var $results = $(results).appendTo($container);
+
var summary = true;
var form = content.form;
@@ -1715,7 +1793,7 @@ define([
});
return results;
};
- var makeFormControls = function (framework, content, update) {
+ var makeFormControls = function (framework, content, update, evOnChange) {
var loggedIn = framework._.sfCommon.isLoggedIn();
var metadataMgr = framework._.cpNfInner.metadataMgr;
@@ -1768,6 +1846,7 @@ define([
console.error(err || data.error);
return void UI.warn(Messages.error);
}
+ window.onbeforeunload = undefined;
if (!update) {
// Add results button
addResultsButton(framework, content);
@@ -1783,7 +1862,53 @@ define([
reset = undefined;
}
+ var invalid = h('div.cp-form-invalid-warning');
+ var $invalid = $(invalid);
+ if (evOnChange) {
+ var origin, priv;
+ if (APP.common) {
+ var metadataMgr = APP.common.getMetadataMgr();
+ priv = metadataMgr.getPrivateData();
+ origin = priv.origin;
+ }
+ evOnChange.reg(function () {
+ var $container = $('div.cp-form-creator-content');
+ var $inputs = $container.find('input:invalid');
+ if (!$inputs.length) {
+ $send.text(update ? Messages.form_update : Messages.form_submit);
+ return void $invalid.empty();
+ }
+ $send.text(update ? Messages.form_updateWarning : Messages.form_submitWarning);
+ var lis = [];
+ $inputs.each(function (i, el) {
+ var $el = $(el).closest('.cp-form-block');
+ var number = $el.find('.cp-form-block-question-number').text();
+ var a = h('a', {
+ href: origin + '#' + Messages._getKey('form_invalidQuestion', [number])
+ }, Messages._getKey('form_invalidQuestion', [number]));
+ $(a).click(function (e) {
+ e.preventDefault();
+ if (!$el.is(':visible')) {
+ var pages = $el.closest('.cp-form-page').index();
+ if (APP.refreshPage) { APP.refreshPage(pages + 1); }
+ }
+ $el[0].scrollIntoView();
+ });
+ var li = h('li', a);
+ lis.push(li);
+ });
+ var list = h('ul', lis);
+ var content = [
+ h('span', Messages.form_invalidWarning),
+ list
+ ];
+ $invalid.empty().append(content);
+ });
+ evOnChange.fire(true);
+ }
+
return h('div.cp-form-send-container', [
+ invalid,
cbox ? h('div', cbox) : undefined,
send, reset
]);
@@ -1801,7 +1926,8 @@ define([
var _answers = Util.clone(answers || {});
delete _answers._proof;
delete _answers._userdata;
- evOnChange.reg(function () {
+ evOnChange.reg(function (noBeforeUnload) {
+ if (noBeforeUnload) { return; }
var results = getFormResults();
if (!answers || Sortify(_answers) !== Sortify(results)) {
window.onbeforeunload = function () {
@@ -1884,6 +2010,7 @@ define([
var elements = [];
+ var n = 1; // Question number
content.order.forEach(function (uid) {
var block = form[uid];
var type = block.type;
@@ -1913,7 +2040,10 @@ define([
var dragHandle;
- var q = h('div.cp-form-block-question', block.q || Messages.form_default);
+ var q = h('div.cp-form-block-question', [
+ h('span.cp-form-block-question-number', (n++)+'.'),
+ h('span', block.q || Messages.form_default)
+ ]);
var editButtons, editContainer;
APP.formBlocks.push(data);
@@ -2013,7 +2143,7 @@ define([
framework._.cpNfInner.chainpad.onSettle(function () {
$(editButtons).show();
UI.log(Messages.saved);
- var _answers = getBlockAnswers(APP.answers, uid);
+ _answers = getBlockAnswers(APP.answers, uid);
data = model.get(newOpts, _answers, null, evOnChange);
if (!data) { data = {}; }
$oldTag.before(data.tag).remove();
@@ -2085,19 +2215,19 @@ define([
var pageContainer = h('div.cp-form-page-container');
var $page = $(pageContainer);
_content.push(pageContainer);
- var refreshPage = function (current) {
+ var refreshPage = APP.refreshPage = function (current) {
$page.empty();
if (!current || current < 1) { current = 1; }
if (current > pages) { current = pages; }
- var left = h('button.btn.btn-secondary.small.cp-prev', [
- h('i.fa.fa-chevron-left'),
- h('span', Messages.form_page_prev)
+ var left = h('button.btn.btn-secondary.cp-prev', [
+ h('i.fa.fa-arrow-left'),
]);
var state = h('span', Messages._getKey('form_page', [current, pages]));
- var right = h('button.btn.btn-secondary.small.cp-next', [
- h('span', Messages.form_page_next),
- h('i.fa.fa-chevron-right'),
+ var right = h('button.btn.btn-secondary.cp-next', [
+ h('i.fa.fa-arrow-right'),
]);
+ if (current === pages) { $(right).css('visibility', 'hidden'); }
+ if (current === 1) { $(left).css('visibility', 'hidden'); }
$(left).click(function () { refreshPage(current - 1); });
$(right).click(function () { refreshPage(current + 1); });
$page.append([left, state, right]);
@@ -2139,7 +2269,7 @@ define([
}
// In view mode, add "Submit" and "reset" buttons
- $container.append(makeFormControls(framework, content, Boolean(answers)));
+ $container.append(makeFormControls(framework, content, Boolean(answers), evOnChange));
};
var getTempFields = function () {
@@ -2172,6 +2302,9 @@ define([
var helpMenu = framework._.sfCommon.createHelpMenu(['text', 'pad']);
$toolbarContainer.after(helpMenu.menu);
+ if (!APP.isEditor) {
+ framework._.toolbar.alone();
+ }
var makeFormSettings = function () {
// Private / public status
@@ -2180,7 +2313,7 @@ define([
var refreshPublic = function () {
$results.empty();
var makePublic = h('button.btn.btn-secondary', Messages.form_makePublic);
- var makePublicDiv = h('div', makePublic);
+ var makePublicDiv = h('div.cp-form-actions', makePublic);
if (content.answers.privateKey) { makePublicDiv = undefined; }
var publicText = content.answers.privateKey ? Messages.form_isPublic : Messages.form_isPrivate;
$results.append(h('span.cp-form-results-type', publicText));
diff --git a/www/form/main.js b/www/form/main.js
index 344c5bb0c..0d288dbe3 100644
--- a/www/form/main.js
+++ b/www/form/main.js
@@ -230,7 +230,7 @@ define([
results[senderCurve] = {
msg: parsed,
hash: hash,
- time: cfg.time
+ time: cfg && cfg.time
};
};
CPNetflux.start(config);