1// We use a counter at the end of this file name to flush people's browser cache. 2 3// This helps us resize an element when its contents change or are resized. Needs improvement. Use like: 4// $('#foo').on("DOMSubtreeModified show hide", function() { do resizing here }); 5(function($) { 6 $.each(['show', 'hide'], function (i, ev) { 7 var el = $.fn[ev]; 8 $.fn[ev] = function() { 9 var ans = el.apply(this, arguments); 10 this.trigger(ev); 11 return ans; 12 }; 13 }); 14})(jQuery); 15 16 17// Jump to the 'value' of the select-list option. 18// use like this: <select onChange=jumpToSelectedValue(this)>... 19function jumpToSelectedValue(selObj) { 20 // eval("parent.location='" + selObj.options[selObj.selectedIndex].value + "'"); // This version doesn't work in iframes. 21 eval("document.location='" + selObj.options[selObj.selectedIndex].value + "'"); 22 eval("location.href = '" + selObj.options[selObj.selectedIndex].value + "'"); 23} 24 25// Jump to the 'id' of the select-list option. 26// use like this: <select onChange=jumpToSelectedId(this)>... 27function jumpToSelectedId(selObj) { 28 // eval("parent.location='" + selObj.options[selObj.selectedIndex].id + "'"); // This version doesn't work in iframes. 29 eval("document.location='" + selObj.options[selObj.selectedIndex].id + "'"); 30 eval("location.href = '" + selObj.options[selObj.selectedIndex].id + "'"); 31} 32 33 34// DEPRECATED 35function count_words(textfield, countfield) { 36 var val = textfield.value.replace(/^\s+/g, "").replace(/\s+$/g, ""); 37 if (val) { val = val + ' '; } 38 countfield.value = val.split(/\s+/).length - 1; 39} 40 41 42function count_words_jq(textfield_name, countfield_id) { 43 var field = $("[name='"+textfield_name+"']") 44 45 val = field.attr("value") 46 $("#" + countfield_id).html(count_words_in_str(val)); 47} 48 49function tiny_mce_count_words_jq(tinymce_id, countfield_id) { 50 var field = tinymce.get(tinymce_id); 51 if (field != undefined) { 52 // I think that the object is not ready for getContent immediately. 53 // val = field.getContent(); 54 try { 55 val = field.getContent(); 56 } 57 catch { 58 val = ''; 59 } 60 $("#" + countfield_id).html(count_words_in_str(val)); 61 } 62} 63 64 65// This is used to remove markdown symbols that would increase the wordcount 66var MARKDOWN_WORDCOUNT_REGEX = /^(?:#{1,2}|=+|-+|\s*>\s|\s*\*+|\s*-\s+|\s*[0-9][\.\)]\s+)/gm 67// This is used to differentiate between markdown italiacs and math. 68// This does not correctly enforce two-star matching with two-stars and one-star matching with one-star. 69var GOOD_MARKDOWN_STAR_REGEX = /(?:\*\*[^\s\*][^\*]+[^\s\*]\*\*)|(?:\*[^\s\*][^\*]+[^\s\*]\*)/gm 70 71 72// This isn't treating html tags exactly like our python word count is. 73function count_words_in_str(val) { 74 val = strip_html(val); 75 // clean space at the beginning and end of lines 76 // val = val.replace(/^\s+/g, "").replace(/\s+$/g, ""); 77 val = val.trim(); 78 // remove patterns that look like markdown. 79 // val = val.replace(/^(?:#{1,2}|=+|-+|\s*>\s|\s*\*+\s+|\s*-\s+|\s*[0-9][\.\)]\s+)/gm, ''); 80 val = val.replace(MARKDOWN_WORDCOUNT_REGEX, ''); 81 // This one should have a space because you wouldn't have a word continuing into a code block 82 val = val.replace(/`{1,3}/g, ' '); 83 // This is strange but it fixes the empty string case. 84 if (val) { val = val + ' '; } 85 return val.split(/\s+/).length - 1; 86} 87 88function count_chars_jq(textfield_name, countfield_id) { 89 var val = $("[name='"+textfield_name+"']").attr("value").replace(/^\s+/g, "").replace(/\s+$/g, "").replace(/<br \/>/g, " "); 90 $("#"+countfield_id).html(val.length); 91} 92 93function tiny_mce_count_chars_jq(tinymce_id, countfield_id) { 94 var field = tinymce.get(tinymce_id); 95 if (field != undefined) { 96 // I think that the object is not ready for getContent immediately. 97 try { 98 val = field.getContent(); 99 } 100 catch { 101 val = ''; 102 } 103 val = val.replace(/^\s+/g, "").replace(/\s+$/g, "").replace(/<br \/>/g, " "); 104 $("#" + countfield_id).html(val.length); 105 } 106} 107 108function _____FILE_SIZE_ERROR_DETECTION______() {} 109 110 111// Taken and modified from http://stackoverflow.com/questions/3717793/javascript-file-upload-size-validation 112 113function get_file_size(file_field) { 114 var file; 115 116 // (Can't use `typeof FileReader === "function"` because apparently 117 // it comes back as "object" on some browsers. So just see if it's there 118 // at all.) 119 if (!window.FileReader) { 120 return 0; 121 } 122 123 // We don't want the jquery object. 124 var input = file_field[0]; 125 126 if (!input) { 127 return 0; 128 } 129 else if (!input.files) { 130 return 0; 131 } 132 else if (!input.files[0]) { 133 return 0; 134 } 135 else { 136 file = input.files[0]; 137 return file.size; 138 } 139} 140 141 142function check_file_size(event) { 143 var file_field = $(event.target); 144 var max_bytes = parseInt(file_field.attr('max_bytes')); 145 146 if (max_bytes != NaN) { 147 if (max_bytes == 0) { 148 return; 149 } 150 var file_size = get_file_size(file_field); 151 if (file_size > max_bytes) { 152 alert('This file is too large. It is ' + readable_file_size(file_size) + ' but should not exceed ' + readable_file_size(max_bytes) + '.'); 153 file_field.val(''); 154 return false;
155 } 156 } 157} 158 159 160function readable_file_size(size, inc_dec) { 161 162 if (typeof (inc_dec) === 'undefined') inc_dec = true; 163 164 if (size < 1024) { 165 // Write it in bytes. 166 return size.toString() + "B"; 167 } else { 168 size = size / 1024.0 169 if (size < 1024) { 170 if (inc_dec) { 171 size = Math.round(size * 100) / 100 172 } else { 173 size = Math.round(size) 174 } 175 return size.toString() + "KB"; 176 } else { 177 size = size / 1024.0 178 if (inc_dec) { 179 size = Math.round(size * 100) / 100 180 } else { 181 size = Math.round(size) 182 } 183 return size.toString() + "MB"; 184 } 185 } 186} 187 188// Set up the change handler for file fields. 189$(function() { 190 $(document).on('change', 'input:file', check_file_size); 191}) 192 193 194function expandcollapse(id) { 195 if (document.getElementById) { 196 // DOM3 = IE5, NS6 197 id_style = document.getElementById(id).style; 198 if(id_style.display == 'none') { 199 id_style.display = ''; // visible (default inline/block for element) 200 } else { 201 id_style.display = 'none'; 202 } 203 } else { 204 // Apparently this works for some browsers. 205 if (document.layers) { 206 if (document.id.display == "none") { 207 document.id.display = 'block'; 208 filter(("img"+id),'imgin'); 209 } else { 210 filter(("img"+id),'imgout'); 211 document.id.display = 'none'; 212 } 213 } else { 214 if (document.all.id.style.visibility == "none") { 215 document.all.id.style.display = 'block'; 216 } else { 217 filter(("img"+id),'imgout'); 218 document.all.id.style.display = 'none'; 219 } 220 } 221 } 222} 223 224 225function expandcollapse3(id) { 226 expandcollapse(id); 227 expandcollapse(id + '_show_span'); 228 expandcollapse(id + '_hide_span'); 229} 230 231 232// DEPRECATED. 233function getElementsByClass(strTagName, strClassName) { 234 var arrElements = (strTagName == "*" && document.all)? document.all : document.getElementsByTagName(strTagName); 235 var arrReturnElements = new Array(); 236 strClassName = strClassName.replace(/-/g, "\-"); 237 var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$)"); 238 var oElement; 239 for(var i=0; i<arrElements.length; i++){ 240 oElement = arrElements[i]; 241 if(oRegExp.test(oElement.className)){ 242 arrReturnElements.push(oElement); 243 } 244 } 245 return (arrReturnElements) 246} 247 248 249function expandcollapse_multi(tag_class) { 250 var els = getElementsByClass('*', tag_class); 251 for (var i=0; i<els.length; i++) { 252 var el = els[i]; 253 var el_style = el.style; 254 if(el_style.display == 'none') { 255 el_style.display = ''; // visible (default inline/block for element) 256 } else { 257 el_style.display = 'none'; 258 } 259 } 260} 261 262 263function expandcollapse_multi3(tag_class) { 264 expandcollapse_multi(tag_class); 265 expandcollapse_multi(tag_class + '_show_span'); 266 expandcollapse_multi(tag_class + '_hide_span'); 267 268 $(document).trigger('expandCollapseMulti') 269} 270 271 272// The below functions work together to hide all (no matter what state they were in initially) or show all. 273function collapse_multi(tag_class) { 274 var els = getElementsByClass('*', tag_class); 275 for (var i=0; i<els.length; i++) { 276 els[i].style.display = 'none'; 277 } 278} 279 280 281function expand_multi(tag_class) { 282 var els = getElementsByClass('*', tag_class); 283 for (var i=0; i<els.length; i++) { 284 els[i].style.display = ''; // visible (default inline/block for element) 285 } 286} 287 288 289function expand_multi3(tag_class) { 290 expand_multi(tag_class); 291 collapse_multi(tag_class + '_show_span'); 292 expand_multi(tag_class + '_hide_span'); 293 294 $(document).trigger('expandCollapseMulti') 295} 296 297 298function collapse_multi3(tag_class) { 299 collapse_multi(tag_class); 300 expand_multi(tag_class + '_show_span'); 301 collapse_multi(tag_class + '_hide_span'); 302 303 $(document).trigger('expandCollapseMulti') 304} 305 306 307// Functions below require jquery. 308 309// Seems to infinitely loop if not given <req_msg>! 310function jqueryGetReplaceElContents(url, el_id, req_msg, callback) { 311 // This is expects url to return just the contents of the element to replace. 312 // It does not replace the element with the returned contents, it just replaces the contents. 313 // If <url> returns an element with <el_id> use jqueryGetReplaceEl() instead. 314 315 // I have turned off most of the slide-down / slide-up animations below since it causes too 316 // much bouncing. Could add a 'duration' parameter to pass on to
316jquery hide/shows to control animations. 317 318 $('#'+el_id).hide(0, function() { 319 $(this).html(req_msg).show(); 320 }); 321 $.ajax({ 322 url: url, 323 type: 'GET', 324 cache: false, 325 data: { 326 'uniq_param': (new Date()).getTime() // This prevents IE from caching. 327 }, 328 dataType: 'html', 329 // contentType: "application/x-www-form-urlencoded; charset=iso-8859-1", 330 // contentType: "text/html; charset=iso-8859-1", 331 contentType: "text/html; charset=windows-1252", 332 // beforeSend: function(xhr) { xhr.setRequestHeader('Accept', "text/html; charset=iso-8859-1") }, 333 334 // This version nests the new element inside the old one, which is not what we want. 335 // Breaks the rule of all elements having unique ids. 336 success: function(data, textStatus, jqXHR) { 337 $('#'+el_id).hide(0, function() { 338 $(this).html(data).show(0, function() { 339 if (callback) { callback() } 340 }) 341 }) 342 } 343 }) 344 return false; 345} 346 347 348function jqueryGetReplaceEl(url, el_id, req_msg, callback) { 349 // This is expects url to return an element with <el_id>. 350 // It replaces the element with the returned element. 351 352 // I have turned off most of the slide-down / slide-up animations below since it causes too 353 // much bouncing. Could add a 'duration' parameter to pass on to jquery hide/shows to control animations. 354 355 $('#'+el_id).hide(0, function() { 356 $(this).html(req_msg).show(); 357 }); 358 $.ajax({ 359 url: url, 360 type: 'GET', 361 cache: false, 362 data: { 363 'uniq_param': (new Date()).getTime() // This prevents IE from caching. 364 }, 365 dataType: 'html', 366 // contentType: "application/x-www-form-urlencoded; charset=iso-8859-1", 367 // contentType: "text/html; charset=iso-8859-1", 368 contentType: "text/html; charset=windows-1252", 369 // beforeSend: function(xhr) { xhr.setRequestHeader('Accept', "text/html; charset=iso-8859-1") }, 370 371 success: function(data, textStatus, jqXHR) { 372 $('#'+el_id).hide(0, function() { 373 $(this).replaceWith(data).show(0, function() { 374 if (callback) { callback() } 375 }) 376 }) 377 } 378 }) 379 return false; 380} 381 382 383function jqueryPostReplaceEl(url, el_id, req_msg, postdata, callback) { 384 // since this pretends to be a button it requires that one of the keys in 385 // postdata be postdata['button_<encrypted_url>'] like when we submit a form 386 // if <url> it will be placed into the post data 387 // <el_id> can be either an element id string or a jQuery object 388 389 if (url) { postdata[url] = 'button'; } // this is how we encode url args with forms 390 391 el = el_id; 392 if (!(el_id instanceof jQuery)) {el = $("#"+el_id);} 393 394 if (!req_msg) { req_msg = "Loading..."; } 395 req_msg = "<span class=\'loading\'><i class=\'fa fa-spinner fa-spin\'></i> "+req_msg+"</span>"; 396 397 el.hide('normal', function(){ 398 $(this).html(req_msg).slideDown('normal'); 399 }); 400 401 // IE won't post to an empty url, so we set it to '?' which will be our default handler 402 $.post('?', postdata, function(result_data) { 403 el.hide('normal', function(){ 404 $(this).html(result_data).slideDown('normal', function() { 405 if (callback) { callback(); } 406 }); 407 }); 408 }); 409 return false; 410} 411 412function mmCgiEncodedPost(post_url, postdata, callback) { 413 // <post_url> should be the result of a link(.., url_only=True) call. 414 var post_loc = post_url[0]; 415 // this is standard for encoding args in a post submission 416 var routing_url = "button_" + post_url.slice(1); 417 418 if (typeof postdata === 'string' || postdata instanceof String) { 419 // postdata is already a query arg style string 420 postdata += '&' + encodeURIComponent(routing_url) + '=button'; 421 } else { 422 // postdata is a data object 423 postdata[routing_url] = "button"; 424 } 425 426 $.post(post_loc, postdata, callback); 427 return false; 428} 429 430function replaceElWithPostResponse($el, post_url, postdata, callback) { 431 return mmCgiEncodedPost(post_url, postdata, function(result_data) { 432 $el.html(result_data); 433 if (callback) { callback(); } 434 }); 435} 436 437function replaceElWithPostResponseAnimated($el, post_url, postdata, loading_msg, callback) { 438 if (loading_msg) { 439 loading_msg = "<span class=\'loading\'><i class=\'fa fa-spinner fa-spin\'></i> " + loading_msg + "</span>"; 440 } else { 441 loading_msg = "<span class=\'loading\'><i class=\'fa fa-spinner fa-spin\'></i> Loading...</span>"; 442 } 443 $el.slideUp(function() {$(this).html(loading_msg).show()}); 444 return mmCgiEncodedPost(post_url, postdata, function(result_data) { 445 $el.hide().html(result_data).slideDown(); 446 if (callback) { callback(); } 447 }); 448} 449 450function replaceElWithPostResponseOverlay($el, post_url, postdata, loading_msg, callback) { 451 add_message_overlay($el, loading_msg); 452 return mmCgiEncodedPost(post_url, postdata, function(result_data) { 453 $el.html(result_data); 454 if (callback) { callback(); } 455 }); 456} 457 458 459function replaceElWithGetResponseOverlay($el, get_url, loading_msg, callback) { 460 add_message_overlay($el, loading_msg); 461 $.ajax({ 462 url: get_url, 463 type: 'GET', 464 cache: false, 465 data: { 466 'uniq_param': (new Date()).getTime() // This prevents IE from caching. 467 }, 468 dataType: 'html', 469 // contentType: "application/x-www-form-urlencoded; charset=iso-8859-1", 470 // contentType: "text/html; charset=iso-8859-1", 471 contentType: "text/html; charset=windows-1252", 472 // beforeSend: function(xhr) { xhr.setRequestHeader('Accept', "text/html; charset=iso-8859-1") }, 473 success: function(data, textStatus, jqXHR) { 474 $el.html(data); 475 if (callback) { callback(); } 476 } 477 }); 478} 479 480 481function add_message_overlay(el, message) { 482 $el = $(el); 483 if (message) { 484 message = "<span class=\'loading\'><i class=\'fa fa-spinner fa-spin\'></i> " + message + "</span>"; 485 } else { 486 message = "<span class=\'loading\'><i class=\'fa fa-spinner fa-spin\'></i> Loading...</span>"; 487 } 488 $el.css("position", "relative"); 489 $el.append( 490 $("<div Class='loading-overlay'></div>").css({ 491 'position': 'absolute', 492 'left': 0, 493 'top': 0, 494 'width': '100%', 495 'height': '100%', 496 'background': 'rgba(255, 255, 255, .7)' 497 }).append( 498 $("<div></div>").css({ 499 'position': 'absolute', 500 'top': '50%', 501 'left': '50%', 502 'transform': 'translate(-50%, -50%)' 503 }).html(message) 504 ) 505 ); 506} 507 508function remove_loading_overlay(el) { 509 $(el).find('.loading-overlay').remove(); 510} 511 512 513function _____IFRAMES______() {} 514 515function resize_iframe(iid, h) { 516 // This one uses no sliding - better for changing the size of an existing iframe. Height only. 517 $('#'+iid).attr({height: h + 'px'}) 518} 519function slide_resize_iframe(iid, h) { 520 // This one uses sliding. Height only 521 if ($('#'+iid).is('iframe')) { 522 $('#'+iid).hide().attr({height: h + 'px'}).slideDown() 523 } else { 524 // Using slideUp() for object tags causes an infinite loop. 525 $('#'+iid).attr({height: h + 'px'}).slideDown() 526 } 527} 528function resize_iframe_to_fit(iid) { 529 // The idea is to get it roughly right, then re-check every half-second. 530 // It is counter-intuitive that we need to wait 1 second after the document is "ready" 531 // to get the right height, but in cases like jquery UI tabs, there is some 532 // js reorganizing of the page that happens during that time. 533 // We can't slide the second time, since in Firefox that causes it to jump 534 // closed and then slide open. 535 536 $(function() { 537 function calc_height() { 538 // Other values of interest: 539 // document.body.scrollHeight, 540 // document.documentElement.scrollHeight, 541 // document.body.offsetHeight, 542 // document.documentElement.offsetHeight, 543 // document.body.clientHeight, 544 // document.documentElement.clientHeight 545 546 // ($.browser.mozilla & $.browser.version == '11.0') 547 if ($.browser.msie || $.browser.opera || !!navigator.userAgent.match(/Trident\/7\./) ) { 548 // Hard to really get IE and opera to shrink. Trust me. 549 return Math.max($(document).height(), $('html').outerHeight(true), $('body').outerHeight(true)) 550 } else { 551 return Math.min($(document).height(), $('html').outerHeight(true)) 552 } 553 } 554 var curr_height = calc_height() 555 556 try { 557 // This won't work if we're embedding it in an iframe outside of linklings. 558 parent.slide_resize_iframe(iid, curr_height) 559 window.parent.jQuery.colorbox.resize({ 560 innerWidth: $(document).width(), 561 innerHeight: $(document).height() 562 }) 563 564 } catch (e) { 565 // Don't do anything here 566 } 567 568 // jquery.ba-resize.min.js seems to have some issues, so do a simple version. 569 // Check every half-second if the height has changed, and adjust. 570 function fix_size(old_height) { 571 var new_height = calc_height() 572 573 // This is a hack to give a viewable window if something has gone wrong. 574 // We don't have a case that this is needed yet. 575 if (new_height < 10) {new_height=500} 576 577 if (new_height != old_height) { 578 parent.resize_iframe(iid, new_height) 579 580 window.parent.jQuery.colorbox.resize({ 581 innerWidth: $(document).width(), 582 innerHeight: $(document).height() 583 }) 584 } 585 window.setTimeout(function() { fix_size(new_height) }, 500) 586 } 587 fix_size(curr_height) 588 }) 589} 590 591 592function rm_iframe(iid, idoc) { 593 // Uses contents of the form tag, if exists, else body tag. 594 var s = $('form', idoc).html(); 595 if (!s.length) { 596 s = $(idoc.body).html(); 597 } 598 $('#'+iid).parent().hide('normal', function () { 599 $(this).html(s).slideDown(); 600 }); 601 // Fix this! 602 vehif_view_mode(parse_iframe_id(iid)); 603} 604 605 606function vehif_rm_iframe(iid, idoc) { 607 // Uses contents of the form tag, if exists, else body tag. 608 var s = $('form', idoc).html(); 609 if (!s.length) { 610 s = $(idoc.body).html(); 611 } 612 $('#'+iid).parent().hide('normal', function () { 613 $(this).html(s).slideDown(); 614 }); 615 vehif_view_mode(parse_iframe_id(iid)); 616} 617 618 619function parse_iframe_id(iid) { 620 var a = iid.split('_'); 621 return a[a.length-1]; 622} 623 624 625function vehif_view_mode(uniq) { 626 var view_id = 'vehif_view_' + uniq; 627 var edit_id = 'vehif_edit_' + uniq; 628 var hide_id = 'vehif_hide_' + uniq; 629 var view2_id = 'vehif_view2_' + uniq; 630 var edit2_id = 'vehif_edit2_' + uniq; 631 var hide2_id = 'vehif_hide2_' + uniq; 632 $('#'+view_id+', #'+edit2_id+', #'+hide2_id).hide();
633 $('#'+view2_id+', #'+edit_id+', #'+hide_id).show(); 634} 635 636 637function vehif_edit_mode(uniq) { 638 var view_id = 'vehif_view_' + uniq; 639 var edit_id = 'vehif_edit_' + uniq; 640 var hide_id = 'vehif_hide_' + uniq; 641 var view2_id = 'vehif_view2_' + uniq; 642 var edit2_id = 'vehif_edit2_' + uniq; 643 var hide2_id = 'vehif_hide2_' + uniq; 644 $('#'+view2_id+', #'+edit_id+', #'+hide2_id).hide(); 645 $('#'+view_id+', #'+edit2_id+', #'+hide_id).show(); 646} 647 648 649function vehif_hide_mode(uniq) { 650 var view_id = 'vehif_view_' + uniq; 651 var edit_id = 'vehif_edit_' + uniq; 652 var hide_id = 'vehif_hide_' + uniq; 653 var view2_id = 'vehif_view2_' + uniq; 654 var edit2_id = 'vehif_edit2_' + uniq; 655 var hide2_id = 'vehif_hide2_' + uniq; 656 $('#'+view2_id+', #'+edit2_id+', #'+hide_id).hide(); 657 $('#'+view_id+', #'+edit_id+', #'+hide2_id).show(); 658} 659 660 661function js_link_select(link_ctr, grp_ctr) { 662 // el_id was selected: unselect all, and then select the right one. 663 $('.link_group_'+grp_ctr+'_unselected').show(); 664 $('.link_group_'+grp_ctr+'_selected').hide(); 665 $('#link_'+link_ctr+'_unselected').hide(); 666 $('#link_'+link_ctr+'_selected').show(); 667} 668 669 670function check_reqs__old() { 671 var missing = false; 672 $('.required_field').each(function(i) { 673 if (!$(this).val().length) { 674 missing = true; 675 if (this.id) { 676 $('[for='+this.id+']').css("color","red"); 677 } 678 $(this).css("border","solid red 1px"); 679 } 680 }); 681 if (missing) { 682 alert('Some required fields have not been completed. Please verify that all fields marked with red are filled out.'); 683 return false; 684 } 685 return true; 686} 687 688 689function check_reqs() { 690 // This is an improved version that works with adding the 'required_field' class 691 // to input objects using the input_class argument. 692 let filter_find = '.jqx-combobox, text, textarea, input, hidden, file, password, checkbox, radio, select'; 693 var missing = false; 694 // The not() here is because adding 'required_field' as an input_class 695 // adds it to the label as well as a div wrapping the element 696 $('.required_field').not('[class$=input_label_div]').each(function(i) { 697 let input_el; 698 input_el = $(this).filter(filter_find); 699 if (!input_el.length) { 700 // If the class wasn't directly on the input element then find an input in it's descendants. 701 input_el = $(this).find(filter_find); 702 } 703 let val = get_field_values(input_el); 704 // Don't include empty strings as values 705 val = val.filter(item => item); 706 if (!val.length) { 707 missing = true; 708 if (this.id) { 709 $('[for='+this.id+']').css("color","red"); 710 } 711 $(this).css("border","solid red 1px"); 712 713 // Set border for stars 714 let stars_box = $(this).closest('.stars_input_box'); 715 if (stars_box.length > 0) { 716 stars_box.css("border", "solid red 1px"); 717 } 718 } 719 }); 720 721 if (missing) { 722 alert('Some required fields have not been completed. Please verify that all fields marked with red are filled out.'); 723 return false; 724 } 725 return true; 726} 727 728 729function highlight_field_label(field, multi_ctr) { 730 // Brefly highlights the label for <field>. 731 732 var multi_fld = field; 733 if (multi_ctr !== '') { 734 multi_fld = field + '_' + multi_ctr; 735 } 736 737 var input_ob = $('[name=' + multi_fld + ']'); 738 739 // There may be other special cases here. 740 if ( 741 input_ob.parents('.auto_height_cb_widget').length > 0 || 742 input_ob.parents('.cb_widget').length > 0 743 ) { 744 var input_class = field + '_cb'; 745 if (multi_ctr != '') { 746 input_class = input_class + '_' + multi_ctr; 747 } 748 var label_ob = $('label[cbs_for=' + input_class + ']'); 749 } else { 750 var input_id = input_ob.attr('id'); 751 var label_ob = $('label[for=' + input_id + ']'); 752 } 753 754 jump_to_highlight_obj(label_ob, input_ob); 755} 756 757 758function get_input_connected_to_label(label_ob) { 759 760 // This is used to get the input element that is connected to a label. 761 762 if (!label_ob) { return null; } 763 764 let input_obj; 765 if (label_ob.attr('for')) { 766 input_obj = $('#' + label_ob.attr('for')); 767 } else if (label_ob.attr('cbs_for')) { 768 input_obj = $('.' + label_ob.attr('cbs_for')).first(); 769 } 770 771 return input_obj; 772} 773 774 775function jump_to_highlight_obj(obj, focus_field_obj=null) { 776 777 // Brefly highlights a specific jQuery object. Also 778 // assigns focus to the form field associated with the object, if possible. 779 if (!obj) { return; } 780 781 let focus_obj; 782 if (focus_field_obj) {
783 focus_obj = focus_field_obj; 784 } else { 785 focus_obj = get_input_connected_to_label(obj); 786 if (!focus_obj || !focus_obj.length) { 787 focus_obj = obj; 788 } else { 789 focus_obj = focus_obj.first(); 790 } 791 } 792 793 let focus_obj_id = focus_obj.attr('id'); 794 795 focus_obj.focus(); 796 797 let closest_div = obj.closest('div'); 798 799 const highlight_opts = { 800 duration: 750, 801 queue: true, 802 }; 803 804 closest_div.addClass('highlight', highlight_opts); 805 closest_div.removeClass('highlight', highlight_opts); 806 closest_div.addClass('highlight', highlight_opts); 807 closest_div.removeClass('highlight', highlight_opts); 808} 809 810 811function test_cookies() { 812 $.removeCookie('testcookiesenabled'); 813 $.cookie('testcookiesenabled', 'enabled'); 814 if ($.cookie('testcookiesenabled')) { 815 $.removeCookie('testcookiesenabled'); 816 } else { 817 alert("For security reasons, this site requires that your browser has cookies enabled, but your browser has them disabled. Please change your browser settings."); 818 } 819} 820 821function _____QTIP______() {} 822 823// Make qtips disappear when the escape key is pressed. 824$(function() { 825 $(window).on('keydown', function(event) { 826 if(event.keyCode === 27) { 827 $('.qtip').qtip('hide', event); 828 } 829 }); 830}) 831 832 833var setup_info_links_throttle = null; 834var setup_info_links_request = null; 835 836function setup_info_links() { 837 838 // Setting up info links is an expensive operation 839 // 0.25 - 0.75 seconds 840 // With asynchronous snippets we are calling this after we've loaded each 841 // snippet. This is onerous and probably unnecessary. Here we wrap and 842 // rate limit calls to this function. 843 844 var rate_limit = 2500; // call it no more than every 2.5 seconds? 845 846 if (!setup_info_links_throttle) { 847 // console.log("setting throttle _setup_info_links"); 848 setup_info_links_throttle = setInterval( 849 function() { 850 if (setup_info_links_request) { 851 // console.log("calling _setup_info_links"); 852 _setup_info_links(); 853 setup_info_links_request = false; 854 } else { 855 // if there hasn't been a request in the last interval, then 856 // we're done for now. 857 // console.log("clearing throttle _setup_info_links"); 858 clearInterval(setup_info_links_throttle); 859 setup_info_links_throttle = false; 860 } 861 }, 862 rate_limit 863 ); 864 865 _setup_info_links(); 866 867 } else { 868 // console.log("throttling _setup_info_links"); 869 setup_info_links_request = true; 870 } 871} 872 873 874function _setup_info_links() { 875 876 // Create qtip pop-ups. 877 878 // button: "<img src='includes/images/action_delete.png' width='16px' height='16px' alt='close' />" 879 880 var qtip_content_helper = function(el, no_clone) { 881 /* <no_clone> is for when we don't want the contents cloned every time 882 the qtip is shown. We started cloning when we needed qtip to not 883 remove the contents from its current location; this was important when 884 doing in-page searching in the presence of tooltips. */ 885 886 // only include the title if we have one. 887 if ($($(el).attr('title')).length > 0) { 888 title_opts = { 889 // Should 'this' below be 'el'? 890 text: function(api) { var title_id = $(this).attr('oldtitle'); return title_id ? $(title_id).clone() : null; }, 891 button: true 892 } 893 } else { 894 title_opts = '' 895 } 896 897 if (no_clone) { 898 return { 899 title: title_opts, 900 text: function(api) { return $($(el).attr('rel')) } 901 } 902 } else { 903 return { 904 title: title_opts, 905 text: function(api) { return $($(el).attr('rel')).clone() } 906 } 907 } 908 } 909 910 // Try to make sure they can view the whole pop-up on small screens. 911 // This works better for people who need to click on scrollbars. 912 // var position_args = {adjust: {method: 'shift flip'}, viewport: $(window)}; 913 914 // var position_args = {adjust: {method: 'shift shift'}, viewport: $(window)}; 915 916 // Try to put it within the form so that buttons work. 917 if ($('form#thisForm').length > 0) {
918 var position_args = {adjust: {method: 'shift shift'}, container: $('form#thisForm'), viewport: $(window)}; 919 } else { 920 var position_args = {adjust: {method: 'shift shift'}, viewport: $(window)}; 921 } 922 923 // modal ttips 924 modal_position = { 925 my: 'center', 926 at: 'center', 927 target: $(window) // Or $(document.body), if you don't want it centered as you scroll 928 }; 929 930 // This works better for people who can drag-scroll. 931 // var position_args = {adjust: {method: 'none none'}}; 932 // I think the ideal would be to use force people on small screens to click the 'x' button (or 'esc' key). 933 // var position_args = {my: 'top left', at: 'bottom right', target: 'event', adjust: {method: 'shift flip'}, viewport: $(window)}; 934 $('.ttip_object_info, .ttip_object_info_blue, .ttip_object_info_white').each(function() { 935 // pull other attributes out of the object 936 var width_class = $(this).attr('width_class') 937 938 var no_clone = false; 939 if ($(this).attr('clone') == 'no_clone') { 940 no_clone = true; 941 } 942 943 var show = { solo: true }; 944 var hide; 945 if ($(this).attr('hover') != 'hover') { 946 show['event'] = 'click'; 947 hide = { event: 'unfocus', leave: false }; 948 } 949 950 var classes = 'qtip-rm qtip-rounded rm-qtip ' 951 var position = position_args; 952 if ($(this).attr('modal') == 'modal') { 953 show['event'] = 'click'; 954 show['modal'] = { on: true }; 955 hide = { event: 'unfocus', leave: false }; 956 position = modal_position; 957 classes = 'qtip-rm qtip-rounded rm-qtip rm-qtip-modal ' 958 } 959 960 $(this).qtip({ 961 content: qtip_content_helper(this, no_clone=no_clone), 962 show: show, 963 hide: hide, 964 style: {widget: false, classes: classes + width_class, tip: {width: 15, height: 10}}, 965 position: position 966 }) 967 }) 968} 969 970 971function setup_tab_menu_qtips() { 972 973 // the hide and show objects used in calling qtip needs to be different if we're on a touchscreen. 974 var hide_args = { 975 fixed: true, 976 delay: 50, 977 effect: function() { $(this).slideUp(50); } 978 } 979 var show_args = { 980 solo: true, 981 event: 'hover', 982 delay: 100, 983 effect: function() { $(this).slideDown(100); } 984 } 985 986 987 if('ontouchstart' in document.documentElement) { 988 var hide_args = { 989 fixed: true, 990 delay: 50, 991 event: 'unfocus', 992 effect: function() { $(this).slideUp(50); } 993 } 994 var show_args = { 995 solo: true, 996 event: 'click', 997 delay: 100, 998 effect: function() { $(this).slideDown(100); } 999 } 1000 } 1001 1002 $('.tab_menu_label').each(function() { 1003 $(this).qtip({ 1004 content: { text: function(api) { return $($(this).attr('rel')) } }, 1005 // show: {solo: true, event: 'hover', delay: 100, 1006 // effect: function() { $(this).slideDown(100); } 1007 // }, 1008 show: show_args, 1009 // hide: {fixed: true, delay: 50, 1010 // effect: function() { $(this).slideUp(50); } 1011 // }, 1012 hide: hide_args, 1013 events: { 1014 show: function(event, api) { $(api.elements.target).parent().addClass('active') }, 1015 hide: function(event, api) { $(api.elements.target).parent().removeClass('active') } 1016 }, 1017 style: { 1018 widget: false, classes: 'qtip-rm-tab-menu', tip: false, 1019 }, 1020 position: { 1021 adjust: {method: 'none none'}, 1022 // viewport: $(window), 1023 my: 'top left', at: 'bottom left', adjust: {y: 1} 1024 } 1025 }) 1026 }) 1027} 1028 1029 1030function setup_filterable_lists() { 1031 1032 // custom css expression for a case-insensitive contains() 1033 jQuery.expr[':'].Contains = function(a,i,m){ 1034 return (a.textContent || a.innerText || "").toUpperCase().indexOf(m[3].toUpperCase())>=0; 1035 }; 1036 1037 // simple list filter for javascript.filterable_eo_list() 1038 $(".list_filter") 1039 .change( function () { 1040 1041 var filter = $(this).val(); 1042 var filt_list = $("#filterable_list_" + $(this).attr("id")); 1043 if(filter) { 1044 // this finds all links in a list that contain the input, 1045 // and hide the ones not containing the input while showing the ones that do 1046 1047 //filt_list.find("div:not(:Contains(" + filter + "))").parent().slideUp(); 1048 //filt_list.find("div:Contains(" + filter + ")").parent().slideDown(); 1049 1050 filt_list.find(".filt_str:not(:Contains(" + filter + "))").parent().hide(); 1051 filt_list.find(".filt_str:Contains(" + filter + ")").parent().show(); 1052 1053 //filt_list.hide(); 1054 } else { 1055 filt_list.find(".filt_str").parent().show(); 1056 } 1057 return false;
1058 }) 1059 .keyup( function () { 1060 // fire the above change event after every letter 1061 $(this).change(); 1062 }); 1063 1064 return false; 1065 1066 // like the above (sortof), but specifically tailored for filtering the roadmap 1067 // This is now handled by an insta filter setup in rm_page.py 1068 // $(".roadmap_filter") 1069 // .change( function () { 1070 // $(document) 1071 // .on( 'change', ".roadmap_filter", function () { 1072 // 1073 // var filter = $(this).val(); 1074 // var filt_list = $("#filterable_list_" + $(this).attr("id")); 1075 // if(filter) { 1076 // // remove all highlights 1077 // //filt_list.find("li>span").removeHighlight(); 1078 // filt_list.find("li").removeHighlight(); 1079 // //filt_list.find(".glossary>div>span").removeHighlight(); 1080 // filt_list.find(".glossary>div").removeHighlight(); 1081 // 1082 // // this finds all li elements that contain the input, 1083 // // and hide the ones not containing the input while showing the ones that do 1084 // //filt_list.find("li>span:not(:Contains(" + filter + "))").parent().hide(); 1085 // filt_list.find("li:not(:Contains(" + filter + "))").hide(); 1086 // //filt_list.find("li>span:Contains(" + filter + ")").each(function(){ 1087 // filt_list.find("li:Contains(" + filter + ")").each(function(){ 1088 // el = $(this); 1089 // el.show(); 1090 // el.parents("li").show(); 1091 // if (filter.length > 2) { 1092 // // rather just doing el.highlight() we highlight in el's children 1093 // // that are not <ul>. This eliminates a bug where we double highlight 1094 // // li>ul>li constructs, resulting in strangely eliminated text when 1095 // // removing highlights. 1096 // el.children().not("ul") 1097 // .highlight(filter); 1098 // } 1099 // }); 1100 // 1101 // // this handles the glossary 1102 // //filt_list.find(".glossary>div>span:not(:Contains(" + filter + "))").parent().hide(); 1103 // filt_list.find(".glossary>div:not(:Contains(" + filter + "))").hide(); 1104 // //filt_list.find(".glossary>div>span:Contains(" + filter + ")").each(function(){ 1105 // filt_list.find(".glossary>div:Contains(" + filter + ")").each(function(){ 1106 // el = $(this); 1107 // el.show(); 1108 // if (filter.length > 2) { 1109 // el.highlight(filter); 1110 // } 1111 // }); 1112 // 1113 // // yoink the glossary header 1114 // $("#glossary-header").hide(); 1115 // 1116 // // make sure all the sections are expanded or not, as appropriate 1117 // // basically, close and remove the empty ones, and show and open the ones with content 1118 // filt_list.find("div>a.clickable").each(function(){ 1119 // link = $(this); 1120 // if (link.css('display') != 'none') { // there are usually two of these links that toggle... we want the currently visible one 1121 // 1122 // list_container = link.parent().find("div.arrow-slidedown>ol"); 1123 // if (!list_container.length) { 1124 // list_container = link.parent().find("div.arrow-slidedown>blockquote"); 1125 // items = "div" 1126 // } else { 1127 // items = "li" 1128 // } 1129 // is_shown = list_container.is(":visible"); 1130 // should_show = false; 1131 // list_container.children(items).each(function(){ 1132 // if ($(this).css('display') != 'none') { should_show = true; } 1133 // }); 1134 // 1135 // // open or close the the section 1136 // if (is_shown != should_show) { 1137 // link.click(); 1138 // } 1139 // 1140 // // show or hide the section 1141 // if (!should_show) { 1142 // link.parent().hide(); 1143 // } else { 1144 // link.parent().show(); 1145 // } 1146 // 1147 // } 1148 // }); 1149 // 1150 // } else { 1151 // // show all items, remove highlights 1152 // //filt_list.find("li>span").each(function(){ 1153 // filt_list.find("li").each(function(){ 1154 // el = $(this); 1155 // el.removeHighlight(); 1156 // el.show(); 1157 // }); 1158 // //filt_list.find(".glossary>div>span").each(function(){ 1159 // filt_list.find(".glossary>div").each(function(){ 1160 // el = $(this); 1161 // el.removeHighlight(); 1162 // el.show(); 1163 // }); 1164 // 1165 // // but close and show all sections 1166 // filt_list.find("div>a.clickable").each(function(){ 1167 // link = $(this); 1168 // if (link.css('display') != 'none') { // there are usually two of these links that toggle... we want the currently visible one 1169 // list_container = link.parent().find("div.arrow-slidedown>ol"); 1170 // if (!list_container.length) { 1171 // list_container = link.parent().find("div.arrow-slidedown>blockquote"); 1172 // } 1173 // is_shown = list_container.is(":visible"); 1174 // if (is_shown) { 1175 // link.click(); 1176 // } 1177 // link.parent().show(); 1178 // } 1179 // }); 1180 // 1181 // // put back glossary header 1182 // $("#glossary-header").show(); 1183 // } 1184 // return false;
1185 // }) 1186 // // .keyup( function () { 1187 // .on( 'keyup', ".roadmap_filter", function () { 1188 // // fire the above change event after every letter 1189 // $(this).change(); 1190 // }); 1191} 1192 1193 1194// ******************* reordering functions ********************************* 1195 1196// ###################### not yet working 1197 1198function make_deletes_persistant(div_class) { 1199 $("." + div_class).each(function(i) { 1200 if($(this).children(':input[name$="delete"]').val() == "1") { 1201 $(this).hide() 1202 $(this).removeClass(div_class) 1203 $(this).parent().append($(this)) 1204 } 1205 }); 1206 return false 1207} 1208 1209 1210function sort_by_order_field(a, b) { 1211 var a_order = $(a).children(':input[name$="order"]').val() 1212 //var a_order = $(a'> input[name$="order"]').val() 1213 var b_order = $(b).children(':input[name$="order"]').val() 1214 //var b_order = $(b + '> input[name$="order"]').val() 1215 return (a_order < b_order) ? -1 : (a_order > b_order) ? 1 : 0; 1216} 1217 1218 1219function make_author_sort_persistant(div_class) { 1220 var authors = $('.' + div_class + ':visible') 1221 var template = $('.' + div_class + ':hidden') 1222 //authors.hide() 1223 authors.sort(sort_by_order_field) 1224 authors.each(function(i) { 1225 $(this).parent().children(':nth-child('+(i+2)+')').after($(this)) 1226 }) 1227 //authors.show() 1228 return false 1229} 1230 1231 1232function hide_show_from_selector_val($selector, suffix, instant) { 1233 // shows elements with class of selector.val()+suffix 1234 // hides elements with class of any non-selected selector vals + suffix 1235 // Use 'instant' to skip the animation - often needed when form elements are to be hide/shown. 1236 1237 // hide all 1238 $selector.children("option").each(function() { 1239 non_selected_val = $(this).val(); 1240 non_selected_el = $("."+non_selected_val+suffix); 1241 if (instant) { non_selected_el.hide(); } else { non_selected_el.slideUp(); } 1242 }); 1243 1244 // show selected 1245 sel_val = $selector.val(); 1246 selected_el = $("."+sel_val+suffix); 1247 if (instant) { selected_el.show(); } else { selected_el.slideUp(); } 1248 1249 return false; 1250} 1251 1252 1253// ################### end not yet working 1254 1255function _______MARKDOWN______() { } 1256 1257// MARKED 1258// https://marked.js.org/#/USING_ADVANCED.md 1259// marked.setOptions({ 1260// pedantic: false, 1261// breaks: true 1262// }); 1263// function __markdown(text) { 1264// var html = marked(text); 1265// return html; 1266// } 1267 1268// SHOWDOWN 1269// https://github.com/showdownjs/showdown/wiki/Showdown-options 1270 1271 1272var escape_parens_for_mathjax = function (converter) { 1273 // it could be that we just want to automatically replace slashes with double slashes 1274 return [ 1275 { 1276 type: 'lang', 1277 regex: /\\\(.*?\\\)/g, 1278 replace: function (match, content) { 1279 return '\\\\(' + match.slice(2, -2) + '\\\\)'; 1280 } 1281 }, 1282 { 1283 type: 'lang', 1284 regex: /\\\[.*?\\\]/g, 1285 replace: function (match, content) { 1286 // This one seems to work, but will probably break if [] is used inside the latex 1287 // return match.replace('\[', '\\[').replace('\]', '\\]') 1288 return '\\\\[' + match.slice(2, -2) + '\\\\]'; 1289 } 1290 }, 1291 { 1292 type: 'lang', 1293 regex: /\\\{.*?\\\}/g, 1294 replace: function (match, content) { 1295 return '\\\\{' + match.slice(2, -2) + '\\\\}'; 1296 } 1297 }, 1298 ]; 1299}; 1300 1301 1302var escape_greater_thans_for_blockquote = function (converter) { 1303 // Allow greater thans as the first thing on a line. 1304 return [ 1305 { 1306 type: 'lang', 1307 regex: /<br>\s*>/gm, 1308 replace: function (match, content) { 1309 return '<br>>'; 1310 } 1311 }, 1312 { 1313 type: 'lang', 1314 regex: /^\s*>/gm, 1315 replace: function (match, content) { 1316 return '>'; 1317 } 1318 }, 1319 ]; 1320}; 1321 1322 1323function __markdown(text) { 1324 var converter = new showdown.Converter({ 1325 // tables: true, 1326 simpleLineBreaks: true, 1327 extensions: [escape_parens_for_mathjax, escape_greater_thans_for_blockquote], 1328 literalMidWordUnderscores: true 1329 }); 1330 var html = converter.makeHtml(text); 1331 return html; 1332} 1333 1334 1335function emulate_prep_text(text) { 1336 // Treat text the same way we do coming out of the db. See html_util.prep_text() 1337 text = text.replace(/&/g, '&'); 1338 text = text.replace(/>/g, '>').replace(/</g, '<'); 1339 1340 return text; 1341} 1342 1343 1344function unescape_for_showdown_display(text) { 1345 1346 // Showdown double-escapes '<>' in code sections. Undo this for display. 1347 text = text.replace(/&/g, '&'); 1348 1349 return text 1350} 1351 1352 1353function preview_markdown(source_id, target_id, render_mathjax) { 1354 var text = document.getElementById(source_id).value; 1355 1356 text = emulate_prep_text(text); 1357 1358 var target = document.getElementById(target_id); 1359 var html = __markdown(text); 1360 1361 html = unescape_for_showdown_display(html) 1362 1363 if (!html) { 1364 html = "<br><br><div style='color: gray; font-style: italic; text-align: center;'>No text has been entered yet, so there is nothing to preview.</div><br><br>" 1365 } 1366 target.innerHTML = html; 1367 if (render_mathjax) { 1368 MathJax.Hub.Queue(["Typeset", MathJax.Hub, target]); 1369 } 1370} 1371 1372// This allows common punctuation as well as spaces for the character after a closing *. 1373// var good_markdown_star_regex = /(?:\s|^)\*+[^\*]+\*+(?:\s|$|[.,:;])/gm 1374function looks_like_math(paragraph) { 1375 var matches = paragraph.match(/\*+/g); 1376 if (matches && matches.length > 1) { 1377 // Remove cases that look like good italics. 1378 paragraph = paragraph.replace(GOOD_MARKDOWN_STAR_REGEX, '') 1379 paragraph = paragraph.replace(MARKDOWN_WORDCOUNT_REGEX, '') 1380 // Any remaining *s look like math. 1381 var math_star_matches = paragraph.match(/\*+/g); 1382 if (math_star_matches != null) { 1383 return true; 1384 } 1385 } 1386 1387 return false;
1388} 1389 1390 1391var markdown_sects = 0; 1392function process_markdown(i, el) { 1393 var contents = $(el).clone(); 1394 1395 contents.find("br").replaceWith("\n"); 1396 var result = __markdown(contents.html()); 1397 1398 // replace <p> with <br><br> 1399 var comp_result = $('<div>' + result + '</div>'); 1400 comp_result.find('p').replaceWith(function(el) { 1401 var ans = $(this).html(); 1402 if (comp_result.find('p').length > 1) { 1403 ans = ans + '<br><br>'; 1404 } 1405 return ans; 1406 }) 1407 1408 result = comp_result.html() 1409 // Get rid of trailing BRs 1410 result = result.replace(/(<br>\s*)+$/, '') 1411 // Get rid of extra spaces 1412 // result = result.replace(/\s+/g, ' ').trim() 1413 1414 if (result.trim().replace(/\s+/g, '').replace(/<br>/g, '') == contents.html().trim().replace(/\s+/g, '')) { 1415 return; 1416 } else { 1417 // If a paragraph looks like it contains *s in a mathy way don't show the markdown view on load. 1418 var show_orig = false; 1419 var paragraphs = contents.html().split('\n\n'); 1420 $(paragraphs).each(function(i, el) { 1421 show_orig = looks_like_math(this); 1422 if (show_orig) { return false; }; 1423 }); 1424 1425 var orig_markdown_sect_class = 'markdown_sect_' + markdown_sects; 1426 var new_markdown_sect_class = orig_markdown_sect_class + '_markdown'; 1427 var toggle_link_class = orig_markdown_sect_class + '_toggle'; 1428 1429 markdown_sects = markdown_sects + 1; 1430 1431 // Add class to old section 1432 $(el).wrapInner('<div class="' + orig_markdown_sect_class + '">'); 1433 $('.' + orig_markdown_sect_class).hide(); 1434 1435 result = unescape_for_showdown_display(result) 1436 1437 // Put in new section 1438 // var new_sect = $("<div style='color: red;'>" + result + "</div>"); 1439 var new_sect = $("<div>" + result + "</div>"); 1440 new_sect.addClass(new_markdown_sect_class); 1441 $('.' + orig_markdown_sect_class).after(new_sect); 1442 1443 // Put in toggle links 1444 var links = $(` 1445 <span class="` + toggle_link_class + `"> 1446 <div class="active_toggle_button clickable" title="Toggle whether the text is formatted by Markdown or not.">markdown</div> 1447 </span> 1448 `); 1449 links.push($(` 1450 <span class="` + toggle_link_class + `"> 1451 <div class="inactive_toggle_button clickable" title="Toggle whether the text is formatted by Markdown or not.">markdown</div> 1452 </span> 1453 `).hide()); 1454 1455 var wrapper = $('<div style="float: right;"></div>'); 1456 links.insertBefore('.' + orig_markdown_sect_class); 1457 links.wrap(wrapper); 1458 1459 var clear = $('<div></div>').addClass('clear'); 1460 clear.insertBefore('.' + orig_markdown_sect_class); 1461 1462 $('.' + toggle_link_class).click(function () { 1463 $('[class^=' + orig_markdown_sect_class).toggle(); 1464 }) 1465 1466 if (show_orig) { 1467 $('[class^=' + orig_markdown_sect_class).toggle(); 1468 } 1469 } 1470} 1471 1472 1473function toggle_monospace(el) { 1474 $(el).toggleClass('monospace'); 1475 $(el).find('.toggle_monospace').toggle(); 1476} 1477 1478 1479function process_monospace(i, el) { 1480 1481 // Put in toggle links 1482 var links = $(); 1483 links.push($(` 1484 <span class="toggle_monospace"> 1485 <div class="inactive_toggle_button clickable" title="Toggle whether the font letter-width is fixed or proportional."> 1486 monospace 1487 </div> 1488 </span> 1489 `).hide()); 1490 links.push($(` 1491 <span class="toggle_monospace"> 1492 <div class="active_toggle_button clickable" title="Toggle whether the font letter-width is fixed or proportional."> 1493 monospace 1494 </div> 1495 </span> 1496 `)); 1497 1498 var wrapper = $('<div style="float: right;"></div>'); 1499 links.prependTo(wrapper); 1500 wrapper.prependTo(el); 1501 1502 // This may already be there from the markdown 1503 if ($(el).find('.clear').length == 0){ 1504 var clear = $('<div></div>').addClass('clear'); 1505 clear.insertAfter(wrapper); 1506 } 1507 1508 $(el).find('.toggle_monospace').click(function() {toggle_monospace(el);}) 1509 toggle_monospace(el); 1510} 1511 1512 1513function _______MULTI_BLOCK_BUTTONS______() {} 1514 1515 1516function delete_mb_entry(min, max, button_class, uniq, div_class, to_delete, delete_name) { 1517 1518 /* The 'delete entry' button was pressed; don't clear deleted fields. */ 1519 1520 var to_delete = $("#" + to_delete) 1521 to_delete.animate({ 1522 "height": "toggle" 1523 }, "slow", function() { 1524 to_delete.removeClass(div_class) 1525 to_delete.parent().append(to_delete) 1526 jQuery("#" + delete_name).val("1") 1527 fix_multi_block_buttons(min, max, div_class, button_class, uniq) 1528 }) 1529 return false 1530} 1531 1532 1533function delete_mb_entry_and_clear(min, max, button_class, uniq, div_class, to_delete, delete_name) { 1534 1535 /* The 'delete entry' button was pressed; clear out deleted fields. */ 1536 1537 var to_delete = $("#" + to_delete) 1538 to_delete.animate({ 1539 "height": "toggle" 1540 }, "slow", function() { 1541 to_delete.find('input').val('') // Clear fields in the deleted entry. (What about textarea and select?) 1542 to_delete.removeClass(div_class) 1543 to_delete.parent().append(to_delete) 1544 jQuery("#" + delete_name).val("1") 1545 fix_multi_block_buttons(min, max, div_class, button_class, uniq) 1546 }) 1547 return false 1548} 1549 1550 1551function get_tinymce_id_settings_map_for_container(container) { 1552 // Find any tinyMCE instances in container. 1553 // store their settings and remove the instance. 1554 1555 var tinymce_settings_map = {}; 1556 var editors = tinymce.editors; 1557 1558 $.each(editors, function(i, editor) { 1559 if (typeof editor != 'undefined') { 1560 var editor_id = editor.id; 1561 var editor_dom_ob = $('#' + editor_id) 1562 if (container.has(editor_dom_ob).length > 0) { 1563 tinymce_settings_map[editor_id] = editor.settings; 1564 tinymce.remove('#' + editor_id); 1565 } 1566 } 1567 }) 1568 return tinymce_settings_map; 1569} 1570 1571 1572function tinymce_editor_exists(tinymce_id) { 1573 for (var i in tinymce.editors) { 1574 if (tinymce.editors[i].id == tinymce_id) {return true} 1575 } 1576 return false 1577} 1578 1579 1580function restore_tinymce_instances_from_map(tinymce_settings_map) { 1581 // Restore the tinymce instances. 1582 for (var tinymce_id in tinymce_settings_map) { 1583 if (!tinymce_settings_map.hasOwnProperty(tinymce_id)) { 1584 //The current property is not a direct property of tinymce_settings_map 1585 continue; 1586 }
1587 // Do not make it if there is already one with that id. 1588 if (tinymce_editor_exists(tinymce_id)) { 1589 continue; 1590 } 1591 1592 var editor = new tinymce.Editor(tinymce_id, tinymce_settings_map[tinymce_id], tinymce.EditorManager); 1593 editor.render(); 1594 } 1595} 1596 1597 1598function handle_move_mb_entry_up(min, max, button_class, uniq, div_class, current) { 1599 1600 /* The 'move entry up' button was pressed. */ 1601 1602 var tinymce_settings_map = {}; 1603 1604 var current = $("#" + current); 1605 var index = current.children("." + uniq + "_reorder").val(); 1606 if(index > 0) { 1607 current.animate({ 1608 "opacity": "toggle" 1609 }, "slow", function() { 1610 // Find any tinyMCE instances that will be moved. 1611 // store their settings and remove the instance. 1612 var moving_el = current.prev("." + div_class); 1613 tinymce_settings_map = get_tinymce_id_settings_map_for_container(moving_el); 1614 current.after(current.prev("." + div_class)); 1615 }).animate({ 1616 "opacity": "toggle" 1617 }, "slow", function() { 1618 fix_multi_block_buttons(min, max, div_class, button_class, uniq); 1619 restore_tinymce_instances_from_map(tinymce_settings_map); 1620 }); 1621 } 1622 return false; 1623} 1624 1625 1626function handle_move_mb_entry_down(min, max, button_class, uniq, div_class, current) { 1627 1628 /* The 'move entry down' button was pressed. */ 1629 var tinymce_settings_map = {}; 1630 1631 var current = $("#" + current); 1632 var curr_max = $("." + div_class).length-2; //length-2 because of template div and zero-indexing. 1633 var index = current.children("." + uniq + "_reorder").val(); 1634 if(index < curr_max) { 1635 current.animate({ 1636 "opacity": "toggle" 1637 }, "slow", function() { 1638 var moving_el = current; 1639 // Find any tinyMCE instances that will be moved. 1640 // store their settings and remove the instance. 1641 tinymce_settings_map = get_tinymce_id_settings_map_for_container(moving_el); 1642 current.next("." + div_class).after(current); 1643 }).animate({ 1644 "opacity": "toggle" 1645 }, "slow", function() { 1646 fix_multi_block_buttons(min, max, div_class, button_class, uniq); 1647 restore_tinymce_instances_from_map(tinymce_settings_map); 1648 }); 1649 } 1650 return false; 1651} 1652 1653 1654function get_multi_block_count(div_class) { 1655 1656 /* Get the count of active, non-template/deleted multi-blocks whether the 1657 whole thing is hidden or not. */ 1658 1659 var blocks = $("."+div_class); 1660 var count = 0; 1661 blocks.each(function() { 1662 var delete_field = $(this).find("[name$='delete']"); 1663 // ignore template block 1664 if (delete_field.attr('name').indexOf('%%') == -1) { 1665 // ignore deleted blocks 1666 if (delete_field.val() != 1) { 1667 count += 1; 1668 } 1669 } 1670 }) 1671 return count; 1672} 1673 1674 1675function fix_multi_block_buttons(min, max, div_class, button_class, uniq) { 1676 1677 /* Fix which up/down reorder buttons, delete buttons, and add entry 1678 buttons are showing. <min> and <max> are the required min and max allowed. 1679 */ 1680 1681 // This is unreliable as we are hiding auth sections. 1682 // var count = $("."+div_class+":visible").length 1683 var count = get_multi_block_count(div_class) 1684 1685 // Fix delete buttons. 1686 if(count <= min) { 1687 $("."+uniq+'_delete_button').hide() 1688 } else { 1689 $("."+uniq+'_delete_button').show() 1690 } 1691 1692 // Fix add entry button. 1693 if(max == 0) { 1694 // No max. 1695 $("."+button_class).show() 1696 } else { 1697 if(count >= max) { 1698 $("."+button_class).hide() 1699 } else { 1700 $("."+button_class).show() 1701 } 1702 } 1703 1704 // Fix reorder buttons. 1705 if(count <= 1) { 1706 // No need for reorder buttons. 1707 $("."+uniq+"_reorder_button_up").hide() 1708 $("."+uniq+"_reorder_button_down").hide() 1709 } else { 1710 // Up button on all but first. 1711 $("."+uniq+"_reorder_button_up").show() 1712 $("."+uniq+"_reorder_button_up:first").hide() 1713 // Down button on all but last (last visible). 1714 $("."+uniq+"_reorder_button_down").show() 1715 $("."+uniq+"_reorder_button_down:visible:last").hide() 1716 } 1717
1718 // When we load these pages we want the numbers to straighten themselves out in case the user doesn't change anything pre-submit. 1719 $("." + uniq + "_reorderable > ." + uniq + "_reorder").each(function(i) { 1720 $(this).val(i); 1721 }); 1722} 1723 1724 1725function _______OTHER______() {} 1726 1727 1728// Return a helper with preserved width of cells 1729var fixHelper = function(e, ui) { 1730 ui.children().each(function() { 1731 $(this).width($(this).width()); 1732 }); 1733 return ui; 1734}; 1735 1736 1737// This is for dhtmlx grids. 1738function str_natural(a, b, order) { 1739 function chunkify(t) { 1740 var tz = new Array(); 1741 var x = 0, y = -1, n = 0, i, j; 1742 1743 while (i = (j = t.charAt(x++)).charCodeAt(0)) { 1744 var m = (i == 46 || (i >=48 && i <= 57)); 1745 if (m !== n) { 1746 tz[++y] = ""; 1747 n = m; 1748 } 1749 tz[y] += j; 1750 } 1751 return tz; 1752 } 1753 1754 var aa = chunkify(a.toLowerCase()); 1755 var bb = chunkify(b.toLowerCase()); 1756 1757 for (x = 0; aa[x] && bb[x]; x++) { 1758 if (aa[x] !== bb[x]) { 1759 var c = Number(aa[x]), d = Number(bb[x]); 1760 if (c == aa[x] && d == bb[x]) { 1761 return (c - d)*(order=="asc"?1:-1); 1762 } else return ((aa[x] > bb[x]) ? 1 : -1)*(order=="asc"?1:-1); 1763 } 1764 } 1765 return (aa.length - bb.length)*(order=="asc"?1:-1); 1766} 1767 1768 1769function _______SET_UP_SORTABLES______() {} 1770 1771/* 1772we have two different sortable tools, 1773jquery_ui_sortable doesn't work in IE 9 & 10 1774and the other doesn't work in colorboxes in safari. 1775Decide here which we are using based on the users browser. 1776 1777new one from: 1778https://github.com/RubaXa/Sortable 1779 1780A note about args that are functions: jquery_ui_sortable sends two arguments into 1781functions: <event>, <ui>, while the other just sends <event>. The two <event> 1782variables are not equivilant though. In jquery_ui_sortable the object that was 1783just sorted is ui.item, and it is event.item in the new one. If you're going to 1784use the arguments passed in you need to give two functions that use the 1785appropriate arguments. 1786*/ 1787 1788$(function() { // Wait till the page is loaded so we're sure both kinds are there. 1789 if ($.browser.msie && parseInt($.browser.version, 10) > 8) { 1790 jQuery.fn.extend({ 1791 rm_sortable: function(args) { 1792 var new_args = {} 1793 $.each(args, function(key, val) { 1794 if (key == 'start' && !('onStart' in args)) { 1795 new_args['onStart'] = val; 1796 } else if (key == 'stop' && !('onEnd' in args)) { 1797 new_args['onEnd'] = val; 1798 } else if (key == 'update' && !('onUpdate' in args)) { 1799 new_args['onUpdate'] = val; 1800 } else if (key == 'helper') { 1801 // ignore this 1802 } else { 1803 new_args[key] = val; 1804 } 1805 }); 1806 return this.sortable(new_args); 1807 } 1808 }); 1809 } else { 1810 jQuery.fn.extend({ 1811 rm_sortable: function(args) { 1812 var new_args = {} 1813 $.each(args, function(key, val) { 1814 if (key == 'onStart' && !('start' in args)) { 1815 new_args['start'] = val; 1816 } else if (key == 'onEnd' && !('stop' in args)) { 1817 new_args['stop'] = val; 1818 } else if (key == 'onUpdate' && !('update' in args)) { 1819 new_args['update'] = val; 1820 } else if (key == 'ghostClass') { 1821 // ignore this 1822 } else { 1823 new_args[key] = val; 1824 } 1825 }); 1826 return this.jquery_ui_sortable(new_args); 1827 } 1828 }); 1829 } 1830}) 1831 1832// When the document is ready set up our sortable with it's inherant function(s) 1833// function make_table_sortable(table_id) { 1834// var width = jQuery("#" + table_id).width() 1835// $("#" + table_id + " tbody > tr:even").removeClass('odd even').addClass('odd') 1836// $("#" + table_id + " tbody > tr:odd").removeClass('odd even').addClass('even') 1837// 1838// $("#" + table_id + " tbody").sortable({ 1839// // axis: 'y', // This causes strange behaviour. 1840// start: function(event, ui) { 1841// $("body").disableSelection() 1842// $("#" + table_id).width(width) 1843// }, 1844// stop: function(event, ui) { $("body").enableSelection() }, 1845// helper: fixHelper, 1846// handle: '.ui-icon-arrowthick-2-n-s', 1847// update: function(event, ui) { 1848// $(":input[name$='order']:hidden").each(function(i) { 1849// $(this).val(i); 1850// }); 1851// $("#" + table_id + " tbody > tr:even").removeClass('odd even').addClass('odd'); 1852// $("#" + table_id + " tbody > tr:odd").removeClass('odd even').addClass('even'); 1853// // Don't give even-odd classes to the header rows. 1854// $("#" + table_id + " tbody > tr > th").parents("#" + table_id + " tbody > tr").removeClass('odd even'); 1855// } 1856// }) 1857// // In the beginning, fill in any missing order values. 1858// $(":input[name$='order']:hidden").each(function(i) { 1859// $(this).val(i); 1860// }); 1861// // Don't give even-odd classes to the header rows. 1862// $("#" + table_id + " tbody > tr > th").parents("#" + table_id + " tbody > tr").removeClass('odd even'); 1863// } 1864 1865// jQuery UI sortable breaks when you have to scroll in IE. 1866// This version uses the sortable from https://github.com/RubaXa/Sortable. 1867function make_table_sortable(table_id) { 1868 var width = jQuery("#" + table_id).width() 1869 $("#" + table_id + " > tbody > tr:even").removeClass('odd even').addClass('odd') 1870 $("#" + table_id + " > tbody > tr:odd").removeClass('odd even').addClass('even') 1871 1872 $("#" + table_id + " > tbody").rm_sortable({
1873 // Shared options: 1874 handle: '.ui-icon-arrowthick-2-n-s', 1875 1876 start: function(event, ui) { 1877 $("body").disableSelection() 1878 $("#" + table_id).width(width) 1879 }, 1880 stop: function(event, ui) { $("body").enableSelection() }, 1881 update: function(event, ui) { 1882 $(":input[name$='order']:hidden").each(function(i) { 1883 $(this).val(i); 1884 }); 1885 $("#" + table_id + " > tbody > tr:even").removeClass('odd even').addClass('odd'); 1886 $("#" + table_id + " > tbody > tr:odd").removeClass('odd even').addClass('even'); 1887 // Don't give even-odd classes to the header rows. 1888 $("#" + table_id + " > tbody > tr > th").parents("#" + table_id + " > tbody > tr").removeClass('odd even'); 1889 }, 1890 // Options for jquery_ui_sortable: 1891 helper: fixHelper, 1892 1893 // Options for ie_sortable 1894 ghostClass: 'ghost' 1895 }) 1896 // In the beginning, fill in any missing order values. 1897 $(":input[name$='order']:hidden").each(function(i) { 1898 $(this).val(i); 1899 }); 1900 // Don't give even-odd classes to the header rows. 1901 $("#" + table_id + " tbody > tr > th").parents("#" + table_id + " tbody > tr").removeClass('odd even'); 1902} 1903 1904 1905// Global cache for domain-affiliation used to autofill affiliations when a domain has already been used in a form. 1906var domain_aff_map = {} 1907function fill_dflt_aff_from_email(email_fld, aff_fld, mapping) { 1908 1909 // Helps with institution management when filling out contact info /
1909sub forms. 1910 $(document).on('change blur', "input[name='"+email_fld+"']", function() { 1911 var email = $(this).attr('value') 1912 if (email && email.indexOf('@')) { 1913 var domain = email.slice(email.indexOf('@')+1) 1914 fill_dflt(aff_fld, domain) 1915 } 1916 }) 1917 // Fill domain_aff_map on email field blur with domain - affiliation pair. 1918 $(document).on('blur', "input[name='"+email_fld+"']", function() { 1919 var email = $(this).attr('value') 1920 var aff = $("input[name='"+aff_fld+"']").val() 1921 if (aff && email && email.indexOf('@')) { 1922 var domain = email.slice(email.indexOf('@')+1) 1923 domain_aff_map[domain] = aff 1924 } 1925 }) 1926 // Fill domain_aff_map on affiliation field blur with domain - affiliation pair. 1927 $(document).on('blur', "input[name='"+aff_fld+"']", function() { 1928 var email = $("input[name='"+email_fld+"']").val() 1929 var aff = $(this).val() 1930 if (aff && email && email.indexOf('@')) { 1931 var domain = email.slice(email.indexOf('@')+1) 1932 domain_aff_map[domain] = aff 1933 } 1934 }) 1935 1936 function fill_dflt(aff_f, domain) { 1937 var aff_el = $("input[name='"+aff_f+"']") 1938 if (!aff_el.attr('value')) { 1939 var dflt = mapping[domain] 1940 if(dflt != undefined) { 1941 // Fill the affiliation using the site wide domain-aff map. 1942 aff_el.attr('value', dflt) 1943 } else { 1944 // If there isn't a value for the domain in the pre-approved mapping, use other examples from the same form. 1945 var dflt = domain_aff_map[domain] 1946 if(dflt != undefined) { 1947 aff_el.attr('value', dflt) 1948 } 1949 } 1950 } 1951 } 1952} 1953 1954// Global cache for affiliation-country used to autofill countries when an aff has already been used in a form. 1955var aff_country_map = {} 1956function fill_dflt_country_from_aff(aff_fld, country_fld, mapping) { 1957 1958 $(document).on('change blur', "input[name='"+aff_fld+"']", function() { 1959 var aff = $(this).attr('value').toLowerCase(); 1960 if (aff) { 1961 fill_dflt(country_fld, aff); 1962 } 1963 }) 1964 // Fill aff_country_map on aff field blur with aff - country pair. 1965 $(document).on('blur', "input[name='"+aff_fld+"']", function() { 1966 var aff = $(this).attr('value').toLowerCase(); 1967 var country = $("[name='"+country_fld+"']").val(); 1968 if (aff && country) { 1969 aff_country_map[aff] = country; 1970 } 1971 }) 1972 // Fill aff_country_map on country field blur with aff - country pair. 1973 $(document).on('blur', "[name='"+country_fld+"']", function() { 1974 var aff = $("input[name='"+aff_fld+"']").val().toLowerCase() 1975 var country = $(this).val() 1976 if (aff && country) { 1977 aff_country_map[aff] = country; 1978 } 1979 }) 1980 1981 function fill_dflt(country_fld, aff) { 1982 var country_el = $("[name='"+country_fld+"']") 1983 if (!country_el.attr('value')) { 1984 var dflt = mapping[aff]; 1985 if(dflt != undefined) { 1986 // Fill the affiliation using the site wide domain-aff map. 1987 country_el.attr('value', dflt) 1988 } else { 1989 // If there isn't a value for the domain in the pre-approved mapping, use other examples from the same form. 1990 var dflt = aff_country_map[aff]; 1991 if(dflt != undefined) { 1992 country_el.attr('value', dflt) 1993 } 1994 } 1995 } 1996 } 1997} 1998 1999state_country_map = { 2000 'alabama': 'United States of America', 2001 'alaska': 'United States of America', 2002 'arizona': 'United States of America', 2003 'arkansas': 'United States of America', 2004 'california': 'United States of America', 2005 'colorado': 'United States of America', 2006 'connecticut': 'United States of America', 2007 'delaware': 'United States of America', 2008 'florida': 'United States of America', 2009 'georgia': 'United States of America', 2010 'hawaii': 'United States of America', 2011 'idaho': 'United States of America', 2012 'illinois': 'United States of America', 2013 'indiana': 'United States of America', 2014 'iowa': 'United States of America', 2015 'kansas': 'United States of America', 2016 'kentucky': 'United States of America', 2017 'louisiana': 'United States of America', 2018 'maine': 'United States of America', 2019 'maryland': 'United States of America', 2020 'massachusetts': 'United States of America', 2021 'michigan': 'United States of America', 2022 'minnesota': 'United States of America', 2023 'mississippi': 'United States of America', 2024 'missouri': 'United States of America', 2025 'montana': 'United States of America', 2026 'nebraska': 'United States of America', 2027 'nevada': 'United States of America', 2028 'new hampshire': 'United States of America', 2029 'new jersey': 'United States of America', 2030 'new mexico': 'United States of America', 2031 'new york': 'United States of America', 2032 'north carolina': 'United States of America', 2033 'north dakota': 'United States of America', 2034 'ohio': 'United States of America', 2035 'oklahoma': 'United States of America', 2036 'oregon': 'United States of America', 2037 'pennsylvania': 'United States of America', 2038 'rhode island': 'United States of America', 2039 'south carolina': 'United States of America', 2040 'south dakota': 'United States of America', 2041 'tennessee': 'United States of America', 2042 'texas': 'United States of America', 2043 'utah': 'United States of America', 2044 'vermont': 'United States of America', 2045 'virginia': 'United States of America', 2046 'washington': 'United States of America', 2047 'west virginia': 'United States of America', 2048 'wisconsin': 'United States of America', 2049 'wyoming': 'United States of America', 2050 'al': 'United States of America', 2051 'ak': 'United States of America', 2052 'az': 'United States of America', 2053 'ar': 'United States of America', 2054 'ca': 'United States of America', 2055 'co': 'United States of America', 2056 'ct': 'United States of America', 2057 'de': 'United States of America', 2058 'fl': 'United States of America', 2059 'ga': 'United States of America', 2060 'hi': 'United States of America', 2061 'id': 'United States of America', 2062 'il': 'United States of America', 2063 'in': 'United States of America', 2064 'ia': 'United States of America', 2065 'ks': 'United States of America', 2066 'ky': 'United States of America', 2067 'la': 'United States of America', 2068 'me': 'United States of America', 2069 'md': 'United States of America', 2070 'ma': 'United States of America', 2071 'mi': 'United States of America', 2072 'mn': 'United States of America', 2073 'ms': 'United States of America', 2074 'mo': 'United States of America', 2075 'mt': 'United States of America', 2076 'ne': 'United States of America', 2077 'nv': 'United States of America', 2078 'nh': 'United States of America', 2079 'nj': 'United States of America', 2080 'nm': 'United States of America', 2081 'ny': 'United States of America', 2082 'nc': 'United States of America', 2083 'nd': 'United States of America', 2084 'oh': 'United States of America', 2085 'ok': 'United States of America', 2086 'or': 'United States of America', 2087 'pa': 'United States of America', 2088 'ri': 'United States of America', 2089 'sc': 'United States of America', 2090 'sd': 'United States of America', 2091 'tn': 'United States of America', 2092 'tx': 'United States of America', 2093 'ut': 'United States of America', 2094 'vt': 'United States of America', 2095 'va': 'United States of America', 2096 'wa': 'United States of America', 2097 'wv': 'United States of America', 2098 'wi': 'United States of America', 2099 'wy': 'United States of America', 2100 2101 'ab': 'Canada', 2102 'alberta': 'Canada', 2103 'bc': 'Canada', 2104 'british columbia': 'Canada', 2105 'mb': 'Canada', 2106 'manitoba': 'Canada', 2107 'nb': 'Canada', 2108 'new brunswick': 'Canada', 2109 'nf': 'Canada', 2110 'newfoundland': 'Canada', 2111 'nt': 'Canada', 2112 'northwest territories': 'Canada', 2113 'ns': 'Canada', 2114 'nova scotia': 'Canada', 2115 'on': 'Canada', 2116 'ontario': 'Canada', 2117 'pe': 'Canada', 2118 'prince edward island': 'Canada', 2119 'qc': 'Canada', 2120 'quebec': 'Canada', 2121 'sk': 'Canada', 2122 'saskatchewan': 'Canada', 2123 'yt': 'Canada', 2124 'yukon': 'Canada', 2125 2126 'australian capital territory': 'Australia', 2127 'act': 'Australia', 2128 'new south wales': 'Australia', 2129 'nsw': 'Australia', 2130 'northern territory': 'Australia', 2131 'nt': 'Australia', 2132 'queensland': 'Australia', 2133 'qld': 'Australia', 2134 'south australia': 'Australia', 2135 'sa': 'Australia', 2136 'tasmania': 'Australia', 2137 'tas': 'Australia', 2138 'victoria': 'Australia', 2139 'vic': 'Australia', 2140 'western australia': 'Australia', 2141 // 'wa': 'Australia', 2142 2143 'aguascalientes': 'Mexico', 2144 'ags': 'Mexico', 2145 'baja california': 'Mexico', 2146 // 'bc': 'Mexico', 2147 'baja california sur': 'Mexico', 2148 'bcs': 'Mexico', 2149 'campeche': 'Mexico', 2150 'camp': 'Mexico', 2151 'chiapas': 'Mexico', 2152 'chis': 'Mexico', 2153 'chihuahua': 'Mexico', 2154 'chih': 'Mexico', 2155 'coahuila': 'Mexico', 2156 'coah': 'Mexico', 2157 'colima': 'Mexico', 2158 'col': 'Mexico', 2159 'distrito federal': 'Mexico', 2160 'df': 'Mexico', 2161 'durango': 'Mexico', 2162 'dgo': 'Mexico', 2163 'guanajuato': 'Mexico', 2164 'gto': 'Mexico', 2165 'guerrero': 'Mexico', 2166 'gro': 'Mexico', 2167 'hidalgo': 'Mexico', 2168 'hgo': 'Mexico',
2169 'jalisco': 'Mexico', 2170 'jal': 'Mexico', 2171 // 'méxico': 'Mexico', 2172 'mexico': 'Mexico', 2173 'edm': 'Mexico', 2174 // 'michoacán': 'Mexico', 2175 'michoacan': 'Mexico', 2176 'mich': 'Mexico', 2177 'morelos': 'Mexico', 2178 'mor': 'Mexico', 2179 'nayarit': 'Mexico', 2180 'nay': 'Mexico', 2181 // 'nuevo león': 'Mexico', 2182 'nuevo leon': 'Mexico', 2183 'nl': 'Mexico', 2184 'oaxaca': 'Mexico', 2185 'oax': 'Mexico', 2186 'puebla': 'Mexico', 2187 'pue': 'Mexico', 2188 // 'querétaro': 'Mexico', 2189 'queretaro': 'Mexico', 2190 'qro': 'Mexico', 2191 'quintana roo': 'Mexico', 2192 'qroo': 'Mexico', 2193 'san luis potosi': 'Mexico', 2194 'slp': 'Mexico', 2195 'sinaloa': 'Mexico', 2196 'sin': 'Mexico', 2197 'sonora': 'Mexico', 2198 'son': 'Mexico', 2199 'tabasco': 'Mexico', 2200 'tab': 'Mexico', 2201 'tamaulipas': 'Mexico', 2202 'tamps': 'Mexico', 2203 'tlaxcala': 'Mexico', 2204 'tlax': 'Mexico', 2205 'veracruz': 'Mexico', 2206 'ver': 'Mexico', 2207 // 'yucatán': 'Mexico', 2208 'yucatan': 'Mexico', 2209 'yuc': 'Mexico', 2210 'zacatecas': 'Mexico', 2211 'zac': 'Mexico', 2212 2213 // These are counties not states. 2214 //'baden-württemberg': 'Germany', 2215 'baden-wurttemberg': 'Germany', 2216 'bw': 'Germany', 2217 'bayern': 'Germany', 2218 'by': 'Germany', 2219 'berlin': 'Germany', 2220 'be': 'Germany', 2221 'brandenburg': 'Germany', 2222 'bb': 'Germany', 2223 'bremen': 'Germany', 2224 'hb': 'Germany', 2225 'hamburg': 'Germany', 2226 'hh': 'Germany', 2227 'hessen': 'Germany', 2228 'he': 'Germany', 2229 'mecklenburg-vorpommern': 'Germany', 2230 'mv': 'Germany', 2231 'niedersachsen': 'Germany', 2232 'ni': 'Germany', 2233 'nordrhein-westfalen': 'Germany', 2234 'nw': 'Germany', 2235 'rheinland-pfalz': 'Germany', 2236 'rp': 'Germany', 2237 'saarland': 'Germany', 2238 'sl': 'Germany', 2239 'sachsen': 'Germany', 2240 'sn': 'Germany', 2241 'sachsen-anhalt': 'Germany', 2242 'st': 'Germany', 2243 'schleswig-holstein': 'Germany', 2244 'sh': 'Germany', 2245 // 'thüringen': 'Germany', 2246 'thuringen': 'Germany', 2247 'th': 'Germany', 2248 2249 // These are counties 2250 'carlow': 'Ireland', 2251 'cw': 'Ireland', 2252 'cavan': 'Ireland', 2253 'cn': 'Ireland', 2254 'clare': 'Ireland', 2255 'ce': 'Ireland', 2256 'cork': 'Ireland', 2257 'c': 'Ireland', 2258 'donegal': 'Ireland', 2259 'dl': 'Ireland', 2260 'dublin': 'Ireland', 2261 'd': 'Ireland', 2262 'galway': 'Ireland', 2263 'g': 'Ireland', 2264 'kerry': 'Ireland', 2265 // 'ky': 'Ireland', 2266 'kildare': 'Ireland', 2267 'ke': 'Ireland', 2268 'kilkenny': 'Ireland', 2269 'kk': 'Ireland', 2270 'laois': 'Ireland', 2271 'ls': 'Ireland', 2272 'leitrim': 'Ireland', 2273 'lm': 'Ireland', 2274 'limerick': 'Ireland', 2275 'lk': 'Ireland', 2276 'longford': 'Ireland', 2277 'ld': 'Ireland', 2278 'louth': 'Ireland', 2279 'lh': 'Ireland', 2280 'mayo': 'Ireland', 2281 // 'mo': 'Ireland', 2282 'meath': 'Ireland', 2283 'mh': 'Ireland', 2284 'monaghan': 'Ireland', 2285 // 'mn': 'Ireland', 2286 'offaly': 'Ireland', 2287 'oy': 'Ireland', 2288 'roscommon': 'Ireland', 2289 'rn': 'Ireland', 2290 'sligo': 'Ireland', 2291 'so': 'Ireland', 2292 'tipperary': 'Ireland', 2293 'ta': 'Ireland', 2294 'waterford': 'Ireland', 2295 'wd': 'Ireland', 2296 'westmeath': 'Ireland', 2297 'wh': 'Ireland', 2298 'wexford': 'Ireland', 2299 'wx': 'Ireland', 2300 'wicklow': 'Ireland', 2301 'ww': 'Ireland', 2302 2303 // These are provinces 2304 'drenthe': 'Netherlands', 2305 'dr': 'Netherlands', 2306 'flevoland': 'Netherlands', 2307 // 'fl': 'Netherlands', 2308 'friesland': 'Netherlands', 2309 'fr': 'Netherlands', 2310 'gelderland': 'Netherlands', 2311 'ge': 'Netherlands', 2312 'groningen': 'Netherlands', 2313 'gr': 'Netherlands', 2314 'limburg': 'Netherlands', 2315 'li': 'Netherlands', 2316 'noord-brabant': 'Netherlands', 2317 'nb': 'Netherlands', 2318 'noord-holland': 'Netherlands', 2319 // 'nh': 'Netherlands', 2320 'overijssel': 'Netherlands', 2321 'ov': 'Netherlands', 2322 'utrecht': 'Netherlands', 2323 'ut': 'Netherlands', 2324 'zeeland': 'Netherlands', 2325 'ze': 'Netherlands', 2326 'zuid-holland': 'Netherlands', 2327 'zh': 'Netherlands', 2328} 2329 2330// This fills in the country field based on the input in the state field. 2331function fill_country_from_state(state_fld_name, country_fld_name) { 2332 2333 var state_sel = '[name="' + state_fld_name + '"]' 2334 var country_sel = '[name="' + country_fld_name + '"]' 2335 $(document).on('blur', state_sel, function() { 2336 var country_ob = $(country_sel) 2337 // Only change country if there's nothing there yet. 2338 if (!country_ob.val()) { 2339 var state = $.trim($(state_sel).val().toLowerCase()) 2340 var country = state_country_map[state] 2341 if (country != undefined) { 2342 country_ob.val(country); 2343 } 2344 } 2345 }) 2346} 2347 2348 2349function fill_out_message_and_recips(on_load) { 2350 var message = $('#message_selector').val() 2351 if(message == 'new') { 2352 $('#attendance_section').show(); 2353 $('#message_contents_section').show(); 2354 $('#preview_message_buttons').show(); 2355
2356 //$('.recipient_set_cb').parent().show() 2357 $('.recipient_set_cb').each(function(i) { 2358 $(this).attr('checked',false) 2359 }) 2360 if (on_load != true) { 2361 $('[name="subject"]').val('') 2362 $('[name="body"]').val('') 2363 } 2364 $('#recipient_instructions').html('') 2365 } 2366 else if(message == 'registered_no_meals') { 2367 $('#attendance_section').hide(); 2368 $('#message_contents_section').show(); 2369 $('#preview_message_buttons').show(); 2370 2371 //$('.recipient_set_cb').parent().hide() 2372 $('.recipient_set_cb:eq(1)').attr('checked',true) // need something checked to not set off form error checking 2373 $('[name="subject"]').val('Register for REGISTERED_MEETINGS meals') 2374 $('[name="body"]').val('You have registered to attend the REGISTERED_MEETINGS meeting(s), but have not yet indicated whether you will attend any meals/events or not. Please do so right away - otherwise we will ignore your meeting registration and assume you are not able to attend the meeting(s) after all. Remember: please only sign up for meals/events that you are sure you will attend. \n\nDo so here: SIGNUP_URL') 2375 $('#recipient_instructions').html('The recipient list will be people who have registered to attend the meeting(s) but have not completed <b>any</b> meal/event sign-ups.') 2376 } 2377 else if(message == 'registered_incomplete_meals') { 2378 $('#attendance_section').hide(); 2379 $('#message_contents_section').show(); 2380 $('#preview_message_buttons').show(); 2381 2382 $('.recipient_set_cb:eq(1)').attr('checked',true) // need something checked to
2382not set off form error checking 2383 $('[name="subject"]').val('Finish registering for REGISTERED_MEETINGS meals') 2384 $('[name="body"]').val('You have registered for the following meals/events at the REGISTERED_MEETINGS meeting(s):\nMEALS_ATTENDING\nThere are still some meals/events for which you have not indicated whether you will attend or not. Please log in to the committee website and update them right away. Reminder: please only sign up for meals/events that you are sure you will attend. \n\nDo so here: SIGNUP_URL') 2385 $('#recipient_instructions').html('The recipient list will be people who have registered to attend the meeting(s) and have completed <b>some but not all</b> meal/event sign-ups.') 2386 } 2387 else if(message == 'meals_not_registered') { 2388 $('#attendance_section').hide(); 2389 $('#message_contents_section').show(); 2390 $('#preview_message_buttons').show(); 2391 2392 $('.recipient_set_cb:eq(1)').attr('checked',true) // need something checked to not set off form error checking 2393 $('[name="subject"]').val('Register for NO_RESPONSE_MEETINGS meeting(s)') 2394 $('[name="body"]').val('You have registered for meals/events at the NO_RESPONSE_MEETINGS meeting(s), but have not yet signed up to attend. Please do so right away - otherwise we will ignore your meal/event signups and assume you are not able to attend the meeting(s) after all. Remember: please only sign up for meals/events that you are sure you will attend. \n\nDo so here: SIGNUP_URL') 2395 $('#recipient_instructions').html('The recipient list will be people who have signed up for meals/events for a meeting they are no longer registered to attend.') 2396 } 2397 else if(message == 'invited_no_response') { 2398 $('#attendance_section').hide(); 2399 $('#message_contents_section').show(); 2400 $('#preview_message_buttons').show(); 2401 2402 $('.recipient_set_cb:eq(1)').attr('checked',true) // need something checked to
2402not set off form error checking 2403 $('[name="subject"]').val('Will you attend NO_RESPONSE_MEETINGS meeting(s)?') 2404 $('[name="body"]').val('You have not indicated whether or not you will be attending the NO_RESPONSE_MEETINGS meeting(s). Please log in to the committee website and either sign up for the meeting(s) AND meals/events, or indicate that you will not be able to attend. This information is important for our meeting planning, so please do it right away. \n\nDo so here: SIGNUP_URL') 2405 $('#recipient_instructions').html('The recipient list will be people who have been invited to the meeting(s) but have not yet indicated whether they will attend, nor have they signed up for meals/events at the meeting(s).') 2406 } 2407 else if(message == 'registered_and_mealed') { 2408 $('#attendance_section').hide(); 2409 $('#message_contents_section').show(); 2410 $('#preview_message_buttons').show(); 2411 2412 $('.recipient_set_cb:eq(1)').attr('checked',true) // need something checked to not set off form error checking 2413 $('[name="subject"]').val('Reminder: REGISTERED_MEETINGS meeting(s)') 2414 $('[name="body"]').val('You have registered for the following meals/events at the REGISTERED_MEETINGS meeting(s):\nMEALS_ATTENDING\nIf your travel plans have changed and the list is no longer accurate, please log in to the committee website and update them right away. Reminder: please only sign up for meals/events that you are sure you will attend. \n\nDo so here: SIGNUP_URL') 2415 $('#recipient_instructions').html('The recipient list will be people who have registered to attend the meeting(s) and have completed their meal/event sign-ups.') 2416 } 2417 else { 2418 $('#attendance_section').hide(); 2419 $('#message_contents_section').hide(); 2420 $('#preview_message_buttons').hide(); 2421 } 2422} 2423 2424 2425function toggle_visible(class_type) { 2426 var preview = $('.' + class_type) 2427 preview.toggle() 2428 $('.link' + class_type).html() 2429} 2430 2431 2432function hide_message_previews() { 2433 $('div[class^="usr"]').hide() 2434} 2435 2436function ______AJAX_UTILITIES________() {} 2437 2438function decode_return_data(data) { 2439 2440 // This tries to do a better job of handling parsing errors caused by python bugs. 2441 // This handles responses created by util.package_ajax_response() and 2442 // improves handling python errors in response to ajax requests. 2443 2444 // This has alredy been decoded? 2445 if (typeof data == 'object') { 2446 return data; 2447 } 2448 2449 try { 2450 var return_data = jQuery.parseJSON(data); 2451 } 2452 catch(err) { 2453 var return_data = {}; 2454 return_data['response'] = 'fail'; 2455 return_data['message'] = data; 2456 } 2457 2458 // We're always expecting an object ot be returned. 2459 if (typeof return_data === 'undefined' || return_data === null) { 2460 return_data = {}; 2461 } 2462 2463 return return_data 2464} 2465 2466 2467function put_file(url, file, upload_event_action_map, success_callback, timelimit, onerror_callback) { 2468 2469 // This is made specifically for uploading files. 2470 // <file> can be a file object or a blob. 2471 // This is a wrapper around XMLHttpRequest that will retry the request 2472 // if it fails. It will also retry if the request times out. 2473 // It will retry until the timelimit is reached. 2474 // It will retry with exponential backoff. 2475 // If the request fails with a 403, it will throw an exception. 2476 // If the timelimit is reached, it will throw an exception. 2477 2478 let delay; 2479 let request = new XMLHttpRequest(); 2480 2481 request.addEventListener('loadend', function (event) { 2482 if (request.status < 200 || request.status >= 300) { 2483 if (request.status == 403) { 2484 // If it's a bad url, we don't want to retry. 2485 onerror_callback(); 2486 } 2487 openAndSend(request) 2488 } else { 2489 if (success_callback) { 2490 success_callback(request) 2491 } 2492 return request; 2493 } 2494 }); 2495 2496 for (let e in upload_event_action_map) { 2497 request.upload.addEventListener(e, upload_event_action_map[e]); 2498 } 2499 2500 function openAndSend(request) { 2501 if (timelimit <= 0) { 2502 onerror_callback(); 2503 } else { 2504 if (delay === undefined) {
2505 delay = 0; 2506 } else if (delay === 0) { 2507 // delay = Math.random() * 500; 2508 delay = 500; 2509 } else { 2510 delay = delay * 2; 2511 } 2512 2513 setTimeout(function() { 2514 timelimit = timelimit - delay; 2515 request.open('PUT', url, true); 2516 request.send(file); 2517 }, delay); 2518 } 2519 2520 } 2521 openAndSend(request) 2522} 2523 2524 2525function _______FUNCTIONS_CALLED_ON_READY______() {} 2526 2527// Handle 'return' key with care: explicitly click the first button after the 2528// focussed element. 2529$(function() { 2530 //$(document).keypress(function(event) { 2531 $(document).keypress(function(event) { 2532 if (event.keyCode == 13) { 2533 if (event && event.target && event.target.type && event.target.type == 'textarea') { 2534 return true // Allow return character in textareas. 2535 } 2536 if (event && event.target && $(event.target).hasClass("no_submit")) { 2537 // target specifies that we stop form submission 2538 event.cancelBubble = true; 2539 event.returnValue = false; 2540 // but we won't return false yet... we might still click a button 2541 } 2542 if ($.browser.msie && parseFloat($.browser.version) < 8) { 2543 // Turn off all return-key form submission for old versions of IE. 2544 event.cancelBubble = true; 2545 event.returnValue = false; 2546 return false; 2547 } 2548 if (!$(event.target).is(":input")) { 2549 // Only pay attention to return keys when form elements are in focus. 2550 event.cancelBubble = true; 2551 event.returnValue = false; 2552 return false; 2553 } 2554 clicked = false 2555 focus_el = $(document.activeElement) 2556 if (focus_el == $('body')) { alert('body in focus') } 2557 // Loop through focus_el and all its ancestor elements. 2558 anc = $($.makeArray(focus_el.add(focus_el.parents())).reverse()) 2559 anc = anc.slice(0, anc.length-2) // Ignore whole-page focus case. 2560 anc.each(function(index, element) { 2561 // If an ancestor has the no_submit class, don't keep looking. 2562 // Buttons contained in the ancestor with the no_submit class 2563 // will still be submitted. 2564 if ($(element).hasClass("no_submit")) { 2565 event.cancelBubble = true; 2566 event.returnValue = false; 2567 return false; 2568 } 2569 // Loop through 'this' and all of its later siblings. 2570 $(element).add($(element).nextAll()).each(function(index2, el2) { 2571 submits = $(el2).find(':submit, :button').filter(':visible') 2572 if (submits.length) { 2573 submits.first().focus().click() 2574 clicked = true 2575 return false; // Stop the 'each' loop. 2576 } 2577 }) 2578 if (clicked) { 2579 return false; // Stop the 'each' loop. 2580 } 2581 }) 2582 // The above didn't find a button; don't submit anything. 2583 event.cancelBubble = true; 2584 event.returnValue = false; 2585 return false; 2586 } 2587 }); 2588}); 2589 2590// Allow tabs in textareas with the 'll_tabbable' class 2591$(function() { 2592 $(document).delegate('.ll_tabbable textarea', 'keydown', function(e) { 2593 var keyCode = e.keyCode || e.which; 2594 2595 if (keyCode == 9) { 2596 e.preventDefault(); 2597 var start = $(this).get(0).selectionStart; 2598 var end = $(this).get(0).selectionEnd; 2599 2600 // set textarea value to: text before caret + tab + text after caret 2601 $(this).val($(this).val().substring(0, start) 2602 + " " 2603 + $(this).val().substring(end)); 2604 2605 // put caret at right position again 2606 $(this).get(0).selectionStart = 2607 $(this).get(0).selectionEnd = start + 4; 2608 } 2609 }); 2610}) 2611 2612 2613// When a form is submitted start a timer for 10 seconds during which form resubmission is disallowed. 2614// Better: get confirmation and skip the timer. 2615$(function() { 2616 let allow_form_submission = true; 2617 let skip_all_checks = false;
2618 2619 2620 // function start_allow_countdown() { 2621 // setTimeout( 2622 // function() { 2623 // allow_form_submission = true; 2624 // }, 2625 // 10000 2626 // ); 2627 // } 2628 2629 $(document).on('submit', '#thisForm', function(e) { 2630 // TODO: Update this to respond to any form submission. 2631 // $(document).on('submit', 'form', function(e) { 2632 2633 // We set this to true on the callback from the colorbox modal, so that we don't 2634 // get stuck in a continuous loop after programatically clicking the submit button. 2635 // We're clicking the submit button rather than calling submit() directly because 2636 // the submit behavior depends on the parameters of the <input> button that was clicked. 2637 if (skip_all_checks) { 2638 skip_all_checks = false; 2639 return true; 2640 } 2641 2642 // Block form submission if the clicked button is a blocking button 2643 // and errors are detected in the page. 2644 2645 // Get the clicked button 2646 let clicked_button = $(e.originalEvent.submitter); 2647 2648 let do_block = false; 2649 let do_submit = false; 2650 2651 for (let blocking_handler of BLOCKING_FORM_HANDLERS) { 2652 if(blocking_handler.block_submit_event_if_errors(clicked_button)) { 2653 do_block = true; 2654 } 2655 } 2656 2657 if (do_block) { 2658 e.preventDefault(); 2659 return false; 2660 } 2661 2662 // Allow skipping this confirmation for some buttons. 2663 // The __skip_multi_click_check flag is an optional global setting from sr_cgi. 2664 // TODO: We CAN, in fact, get the button that was clicked to trigger this, so 2665 // this could be transitioned to a button-specific behavior. 2666 2667 if (window.__skip_multi_click_check) { 2668 window.__skip_multi_click_check = false; 2669 do_submit = true; 2670 } else if (allow_form_submission) { 2671 allow_form_submission = false; 2672 do_submit = true; 2673 } else { 2674 // If we reached here, we detected that the user is trying to re-submit the form. 2675 // Present them a confirmation dialog to ask if they really want to do this. 2676 do_submit = confirm('Your data is being processed; are you sure you want to re-submit?'); 2677 } 2678 2679 if (do_submit) { 2680 let showed_modal = show_submit_waiting_modal_if_has_def( 2681 clicked_button, 2682 { 2683 onComplete: function() { 2684 // Re-click the clicked_button to resubmit the form, but pass 2685 // a flag so that the event handler immediately returns true. 2686 // We're needing to re-click the button for Safari. If instead we return true; 2687 // directly, Safari will stop processing the modal dialog before it's 2688 // finished displaying. 2689 skip_all_checks = true; 2690 clicked_button.click(); 2691 }, 2692 } 2693 ); 2694 if (showed_modal) { 2695 // We're preventing the first click from registering so that clicked_button.click() is 2696 // used instead. 2697 e.preventDefault(); 2698 return false; 2699 } else { 2700 return true; 2701 } 2702 2703 } else { 2704 e.preventDefault(); 2705 return false; 2706 } 2707 }); 2708}); 2709 2710 2711// Process any elements that should be displayed using a markdown interpreter 2712$(function () { 2713 $('.markdown_display').each(process_markdown); 2714 $('.monospace').each(process_monospace); 2715}) 2716 2717// $(function() { 2718// show_touch_elements_for_touch_devices(); 2719// }); 2720 2721 2722function ________COMBOBOX_STUFF________() {} 2723 2724$(function(){ 2725 // Improve the click listener on the combo-box widget to focus on the text-area when it is clicked anywhere. 2726 $(document).on('click', '.jqx-combobox-content', function(event_ob) { 2727 var target = $(event_ob.target); 2728 var ta = target.find('input[type=textarea]'); 2729 if (ta.length > 0) { 2730 ta[0].focus(); 2731 } 2732 }) 2733 2734 // Set up the checkbox version of combo-boxes to open when you click on the whitespace too. 2735 $(document).on('click', '.jqx_checkbox', function(event_ob) { 2736 var target = $(event_ob.target); 2737 var jqx_ob = $(target[0]).parents('.jqx_checkbox'); 2738 if (jqx_ob.jqxComboBox('isOpened')) { 2739 jqx_ob.jqxComboBox('close'); 2740 } else { 2741 jqx_ob.jqxComboBox('open'); 2742 } 2743 }) 2744 2745 // Focus back on the textarea when something changes. 2746 // This is causing trouble in Chrome, when two comboboxes have contents it won't let anything else have focus. 2747 // $(document).on('select unselect', '.jqx_multiselect', function(event) { 2748 // $(event.target).focus(); 2749 // }) 2750 2751 // $('.jqx_detector').on('open', function(event) { 2752 // $(event.target).focus(); 2753 // }) 2754 2755 // $(window).on('keydown', function(event) { 2756 // if(event.keyCode == 8 & $(event.target).hasClass('jqx-combobox-input')) { 2757 // $(event.target).focus(); 2758 // } 2759 // }); 2760 2761 window.setInterval(init_combo_boxes, 500); 2762 2763}) 2764 2765function init_combo_boxes() { 2766 // Comboboxes can't be initialized while they're hidden. 2767 $('.jqx_detector:visible').each(function(i, el) { 2768 var cbox = $(el); 2769 // console.log(cbox) 2770 // This could be jqx-widget 2771 if (!cbox.hasClass('jqx-combobox')) { 2772 var cbox_id = cbox.attr('id'); 2773 var init_func = 'init_combo_box_' + cbox_id + '()'; 2774 // This gets called sometimes when there isn't an init_func. 2775 // eval(init_func); 2776 try { 2777 eval(init_func); 2778 } 2779 catch(err) {} 2780 } 2781 }) 2782} 2783 2784// var combo_box_inits = [] 2785// 2786// function init_combo_boxes() { 2787// var i = 0 2788// for (i = 0; i < combo_box_inits.length; i++) { 2789// combo_box_inits[i](); 2790// } 2791// // reset it for next time. 2792// combo_box_inits = [] 2793// } 2794 2795 2796function build_combo_box_data(disp_vals, val_vals) { 2797 var data = new Array(); 2798 var k = 0; 2799 for (var i = 0; i < disp_vals.length; i++) { 2800 var row = {}; 2801 row["disp"] = disp_vals[k]; 2802 row["val"] = val_vals[k]; 2803 data[i] = row; 2804 k++; 2805 } 2806 var source = 2807 { 2808 localdata: data, 2809 datatype: "array" 2810 }; 2811 var dataAdapter = new $.jqx.dataAdapter(source); 2812 2813 return dataAdapter; 2814} 2815 2816 2817function _______TO_DO_LIST_______() {} 2818 2819function show_hide_no_to_dos_rows() { 2820 var rows = $('#to_do_table tr:visible'); 2821 if (rows.length > 0) { 2822 $('#no_to_dos_row').fadeOut(); 2823 } else { 2824 $('#no_to_dos_row').fadeIn(); 2825 } 2826 var done_rows = $('#finished_table .to_do_item'); 2827 // > 1 here because there's the TH row. 2828 if (done_rows.length > 0) { 2829 $('#no_done_to_dos_row').fadeOut(); 2830 $('#finished_table_header').fadeIn(); 2831 } else { 2832 $('#finished_table_header').fadeOut(); 2833 $('#no_done_to_dos_row').fadeIn(); 2834 } 2835} 2836 2837 2838function update_to_do_alert() { 2839 2840 var to_do_alert = $('#to_do_alert'); 2841 var new_num = $('#to_do_table .to_do_item').length; 2842 if (new_num > 0) { 2843 var alert_span = $('#to_do_alert_num'); 2844 to_do_alert.fadeIn(); // This may or may not be hidden. 2845 alert_span.html(new_num); 2846 } else { 2847 to_do_alert.fadeOut(); 2848 } 2849 2850} 2851 2852function check_to_do_item(event, check_url, uncheck_url) { 2853 var cb = $(event.target); 2854 if (!cb.hasClass('to_do_cb')) { 2855 cb = $(cb.parent()); 2856 } 2857 var to_do_id = cb.attr('to_do_id'); 2858 var row = cb.parents('.to_do_item'); 2859 2860 // someone clicked something where we couldn't find an id - just bail. 2861 if (typeof to_do_id == "undefined") { 2862 return 2863 } 2864 2865 // Figure out whether it's being checked off or unchecked. 2866// if (cb.attr('checked')) { 2867 if (cb.attr('status') == 'unchecked') { 2868 row.css('color', 'gray'); 2869 var button = 'button_' + check_url; 2870 var success_func = function(cb_) { 2871 // var row = cb_.parents('.to_do_item'); 2872 row.fadeOut(500, function() { 2873 row.find('.disp_ts').html('just now'); 2874 row.css('color', 'gray'); 2875 var dest_table = $('#finished_table'); 2876 if (dest_table.length > 0) { 2877 // This is when we're in the to-do page. 2878 dest_table.find('#finished_table_header').after(row); 2879 row.fadeIn(500); 2880 } else { 2881 // This is when we're just in the to-do box. 2882 row.remove(); 2883 } 2884 show_hide_no_to_dos_rows(); 2885 update_to_do_alert(); 2886 }); 2887 } 2888 cb.attr('status', 'checked'); 2889 cb.find('.action_verb').html('restore'); 2890 } else { 2891 row.css('color', 'black'); 2892 var button = 'button_' + uncheck_url; 2893 var success_func = function(cb_) { 2894 // var row = cb_.parents('.to_do_item'); 2895 row.fadeOut(500, function() { 2896 row.find('.disp_ts').html(''); 2897 row.css('color', 'black'); 2898 var dest_table = $('#to_do_table') 2899 dest_table.find('tr:first').before(row); 2900 show_hide_no_to_dos_rows(); 2901 row.fadeIn(500); 2902 update_to_do_alert(); 2903 }); 2904 } 2905 cb.attr('status', 'unchecked'); 2906 cb.find('.action_verb').html('hide item'); 2907 } 2908 2909 // Gather args. 2910 var data = { 2911 'uniq_param': (new Date()).getTime(), // This prevents IE from caching. 2912 'do_not_print': 'True', // Not sure if this is needed. 2913 'to_do_id': to_do_id, 2914 } 2915 data[button] = 'Submit' 2916 2917 // Encode arguments. 2918 data = jQuery.param(data); 2919 2920 // show_updating() 2921 2922 // Make request 2923 return $.ajax({ 2924 url: ".", 2925 type: "POST", 2926 cache: false, 2927 data: data 2928 }).done(function(return_data, textStatus, jqXHR) { 2929 // var return_data = jQuery.parseJSON(return_data); 2930 var return_data = decode_return_data(return_data); 2931 2932 if (return_data.response == 'success') { 2933 success_func(cb); 2934 } else { 2935 $.colorbox({ 2936 html: return_data.message, 2937 overlayClose: false, 2938 trapFocus: true,
2939 closeButton: false, 2940 opacity: .3, 2941 className: 'modal_form_colorbox', 2942 onComplete: setup_info_links 2943 }) 2944 return 2945 } 2946 2947 }) 2948 2949} 2950 2951 2952function _______OTHER______() {} 2953 2954 2955function check_allow_other(other_field_name) { 2956 2957 // This disables the other field when the other checkbox isn't checked in checkbox widgets. 2958 2959 var checkbox = $("input[value='"+other_field_name+"']") 2960 var text_field = $("input[name='"+other_field_name+"']") 2961 2962 if (checkbox.is(':checked')) { 2963 text_field.removeAttr('disabled') 2964 } else { 2965 text_field.attr('disabled', 'disabled') 2966 text_field.val('') 2967 } 2968} 2969 2970 2971function setup_check_select_other(select_name, other_name) { 2972 2973 var s_selector = '[name="' + select_name + '"]' 2974 var s = $(s_selector) 2975 var t = $('[name="' + other_name + '"]') 2976 2977 check_select_other(s, t) 2978 $(document).on('change', s_selector, function () { 2979 check_select_other(s, t) 2980 }) 2981} 2982 2983 2984function check_select_other(s, t) { 2985 2986 if (s.val()) { 2987 t.val('') 2988 t.attr('disabled', 'disabled') 2989 } else { 2990 t.removeAttr('disabled') 2991 } 2992 2993} 2994 2995 2996function import_reservation(date_id, start_hour, start_min, end_hour, end_min, room) { 2997 2998 /* This is for the prog_pour. It moves defaults for a space 2999 reservation into a session's info. */ 3000 3001 $('[name="date"]').val(date_id) 3002 $('[name="start_hour"]').val(start_hour) 3003 $('[name="start_min"]').val(start_min) 3004 $('[name="end_hour"]').val(end_hour) 3005 $('[name="end_min"]').val(end_min) 3006 $('[name="room"]').val(room) 3007 $('[name="time"]').val('') 3008} 3009 3010 3011function set_nat_width(image_ob) { 3012 3013 // This should only be called on the load event of image_ob. Otherwise the width will be 0. 3014 // Sets the property 'nat_width' for <image_ob>. This is used to make a good min-width and max for resizing. 3015 // This could be expanded to set <nat_height> and reset min-height too. 3016 3017 alert('set_nat_width is not working, do not use it') 3018 3019 var off_screen_image = new Image(); 3020 off_screen_image.src = image_ob.attr("src"); 3021 $(off_screen_image).on('load', function() { 3022 image_ob.attr('nat_width', off_screen_image.width) 3023 reset_min_width(image_ob) 3024 }) 3025} 3026 3027 3028function reset_min_width(image_ob) { 3029 3030 // resets the default min-width for image_ob to the natural size of the image if it is smaller than the min-width. 3031 3032 alert('reset_min_width is not working, do not use it') 3033 3034 var min = image_ob.css('min-width') 3035 var min_num = min.slice(0, min.indexOf('px')) 3036 var real_size = image_ob.attr('nat_width') 3037 if (parseInt(min_num) > parseInt(real_size)) { 3038 image_ob.css('min-width', real_size + 'px') 3039 $(window).resize() // Trigger a resize to redraw the image. 3040 } 3041} 3042 3043 3044 3045function _______USER_GROUPING_IFRAME_WIDGET______() {} 3046 3047 3048 3049function _____GENERAL_HELPER_FUNCTIONS_________() {} 3050 3051 3052function get_input_field_type(field) { 3053 // Find the input type of a jquery object <field>. 3054 // can be radio, text, checkbox, textarea, select 3055 3056 if (field.length < 1) { 3057 console.error('get_input_field_type: field not found'); 3058 return ''; 3059 } 3060 3061 type = field.attr('type') 3062 if (type) { 3063 type = type.toLowerCase() // For 'radio', 'text', 'checkbox', 'file', 'password' 3064 } else if (jQuery.inArray(type, ['text', 'checkbox', 'radio', 'file']) == -1) { 3065 type = field.get(0).nodeName.toLowerCase() // For 'textarea', 'select' 3066 } 3067 return type 3068} 3069 3070 3071function get_input_field_first_opt(field) { 3072 3073 // Get the first non-"" option of a select, radio, or checkbox field. 3074 3075 // for radios / checkboxes, field will be a list of inputs. 3076 3077 let type = get_input_field_type(field); 3078 if (type == 'select') { 3079 let val = ''; 3080 jQuery(field.find('option')).each(function (index, opt) { 3081 if (opt.value != '') { 3082 val = opt.value; 3083 return false; 3084 } 3085 }); 3086 return val; 3087 } else if (type == 'radio') { 3088 return jQuery(field.first()).val(); 3089 } else if (type == 'checkbox') { 3090 return field.first().val(); 3091 } else { 3092 console.error('get_input_field_first_opt: field type not implented'); 3093 } 3094} 3095 3096 3097function tanslate_seconds_to_min_secs(seconds) { 3098 var mins = Math.floor(seconds / 60); 3099 var secs = seconds - (mins * 60); 3100 3101 // if(mins.toString().length == 1) mins = '0' + mins; 3102 // if(secs.toString().length == 1) secs = '0' + secs; 3103 3104 // return mins + ':' + secs; 3105 // return mins + ':' + secs + ' min'; 3106 return mins + 'min ' + secs + 's'; 3107} 3108 3109 3110function _____REQUIRED_INPUT_FIELD________() {} 3111 3112 3113var setup_required_input_field_checks_has_been_called = false;
3114function setup_required_input_field_checks() { 3115 3116 // This only needs to be called once per page. 3117 if (setup_required_input_field_checks_has_been_called) { 3118 return false; 3119 } 3120 setup_required_input_field_checks_has_been_called = true; 3121 3122 // tinymce.on( 'AddEditor', function(ed) { 3123 // console.log(ed) 3124 // ed.on('keyup', function (e) { 3125 // console.log('yo') 3126 // check_required_input_filled(e) 3127 // }) 3128 // }) 3129 3130 jQuery(document).ready(function () { 3131 // setup events. 3132 jQuery(document).on('change blur focus', 'input, select, textarea, .jqx_detector', function () { 3133 check_required_input_filled(jQuery(this)); 3134 }) 3135 3136 jQuery(document).on('keyup', 'input, textarea', function () { 3137 check_required_input_filled(jQuery(this)); 3138 }) 3139 3140 $('input, select, textarea, .jqx_detector').each(function (index, el) { 3141 // If this isn't an input in a multi-block template then check it now. 3142 if ($(this).attr('name') && $(this).attr('name').indexOf('%%') == -1) { 3143 check_required_input_filled($(this)); 3144 } 3145 }) 3146 3147 BLOCKING_FORM_HANDLERS = setup_blocking_form_handlers(); 3148 }); 3149} 3150 3151 3152function check_required_input_filled(caller_field) { 3153 3154 // This checks to see whether <caller_field>, which is required in some way, has a value (so requirement is filled). 3155 3156 // Make sure this isn't a multi-field template 3157 if (!$(caller_field).attr('name') || $(caller_field).attr('name').indexOf('%%') != -1) {return} 3158 3159 var values = get_field_values(caller_field) 3160 3161 // straight-forward label 3162 var label_ob = jQuery("label[for='" + caller_field.attr('id') + "']") 3163 3164 // This may be an 'other' field. 3165 var caller_id = caller_field.attr('id') 3166 if (caller_id && caller_id.indexOf('_other') != -1) { 3167 orig_id = caller_id.substring(0, caller_id.length - 6) 3168 label_ob = $("label[for='" + orig_id + "']") 3169 } 3170 3171 // check for checkbox label first - allow both labels and spans 3172 // var checkboxes_label_ob = jQuery("label[cbs_for='" + caller_field.attr('class') + "']") 3173 var checkboxes_label_ob = jQuery("[cbs_for='" + caller_field.attr('class') + "']") 3174 if (checkboxes_label_ob.length > 0) { 3175 label_ob = checkboxes_label_ob 3176 } else if (label_ob.length == 0) { 3177 label_ob = jQuery("label[for='" + caller_field.attr('name') + "']") 3178 } 3179 3180 // Change classes as needed. 3181 // if (values.length > 0) { 3182 if (values[0] != '') { 3183 label_ob.removeClass('required_if_font') 3184 // label_ob.children().removeClass('error_font') 3185 } else if (label_ob.hasClass('required_flag')) { 3186 label_ob.addClass('required_if_font') 3187 } 3188} 3189 3190 3191function check_fields_dependant_on(input_label) { 3192 3193 // <input_label> is a label for an input object. 3194 // This figures out what it is and calls check_required_input_filled on the appropriate dom element. 3195 3196 dependant_input = input_label.attr('cbs_for') 3197 3198 if (typeof dependant_input == 'undefined') { 3199 dependant_input = input_label.attr('for') 3200 var dependant_input_objects = jQuery('#' + dependant_input) 3201 if (dependant_input_objects.length != 0) { 3202 // Do we need this? 3203 // Include the other field in this. I'm assuming there won't be any inputs that are only other fields. 3204 // $.merge(dependant_input_objects, jQuery('#' + dependant_input + '_other')) 3205 3206 jQuery.each(dependant_input_objects, function (index, element) { 3207 check_required_input_filled(jQuery(element)) 3208 }) 3209 return false 3210 // return true 3211 } 3212 } 3213 3214 // <input_label> is the label for a collection of checkboxes. 3215 var dependant_input_objects = jQuery('.' + dependant_input) 3216 $.merge(dependant_input_objects, $('.' + dependant_input + "_other")) 3217 if (dependant_input_objects.length != 0) { 3218 jQuery.each(dependant_input_objects, function (index, element) { 3219 check_required_input_filled(jQuery(element)) 3220 }) 3221 } 3222} 3223 3224 3225function get_field_values(field_ob) { 3226 // Returns the values of <field_ob>. 3227 // Returns [''] if no value is found (we want an empty input field to be reliably testable). 3228 3229 if (field_ob.length == 0) { 3230 return [''] 3231 } 3232 3233 var values = [] 3234 if (field_ob.hasClass('jqx-combobox')) { 3235 // Two cases: multi or single. 3236 if (field_ob.jqxComboBox('multiSelect')) { 3237 // This returns an array of objects. 3238 var values_ = field_ob.jqxComboBox('getSelectedItems'); 3239 if (values_.length > 0) { 3240 for (var i=0; i<values_.length; i++) { 3241 val_ob = values_[i]; 3242 if (typeof(val_ob) == 'object') { 3243 values.push(val_ob.value); 3244 } 3245 } 3246 } 3247 } else { 3248 var value_ = field_ob.jqxComboBox('getSelectedItem'); 3249 if (value_ != undefined) { 3250 values.push(value_.value) 3251 } 3252 } 3253 } else { 3254 // Find the type of field_ob 3255 var type = get_input_field_type(field_ob) 3256 // console.log('type', type) 3257 3258 // Find the current value(s) for <field_ob>. 3259 if (jQuery.inArray(type, ['text', 'textarea', 'hidden', 'file', 'password']) != -1) { 3260 var file_field_name = field_ob.attr("name") + '_infn' 3261 var file_field_ob = jQuery("[name='"+file_field_name+"']") 3262 3263 // console.log('triggered') 3264 // var field_id = field_ob.attr('id') 3265 // var tinymce_ob = tinyMCE.get(field_id) 3266 // if (field_id && typeof(tinymce_ob) != 'undefined') { 3267 // values = tinymce.get(field_id).getContent() || [''] 3268 // console.log(values) 3269 // } else { 3270 // values = field_ob.val() || file_field_ob.val() || [''] 3271 // } 3272 3273 values = field_ob.val() || file_field_ob.val() || [''] 3274 3275 } else if (type == 'select') { 3276 var selected = field_ob.find('option:selected') 3277 // This version only gets one value (I believe). 3278 // values = selected.val() || [] 3279 selected.each(function(i) { 3280 var value = jQuery(this).val() 3281 if (value != '') { 3282 values[i] = value 3283 } 3284 }) 3285 3286 // If we couldn't find a value in the select list, see if there's something in the 'other' text field. 3287 if (values.length == 0) { 3288 select_id = field_ob.attr('id') 3289 other_id = select_id + '_other' 3290 other_ob = $('#' + other_id) 3291 if (other_ob.length > 0 && other_ob.val()) { 3292 values.push(other_ob.val()) 3293 } 3294 } 3295 } else if (jQuery.inArray(type, ['checkbox', 'radio']) != -1) { 3296 var name = field_ob.attr("name") 3297 var checked = jQuery("input[name='"+name+"']:checked") 3298 // values = checked.val() || [] // returns the first matched elements value 3299 // This version gets all the values. 3300 checked.each(function(i) {values[i] = jQuery(this).val()}) || [''] 3301 } 3302 } 3303 3304 if (!jQuery.isArray(values)) { 3305 values = [values] 3306 } 3307 3308 if (values.length == 0) { values = [''] } 3309 3310 return values 3311 3312 // // It's ok to count the 'other' checkbox as having a value when its checked even if the text is empty. 3313 // // Checkboxes use a particular pattern for the 'other' checkbox, we want to get the real value, not the convention. 3314 // var new_values = [] 3315 // for (var i=0; i<values.length; i++) { 3316 // var val_str = values[i] 3317 // if (val_str.match("_other$")) { 3318 // var new_val = $('[name="' + val_str + '"]').val() 3319 // if (new_val) {new_values.push(new_val)} 3320 // } else { 3321 // new_values.push(val_str) 3322 // } 3323 // } 3324 // console.log(field_ob, new_values) 3325 // return new_values 3326} 3327 3328 3329function set_field_val(field_ob, val) { 3330 // Sets the selected value of <field_ob> to <val>. 3331 3332 if (field_ob.hasClass('jqx-combobox')) { 3333 console.error('combobox Not Implemented for set_field_val()'); 3334 } else { 3335 // Find the type of field_ob 3336 var type = get_input_field_type(field_ob) 3337 3338 if (jQuery.inArray(type, ['text', 'textarea', 'hidden', 'file', 'password']) != -1) { 3339 var file_field_name = field_ob.attr("name") + '_infn' 3340 var file_field_ob = jQuery("[name='" + file_field_name + "']") 3341 3342 field_ob.val(val); 3343 if (file_field_ob.length > 0) { 3344 file_field_ob.val(val); 3345 } 3346 3347 } else if (type == 'select') { 3348 field_ob.val(val); 3349 3350 // TODO - improve this to set the other field if we can't find the value in a select. 3351 3352 // // If we couldn't find a value in the select list, see if there's something in the 'other' text field. 3353 // if (values.length == 0) { 3354 // select_id = field_ob.attr('id') 3355 // other_id = select_id + '_other' 3356 // other_ob = $('#' + other_id) 3357 // if (other_ob.length > 0 && other_ob.val()) { 3358 // values.push(other_ob.val()) 3359 // } 3360 // } 3361 } else if (jQuery.inArray(type, ['checkbox', 'radio']) != -1) { 3362 var name = field_ob.attr("name") 3363 var checked = jQuery("input[name='" + name + "'][value='" + val + "']").attr('checked', true) 3364 } 3365 } 3366 3367} 3368 3369 3370function setup_check_required(label_selector, dependent_field_id) { 3371
3372 // This does the basic is required check. 3373 3374 var label_ob = jQuery(label_selector) 3375 label_ob.addClass('required_if_font required_flag') 3376 check_fields_dependant_on(jQuery('#' + dependent_field_id)) 3377} 3378 3379 3380function check_req_if(field_ob, label_selector, vals, dependent_field_id) { 3381 3382 // This checks the value in <field_ob> against <vals>, and if it is in <vals>, turns <label_selector> red to indicate it is now required. 3383 // <label_selector> is the label of the dependent field. 3384 // <dependent_field_id> is the id of the field required if <field_ob>.val() in <vals>. In the case of checkboxes it is the id of the field's label. 3385 3386 // Get the values of the field object 3387 var values = get_field_values(field_ob); 3388 3389 var found = false; 3390 var label_ob = jQuery(label_selector); 3391 jQuery.each(vals, function(i1, v1) { 3392 jQuery.each(values, function(i2, v2) { 3393 if (v1 == v2) { 3394 label_ob.addClass('required_if_font required_flag') 3395 found = true 3396 return false // This tells .each() to stop (like break). 3397 } 3398 }) 3399 if (found) return false // Break out of outer loop. 3400 }) 3401 if (!found) { 3402 label_ob.removeClass('required_if_font required_flag') 3403 } 3404 3405 // If this is required but filled we don't want to make it red 3406 check_fields_dependant_on(jQuery('#' + dependent_field_id)) 3407} 3408 3409 3410function get_other_field_selector(field_ob) { 3411 3412 // If <field_ob> has an attached 'other' field than this returns a selector string to get it. 3413 3414 var field_id = field_ob.attr('id') 3415 var other_id = field_id + '_other' 3416 other_ob = $('#' + other_id) 3417 if (other_ob.length == 1) { 3418 return '#' + other_id 3419 } else { 3420 return get_other_field_selector_by_name(field_ob) 3421 } 3422} 3423 3424function get_other_field_selector_by_name(field_ob) 3425{ 3426 3427 // If <field_ob> has an attached 'other' field than this returns a selector string to get it. 3428 3429 var field_name = field_ob.attr('name') 3430 var other_name = field_name + '_other' 3431 other_ob = $('[name="' + other_name + '"]') 3432 if (other_ob.length == 1) { 3433 return '[name="' + other_name + '"]' 3434 } else { 3435 return false 3436 } 3437} 3438 3439 3440function setup_other_check_req_if(field_selector, label_selector, vals, dependent_field_id) { 3441 3442 // This sets up the check_req_if for the 'other' field case. It doesn't 3443 // need to deal with the multi vs. plain trouble because that is dealt with in the caller. 3444 3445 var field_ob = $(field_selector) 3446 // // If there is an 'other' field, get that selector and set that up too. 3447 // var other_selector = get_other_field_selector(field_ob) 3448 // if (other_selector) {setup_check_req_if(other_selector, label_selector, vals, dependent_field_id)} 3449 3450 // var type = get_input_field_type(field_ob) 3451 3452 // if (jQuery.inArray(type, ['text', 'textarea']) != -1) { 3453 // $(document).on('keyup', field_selector, 3454 // function () { 3455 // check_req_if(field_ob, label_selector, vals, dependent_field_id) 3456 // } 3457 // ) 3458 // } 3459 // $(document).on('change', field_selector, 3460 // function () { 3461 // check_req_if(field_ob, label_selector, vals, dependent_field_id) 3462 // } 3463 // ) 3464 3465 $(document).on('keyup change', field_selector, 3466 function () { 3467 check_req_if(field_ob, label_selector, vals, dependent_field_id) 3468 } 3469 ) 3470 3471 check_req_if(field_ob, label_selector, vals, dependent_field_id) 3472} 3473 3474 3475function setup_check_req_if(field, label_selector, vals, dependent_field_id, multi_suffix) { 3476 3477 field_selector = "[name='" + field + "']" 3478 3479 var field_ob = $(field_selector) 3480 // If we haven't found the trigger field try the multi case. 3481 if (field_ob.length == 0) { 3482 field_selector = "[name='" + field + multi_suffix + "']" 3483 field_ob = $(field_selector) 3484 } 3485 3486 // If there is an 'other' field, get that selector and set that up too. 3487 var other_selector = get_other_field_selector(field_ob) 3488 if (other_selector) {setup_other_check_req_if(other_selector, label_selector, vals, dependent_field_id)} 3489 3490 // var type = get_input_field_type(field_ob) 3491
3492 // if (jQuery.inArray(type, ['text', 'textarea']) != -1) { 3493 // $(document).on('keyup', field_selector, 3494 // function () { 3495 // check_req_if(field_ob, label_selector, vals, dependent_field_id) 3496 // } 3497 // ) 3498 // } 3499 // $(document).on('change', field_selector, 3500 // function () { 3501 // check_req_if(field_ob, label_selector, vals, dependent_field_id) 3502 // } 3503 // ) 3504 3505 $(document).on('keyup change', field_selector, 3506 function () { 3507 check_req_if(field_ob, label_selector, vals, dependent_field_id) 3508 } 3509 ) 3510 3511 check_req_if(field_ob, label_selector, vals, dependent_field_id) 3512} 3513 3514 3515function check_req_if_not(field_ob, label_selector, vals, dependent_field_id) { 3516 3517 // This checks the value in <field_ob> against <vals>, and if it is in <vals>, turns <label_selector> black to indicate it is no longer required. 3518 // <label_selector> is the label of the dependent field. 3519 // <dependent_field_id> is the id of the field required if <field_ob>.val() not in <vals>. In the case of checkboxes it is the id of the field's label. 3520 3521 // Get the values of the field object 3522 var values = get_field_values(field_ob) 3523 var found = false 3524 var label_ob = jQuery(label_selector) 3525 jQuery.each(vals, function(i1, v1) { 3526 jQuery.each(values, function(i2, v2) { 3527 if (v1 == v2) { 3528 found = true 3529 return false // This tells .each() to stop (like break). 3530 } 3531 }) 3532 if (found) return false // Break out of outer loop. 3533 }) 3534 if (found) { 3535 label_ob.removeClass('required_if_font required_flag') 3536 } else { 3537 label_ob.addClass('required_if_font required_flag') 3538 } 3539 3540 // If this is required but filled we don't want to make it red 3541 check_fields_dependant_on(jQuery('#' + dependent_field_id)) 3542 3543} 3544 3545 3546function setup_other_check_req_if_not(field_selector, label_selector, vals, dependent_field_id) { 3547 3548 // This sets up the check_req_if_not for the 'other' field case. It doesn't 3549 // need to deal with the multi vs. plain trouble because that is dealt with in the caller. 3550 3551 var field_ob = $(field_selector) 3552 3553 // // If there is an 'other' field, get that selector and set that up too. 3554 // var other_selector = get_other_field_selector(field_ob) 3555 // if (other_selector) {setup_check_req_if_not(other_selector, label_selector, vals, dependent_field_id)} 3556 3557 // var type = get_input_field_type(field_ob) 3558 3559 // if (jQuery.inArray(type, ['text', 'textarea']) != -1) { 3560 // $(document).on('keyup', field_selector, 3561 // function () { 3562 // check_req_if_not(field_ob, label_selector, vals, dependent_field_id) 3563 // } 3564 // ) 3565 // } else { 3566 // $(document).on('change', field_selector, 3567 // function () { 3568 // check_req_if_not(field_ob, label_selector, vals, dependent_field_id) 3569 // } 3570 // ) 3571 // } 3572 3573 $(document).on('keyup change', field_selector, 3574 function () { 3575 check_req_if_not(field_ob, label_selector, vals, dependent_field_id) 3576 } 3577 ) 3578 3579 check_req_if_not(field_ob, label_selector, vals, dependent_field_id) 3580} 3581 3582 3583function setup_check_req_if_not(field, label_selector, vals, dependent_field_id, multi_suffix) { 3584 3585 field_selector = "[name='" + field + "']" 3586 3587 var field_ob = $(field_selector) 3588 // If we haven't found the trigger field try the multi case. 3589 if (field_ob.length == 0) { 3590 field_selector = "[name='" + field + multi_suffix + "']" 3591 field_ob = $(field_selector) 3592 } 3593 3594 // If there is an 'other' field, get that selector and set that up too. 3595 var other_selector = get_other_field_selector(field_ob) 3596 if (other_selector) {setup_other_check_req_if_not(other_selector, label_selector, vals, dependent_field_id)} 3597 3598 // var type = get_input_field_type(field_ob) 3599 3600 // if (jQuery.inArray(type, ['text', 'textarea']) != -1) { 3601 // 3602 // $(document).on('keyup', field_selector, 3603 // function () { 3604 // check_req_if_not(field_ob, label_selector, vals, dependent_field_id) 3605 // } 3606 // ) 3607 // } else { 3608 // $(document).on('change', field_selector, 3609 // function () { 3610 // check_req_if_not(field_ob, label_selector, vals, dependent_field_id) 3611 // } 3612 // ) 3613 // } 3614
3615 // I don't know why we were checking for the type of field above instead of just attaching it to both events. 3616 $(document).on('keyup change', field_selector, 3617 function () { 3618 check_req_if_not(field_ob, label_selector, vals, dependent_field_id) 3619 } 3620 ) 3621 3622 check_req_if_not(field_ob, label_selector, vals, dependent_field_id) 3623} 3624 3625 3626function check_req_if_none(label_selector, dependent_field_id, new_fields) { 3627 3628 // <label_selector> is the label of the dependent field. 3629 // <dependent_field_id> is the id of the field required if <new_fields> don't have any values. In the case of checkboxes it is the id of the field's label. 3630 var found = false 3631 for (var i=0; i<new_fields.length; i++) { 3632 var field = new_fields[i] 3633 3634 // Get the values of the field object 3635 var values = get_field_values(field) 3636 3637 // if (values.length > 0) { 3638 if (values[0] != '') { 3639 found = true 3640 break 3641 } 3642 } 3643 var label_ob = jQuery(label_selector) 3644 if (found) { 3645 label_ob.removeClass('required_if_font required_flag') 3646 } else { 3647 label_ob.addClass('required_if_font required_flag') 3648 } 3649 3650 // If this is required but filled we don't want to make it red 3651 check_fields_dependant_on(jQuery('#' + dependent_field_id)) 3652} 3653 3654 3655function setup_check_req_if_none(fields, label_selector, dependent_field_id) { 3656 3657 // <fields> are the fields that need to be empty for dependent_field_id to be required. 3658 3659 var new_fields = [] 3660 3661 // populate new_fields with jquery objects. 3662 for (var i=0; i<fields.length; i++) { 3663 var new_field_ob = jQuery("[name='"+fields[i]+"']") 3664 // fields includes fields that might not exist depending on whether they are multi-fields or not. Only include fields we can find. 3665 if (new_field_ob.length > 0) { 3666 new_fields.push(new_field_ob) 3667 3668 // If <new_field_ob> has an attached 'other' field, include that in these checks too 3669 var other_selector = get_other_field_selector(new_field_ob) 3670 if (other_selector) {new_fields.push($(other_selector))} 3671 } 3672 } 3673 3674 check_req_if_none(label_selector, dependent_field_id, new_fields) 3675 jQuery.each(new_fields, function(key, val) { 3676 // type = get_input_field_type(val) 3677 // if (jQuery.inArray(type, ['text', 'textarea']) != -1) { 3678 // val.keyup(function() {check_req_if_none(label_selector, dependent_field_id, new_fields)}) 3679 // } else { 3680 // val.change(function() {check_req_if_none(label_selector, dependent_field_id, new_fields)}) 3681 // } 3682 3683 $(document).on('keyup change', val, function() {check_req_if_none(label_selector, dependent_field_id, new_fields)}) 3684 }) 3685} 3686 3687 3688function check_req_if_any(label_selector, dependent_field_id, new_fields) { 3689 3690 // <label_selector> is the label of the dependent field. 3691 // <dependent_field_id> is the id of the field required if any of <new_fields> have a value. In the case of checkboxes it is the id of the field's label. 3692 3693 var found = false 3694 for (var i=0; i<new_fields.length; i++) { 3695 var field = new_fields[i] 3696 3697 // Get the values of the field object 3698 var values = get_field_values(field) 3699 3700 // if (values.length > 0) { 3701 if (values[0] != '') { 3702 found = true 3703 break 3704 } 3705 } 3706 var label_ob = jQuery(label_selector) 3707 if (found) { 3708 label_ob.addClass('required_if_font required_flag') 3709 } else { 3710 label_ob.removeClass('required_if_font required_flag') 3711 } 3712 3713 // If this is required but filled we don't want to make it red 3714 // check_fields_dependant_on(jQuery('#' + dependent_field_id)) 3715 // We are always getting label_ids now: 3716 check_fields_dependant_on(jQuery(label_selector)) 3717} 3718 3719 3720function setup_check_req_if_any(fields, label_selector, dependent_field_id) { 3721 3722 // <fields> are the fields that make dependent_field_id required if any have any value. 3723 3724 var new_fields = []; 3725 3726 // populate new_fields with jquery objects. 3727 for (var i=0; i<fields.length; i++) { 3728 var field_name = fields[i]; 3729 var field_object = jQuery("[name='"+field_name+"']"); 3730 // fields includes fields that might not exist depending on whether they are multi-fields or not. Only include fields we can find. 3731 if (field_object.length > 0) { 3732 new_fields.push(field_object); 3733 3734 // If <field_object> has an attached 'other' field, include that in these checks too 3735 var other_selector = get_other_field_selector(field_object); 3736 if (other_selector) {new_fields.push($(other_selector))}; 3737 } 3738 } 3739 3740 check_req_if_any(label_selector, dependent_field_id, new_fields); 3741 3742 jQuery.each(new_fields, function(key, val) { 3743 // val.change(function() {check_req_if_any(label_selector, dependent_field_id, new_fields)}) 3744 // type = get_input_field_type(val)
3745 // if (jQuery.inArray(type, ['text', 'textarea']) != -1) { 3746 // val.keyup(function() {check_req_if_any(label_selector, dependent_field_id, new_fields)}) 3747 // } 3748 $(document).on('keyup change', val, function() {check_req_if_any(label_selector, dependent_field_id, new_fields)}); 3749 }) 3750} 3751 3752 3753 3754function ______BLOCKING_FIELD_REQUIREMENTS_V2______() {} 3755 3756let BLOCKING_FORM_HANDLERS = []; 3757let blocking_form_handler_id_counter = 0; 3758 3759class BlockingFormHandler { 3760 constructor(form) { 3761 this.form = form; 3762 3763 blocking_form_handler_id_counter++; 3764 this.id = blocking_form_handler_id_counter; 3765 3766 this.blocking_buttons_selector = '.blocking-error-check'; 3767 this.blocking_buttons = this.form.find(this.blocking_buttons_selector); 3768 3769 // Make a unique id for the error message div and span. 3770 this.blocking_error_msg_div_id = 'blocking_error_message_' + this.id; 3771 this.blocking_error_msg_span_id = 'blocking_error_message_contents_' + this.id; 3772 this.blocking_error_msg_div = null; 3773 this.blocking_error_msg_span = null; 3774 } 3775 3776 setup_blocking_error_behavior() { 3777 if (!this.blocking_buttons || this.blocking_buttons.length == 0) { 3778 return; 3779 } 3780 3781 let first_blocking_button = this.blocking_buttons.first(); 3782 let first_blocking_button_div = first_blocking_button.closest('div'); 3783 this.blocking_error_msg_div = jQuery(`<div id="${this.blocking_error_msg_div_id}" class="error_msg_box centered" style="margin:15px;"></div>`); 3784 this.blocking_error_msg_span = jQuery(`<span id="${this.blocking_error_msg_span_id}"></span>`); 3785 this.blocking_error_msg_div.append(this.blocking_error_msg_span); 3786 this.blocking_error_msg_div.insertBefore(first_blocking_button_div); 3787 this.blocking_error_msg_div.hide(); 3788 3789 this.blocking_error_msg_span.on('click', () => { 3790 jump_to_first_form_error_2(this.form); 3791 }); 3792 3793 this.form.on('change blur focus', 'input, select, textarea, .jqx_detector', () => { 3794 // Having to setTimeout = 0 to force this event to be handled last. 3795 // Otherwise, it fires before the $(document).on('change blur focus') events, for some reason, 3796 // even though it is registered after them. 3797 setTimeout(() => { 3798 this.update_blocking_error_message(); 3799 }, 0); 3800 }); 3801 3802 this.form.on('keyup', 'input, textarea', () => { 3803 // Having to setTimeout = 0 to force this event to be handled last. 3804 // Otherwise, it fires before the $(document).on('keydown') events, for some reason, 3805 // even though it is registered after them. 3806 setTimeout(() => { 3807 this.update_blocking_error_message(); 3808 }, 0); 3809 }); 3810 3811 this.update_blocking_error_message(); 3812 } 3813 3814 block_submit_event_if_errors(elem) { 3815 // If no, returns false. 3816 3817 // If this handler has no blocking buttons, ignore. 3818 if (!this.has_blocking_buttons()) { 3819 return false; 3820 } 3821 3822 // Test whether this calling element (button) is in this form. If not, do not block. 3823 if (!this.form.has(elem).length) { 3824 return false; 3825 } 3826 3827 // Test whether this calling element is a blocking button. If not, do not block. 3828 if (!elem.is(this.blocking_buttons_selector)) { 3829 return false; 3830 } 3831 3832 // Get form errors 3833 let active_errors = get_active_form_errors(this.form); 3834 3835 // If there are no unresolved requirements, do not block. 3836 if (!active_errors || active_errors.length == 0) { 3837 return false; 3838 } 3839 3840 // Looks like there is at least one error. 3841 // Flash the error message and return true. 3842 this.flash_blocking_error_message(); 3843 return true; 3844 } 3845 3846 has_blocking_buttons() { 3847 return (this.blocking_buttons || this.blocking_buttons.length > 0); 3848 } 3849 3850 flash_blocking_error_message() { 3851 // this.blocking_error_msg_div.focus(); 3852 jump_to_highlight_obj(this.blocking_error_msg_div); 3853 } 3854 3855 update_blocking_error_message() { 3856 if (!this.has_blocking_buttons()) { 3857 return; 3858 } 3859 3860 // // Get unresolved reqs (required and required_if-type fields) 3861 // let unresolved_reqs = get_unresolved_required_fields(this.form); 3862 3863 /// Get all active form errors 3864 let active_errors = get_active_form_errors(this.form); 3865 3866 // If there are no unresolved requirements, hide the error message. 3867 if (!active_errors || active_errors.length == 0) { 3868 this.blocking_error_msg_div.hide(); 3869 return; 3870 } else { 3871 let error_text = this.build_blocking_error_msg(active_errors.length); 3872 3873 // Set the error message text if it has changed 3874 let current_error_text = this.blocking_error_msg_span.html(); 3875 if (current_error_text != error_text) { 3876 this.blocking_error_msg_span.html(error_text); 3877 } 3878 this.blocking_error_msg_div.show(); 3879 } 3880 } 3881 3882 build_blocking_error_msg(error_count) { 3883 let plural = (error_count > 1) ? 's' : ''; 3884 let error_msg = `Found ${error_count} error${plural}. <a href="javascript:void(0)">Click to see first one</a>.`; 3885 return error_msg; 3886 } 3887} 3888 3889 3890function jump_to_first_form_error_2(form) { 3891 3892 // Jump to the first unresolved required field. 3893 3894 let first_error = get_first_form_error(form); 3895 jump_to_highlight_obj(first_error); 3896} 3897 3898 3899function get_first_form_error(form) { 3900 3901 let active_form_errors = get_active_form_errors(form); 3902 3903 if (!active_form_errors || active_form_errors.length == 0) { 3904 return null; 3905 } 3906 3907 let highest_error = null; 3908 let highest_offset_top = Number.POSITIVE_INFINITY; 3909 3910 active_form_errors.each(function() { 3911 let error = jQuery(this); 3912 let offset_top = error.offset().top; 3913 3914 if (offset_top < highest_offset_top) {
3915 highest_error = error; 3916 highest_offset_top = offset_top; 3917 } 3918 }); 3919 3920 return highest_error; 3921} 3922 3923 3924function get_active_form_errors(form) { 3925 let unresolved_reqs = get_unresolved_required_fields(form); 3926 // TODO: Turn this on once we have tooltips working 3927 // let email_problems = get_email_field_problems(form); 3928 let email_problems = []; 3929 3930 if ( 3931 (!unresolved_reqs || unresolved_reqs.length == 0) 3932 && (!email_problems || email_problems.length == 0) 3933 ) { 3934 return null; 3935 } 3936 3937 // Combine the two lists together and return 3938 let errors = jQuery.merge(unresolved_reqs, email_problems); 3939 return errors; 3940} 3941 3942 3943function form_has_errors(form) { 3944 let errors = get_active_form_errors(form); 3945 if (errors && errors.length > 0) { 3946 return true; 3947 } 3948 return false; 3949} 3950 3951 3952function first_unresolved_required_field(form) { 3953 3954 // Returns the first unresolved required field label. 3955 // - class 'required_if_font' is present 3956 // - class 'required_flag' is present 3957 // - element is not hidden 3958 // - element is not disabled 3959 3960 3961 let first_unresolved = form.find('.required_if_font.required_flag:visible:not(:disabled)').first(); 3962 3963 // Filter first_unresolved by elements without tag data_non_blocking_error="True" 3964 first_unresolved = first_unresolved.filter(function() { 3965 return !(jQuery(this).attr('data_non_blocking_error') == 'True'); 3966 }); 3967 3968 // If there are no unresolved requirements, return null. 3969 if (!first_unresolved || first_unresolved.length == 0) { 3970 return null; 3971 } 3972 return first_unresolved; 3973} 3974 3975 3976function get_unresolved_required_fields(form) { 3977 3978 // Returns all unresolved required field labels 3979 // - class 'required_if_font' is present 3980 // - class 'required_flag' is present 3981 // - element is not hidden 3982 // - element is not disabled 3983 3984 let unresolved_reqs = form.find('.required_if_font.required_flag:visible:not(:disabled)'); 3985 3986 // Filter first_unresolved by elements without tag data_non_blocking_error="True" 3987 unresolved_reqs = unresolved_reqs.filter(function() { 3988 return !(jQuery(this).attr('data_non_blocking_error') == 'True'); 3989 }); 3990 3991 return unresolved_reqs; 3992 3993} 3994 3995 3996function has_unresolved_required_fields(form) { 3997 3998 // Returns true if there are any unresolved required fields found in the form. 3999 4000 let first_unresolved = first_unresolved_required_field(form); 4001 if (first_unresolved) { 4002 return true; 4003 } 4004 return false; 4005 4006} 4007 4008 4009function get_active_required_fields(form) { 4010 4011 // Returns all required and required_if-type fields (their label elements, specifically) in the form. 4012 4013 let reqs = form.find('.required_flag:visible:not(:disabled)'); 4014 4015 // Filter first_unresolved by elements without tag data_non_blocking_error="True" 4016 reqs = reqs.filter(function() { 4017 return !jQuery(this).attr('data_non_blocking_error'); 4018 }); 4019 4020 return reqs; 4021} 4022 4023 4024function filter_labels_by_looks_like_email(label_fields) { 4025 4026 // Returns all required and required_if-type fields that look like email fields. 4027 // Test is based on whether 'email' is in the label text. 4028 4029 let email_fields = label_fields.filter(function() { 4030 // Search the contents of the <label> tag for the 'email' string 4031 // Should match both 'email' and 'e-mail' 4032 return jQuery(this).text().match(/e-?mail/i); 4033 }); 4034 return email_fields; 4035} 4036 4037 4038function get_email_field_problems(form) { 4039 4040 // Returns a list of email fields that do not pass email parsing 4041 // Uses the field_labels if provided, otherwise uses the form to find the fields. 4042 4043 let active_reqs = get_active_required_fields(form); 4044 if (!active_reqs || active_reqs.length == 0) { 4045 return null; 4046 } 4047 4048 let email_field_problems = []; 4049 active_reqs.each(function() { 4050 let field_label = jQuery(this); 4051 4052 let email_field_label = filter_labels_by_looks_like_email(field_label); 4053 4054 if (!email_field_label || email_field_label.length == 0) { 4055 return; 4056 } 4057 4058 let field = get_input_connected_to_label(field_label); 4059 if (!field || field.length == 0) { 4060 return; 4061 } 4062 4063 let field_type = get_input_field_type(field); 4064 if (field_type != 'text') { 4065 return; 4066 } 4067 4068 let field_value = field.val(); 4069 if (!field_value) { 4070 return; 4071 } 4072 4073 let email = parse_email(field_value); 4074 if (!email) { 4075 email_field_problems.push(field_label); 4076 } 4077 }); 4078 4079 // Convert this list of jQuery objects to a jQuery object. 4080 email_field_problems = jQuery(email_field_problems); 4081 return email_field_problems; 4082} 4083 4084 4085function parse_email(email_str) { 4086 4087 // Returns the email string if it is a valid email address, otherwise returns null. 4088 // This is a very simple test for email validity. 4089 4090 let email_regex = /\S+@\S+\.\S+/; 4091 if (email_str.match(email_regex)) { 4092 return email_str; 4093 } 4094 return null; 4095} 4096 4097 4098function setup_blocking_form_handlers() { 4099 let forms = jQuery('form'); 4100 let blocking_form_handlers = []; 4101 4102 forms.each(function() { 4103 let form = jQuery(this); 4104 let handler = new BlockingFormHandler(form); 4105 handler.setup_blocking_error_behavior(); 4106 blocking_form_handlers.push(handler); 4107 }); 4108 4109 return blocking_form_handlers; 4110} 4111 4112 4113 4114function ______SHOW_IF___________() {} 4115 4116 4117function get_show_hide_targets(target_field_id, is_target) { 4118 4119 if (is_target) { 4120 return jQuery('#' + target_field_id); 4121 } 4122 // We need different targets for different layout classes. 4123 var target_obs = jQuery('.input_format_tr.left_right_cols'); 4124 jQuery.merge(target_obs, jQuery('.input_format_tr.one_col')); 4125 jQuery.merge(target_obs, jQuery('.input_format_tr.two_cols > td')); 4126 jQuery.merge(target_obs, jQuery('.input_format_tr.three_cols > td')); 4127 jQuery.merge(target_obs, jQuery('.input_format_tr.four_cols > td')); 4128 target_obs = target_obs.has('#' + target_field_id) 4129 4130 return target_obs 4131} 4132 4133 4134function arrays_overlap(array1, array2) { 4135 4136 var found = false;
4137 jQuery.each(array1, function(i1, v1) { 4138 jQuery.each(array2, function(i2, v2) { 4139 if (v1 == v2) { 4140 found = true; 4141 return false; // This tells .each() to stop (like break). 4142 } 4143 }) 4144 if (found) return false; // Break out of outer loop. 4145 }) 4146 4147 return found; 4148} 4149 4150 4151function setup_check_show_if(target_field_id, target_label_id, trigger_field_name, trigger_vals, multi_suffix, is_target) { 4152 4153 var field_selector = "[name='" + trigger_field_name + "']"; 4154 4155 var trigger_field_ob = $(field_selector) 4156 // If we haven't found the trigger field try the multi case. 4157 if (trigger_field_ob.length == 0) { 4158 field_selector = "[name='" + trigger_field_name + multi_suffix + "']" 4159 trigger_field_ob = $(field_selector) 4160 } 4161 4162 // If there is an 'other' field, get that selector and set that up too. 4163 var other_selector = get_other_field_selector(trigger_field_ob) 4164 if (other_selector) { 4165 setup_other_check_show_if(target_field_id, target_label_id, other_selector, trigger_vals, is_target); 4166 } 4167 4168 $(document).on('keyup change', field_selector, 4169 function () { 4170 check_show_if(target_field_id, target_label_id, trigger_field_ob, trigger_vals, is_target); 4171 } 4172 ) 4173 check_show_if(target_field_id, target_label_id, trigger_field_ob, trigger_vals, is_target); 4174} 4175 4176 4177function setup_other_check_show_if(target_field_id, target_label_id, trigger_field_selector, trigger_vals, is_target) { 4178 4179 // This sets up the check_req_if for the 'other' field case. It doesn't 4180 // need to deal with the multi vs. plain trouble because that is dealt with in the caller. 4181 4182 var trigger_field_ob = $(trigger_field_selector) 4183 4184 $(document).on('keyup change', trigger_field_selector, 4185 function () { 4186 check_show_if(target_field_id, target_label_id, trigger_field_ob, trigger_vals, is_target) 4187 } 4188 ) 4189 check_show_if(target_field_id, target_label_id, trigger_field_ob, trigger_vals, is_target) 4190} 4191 4192 4193function check_show_if(target_field_id, target_label_id, trigger_field_ob, trigger_vals, is_target) { 4194 4195 var values = get_field_values(trigger_field_ob); 4196 var target_obs = get_show_hide_targets(target_field_id, is_target); 4197 4198 if (arrays_overlap(trigger_vals, values)) { 4199 target_obs.show(); 4200 } else { 4201 target_obs.hide(); 4202 } 4203} 4204 4205 4206function setup_check_show_if_not(target_field_id, target_label_id, trigger_field_name, trigger_vals, multi_suffix, is_target) { 4207 4208 var field_selector = "[name='" + trigger_field_name + "']"; 4209 4210 var trigger_field_ob = $(field_selector) 4211 // If we haven't found the trigger field try the multi case. 4212 if (trigger_field_ob.length == 0) { 4213 field_selector = "[name='" + trigger_field_name + multi_suffix + "']" 4214 trigger_field_ob = $(field_selector) 4215 } 4216 4217 // If there is an 'other' field, get that selector and set that up too. 4218 var other_selector = get_other_field_selector(trigger_field_ob) 4219 if (other_selector) { 4220 setup_other_check_show_if_not(target_field_id, target_label_id, other_selector, trigger_vals, is_target); 4221 } 4222 4223 $(document).on('keyup change', field_selector, 4224 function () { 4225 check_show_if_not(target_field_id, target_label_id, trigger_field_ob, trigger_vals, is_target); 4226 } 4227 ) 4228 check_show_if_not(target_field_id, target_label_id, trigger_field_ob, trigger_vals, is_target); 4229} 4230 4231 4232function setup_other_check_show_if_not(target_field_id, target_label_id, trigger_field_selector, trigger_vals, is_target) { 4233 4234 // This sets up the check_req_if for the 'other' field case. It doesn't 4235 // need to deal with the multi vs. plain trouble because that is dealt with in the caller. 4236 4237 var trigger_field_ob = $(trigger_field_selector) 4238 4239 $(document).on('keyup change', trigger_field_selector, 4240 function () { 4241 check_show_if_not(target_field_id, target_label_id, trigger_field_ob, trigger_vals, is_target) 4242 } 4243 ) 4244 check_show_if_not(target_field_id, target_label_id, trigger_field_ob, trigger_vals, is_target) 4245} 4246 4247 4248function check_show_if_not(target_field_id, target_label_id, trigger_field_ob, trigger_vals, is_target) { 4249 4250 var values = get_field_values(trigger_field_ob); 4251 var target_obs = get_show_hide_targets(target_field_id, is_target); 4252 4253 if (arrays_overlap(trigger_vals, values)) { 4254 target_obs.hide(); 4255 } else { 4256 target_obs.show(); 4257 } 4258} 4259 4260 4261function setup_check_show_if_none(target_field_id, trigger_fields, is_target) { 4262 4263 var new_fields = [] 4264
4265 // populate new_fields with jquery objects. 4266 for (var i=0; i<trigger_fields.length; i++) { 4267 var new_field_ob = jQuery("[name='"+trigger_fields[i]+"']") 4268 // fields includes fields that might not exist depending on whether they are multi-fields or not. Only include fields we can find. 4269 if (new_field_ob.length > 0) { 4270 new_fields.push(new_field_ob) 4271 4272 // If <new_field_ob> has an attached 'other' field, include that in these checks too 4273 var other_selector = get_other_field_selector(new_field_ob) 4274 if (other_selector) {new_fields.push($(other_selector))} 4275 } 4276 } 4277 4278 check_show_if_none(target_field_id, new_fields, is_target) 4279 jQuery.each(new_fields, function(key, val) { 4280 $(document).on('keyup change', val, function() {check_show_if_none(target_field_id, new_fields, is_target)}) 4281 }) 4282} 4283 4284 4285function check_show_if_none(target_field_id, trigger_fields, is_target) { 4286 var found = false; 4287 for (var i=0; i<trigger_fields.length; i++) { 4288 var field = trigger_fields[i]; 4289 // Get the values of the field object 4290 var values = get_field_values(field); 4291 if (values[0] != '') { 4292 found = true; 4293 break; 4294 } 4295 } 4296 4297 var target_obs = get_show_hide_targets(target_field_id, is_target); 4298 if (found) { 4299 target_obs.hide(); 4300 } else { 4301 target_obs.show(); 4302 } 4303} 4304 4305 4306function setup_check_show_if_any(target_field_id, trigger_fields, is_target) { 4307 4308 var new_fields = [] 4309 4310 // populate new_fields with jquery objects. 4311 for (var i=0; i<trigger_fields.length; i++) { 4312 var new_field_ob = jQuery("[name='"+trigger_fields[i]+"']") 4313 // fields includes fields that might not exist depending on whether they are multi-fields or not. Only include fields we can find. 4314 if (new_field_ob.length > 0) { 4315 new_fields.push(new_field_ob) 4316 4317 // If <new_field_ob> has an attached 'other' field, include that in these checks too 4318 var other_selector = get_other_field_selector(new_field_ob) 4319 if (other_selector) {new_fields.push($(other_selector))} 4320 } 4321 } 4322 4323 check_show_if_any(target_field_id, new_fields, is_target) 4324 jQuery.each(new_fields, function(key, val) { 4325 $(document).on('keyup change', val, function() {check_show_if_any(target_field_id, new_fields, is_target)}) 4326 }) 4327} 4328 4329 4330function check_show_if_any(target_field_id, trigger_fields, is_target) { 4331 var found = false; 4332 for (var i=0; i<trigger_fields.length; i++) { 4333 var field = trigger_fields[i]; 4334 // Get the values of the field object 4335 var values = get_field_values(field); 4336 if (values[0] != '') { 4337 found = true; 4338 break; 4339 } 4340 } 4341 4342 var target_obs = get_show_hide_targets(target_field_id, is_target); 4343 if (found) { 4344 target_obs.show(); 4345 } else { 4346 target_obs.hide(); 4347 } 4348} 4349 4350 4351function ______FORM_SUBMIT_WAITING_MODAL_______() {} 4352 4353const SUBMIT_WAITING_MODAL_CONTENTS_ID_TAG_NAME = 'data-waiting-modal-contents-id'; 4354const SUBMIT_WAITING_MODAL_TITLE_ID_TAG_NAME = 'data-waiting-modal-title-id'; 4355 4356 4357function show_submit_waiting_modal_if_has_def(linked_elem, modal_args={}) { 4358 4359 // Check whether this element has the tag, 'data-waiting-modal-def-id'. If it 4360 // does, and it has a value, find the element with that id. That is our modal 4361 // definition element. 4362 // Parse that waiting_modal_def element to extract the title and contents. 4363 // Modal def format: 4364 // <div class="waiting_modal_def"> 4365 // <div class="waiting_modal_title">Title</div> 4366 // <div class="waiting_modal_contents">Contents</div> 4367 // </div> 4368 4369 let modal_contents_id = linked_elem.attr(SUBMIT_WAITING_MODAL_CONTENTS_ID_TAG_NAME); 4370 if (!modal_contents_id) { 4371 return false; 4372 } 4373 4374 let modal_title_id = linked_elem.attr(SUBMIT_WAITING_MODAL_TITLE_ID_TAG_NAME); 4375 let modal_title_contents = ''; 4376 if (modal_title_id) { 4377 modal_title_contents = jQuery('#' + modal_title_id).html(); 4378 } 4379 4380 show_submit_waiting_modal(modal_contents_id, modal_title_contents, modal_args); 4381 return true; 4382} 4383 4384 4385function show_submit_waiting_modal(contents_id = '', title_html = '', args = {}) { 4386 4387 jQuery.colorbox({ 4388 width:'600px', 4389 height:'400px', 4390 top: '100px', 4391 inline:true, 4392 href:'#' + contents_id, 4393 // html: contents_html, 4394 title: title_html, 4395 overlayClose: false, 4396 trapFocus: true, 4397 closeButton: true, 4398 returnFocus: true, 4399 speed: 50, 4400 ...args, 4401 // onComplete: function() { 4402 // // Callback that fires right after loaded content is displayed. 4403 // %s 4404 // }, 4405 // onCleanup: function() {} 4406 // onClosed: function() { 4407 // // Callback that fires once ColorBox is closed. 4408 // %s 4409 // } 4410 }); 4411} 4412 4413 4414 4415 4416function ______OTHER_FIELD_TESTERS______() {} 4417 4418 4419function contains_html(field_sel) { 4420 // Returns true if any fields selected by <field_sel> contains any HTML tags. 4421 // note that this detects "<ANYTHING>" as html, but not "< div>" which is standard browser behavior 4422 4423 var found = false;
4424 4425 $(field_sel).each(function() { 4426 // I don't know why there's a try here 4427 try { 4428 var contents = $(this).val() // this is a string 4429 contents = contents.trim(); // Get rid of starting / trailing white space 4430 4431 if (contents.length == 0) { 4432 // No contents - skip to the next field 4433 return true; 4434 } 4435 4436 var parsed = $.parseHTML(contents); // Returns an array of contents elements 4437 if (parsed.length > 1) { 4438 // multiple items means that there is more than just text. 4439 // If it was just text it would have a single #text element 4440 found = true; 4441 } else if ($(parsed[0]).nodeName.toLowerCase() != '#text') { 4442 // if the parsed text isn't #text then it is wrapped in a tag. 4443 found = true; 4444 } 4445 return !found // Breaks out of each if found. 4446 } catch(err) {} 4447 }); 4448 return found 4449} 4450 4451 4452function ______MISC_____() {} 4453 4454 4455var tag_regex = new RegExp("<\s*/?\s*[a-zA-Z]+.*?>", 'gs') 4456function strip_html(s, replace_with=' ') { 4457 // This can't deal with the script tag properly - it returns the contents of the script. Improve when needed. 4458 if (typeof s == 'undefined') { return ''; } 4459 // We should improve this to account for "spacy" tags vs non "spacy" tags. 4460 var ans = s.replace(tag_regex, replace_with); 4461 return ans; 4462} 4463 4464var entityMap = { 4465 "&": "&", 4466 "<": "<", 4467 ">": ">", 4468 '"': '"', 4469 "'": ''', 4470 "/": '/' 4471}; 4472 4473function escape_html(string) { 4474 return String(string).replace(/[&<>"'\/]/g, function (s) { 4475 return entityMap[s]; 4476 }); 4477} 4478 4479 4480function _____STARS_RATING_WIGET_____(){} 4481 4482/* These routines are used by star_rating_widget.py */ 4483 4484 4485function make_selectable_stars(stars_id) { 4486 4487 var stars_el = $('#'+stars_id); 4488 var star = stars_el.children('.rm_star'); 4489 4490 star.hover(function() { 4491 // hover enter 4492 4493 var $this = $(this); 4494 4495 // when starting a hover we store the saved non-hover value in '_tmp_saved_val' 4496 var non_hover_saved_val = $this.parent().data('_tmp_saved_val'); 4497 if (!non_hover_saved_val) { 4498 var lit_stars = $this.parent().children('.fa-star'); 4499 curr_val = lit_stars.length; 4500 $this.parent().data('_tmp_saved_val', curr_val); 4501 } 4502 4503 // hide show correct stars 4504 $this.removeClass('fa-star-o fa-star-half-o'); 4505 $this.addClass('fa-star'); 4506 4507 $prev_stars = $this.prevAll('.rm_star'); 4508 $prev_stars.removeClass('fa-star-o fa-star-half-o'); 4509 $prev_stars.addClass('fa-star'); 4510 4511 $next_stars = $this.nextAll('.rm_star'); 4512 $next_stars.removeClass('fa-star fa-star-half-o'); 4513 $next_stars.addClass('fa-star-o'); 4514 4515 // fire custom dom event 4516 var curr_hover_val = $prev_stars.length + 1; 4517 $this.parent().trigger('rm-stars-hover-val-update', [curr_hover_val]); 4518 4519 }, function(){ 4520 // hover exit 4521 4522 var $this = $(this); 4523 4524 // return stars to non-hover value 4525 var non_hover_saved_val = $this.parent().data('_tmp_saved_val'); 4526 if (non_hover_saved_val || non_hover_saved_val === 0) { 4527 $this.parent().data('_tmp_saved_val', null); 4528 $this.parent().trigger('rm-stars-hover-val-update', [non_hover_saved_val]); 4529 4530 update_stars_display_for_value(stars_el, non_hover_saved_val); 4531 } 4532 }); 4533 4534 star.click(function(){ 4535 // trigger custom dom event on stars element with value of clicked star 4536 var $this = $(this); 4537 4538 // we get the value of the clicked star by counting sibling stars 4539 // that are previous to it in the dom 4540 var num_previous_stars = $this.prevAll('.rm_star').length; 4541 var clicked_star_value = num_previous_stars + 1; 4542 4543 // current policy is that if the user clicks the currently 4544 // selected star, then we clear the star value -- set it to 0 4545 var curr_non_hover_val = $this.parent().data('_tmp_saved_val'); 4546 if (clicked_star_value == curr_non_hover_val) { 4547 // clicked_star_value = 0; 4548 // Giving this a value of 0 breaks the required js 4549 clicked_star_value = ''; 4550 } 4551 4552 stars_el.trigger('rm-stars-val-update', [clicked_star_value]); 4553 }); 4554 4555 stars_el.on('rm-stars-val-update', function(e, new_val){ 4556 $this = $(this); 4557 $this.data('_tmp_saved_val', new_val); 4558 update_stars_display_for_value(stars_el, new_val); 4559 }); 4560 4561 function update_stars_display_for_value(stars_el, value) { 4562 var stars = stars_el.children('.rm_star'); 4563 4564 stars.slice(0, value) 4565 .removeClass('fa-star-o fa-star-half-o') 4566 .addClass('fa-star'); 4567 4568 stars.slice(value) 4569 .removeClass('fa-star fa-star-half-o') 4570 .addClass('fa-star-o') 4571 } 4572} 4573 4574 4575function make_selectable_labeled_stars(labeled_stars_id, stars_opts) { 4576 4577 var labeled_stars = $('#'+labeled_stars_id); 4578 var stars_label = labeled_stars.find('.rm_stars_label'); 4579 var stars = labeled_stars.find('.rm_stars'); 4580 4581 stars.on('rm-stars-hover-val-update', function(e, val) { 4582 stars_label.html(stars_opts[val]); 4583 4584 // Try to improve the min-width as different labels are shown. 4585 var width = stars_label.css('width').slice(0, -2); 4586 var old_min_width = stars_label.css('min-width').slice(0, -2); 4587 width = parseInt(width); 4588 old_min_width = parseInt(old_min_width); 4589 var new_min = Math.max(width, old_min_width); 4590 stars_label.css('min-width', new_min); 4591 }); 4592 4593 stars.on('rm-stars-val-update', function(e, val) { 4594 var new_label = stars_opts[val] 4595 if (val == '') { 4596 // We've replaced 0 with '' as a val to let required_js work. 4597 new_label = stars_opts[0]; 4598 } 4599 stars_label.html(new_label); 4600 }); 4601} 4602 4603 4604function connect_stars_input_hidden_field(input_widget_id, hidden_field_id) { 4605 4606 var input_widget_el = $('#'+input_widget_id); 4607 var hidden_field_el = $('#'+hidden_field_id); 4608 var stars = input_widget_el.find('.rm_stars'); 4609 stars.on('rm-stars-val-update', function(e, val) { 4610 hidden_field_el.val(val); 4611 // This isn't triggered on the hidden text field so we have to trigger 4612 // it by hand. 4613 hidden_field_el.trigger( "change" ); 4614 }); 4615} 4616 4617 4618function _____TOUCH_CHECKING___(){} 4619 4620/* Modernizr 2.8.3 (Custom Build) | MIT & BSD 4621 * Build: http://modernizr.com/download/#-touch-cssclasses-teststyles-prefixes-cssclassprefix:modernizr! 4622 4623Includings only touch device detection. 4624Example: if (Modernizr.touch) { alert("touch") } 4625 4626 */ 4627;window.Modernizr=function(a,b,c){function w(a){j.cssText=a}function x(a,b){return w(m.join(a+";")+(b||""))}function y(a,b){return typeof a===b}function z(a,b){return!!~(""+a).indexOf(b)}function A(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:y(f,"function")?f.bind(d||b):f}return!1}var d="2.8.3",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l={}.toString,m=" -webkit- -moz- -o- -ms- ".split(" "),n={},o={},p={},q=[],r=q.slice,s,t=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["­",'<style id="s',h,'">',a,"</style>"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},u={}.hasOwnProperty,v;!y(u,"undefined")&&!y(u.call,"undefined")?v=function(a,b){return u.call(a,b)}:v=function(a,b){return b in a&&y(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;
4627var d=r.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(r.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(r.call(arguments)))};return e}),n.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:t(["@media (",m.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c};for(var B in n)v(n,B)&&(s=B.toLowerCase(),e[s]=n[B](),q.push((e[s]?"":"no-")+s));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)v(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" modernizr-"+(b?"":"no-")+a),e[a]=b}return e},w(""),i=k=null,e._version=d,e._prefixes=m,e.testStyles=t,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" modernizr-js modernizr-"+q.join(" modernizr-"):""),e}(this,this.document);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.