2016-05-02

Tweaked wrappers: now selects should be wrapped in a span, not a div

New page

//KForms 2.3.1

//Moving to a new page for 2016, even though I haven't yet made enough changes to merit a new version number

// todo:

// fix the duplicate subforms in the dropdown bug

//Changelog

//later in 2.3.1: new team selector

//in 2.3.1: form warns you when silent logout bug has happened. (and old warnings are removed correctly if you logout/in in another tab & don't reload)

//in 2.3: subform-mandatory forms e.g. Check In

//in 2.2.1: confirm before unsubmit a signed form, clean up messaging and allow custom error msgs

//in 2.2: submit and submitted-warning, permissions by role

//in 2.1: smarter validation, ability to add a warning on any element,

//ability to validate a DFF on change as well as on submit

//in 2.0: view/edit/admin modes, changing team/mode without reloading

// later:

//- consider rewriting owner_info / team_id / author global var logic like this:

// owner = {type: "user", key: "kdrinkwa", info: {stuff that came from ajax}}

// -default Ajax options using extend()

// =====================================================

// BLOCK

// CUSTOM ERROR CLASSES

// =====================================================

function AjaxError(message) {

this.name = "AjaxError";

this.message = message;

this.stack = (new Error()).stack;

}

AjaxError.prototype = Object.create(Error.prototype);

function PerlError(message) {

this.name = "PerlError";

this.message = message;

this.stack = (new Error()).stack;

}

PerlError.prototype = Object.create(Error.prototype);

function FormError(message) {

this.name = "FormError";

this.message = message;

this.stack = (new Error()).stack;

}

FormError.prototype = Object.create(Error.prototype);

// =====================================================

// BLOCK

// URL parser and String.startsWith

// =====================================================

//This function parses a URL (or the current page's URL, if no argument is passed in)

//and returns an object full of useful properties.

//Reference:

// http://www.abeautifulsite.net/parsing-urls-in-javascript/

// https://gist.github.com/jlong/2428561

function parseURL(url) {

if (typeof url == "undefined") {

parser = window.location;

}

else {

var parser = document.createElement('a');

// Let the browser do the work

parser.href = url;

}

var searchObject = {},

queries, split, i;

// Convert query string to object

queries = parser.search.replace(/^\?/, '').split('\u0026');

for( i = 0; i < queries.length; i++ ) {

split = queries[i].split('=');

searchObject[split[0]] = split[1];

}

return {

protocol: parser.protocol,

host: parser.host,

hostname: parser.hostname,

port: parser.port,

pathname: parser.pathname,

search: parser.search,

searchObject: searchObject,

hash: parser.hash

};

}

if (typeof String.prototype.startsWith != "function") {

String.prototype.startsWith = function(str) {

return this.slice(0, str.length) == str;

}

}

// =====================================================

// BLOCK

// CONSTANTS AND QUERY STRING PARAMETERS

// =====================================================

var default_messages = {

kicking_to_view_mode: "Returning to View mode.",

not_kicking_to_view_mode: "Please return to View mode.",

user_cannot_edit: "You are not a member of the selected team.\nIf this is an error, make sure you are logged in and your account is included on your team's roster.",

user_cannot_submit: "You do not have permission to submit this form.",

user_cannot_use_admin_mode: "You are not an administrator.",

user_cannot_edit_admin_fields: "That field is only for use by iGEM Headquarters.",

//used by get_user_info for the case of a user-owned form only (right now, those can't be made public)

not_logged_in: "You are not logged in. Please log in (or <a href='http:\/\/igem.org/Account_Apply.cgi'>sign up for an account</a>) to complete this form.",

//to be used by OIC if the user is mid-editing and the silent logout bug happens.

silent_logout_bug: "Oops, the silent logout bug happened!",

form_validation_fail_beginning: "Some required answers are missing:",

unparseable_ajax_response: "There was an error in FORM.CGI. Please inform us of this error by emailing hq (at) igem (dot) org.",

please_confirm_submission: "Please check the box to confirm your submission.",

confirm_deletion: "Are you sure you want to delete this form?\nYou cannot undo this action.",

field_validation_fail_beginning: "Answer missing:",

invalid_email_address: "Invalid email address"

};

var messages = jQuery.extend({}, default_messages, form_info.messages);

var non_admin_DFFs = "[data-form-field]:not([data-form-field^='admin'])"; //convenient selector for non-admin DFFs

var valid_modes = ["view", "edit", "admin"];

var valid_owner_types = ["user", "judge", "team", "lab", "course"];

var valid_permission_groups = ["author", "group_members", "super_users", "any_users", "public"];

var valid_roles = ["Primary Contact", "Student", "Instructor", "Advisor"];//lol not implemented

var valid_required = ["required", "optional"];

//Explicitly initialize global variables

var mode;

var team_id;

var author;

var owner_info;

var user_info;

var form_displayed = false;

var form_submitted = false; //sigh, it looks like I can't get away without this one

var permissions = {view: false, edit: false, submit: false, admin: false};

var parsed_query_string = parseURL().searchObject;

mode = parsed_query_string["mode"];

if (form_info.owner_type == "team") {

if (typeof team_id == "undefined") {

team_id = parseInt(parsed_query_string["team_id"]);

}

}

else if (form_info.owner_type == "user") {

author = parsed_query_string["author"];

}

else {

throw new FormError("Judge, Lab, and Course owned forms are not implemented yet");

}

// =====================================================

// BLOCK

// VALIDATING INDIVIDUAL FORMS CONFIG

// =====================================================

function validate_form_info() {

assert(typeof form_info == "object", "form_info is not an object");

assert(typeof form_info.name == "string", "form_info.name is not a string");

assert(typeof form_info.display_title == "string", "form_info.display_title is not a string");

assert(valid_modes.indexOf(form_info.default_mode) > -1, "form_info.default_mode is not valid");

assert(valid_owner_types.indexOf(form_info.owner_type) > -1, "form_info.owner_type is not valid");

assert(form_info.permissions instanceof Object, "form_info.permissions is not an object");

["view", "edit", "submit", "admin"].forEach(function(action, index, array) {

assert(form_info.permissions[action] instanceof Array, "form_info.permissions." + action + " is not an array");

form_info.permissions[action].forEach(function(perm_group, index, array) {

assert(valid_permission_groups.indexOf(perm_group) > -1 || valid_roles.indexOf(perm_group) > -1 , "invalid permission group in form_info.permissions." + action);

});

});

assert(typeof form_info.ajax_URL == "string", "form_info.ajax_URL is not a string");

assert(valid_required.indexOf(form_info.validate_unspecified_fields) > -1, "form_info.validate_unspecified_fields is not valid");

}

validate_form_info();

// =====================================================

// BLOCK

// SETUP ON DOCUMENT.READY (yes it is the kitchen sink)

// =====================================================

function document_ready_handler() {

//Validate mode and default to something if invalid

check_mode();

//NOW A WHOLE BUNCH OF RANDOM ELEMENT SETUP

//Display form title

$("#form_title_display").text(form_info.display_title).show();

//read the nokick checkbox and bind it to change nokick

nokick = $("#nokick").prop("checked");

$("#nokick").change(function(event) {

nokick = $(this).prop("checked");

});

//bind author-select input on enter key

$("#author_select").keyup(function(event) {

if (event.which == 13) {

change_author($(this).val());

}

});

//apply wrapper divs for feedback function

$("[data-form-field]:not(textarea)").wrap("<span class='wrapper'><\/span>");

$("textarea[data-form-field]").wrap("<DIV class='wrapper'><\/DIV>");

$("p").find(".wrapper").has("[type=radio], [type=checkbox]").css("margin-left", "2em");

//force create AJ log and error divs

$("#formbody").before("<DIV id='aj_form_errors'></DIV>").after("<DIV id='aj_form_log'></DIV>");

//display log div

//(it will be hidden from non-admin users by default, but if you're debugging for such a user

//then you can go into the console and show it -- you do want it to EXIST for all users.

//So don't move this into an if-user-is-admin clause!)

aj_display_log_div();

//bind click events for .change_mode(team,author) links

//needs to delegate from #bodyContent because not all .change_mode links exist

//at document.ready

$("#bodyContent").on("click", "a.change_mode", function(e) {

var new_action = $(this).attr("data-mode");

change_mode(new_action);

});

$("#bodyContent").on("click", "a.change_team", function(e) {

var new_team = $(this).attr("data-team");

change_team(new_team);

});

$("#bodyContent").on("click", "a.change_author", function(e) {

var new_author = $(this).attr("data-author");

change_author(new_author);

});

//bind hide_admin

$("#hide_admin").click(function(event) {

event.preventDefault();

$(".admins_only").hide();

});

//The event handlers for data-form-fields used to be bound here.

//But because some of them depend on user_info, I've moved them into their own function

//which is called by get_user_info when it's done.

//**This change was dumb. Revert it. But it's tangled, so that will take some doing.

refresh_form();

//Start the chain by getting user info

get_user_info();

$("#team_list_dropdown").on("change", function(e) {

var team_id_chosen = $(this).val();

change_team(parseInt(team_id_chosen));

});

if (form_info.owner_type == "team") {

//put teams in the team list dropdown

jQuery.ajax({

url: "http://igem.org/aj/team_list.cgi",

type: "GET",

timeout: 30000,

dataType: "json",

data: {command: "get_team_list", year: "2015"}, //**Make this auto-update itself

error: function(jqxhr, textStatus, errorThrown) {

general_ajax_error( jqxhr, textStatus, errorThrown,

"Failed to get list of teams");

aj_log("Failed to get list of teams");

},

success: function(data, textStatus, jqxhr) {

put_teams_in_selector(data);

}

});

}

else {

$("#team_list_dropdown").hide();

}

//if it's a user-owned form and no author is specified, it defaults to self.

//but that happens later, in get_user_info success

//(because it can't default to self if you're not logged in)

}

function put_teams_in_selector(team_list) {

//Each element of team_list is an object like

//{ region: "Europe", name: "Aachen", id: "1585" }

var optgroups = { "Africa": "<optgroup label='Africa'>",

"Asia": "<optgroup label='Asia'>",

"Europe": "<optgroup label='Europe'>",

"Latin America": "<optgroup label='Latin America'>",

"North America": "<optgroup label='North America'>"

};

team_list.forEach(function(team) {

optgroups[team.region] += "<option value='" + team.id + "'>" + team.name + "</option>";

});

//I'm not using a for..in loop for this because order is important

$("#team_list_dropdown").append(optgroups["Africa"] + "</optgroup>");

$("#team_list_dropdown").append(optgroups["Asia"] + "</optgroup>");

$("#team_list_dropdown").append(optgroups["Europe"] + "</optgroup>");

$("#team_list_dropdown").append(optgroups["Latin America"] + "</optgroup>");

$("#team_list_dropdown").append(optgroups["North America"] + "</optgroup>");

}

function bind_DFF_event_handlers() {

//bind change handler to all data-form-field elements

//except admin fields and submit-family buttons

//and validate-on-change fields

$(non_admin_DFFs).not("[type='submit']").not(".validate-on-change").change(change_handler_for_most_DFFs);

$(non_admin_DFFs).filter(".validate-on-change").change(DFF_change_handler_with_validation);

//bind handler to admin data-form-fields according to whether user is admin

if(user_info.is_super) {

$("[data-form-field^='admin']").change(admin_DFF_change_handler);

}

else {

$("[data-form-field^='admin']").click(admin_lockout_click_handler);

}

//bind click handlers for submit/delete/return buttons

//These are bound regardless of permissions, BUT

//one_input_changed will reject these changes if the user doesn't have submit permissions

$("[data-form-field=submit][type='submit']").click(click_handler_for_submit);

$("[data-form-field=delete_form][type='submit']").click(click_handler_for_delete);

$("[data-form-field=return_form][type='submit']").click(click_handler_for_return);

$("#popwin_unsubmit").on("click", click_handler_for_return);

$("#popwin_dismiss").on("click", function(event) {

event.preventDefault();

$("#submitted_warning").hide();

});

//bind click handler for submit confirmation box

$("#" + $("[data-form-field=submit]").attr("data-confirmation")).on("click", click_handler_for_confirm_submit);

//bind handler for custom new subform event

//this event is right now triggered by code that's inline in the form page,

//which also handles validation of new sub form fields

//(eventually should be moved into here, though)

$("[data-form-field=sub_form]").on("kforms:newsubform", newsf_handler);

}

$(document).ready(document_ready_handler);

// =====================================================

// BLOCK

// OTHER NAMED EVENT HANDLERS

// =====================================================

function change_handler_for_most_DFFs(event) {

//console.log("change_handler_for_most_DFFs");

if ($(this).attr("data-form-field") == "sub_form") {

console.log("change handler on sub_form");

var offon = ($(this).val() == "") ? "on" : "off";

$("[data-form-field^='sub_form_']").each(function(index, field) {

dffswitch(field, offon);

});

one_input_changed(this, 'store', '', false, false);

}

else if ($(this).attr("data-form-field").startsWith("sub_form_")) {

if ($("[data-form-field=sub_form]").val() == "") { return; }

one_input_changed(this, 'store', '', false, true);

}

else {

one_input_changed(this, 'store', '', false, true);

}

}

function DFF_change_handler_with_validation(event) {

var validation_result = validate_one_field(this);

if (validation_result == "") {

one_input_changed(this, 'store', '', false, true);

}

}

function admin_DFF_change_handler(event) {

//console.log("admin_DFF_change_handler");

one_input_changed(this, 'store', '', false, true);

}

function admin_lockout_click_handler(event) {

//console.log("admin_lockout_click_handler");

event.preventDefault();

alert(messages.user_cannot_edit_admin_fields);

}

function click_handler_for_submit(event) {

event.preventDefault();

//if there are any validation error messages, show them and don't submit

var errors = validate_form_entries();

if(errors.length > 0) {

alert(messages.form_validation_fail_beginning + "\n\n" + errors.join("\n"));

return;

}

//moved confirmation-box-polling to OIC so it happens AFTER permission checking

//finally, actually submit the form

one_input_changed(this, 'submit', '', false, true);

}

function click_handler_for_confirm_submit(event) {

if (!permissions.submit) {

alert(messages.user_cannot_submit);

event.preventDefault();

}

}

function click_handler_for_delete(event) {

//console.log("starting delete click handler");

event.preventDefault();

var confirmed = window.confirm(messages.confirm_deletion);

if (confirmed) {

one_input_changed(this, 'delete_form', '', false, true);

}

}

function click_handler_for_return(event) {

event.preventDefault();

one_input_changed(this, 'return_form', '', false, true);

}

function newsf_handler(event, newname) {

console.log("newsf_handler, value " + newname);

make_new_sub_form(newname, false, true);

one_input_changed(this, 'store', '', true, true);

}

// =====================================================

// BLOCK

// USER AND OWNER INFO

// =====================================================

//Get information about the logged-in user

//(nb: it looks at the login cookie, not at wgUserName)

function get_user_info() {

aj_log("Getting user info");

var request = jQuery.ajax({ url: form_info.ajax_URL,

type: "POST",

timeout: 30000,

data: {command: 'get_user_info'},

dataType: "json",

error: function(jqxhr, textStatus, errorThrown) {

general_ajax_error( jqxhr, textStatus, errorThrown,

"Failed to get information about the user");

},

success: function(data, textStatus, jqxhr) {

aj_log("Received user info: " + jqxhr.responseText);

if(try_parse_received_info(jqxhr.responseText)) {

user_info = jqxhr.responseJSON;

bind_DFF_event_handlers();

//Show hide admin stuff

if (user_info.is_super == "true") { $(".admins_only").show(); }

else { $(".admins_only").hide(); }

//now, if it's a user-owned form, we can handle some special cases at this point

if (form_info.owner_type == "user") {

//If they're not logged in,

//Yell at them and stop the setup chain.

//**eventually I may want to change this, if I want to allow the public to view

//any user-owned forms

if (user_info.user_name == "") {

aj_display_error(messages.not_logged_in);

return;

}

//Now we know they must be logged in, and we can default to self-author if no author specified.

else if (typeof author == "undefined") {

author = user_info.user_name;

}

}

//allow individual forms to have a custom extra response at this point

if (typeof custom_get_user_info_response == "function") {

custom_get_user_info_response(data, textStatus, jqxhr);

}

get_owner_info();

}

else {

aj_display_error(messages.unparseable_ajax_response);

}

}

});

}

//Get information about the form owner

//This calls one of several specialized ajax getters, and then triggers the whole

//rest of form setup upon success of the request.

function get_owner_info(query_param) {

aj_log("Getting owner info");

owner_info = undefined;

//refresh_form();

var ajax_getter;

if (form_info.owner_type == "team") {ajax_getter = get_team_info;}

else if (form_info.owner_type == "user") {ajax_getter = get_author_info;}

else if (form_info.owner_type == "judge") {ajax_getter = get_judge_info;}

else if (form_info.owner_type == "lab") {ajax_getter = get_lab_info;}

else if (form_info.owner_type == "course") {ajax_getter = get_course_info;}

var request = ajax_getter(query_param);

request.done( function (data, textStatus, jqxhr) {

if(try_parse_received_info(jqxhr.responseText)) {

if (data.error) {

//we did the query wrong, oh no

//this case will catch invalid team/lab/course IDs

respond_to_error_in_owner_info();

}

else {

//special case: invalid usernames

//will return empty strings for user_name and full_name

if (form_info.owner_type == "user" && data.user_name == "") {

respond_to_error_in_owner_info();

}

else {

owner_info = data; //put owner info into global object

new_form_setup(); //this used to be set_up_form_and_mode

}

}

if (typeof custom_get_owner_info_response == "function") {

custom_get_owner_info_response(data, textStatus, jqxhr);

}

}

else {

aj_display_error(messages.unparseable_ajax_response);

}

});

}

//**These get_foo_info functions are kind of repetitive. Maybe rewrite later.

//Get information about a user of a specified username.

//If none is specified, the function will look at global var author.

function get_author_info(query_author) {

query_author = (typeof query_author == "undefined") ? author : query_author

return jQuery.ajax({ url: form_info.ajax_URL,

type: "POST",

timeout: 30000,

dataType: "json",

data: {command: 'get_user_info', username: query_author},

error: function(jqxhr, textStatus, errorThrown) {

general_ajax_error( jqxhr, textStatus, errorThrown,

"Failed to get information about author " + query_author);

aj_log("Failed to get author info");

},

success: function(data, textStatus, jqxhr) {

aj_log("Received author info: " + jqxhr.responseText);

}

});

}

function get_judge_info(query_judge_username) {

query_judge_username = (typeof query_judge_username == "undefined") ? judge_username : query_judge_username;

return jQuery.ajax({ url: form_info.ajax_URL,

type: 'POST',

timeout: 30000,

dataType: "json",

data: {command: "get_judge_info", judge_username: query_judge_username},

error: function(jqxhr, textStatus, errorThrown) {

general_ajax_error(jqxhr, textStatus, errorThrown, "Failed to get judge information");

aj_log("Failed to get judge info");

},

success: function(data, textStatus, jqxhr) {

aj_log("Received judge info: " + jqxhr.responseText);

}

});

}

// Get information about a team (and about the user w/r/t that team)

function get_team_info(query_team_id) {

query_team_id = (typeof query_team_id == "undefined") ? team_id : query_team_id;

return jQuery.ajax({ url: form_info.ajax_URL,

type: 'POST',

timeout: 30000,

dataType: "json",

data: {command: 'get_info', form_name: form_info.name, team_id: parseInt(query_team_id)},

error: function(jqxhr, textStatus, errorThrown) {

general_ajax_error(jqxhr, textStatus, errorThrown, "Failed to get team information");

aj_log("Failed to get team info");

},

success: function(data, textStatus, jqxhr) {

aj_log("Received team info: " + jqxhr.responseText);

}

});

}

function get_lab_info(query_lab_id) {

query_lab_id = (typeof query_lab_id == "undefined") ? lab_id : query_lab_id;

return jQuery.ajax({ url: form_info.ajax_URL,

type: 'POST',

timeout: 30000,

dataType: "json",

data: {command: 'get_info', form_name: form_info.name, lab_id: parseInt(query_lab_id)},

error: function(jqxhr, textStatus, errorThrown) {

general_ajax_error(jqxhr, textStatus, errorThrown, "Failed to get lab information");

aj_log("Failed to get lab info");

},

success: function(data, textStatus, jqxhr) {

aj_log("Received lab info: " + jqxhr.responseText);

}

});

}

function get_course_info(query_course_id) {

query_course_id = (typeof query_course_id == "undefined") ? course_id : query_course_id;

return jQuery.ajax({ url: form_info.ajax_URL,

type: 'POST',

timeout: 30000,

dataType: "json",

data: {command: 'get_info', form_name: form_info.name, course_id: parseInt(query_course_id)},

error: function(jqxhr, textStatus, errorThrown) {

general_ajax_error(jqxhr, textStatus, errorThrown, "Failed to get course information");

aj_log("Failed to get course info");

},

success: function(data, textStatus, jqxhr) {

aj_log("Received course info: " + jqxhr.responseText);

}

});

}

function get_sub_form_list() {

aj_log("Getting sub form list");

return jQuery.ajax({ url: form_info.ajax_URL,

type: 'POST',

timeout: 30000,

dataType: "json",

data: {command: 'get_sub_form_list', form_name: form_info.name, team_id: team_id},

//**THIS DATA DOES NOT WORK FOR ANY OWNER TYPE EXCEPT TEAMS.

error: function(jqxhr, textStatus, errorThrown) {

general_ajax_error(jqxhr, textStatus, errorThrown, "Failed to get subforms");

aj_log("Failed to get sub form list");

},

success: function(data, textStatus, jqxhr) {

aj_log("Received sub_form list: " + jqxhr.responseText);

}

});

}

// =====================================================

// BLOCK

// USER INFO RESPONSE

// =====================================================

//This function has been obsoleted by new_form_setup, which is currently in testing.

//function set_up_form_and_mode() {

//Some of this was redundant with show_hide_submitted_status.

// determine_permissions();

// show_owner();

// show_mode();

// if (form_info.owner_type == "team") {

// $("#team_select_inner").slideUp();

// }

// //if the user has permission, go ahead and set up for each mode

// if (permissions[mode] == true) {

// if (mode == "view") {

// // console.log("setup view");

// $(".admins_only").hide();

// }

// else if (mode == "edit") {

// // console.log("setup edit");

// if (!form_submitted) {

// undisable_all_DFFs();

// }

// else {

// $("[data-form-field='return_form']").prop("disabled", false);

// }

// $(".admins_only").hide();

// }

// else if (mode == "admin") {

// // console.log("setup admin");

// undisable_all_DFFs();

// undisable_admin_DFFs();

// $(".admins_only").show();

// }

// //finish displaying the form

// if (!form_displayed) {

// display_form();

// }

// }

// else {

// //NOPE, USER, STAHP THAT

// deny_permission();

// }

// //Show admins the <p> that contains basic access to admin functions

// if(user_info.is_super == "true") {

// $("#admin_link").show();

// }

//}

function respond_to_error_in_owner_info() {

aj_display_error("Invalid " + form_info.owner_type + " selected");

}

//**Moe some of this down in to subform utility functions

function display_form() {

if ($("[data-form-field='sub_form']").length == 1) {

var sfreq = get_sub_form_list();

sfreq.done(function(data, textStatus, jqxhr){

data.sub_forms.forEach(function(sf) {

if (sf.sub_form == "") {

return;

}

make_new_sub_form(sf.sub_form, sf.submitted == "1", false);

});

});

initially_get_old_answers();

}

else {

initially_get_old_answers();

}

}

function deny_permission() {

//because I got rid of the no kick checkbox

nokick = false;

if (!user_info.is_super) { $(".admins_only").hide(); }

var alert_string = "Sorry, you don't have permission to " + mode + " that form.";

//**figure out how to unify this string because it depends on current mode

if (permissions.view) {

if (nokick) {

alert_string += "\n" + messages.not_kicking_to_view_mode;

}

else {

alert_string += "\n" + messages.kicking_to_view_mode;

}

}

if (nokick) {

aj_display_error(alert_string);

}

else {

alert(alert_string);

change_mode("view");

}

}

//This function shows or hides an optional div id="submitted_warning"

//and also does some new stuff and is part of the control flow

//but is partly redundant with set_up_form_and_mode

//so should be cleaned up.

//UPDATE: this function has been obsoleted by new_form_setup and is not called anywhere anymore.

//function show_hide_submitted_status(submitted) {

// if (submitted > 0) {

// $("#submitted_warning").show();

// readonly_all_DFFs();

// if ((permissions["edit"] || permissions["admin"]) && mode != "view") {

// $("[data-form-field='return_form']").prop("disabled", false);

// }

// }

// else {

// $("#submitted_warning").hide();

// if (mode == "edit" && permissions["edit"]) {

// undisable_all_DFFs();

// }

// else if (mode == "admin" && permissions["admin"]) {

// undisable_all_DFFs();

// undisable_admin_DFFs();

// }

// if ((permissions["edit"] || permissions["admin"]) && mode != "view") {

// $("[data-form-field='return_form']").prop("disabled", true);

// }

// }

//}

//**Okay so this needs MAJOR testing.

//The problem is that the interaction of form_submitted, form_displayed, display_form, and OIC

//may create an infinite loop. If we're gonna call this on initial setup AND every time someone hits

//the submit button... can we not call it twice? Problem is OIC calls this when someone hits the su

function new_form_setup() {

determine_permissions();

//special-case because it's common to poke around a bunch of teams, then go back to your team

//and be confused that you can't edit

if (mode == "view" && form_info.default_mode == "edit" && permissions["edit"]) {

mode = "edit";

}

show_owner();

show_mode();

if (!permissions[mode]) {

deny_permission();

}

//Here's where we handle the visibility of all the accessory crap that ISN'T a dff.

if (form_info.owner_type == "team") {

$("#team_select_inner").slideUp(); //this won't get called unless a team has been picked

}

if (form_submitted) {

$("#submitted_warning").show();

}

else {

$("#submitted_warning").hide();

}

if (!form_displayed) {display_form();}

set_up_dffs_for_mode();

}

function set_up_dffs_for_mode(mymode) {

mymode = (typeof mymode == "undefined") ? mode : mymode;

if (!permissions[mode]) {

mymode = "view";

}

//**Carefully review all these conditions against what they should be. Oh god.

$("[data-form-field]").each(function(index, field) {

field = $(field);

var dff = field.attr("data-form-field");

if (dff == "sub_form") { //the subform select

dffswitch(field, "on");

}

else if (dff.startsWith("sub_form")) { //subform accessory fields

dffswitch(field, ($("[data-form-field='sub_form']").val() == "" && mode != "view") ? "on" : "off");

}

else if (dff.startsWith("admin")) { //admin dffs

dffswitch(field, (mode == "admin" && user_info.is_super == "true") ? "on" : "off");

}

else if (dff == "submit") { //submit button

dffswitch(field, (mode != "view" && permissions.submit && !form_submitted) ? "on" : "off");

}

else if (dff == "return_form") { //unsubmit button

dffswitch(field, (mode != "view" && permissions.edit && form_submitted) ? "on" : "off");

}

else if (dff == "delete_form") { //delete button

dffswitch(field, (mode != "view" && permissions.edit) ? "on" : "off");

}

else { //Regular DFF

//This will ALSO check for an empty sub_form (value "").

//Note that a NONEXISTENT sub_form would have val() undefined.

dffswitch(field, (mode != "view" && !form_submitted && $("[data-form-field=sub_form]").val() != "") ? "on" : "off");

}

});

}

// =====================================================

// BLOCK

// OWNER/MODE SHOWING/CHANGING

// =====================================================

//modes!

//All this does is reconcile the view/edit/admin mode indicator at the top of the page

function show_mode() {

//console.log("show_mode");

var m = $("#modes");

if (mode == "view") {

m.html(jQuery.parseHTML("Mode: View <a class='change_mode' data-mode='edit'>(click for edit mode)</a>"));

}

else if (mode == "edit") {

m.html(jQuery.parseHTML("Mode: Edit <a class='change_mode' data-mode='view'>(click for view mode)</a>"));

}

else if (mode == "admin") {

m.html(jQuery.parseHTML("Mode: Admin <a class='change_mode' data-mode='view'>(view)</a> <a class='change_mode' data-mode='edit'>(edit)</a>"));

}

//if the user can't edit, color edit links gray

if (!permissions.edit) {

$("[data-mode='edit']").css("color", "gray");

}

}

function show_owner() {

var owner_name;

var display_text = "[none]";

//case team

if (form_info.owner_type == "team") {

$("#prompt_to_choose_team").hide();

owner_name = owner_info.team_name;

var this_teams_profile_href = "http://igem.org/Team.cgi?id=" + team_id;

display_html = "for Team <a target='_blank' href='" + this_teams_profile_href + "'>" + owner_name + "</a>";

}

//case author

else if (form_info.owner_type == "user") {

owner_name = owner_info.full_name;

display_html = "for user " + owner_name;

}

//case lab, course, judge

else {

//owner_name = display_html = "oops, this form is owned by a lab, course, or judge";

throw new FormError("Can't show_owner for lab/course/judge: not implemented yet");

}

$("#owner_name_display").html(display_html).show();

}

function prompt_to_choose_team() {

//console.log("prompt_to_choose_team");

$("#owner_name_display").hide();

$("#prompt_to_choose_team").show();

$("#team_select_inner").show();

}

function change_mode(new_mode) {

if (form_info.owner_type == "team" && typeof owner_info == "undefined") {

alert("Please choose a team first!"); //**unify? or make team-choosing better

return;

}

//console.log("change_mode");

refresh_form(false);

mode = new_mode;

check_mode();

//set_up_form_and_mode();

new_form_setup();

}

function change_team(new_team_id) {

//console.log("change_team");

refresh_form();

team_id = new_team_id;

get_owner_info();

}

//Change to a different user's form

function change_author(new_author) {

refresh_form();

author = new_author;

get_owner_info();

}

//Checks the mode and sets to default_mode if it's invalid or unspecified

function check_mode() {

if (valid_modes.indexOf(mode) == -1) {

mode = form_info.default_mode;

}

}

//This function goes into the team_select_container and replaces all the

//links with a link to the current page / query string with the selected team_id

//and the current mode.

function replace_links_in_team_selector() {

$("#team_select_inner").find("a").each(function() {

var the_link = $(this);

var existing_href = the_link.attr("href");

var team_id_from_this_link = existing_href.split("?id=")[1];

the_link.removeAttr("href").addClass("change_team").attr("data-team", team_id_from_this_link);

});

}

// =====================================================

// BLOCK

// SENDING AND RECEIVING FORM ANSWERS

// =====================================================

function one_input_changed( input_element, command, effective_date, send_sub_form_accessories, check_for_silent_logout ) {

send_sub_form_accessories = (typeof send_sub_form_accessories == "undefined") ? false : send_sub_form_accessories;

check_for_silent_logout = (typeof check_for_silent_logout == "undefined") ? true : check_for_silent_logout;

var input_element = $( input_element );

//permissions

if (command == "submit" || command == "delete_form") {

if (!permissions["submit"] && !permissions["admin"]) {

alert(messages.user_cannot_submit);

if (typeof custom_refuse_submission == "function") {

custom_refuse_submission(input_element, command, effective_date);

}

return;

}

}

if (command == "submit") {

//check if it has a confirmation box and if it's checked

//if not, refuse submission

//(currently unable to handle multiple confirmation boxes)

var confirmation_box_id = input_element.attr("data-confirmation");

if(typeof confirmation_box_id != "undefined") {

var confirmed = $("#" + confirmation_box_id).prop("checked");

if(!confirmed) {

submission_not_confirmed();

return;

}

}

}

//warning on unsubmitting a signed form

if (command == "return_form") {

if (permissions.edit && !permissions.submit) {

//**can't unify this msg yet

var confirm_string = "Are you sure you want to unsubmit this form?\n\n";

confirm_string += "You will need a " + form_info.permissions.submit.join(" / ") + " to submit the form again."

if (!confirm(confirm_string)) {

return;

}

}

}

feedback(input_element, "sending");

if (owner_info == undefined) {

aj_log( "Error: no form owner specified");

return;

}

//Uncheck all confirmation boxes,

//except on submit, return, and the initial "store" request when input_element is ''

if (command != "submit" && command != "return_form" && input_element.length != 0) {

uncheck_all_confirmations();

}

// Step 1: Format information about the element that changed and send it to the server

// If a sub_form element exists, get its value

var sub_form = $("[data-form-field=sub_form]").val() || '';

var eff_date = ( typeof effective_date !== 'undefined' ) ? effective_date : '2004-01-01';

if (send_sub_form_accessories) {

//in this condition we want to package all the values of the subform accessory fields into a single entry_list

var entry_list = $("[data-form-field^=sub_form_]").get().reduce(function(previous, current, index, array) {

var tmp = get_one_entry($(current));

if (typeof tmp == "undefined") { return previous; }

else { return previous.concat(tmp); }

}, []);

}

else {

var entry_list = get_one_entry( input_element );

}

var json_entries = JSON.stringify( entry_list );

aj_log( "OIC: " + command + ' '+ json_entries );

// When the sub_form changes, we clear all the inputs and they are reloaded by ajax

if ( input_element.attr('data-form-field') == 'sub_form' ) {

clear_form_but_keep_sub_form();

}

// When the sub-form is deleted, we clear all the inputs and they are reloaded by ajax

if ( input_element.attr('data-form-field') == 'delete_form') {

//and delete the corresponding subform, unless it's the empty one

$("[data-form-field=sub_form]").find(":selected").not("[value='']").remove();

clear_form();

}

var data = {command: command, form_name: form_info.name, sub_form: sub_form, eff_date: eff_date, entry_list: json_entries};

if (form_info.owner_type == "team") {

data.team_id = team_id;

}

else if (form_info.owner_type == "user") {

data.author_username = author;

}

else {

throw new FormError("OIC can't deal with labs, courses, or judges");

}

var stor_req = jQuery.ajax({url: form_info.ajax_URL,

type: 'POST',

timeout: 30000,

data: data,

dataType: "json",

error: function(jqxhr, textStatus, errorThrown) {

feedback(input_element, "invalid");

general_ajax_error(jqxhr, textStatus, errorThrown, "Failed to save entry " + input_element.attr("name"));

aj_log("Failed one_input_changed on entry " + input_element.attr("name"));

},

success: function(data, textStatus, jqxhr) {

aj_log( "Received: " +jqxhr.responseText);

if (check_for_silent_logout && data.return_error == "Not logged in") {

respond_to_silent_logout(input_element);

return;

}

form_submitted = parseInt(data.submitted) > 0;

//console.log("Done with OIC! Command was " + command);

if (command == "submit" || command == "return_form") {

//console.log(typeof data.submitted);

//show_hide_submitted_status( data.submitted );

new_form_setup();

if (command == "submit") { mark_current_sub_form_as_submitted(); }

else { mark_current_sub_form_as_unsubmitted(); }

}

if (input_element.get(0) == $("[data-form-field=sub_form]").get(0)) {

//set_up_dffs_for_mode();

new_form_setup();

}

process_received_data( jqxhr.responseText );

// Removes any warnings from other elements of the same name (radio buttons)

unwarn($("[name='" + input_element.attr("name") + "']").not(input_element));

feedback(input_element, "sent");

aj_clear_error();

if(typeof custom_one_input_changed_response == "function") {

//Allow individual forms to have a custom response

custom_one_input_changed_response(input_element, command, effective_date, data, textStatus, jqxhr);

}

}

});

stor_req.done(function(data, textStatus, jqxhr) {

jQuery.noop();

});

}

// Step 1: Format up one entry

function get_one_entry(input_element) {

//console.log("get_one_entry");

var name = input_element.attr("data-form-field");

var answer = input_element.val();

//if the answer is an array, as it is from a select multiple,

//then preemptively JSON.stringify it

if (answer instanceof Array) {

// console.log("Found an Array answer, converting to string");

answer = answer.toString();

// console.log(answer);

}

if (input_element.attr('type') == 'checkbox') {

answer = input_element.prop('checked');

}

else if (input_element.attr('type') == 'radio') {

if (!input_element.prop('checked')) {

return;

}

answer = input_element.prop('value');

}

else if (input_element.attr('type') == 'submit') {

answer = '';

}

return [ {'field_name': name, 'answer':answer } ];

}

// Step: 2 Process the received data for all the inputs

// The received data looks like { ... , : [ { input_name: 'name', answers: 'answer'}, .... ]

function process_received_data( data ) {

//console.log("process_received_data");

try {

data = JSON.parse( data ); //**Rewrite this to use try_parse_received_data

}

catch (err) {

aj_log( 'JSON.parse error: ' + err );

return;

}

aj_display_error(data.error);

var entry_list = data.entry_list || [];

for (var i = 0; i < entry_list.length; i++) {

process_one_received_entry( entry_list[i] );

}

// if (!form_displayed) {

// new_form_setup();

// }

form_displayed = true;

return;

}

//**Make this not fill in admin fields if you're not an admin! Or only some of them. Or something.

function process_one_received_entry(entry) {

var field_name = entry["field_name"] || '';

var answer = entry["answer"] || '';

var input_element = $("[data-form-field='"+ field_name +"']");

var input_type = input_element.attr('type');

if (input_type == 'submit') {

return;

}

//Special case for select-multiples

if (input_element.is('select') && input_element.prop("multiple")) {

// console.log("Hey, this element is a select-multiple, I'm gonna split the answer");

// console.log("The old answer is " + answer);

answer = answer.split(",");

// console.log("The new answer is " + answer);

}

aj_log("Processing " + field_name + " = '" + answer + "' (Found: " +input_element.length+ " element Type: "+input_type+")");

// Radio buttons are identified by form-field and value

if (input_type == 'radio') {

input_element = input_element.filter("[value='"+answer+"']");

input_element.prop('checked', true);

}

else if (input_type == "checkbox") {

if (answer == 'false' || answer == '0') {

answer = false;

}

input_element.prop("checked", answer );

}

else {

input_element.val(answer);

}

}

//Helper function for initially loading in the answers from a new form

function initially_get_old_answers() {

one_input_changed('', 'store', '', false, false); //Store nothing, process all returned answers

feedback($("[data-form-field]"), "none"); //Clear green/yellow feedback divs

//form_displayed = true; //moved to OIC.done

}

function respond_to_silent_logout(input_element) {

input_element = (input_element instanceof jQuery) ? input_element : $(input_element);

var msg = "Your login session has expired.\n Answer to question \"" + input_element.attr("name") + "\" was not saved.\n Please log out and then log in to continue.";

warn(input_element, "Answer not saved. Please log out and log back in.");

alert(msg);

aj_display_error(msg);

}

// =====================================================

// BLOCK

// DETERMINING PERMISSIONS

// =====================================================

//Check the view/edit/submit/admin permissions of the current user on the current form.

//Puts them in global variable permissions.

function determine_permissions() {

//start by setting all permissions to false,

//otherwise you'll keep permissions wrongly when moving from a more-privileged owner

//to a less-privileged

permissions.view = false;

permissions.edit = false;

permissions.submit = false;

permissions.admin = false;

jQuery.each(form_info.permissions, function(perm_name, groups_having_this_perm) {

//console.log("Permission to " + perm_name);

for (index in groups_having_this_perm) {

var perm_group = groups_having_this_perm[index];

//console.log("Is user in group " + perm_group);

if (user_is_in_permission_group(perm_group)) {

//console.log("yes");

permissions[perm_name] = true;

break;

}

}

});

return permissions;

}

//Determine whether the current user falls into a given permission group on the current form.

//groups are ["individualOwner", "group_members", "super_users", "any_users", "public"]

function user_is_in_permission_group(perm_group) {

if (perm_group == "public") {

return true;

}

else if (perm_group == "any_users") {

return user_info.user_name != "";

}

else if (perm_group == "super_users") {

return user_info.is_super == "true";

}

else if (perm_group == "group_members") {

return (typeof owner_info.role == "string" && owner_info.role != "None");

}

else if (perm_group == "author") {

return user_info.user_name.toLowerCase() == author.toLowerCase();

}

else if (valid_roles.indexOf(perm_group) > -1) {

return owner_info.role == perm_group;

}

}

// =====================================================

// BLOCK

// AJAX HELPERS

// =====================================================

//General helper for ajax errors.

//In every case, it will display an alert and an aj_display_error.

//Not unifying these because form writers shouldn't change them.

function general_ajax_error(jqxhr, textStatus, errorThrown, custom_msg) {

//case: timeout error

if (textStatus == "timeout") {

alert("Timeout error");

aj_display_error("Timeout error: there could be a problem with your internet connection, or the server could be under a heavy load. Please try again later.");

}

//case: internet probably unplugged or something

else if (jqxhr.status == 0) {

alert("Unable to connect to server. Please check your Internet connection and try again.");

aj_display_error("Unable to connect to server. Please check your Internet connection and try again.");

}

//case: 4xx/5xx error

else if (400 <= jqxhr.status <= 599) {

alert("Server error [" + jqxhr.status + " " + jqxhr.statusText + "]");

aj_display_error("Server error [" + jqxhr.status + " " + jqxhr.statusText + "]. Please report this error to iGEM HQ. In your message, please include the URL of this page, and the date/time of the error.");

}

//other error

else {

alert("Error in ajax\n[" + textStatus + "]");

custom_msg = (typeof custom_msg == "undefined") ? "general AJAX error" : custom_msg;

aj_display_error(custom_msg);

}

}

//All our ajax requests will receive both a responseJSON and a responseText.

//Here, we attempt parsing the responseText. If there is an error in between

//"ajax request fail" and "you gave wrong parameters to form.cgi", the responseText

//will not be parseable as JSON.

function try_parse_received_info(responseText) {

try {

JSON.parse(responseText);

return true;

}

catch (err) {

aj_log("JSON.parse error: " + err);

aj_display_error(unparseable_ajax_response);

return false;

}

}

// =====================================================

// BLOCK

// SUBFORM STUFF

// =====================================================

//Accepts a string and makes a new choice in the subform dropdown.

//Boolean gotoit (default false) causes that choice to then be selected immediately.

function make_new_sub_form(name, submitted, gotoit) {

gotoit = (typeof gotoit == "undefined") ? false : gotoit;

var option = $("<option>").text(name).attr("value", name);

option.appendTo( submitted ? $("#sub_forms_submitted") : $("#sub_forms_unsubmitted") );

if (gotoit) {

option.prop("selected", true);//.trigger("change");

}

}

//Function and wrappers for moving subforms around.

// - option should be a jQuery object containing an option, or a string equaling the value of an option

// - move_to should be "submitted" or "unsubmitted"

function move_sub_form(option, move_to) {

if (typeof option == "string") {

option = $("[data-form-field=sub_form]").find("[value='" + option + "']");

}

option.detach().appendTo($("#sub_forms_" + move_to));

}

function mark_current_sub_form_as_submitted() {

move_sub_form($("[data-form-field=sub_form]").find(":selected"), "submitted");

}

function mark_current_sub_form_as_unsubmitted() {

move_sub_form($("[data-form-field=sub_form]").find(":selected"), "unsubmitted");

}

function empty_sub_form_list() {

$("[data-form-field=sub_form]").children("optgroup").empty();

}

// =====================================================

// BLOCK

// MISC UTILITY FUNCTIONS

// =====================================================

//Fake "assert" function for assertion testing.

//It is best practice to strip out assertions before code goes to real users.

function assert(condition, message) {

if (!condition) {

message = message || "Assertion failed";

if (typeof FormError !== "undefined") {

throw new FormError(message);

}

throw new Error(message);

}

}

function clear_form_but_keep_sub_form() {

$('[data-form-field][type!=checkbox][type!=radio][type!=submit][data-form-field!=sub_form]').val('');

$('[data-form-field][type=checkbox]').prop('checked', false);

$("[data-form-field][type=radio]").prop('checked', false);

feedback($("[data-form-field]"), "none");

}

function clear_form() {

$("[data-form-field][type!=checkbox][type!=radio][type!=submit][data-form-field!='sub_form']").val('');

$('[data-form-field][type=checkbox]').prop('checked', false);

$("[data-form-field][type=radio]").prop('checked', false);

$("[data-form-field='sub_form']").val("");

feedback($("[data-form-field]"), "none");

}

//Upon first page load, or upon changing owner, we must clear the form to keep the browser from

//autofilling fields, and to keep the last owner's answers from leaving droppings in the new form

//also we readonly all DFFs -- they will be made un-readonly upon successful entry into

//Edit or Admin mode

function refresh_form(also_clear_answers) {

also_clear_answers = (typeof also_clear_answers == "undefined") ? true : false;

if (also_clear_answers) {

empty_sub_form_list();

clear_form();

form_displayed = false;

}

readonly_all_DFFs();

aj_clear_error();

}

//function simply unchecks all confirmation boxes (radio or checkbox) with

//class="confirmation"

function uncheck_all_confirmations() {

$(".confirmation").prop("checked", false);

}

function submission_not_confirmed() {

if (typeof custom_submission_not_confirmed == "function") {

custom_submission_not_confirmed();

}

else {

alert(messages.please_confirm_submission);

}

}

//gets all sets of radio buttons that have a data-form-field attribute

//returns them as an array of "name"s

function get_all_sets_of_radios() {

var names_list = [];

$("[data-form-field][type='radio']").each(function(index) {

//get the name

var this_radio_name = $(this).attr("name");

//check if the name is already in the list. if not, add it

if(names_list.indexOf(this_radio_name) == -1) {

names_list.push(this_radio_name);

}

//else skip it

});

return names_list;

}

// =====================================================

// BLOCK

// READONLY/DISABLE FUNCTIONS

// =====================================================

//Simply disables everything with a data-form-field

function disable_all_DFFs() {

$('[data-form-field]').prop('disabled', true);

}

//Function makes all DFF elements either readonly (for text boxes) or disabled (everything else).

//It does not change any of their event bindings.

function readonly_all_DFFs() {

$('[data-form-field]').each(function() {

var field = $(this);

if (field.is("textarea") || field.is("input[type='text']")) {

field.prop('readonly', true);

}

else {

field.prop('disabled', true);

}

});

}

//undisable all DFFs except admin ones and static ones

function undisable_all_DFFs() {

$(non_admin_DFFs).not(".static").prop('disabled', false).prop('readonly', false);

}

//undisable admin DFFs, except static ones

function undisable_admin_DFFs() {

$('[data-form-field^="admin"]').not(".static").prop("disabled", false).prop("readonly", false);

}

//switch one field to "off" or "on" (tested, works)

function dffswitch(field, offon) {

field = (field instanceof jQuery) ? field : $(field);

if (offon == "off") {

if (field.is("textarea") || field.is("input[type='text']")) {

field.prop('readonly', true);

}

else {

field.prop('disabled', true);

}

}

else if (offon == "on") {

field.prop("disabled", false).attr("readonly", false);

}

else {

throw new FormError("I can't switch field " + field.attr("data-form-field") + " to state " + offon);

}

}

// =====================================================

// BLOCK

// LOG ENTRIES AND ERROR MESSAGES

// =====================================================

// If you wish to Display a Log of Transactions add a div with id= 'aj_form_log'

function aj_display_log_div() {

var div = $('#aj_form_log');

div.addClass("admins_only");

div.html("<DIV id='aj_form_log_inner_div' "

+ "style='display: none; width: 500px;min-height:50px;padding: 3px 10px;margin:10px; "

+ "border: 1px solid gray;background-color:white'>"

+ "<\/DIV>" );

jq_versions();

aj_display_log_buttons();

}

function aj_display_log_buttons() {

var div = $('#aj_form_log_inner_div');

div.before("<button type='button' id='clear_log'>Clear Log<\/button>");

div.before("<button type='button' id='toggle_log'>Toggle Log<\/button>");

$("#clear_log").on("click", aj_display_log_div);

$("#toggle_log").on("click", function(event) {

$("#aj_form_log_inner_div").toggle();

});

}

function aj_log(text) {

var div = $('#aj_form_log_inner_div');

if (div.length == 0) {

return;

}

var old_text = div.html();

div.html(old_text + "<P>" + text + "<\/P>");

}

// Put the jQuery versions in the log box

function jq_versions() {

var jq_v='None';

try {

jq_v = jQuery.fn.jquery || '';

}

catch (err) {}

var ui_v = 'None';

try {

ui_v= jQuery.ui ? jQuery.ui.version || 'pre 1.6' : '';

}

catch (err) {}

aj_log( 'AJ Form Log: [ jQuery v '+ jq_v + '   jQuery UI v ' + ui_v + ' ]' );

}

function aj_display_error(error_text){

if (!error_text) {

return;

}

var div = $('#aj_form_errors');

div.html("<div id='aj_form_error_inner_div'><h5>Software error:</h5>" + error_text + "</div>");

div.show();

}

function aj_clear_error(){

//aj_display_error_text("");

$("#aj_form_errors").text("").hide();

}

// =====================================================

// BLOCK Hey I know you don't like this but don't get rid of it

// SHIELDING It could be useful for sign/lock/submit!

// =====================================================

//All these shield-manipulating functions expect a jQuery object as an argument.

//But if none is given, they default to the single shield on a form, which is assumed

//to have id="shield".

//Shield divs should have class "shield-active" or "shield-inactive" as defined in

//Template:CSS/SafetyForms

function toggle_shield(s) {

s = (typeof s == "undefined") ? $("#shield") : s;

if(s.hasClass("shield-inactive")) {

add_shield(s);

}

else {

remove_shield(s);

}

}

function add_shield(s) {

s = (typeof s == "undefined") ? $("#shield") : s;

s.removeClass("shield-inactive");

s.addClass("shield-active");

}

function remove_shield(s) {

s = (typeof s == "undefined") ? $("#shield") : s;

s.removeClass("shield-active");

s.addClass("shield-inactive");

}

// =====================================================

// BLOCK

// VALIDATION

// =====================================================

function validate_form_entries() {

var fields_to_validate;

var results = [];

$(non_admin_DFFs).each(function(index, field){

field = $(field);

var this_result = "";

var what_the_field_says = field.attr("data-validation");

if (what_the_field_says == "required") {

this_result = validate_one_field(field);

}

else if (what_the_field_says == "optional") {

return;

}

else if (typeof what_the_field_says == "undefined") {

if (form_info.validate_unspecified_fields == "required") {

this_result = validate_one_field(field);

}

}

//If we reach this case, data-validation is set to something custom

//It should be a selector (NOT JUST AN ID) for a radio or checkbox input elsewhere on the page.

//We will require field to be nonempty if the indicated radio/checkbox is checked.

else {

//console.log("Custom validating field " + field.attr("data-form-field") + " with respect to " + what_the_field_says);

var other_field = $(what_the_field_says);

if (other_field.length == 0) {

throw new FormError("Can't validate field " + field.attr("data-form-field") + " with respect to selector '" + what_the_field_says + "'; that field does not exist");

}

else if (other_field.attr('type') != "radio" && other_field.attr('type') != "checkbox") {

throw new FormError("Can't validate field " + field.attr("data-form-field") + " with respect to selector '" + what_the_field_says + "'; that field is not a radio or checkbox");

}

else {

if (other_field.prop("checked")) {

this_result = validate_one_field(field);

}

}

}

//Only add nonempty, nonduplicate strings

if (this_result != "" && results.indexOf(this_result) == -1) {

results.push(this_result);

}

});

return results;

}

function validate_one_field(fld) {

fld = (fld instanceof jQuery) ? fld : $(fld);

var type = fld.attr("type");

var name = fld.attr("name");

var result = "";

var isValid;

if (fld.attr("data-validate-as") == "email") {

return validate_email(fld);

}

if (type == "radio" || type == "checkbox") {

isValid = $("[name='"+name+"']:checked").length > 0;

}

else {

isValid = (fld.val() != "" && fld.val() != null && typeof fld.val() != "undefined");

}

if (!isValid) {

result = messages.field_validation_fail_beginning + " " + name;

feedback(fld, "invalid");

}

return result

}

var email_regex = /^.+@.+\..+/;

function validate_email(fld) {

var result;

if (email_regex.test(fld.val())) {

result = "";

unwarn(fld);

}

else {

result = messages.invalid_email_address;

feedback(fld, "invalid");

warn(fld, messages.invalid_email_address);

}

return result;

}

// =====================================================

// BLOCK

// YELLOW-AND-GREEN FEEDBACK

// =====================================================

var wrapper_none_color = "";

var wrapper_sending_color = "#FFFF99";

var wrapper_sent4 = "#B3FF66";

var wrapper_sent3 = "#C6FF8C";

var wrapper_sent2 = "#D9FFB3";

var wrapper_sent1 = "#ECFFD9";

var wrapper_invalid_color = "pink";

var wrapper_readonly_color = "#EEEEEE";

function feedback(element, command) {

element = (element instanceof jQuery) ? element : $(element);

//select the wrappers to color. If it's a set of radios or checkboxes, we color them all together,

//all that have the same "name" attribute.

// Otherwise, we color every input separately.

// var type = element.attr("ty

Show more