1(function(define) { 2 'use strict'; 3 RequireJS.define('js/student_account/models/LoginModel',[ 4 'jquery', 5 'backbone', 6 'jquery.url' 7 ], function($, Backbone) { 8 return Backbone.Model.extend({ 9 defaults: { 10 email: '', 11 password: '' 12 }, 13 14 ajaxType: '', 15 urlRoot: '', 16 17 initialize: function(attributes, options) { 18 this.ajaxType = options.method; 19 this.urlRoot = options.url; 20 }, 21 22 sync: function(method, model) { 23 var headers = {'X-CSRFToken': $.cookie('csrftoken')}, 24 data = {}, 25 analytics, 26 courseId = $.url('?course_id'); 27 28 // If there is a course ID in the query string param, 29 // send that to the server as well so it can be included 30 // in analytics events. 31 if (courseId) { 32 analytics = JSON.stringify({ 33 enroll_course_id: decodeURIComponent(courseId) 34 }); 35 } 36 37 // Include all form fields and analytics info in the data sent to the server 38 $.extend(data, model.attributes, {analytics: analytics}); 39 40 $.ajax({ 41 url: model.urlRoot, 42 type: model.ajaxType, 43 data: data, 44 headers: headers, 45 success: function() { 46 model.trigger('sync'); 47 }, 48 error: function(error) { 49 model.trigger('error', error); 50 } 51 }); 52 } 53 }); 54 }); 55}).call(this, define || RequireJS.define); 56 57(function(define) { 58 'use strict'; 59 RequireJS.define('js/student_account/models/PasswordResetModel',['jquery', 'backbone'], 60 function($, Backbone) { 61 return Backbone.Model.extend({ 62 defaults: { 63 email: '' 64 }, 65 ajaxType: '', 66 urlRoot: '', 67 68 initialize: function(attributes, options) { 69 this.ajaxType = options.method; 70 this.urlRoot = options.url; 71 }, 72 73 sync: function(method, model) { 74 var headers = { 75 'X-CSRFToken': $.cookie('csrftoken') 76 }; 77 78 // Only expects an email address. 79 $.ajax({ 80 url: model.urlRoot, 81 type: model.ajaxType, 82 data: model.attributes, 83 headers: headers, 84 success: function() { 85 model.trigger('sync'); 86 }, 87 error: function(error) { 88 model.trigger('error', error); 89 } 90 }); 91 } 92 }); 93 }); 94}).call(this, define || RequireJS.define); 95 96(function(define) { 97 'use strict'; 98 RequireJS.define('js/student_account/models/RegisterModel',['jquery', 'backbone', 'jquery.url'], 99 function($, Backbone) { 100 return Backbone.Model.extend({ 101 defaults: { 102 email: '', 103 name: '', 104 username: '', 105 password: '', 106 level_of_education: '', 107 gender: '', 108 year_of_birth: '', 109 mailing_address: '', 110 goals: '' 111 }, 112 ajaxType: '', 113 urlRoot: '', 114 115 initialize: function(attributes, options) { 116 this.ajaxType = options.method; 117 this.urlRoot = options.url; 118 this.nextUrl = options.nextUrl; 119 }, 120 121 sync: function(method, model) { 122 var headers = {'X-CSRFToken': $.cookie('csrftoken')}, 123 data = {next: model.nextUrl}, 124 courseId = $.url('?course_id'); 125 126 // If there is a course ID in the query string param, 127 // send that to the server as well so it can be included 128 // in analytics events. 129 if (courseId) { 130 data.course_id = decodeURIComponent(courseId); 131 } 132 133 // Include all form fields and analytics info in the data sent to the server 134 $.extend(data, model.attributes); 135 136 $.ajax({ 137 url: model.urlRoot, 138 type: model.ajaxType, 139 data: data, 140 headers: headers, 141 success: function() { 142 model.trigger('sync'); 143 }, 144 error: function(error) { 145 model.trigger('error', error); 146 } 147 }); 148 } 149 }); 150 }); 151}).call(this, define || RequireJS.define); 152 153(function(define) { 154 'use strict'; 155 RequireJS.define('js/student_account/models/AccountRecoveryModel',['jquery', 'backbone'], 156 function($, Backbone) { 157 return Backbone.Model.extend({ 158 defaults: { 159 email: '' 160 }, 161 ajaxType: '', 162 urlRoot: '', 163 164 initialize: function(attributes, options) { 165 this.ajaxType = options.method; 166 this.urlRoot = options.url; 167 }, 168 169 sync: function(method, model) { 170 var headers = { 171 'X-CSRFToken': $.cookie('csrftoken') 172 }; 173 174 // Only expects an email address. 175 $.ajax({ 176 url: model.urlRoot, 177 type: model.ajaxType, 178 data: model.attributes, 179 headers: headers, 180 success: function() { 181 model.trigger('sync'); 182 }, 183 error: function(error) { 184 model.trigger('error', error); 185 } 186 }); 187 } 188 }); 189 }); 190}).call(this, define || RequireJS.define); 191 192(function(define) { 193 'use strict'; 194 RequireJS.define('common/js/utils/edx.utils.validate',[ 195 'jquery', 196 'underscore', 197 'underscore.string', 198 'gettext' 199 ], 200 function($, _, _s, gettext) { 201 var utils; 202 203 /* Mix non-conflicting functions from underscore.string 204 * (all but include, contains, and reverse) into the 205 * Underscore namespace. In practice, this mixin is done 206 * by the access view, but doing it here helps keep the 207 * utility self-contained. 208 */ 209 _.mixin(_s.exports()); 210 211 utils = (function() { 212 var _fn = { 213 validate: { 214 215 template: _.template('<li <%- suppressAttr %>><%- content %></li>'), 216 217 msg: { 218 email: gettext("The email address you've provided isn't formatted correctly."), 219 min: gettext('%(field)s must have at least %(count)d characters.'), 220 max: gettext('%(field)s can only contain up to %(count)d characters.'), 221 required: gettext('Please enter your %(field)s.') 222 }, 223 224 field: function(el) { 225 var $el = $(el), 226 required = true, 227 min = true, 228 max = true, 229 email = true, 230 response = {}, 231 isBlank = _fn.validate.isBlank($el); 232 233 if (_fn.validate.isRequired($el)) { 234 if (isBlank) { 235 required = false;
236 } else { 237 min = _fn.validate.str.minlength($el); 238 max = _fn.validate.str.maxlength($el); 239 email = _fn.validate.email.valid($el); 240 } 241 } else if (!isBlank) { 242 min = _fn.validate.str.minlength($el); 243 max = _fn.validate.str.maxlength($el); 244 email = _fn.validate.email.valid($el); 245 } 246 247 response.isValid = required && min && max && email; 248 249 if (!response.isValid) { 250 _fn.validate.removeDefault($el); 251 252 response.message = _fn.validate.getMessage($el, { 253 required: required, 254 min: min, 255 max: max, 256 email: email 257 }); 258 } 259 260 return response; 261 }, 262 263 str: { 264 minlength: function($el) { 265 var min = $el.attr('minlength') || 0; 266 267 return min <= $el.val().length; 268 }, 269 270 maxlength: function($el) { 271 var max = $el.attr('maxlength') || false; 272 273 return (!!max) ? max >= $el.val().length : true; 274 } 275 }, 276 277 isRequired: function($el) { 278 return $el.attr('required'); 279 }, 280 281 isBlank: function($el) { 282 var type = $el.attr('type'), 283 isBlank; 284 285 if (type === 'checkbox') { 286 isBlank = !$el.prop('checked'); 287 } else if (type === 'select') { 288 isBlank = ($el.data('isdefault') === true); 289 } else { 290 isBlank = !$el.val(); 291 } 292 293 return isBlank; 294 }, 295 296 email: { 297 // This is the same regex used to validate email addresses in Django 1.11 298 regex: new RegExp( 299 [ 300 '(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*', 301 '|^"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*"', // eslint-disable-line max-len 302 ')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+)(?:[A-Z0-9-]{2,63})', 303 '|\\[(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\]$' 304 ].join(''), 'i' 305 ), 306 307 valid: function($el) { 308 return $el.attr('type') === 'email' ? _fn.validate.email.format($el.val()) : true; 309 }, 310 311 format: function(str) { 312 return _fn.validate.email.regex.test(str); 313 } 314 }, 315 316 getLabel: function(id) { 317 // Extract the field label, remove the asterisk (if it appears) and any extra whitespace 318 return $('label[for=' + id + '] > span.label-text').text().split('*')[0].trim(); 319 }, 320 321 getMessage: function($el, tests) { 322 var txt = [], 323 label, 324 context, 325 content, 326 customMsg, 327 liveValidationMsg, 328 suppressAttr; 329 330 _.each(tests, function(value, key) { 331 if (!value) { 332 label = _fn.validate.getLabel($el.attr('id')); 333 customMsg = $el.data('errormsg-' + key) || false; 334 liveValidationMsg = 335 $('#' + $el.attr('id') + '-validation-error-msg').text() || false; 336 337 // If the field has a custom error msg attached, use it 338 if (customMsg) { 339 content = customMsg; 340 } else if (liveValidationMsg) { 341 content = liveValidationMsg; 342 } else { 343 context = {field: label}; 344 345 if (key === 'min') { 346 context.count = parseInt($el.attr('minlength'), 10); 347 } else if (key === 'max') { 348 context.count = parseInt($el.attr('maxlength'), 10); 349 } 350 351 content = _.sprintf(_fn.validate.msg[key], context); 352 } 353 354 suppressAttr = ''; 355 if (['username', 'email'].indexOf($el.attr('name')) > -1) { 356 suppressAttr = 'data-hj-suppress'; 357 } 358 359 txt.push(_fn.validate.template({ 360 content: content, 361 suppressAttr: suppressAttr 362 })); 363 } 364 }); 365 366 return txt.join(' '); 367 }, 368 369 // Removes the default HTML5 validation pop-up 370 removeDefault: function($el) { 371 if ($el.setCustomValidity) { 372 $el.setCustomValidity(' '); 373 } 374 } 375 } 376 }; 377 378 return { 379 validate: _fn.validate.field 380 }; 381 }()); 382 383 return utils; 384 }); 385}).call(this, define || RequireJS.define); 386 387RequireJS.define('text',{load: function(id){throw new Error("Dynamic load not allowed: " + id);}}); 388 389RequireJS.define('text!templates/student_account/form_errors.underscore',[],function () { return '<div class="<%- jsHook %> status submission-error">\n <h4 class="message-title"><%- title %></h4>\n <ul class="message-copy">\n <%= HtmlUtils.ensureHtml(messagesHtml) %>\n </ul>\n</div>\n';}); 390 391(function(define) { 392 'use strict'; 393 RequireJS.define('js/student_account/views/FormView',[ 394 'jquery', 395 'underscore', 396 'backbone', 397 'common/js/utils/edx.utils.validate', 398 'edx-ui-toolkit/js/utils/html-utils', 399 'edx-ui-toolkit/js/utils/string-utils', 400 'text!templates/student_account/form_errors.underscore' 401 ], function($, _, Backbone, EdxUtilsValidate, HtmlUtils, StringUtils, formErrorsTpl) { 402 return Backbone.View.extend({ 403 tagName: 'form', 404 el: '', 405 tpl: '', 406 fieldTpl: '#form_field-tpl', 407 formErrorsTpl: formErrorsTpl, 408 formErrorsJsHook: 'js-form-errors', 409 defaultFormErrorsTitle: gettext('An error occurred.'), 410 events: {}, 411 errors: [], 412 formType: '', 413 $form: {}, 414 fields: [], 415 liveValidationFields: [], 416 // String to append to required label fields 417 requiredStr: '', 418 /* 419 Translators: This string is appended to optional field labels on the student login, registration, and 420 profile forms. 421 */ 422 optionalStr: gettext('(optional)'), 423 submitButton: '', 424 isEnterpriseEnable: false, 425 426 initialize: function(data) { 427 this.model = data.model; 428 this.preRender(data); 429 430 this.tpl = $(this.tpl).html(); 431 this.fieldTpl = $(this.fieldTpl).html(); 432 this.buildForm(data.fields); 433 434 this.listenTo(this.model, 'error', this.saveError); 435 }, 436 437 /* Allows extended views to add custom 438 * init steps without needing to repeat 439 * default init steps 440 */ 441 preRender: function(data) { 442 /* Custom code goes here */ 443 return data; 444 }, 445 446 render: function(html) { 447 var fields = html || ''; 448 449 HtmlUtils.setHtml( 450 $(this.el), 451 HtmlUtils.HTML( 452 _.template(this.tpl)({ 453 fields: fields, 454 HtmlUtils: HtmlUtils 455 }) 456 ) 457 ) 458 this.postRender(); 459 460 return this; 461 }, 462 463 postRender: function() { 464 var $container = $(this.el); 465 this.$form = $container.find('form'); 466 this.$formFeedback = $container.find('.js-form-feedback'); 467 this.$submitButton = $container.find(this.submitButton); 468 }, 469 470 buildForm: function(data) { 471 var html = [], 472 i, 473 len = data.length, 474 fieldTpl = this.fieldTpl; 475 476 this.fields = data; 477 478 for (i = 0; i < len; i++) { 479 if (data[i].errorMessages) { 480 data[i].errorMessages = this.escapeStrings(data[i].errorMessages); 481 } 482 483 html.push(HtmlUtils.template(fieldTpl)($.extend(data[i], { 484 form: this.formType, 485 requiredStr: this.requiredStr, 486 optionalStr: this.optionalStr, 487 supplementalText: data[i].supplementalText || '', 488 supplementalLink: data[i].supplementalLink || '', 489 loginIssueSupportLink: data[i].loginIssueSupportLink || '', 490 isEnterpriseEnable: this.isEnterpriseEnable 491 }))); 492 } 493 494 this.render(html.join('')); 495 }, 496 497 /* Helper method to toggle display 498 * including accessibility considerations 499 */ 500 element: { 501 hide: function($el) { 502 if ($el) { 503 $el.addClass('hidden'); 504 } 505 }, 506 507 scrollTop: function($el) { 508 // Scroll to top of selected element 509 $('html,body').animate({ 510 scrollTop: $el.offset().top 511 }, 'slow'); 512 }, 513 514 show: function($el) { 515 if ($el) { 516 $el.removeClass('hidden'); 517 } 518 } 519 }, 520 521 escapeStrings: function(obj) { 522 _.each(obj, function(val, key) { 523 obj[key] = _.escape(val); 524 }); 525 526 return obj; 527 }, 528 529 forgotPassword: function(event) { 530 event.preventDefault(); 531 532 this.trigger('password-help'); 533 }, 534 535 getFormData: function() { 536 var obj = {}, 537 $form = this.$form, 538 elements = $form[0].elements, 539 i, 540 $n, 541 tpl, 542 len = elements.length, 543 $el, 544 $label, 545 key = '', 546 errors = [], 547 validation = {}, 548 $desc, 549 $validationNode; 550 551 for (i = 0; i < len; i++) { 552 $el = $(elements[i]); 553 $label = $form.find('label[for=' + $el.attr('id') + ']'); 554 key = $el.attr('name') || false; 555
556 // Due to a bug in firefox, whitespaces in email type field are not removed. 557 // TODO: Remove this code once firefox bug is resolved. 558 if (key === 'email') { 559 $el.val($el.val().trim()); 560 } 561 562 if (key) { 563 if (this.interesting_fields($el)) { 564 this.remove_validation_error($el, $form); 565 } 566 validation = this.validate(elements[i]); 567 if (validation.isValid) { 568 obj[key] = $el.attr('type') === 'checkbox' ? $el.is(':checked') : $el.val(); 569 $el.removeClass('error'); 570 $label.removeClass('error'); 571 } else { 572 if (this.interesting_fields($el)) { 573 $validationNode = this.get_error_validation_node($el, $form); 574 if ($validationNode) { 575 $n = $.parseHTML(validation.message); 576 tpl = HtmlUtils.template('<i class="fa fa-exclamation-triangle"></i>'); 577 578 HtmlUtils.prepend($n, tpl()); 579 HtmlUtils.append($validationNode, HtmlUtils.HTML($n)); 580 } 581 582 $desc = $form.find('#' + $el.attr('id') + '-desc'); 583 $desc.remove(); 584 } 585 586 errors.push(validation.message); 587 $el.addClass('error'); 588 $label.addClass('error'); 589 } 590 } 591 } 592 593 this.errors = _.uniq(errors); 594 595 return obj; 596 }, 597 remove_validation_error: function($el, $form) { 598 var $validationNode = this.get_error_validation_node($el, $form); 599 if ($validationNode && $validationNode.find('li').length > 0) { 600 $validationNode.empty(); 601 } 602 }, 603 604 get_error_validation_node: function($el, $form) { 605 var $node = $form.find('#' + $el.attr('id') + '-validation-error-msg'); 606 return $node.find('ul'); 607 }, 608 609 interesting_fields: function($el) { 610 return ($el.attr('name') === 'email' || $el.attr('name') === 'password'); 611 }, 612 613 toggleHelp: function(event, $help) { 614 var $el = $(event.currentTarget); 615 var $i = $el.find('i'); 616 617 if ($help.css('display') === 'block') { 618 $help.css('display', 'none'); 619 $i.addClass('fa-caret-right').removeClass('fa-caret-down'); 620 } else { 621 $help.css('display', 'block'); 622 $i.addClass('fa-caret-down').removeClass('fa-caret-right'); 623 } 624 }, 625 626 saveError: function(error) { 627 this.errors = [ 628 StringUtils.interpolate( 629 '<li>{error}</li>', { 630 error: error.responseText 631 } 632 ) 633 ]; 634 this.renderErrors(this.defaultFormErrorsTitle, this.errors); 635 this.scrollToFormFeedback(); 636 this.toggleDisableButton(false); 637 }, 638 639 /* Wrapper for renderFormFeedback provided for convenience since the majority of 640 * our calls to renderFormFeedback are for rendering error messages. 641 */ 642 renderErrors: function(title, errorMessages) { 643 this.clearFormErrors(); 644 this.renderFormFeedback(this.formErrorsTpl, { 645 jsHook: this.formErrorsJsHook, 646 title: title, 647 messagesHtml: HtmlUtils.HTML(errorMessages.join('')) 648 }); 649 }, 650 651 renderFormFeedback: function(template, context) { 652 var tpl = HtmlUtils.template(template); 653 HtmlUtils.prepend(this.$formFeedback, tpl(context)); 654 }, 655 656 /* Allows extended views to add non-form attributes 657 * to the data before saving it to model 658 */ 659 setExtraData: function(data) { 660 return data; 661 }, 662 663 submitForm: function(event) { 664 var data = this.getFormData(); 665 666 if (!_.isUndefined(event)) { 667 event.preventDefault(); 668 } 669 670 this.toggleDisableButton(true); 671 672 if (!_.compact(this.errors).length) { 673 data = this.setExtraData(data); 674 this.model.set(data); 675 this.model.save(); 676 this.clearFormErrors(); 677 } else { 678 this.renderErrors(this.defaultFormErrorsTitle, this.errors); 679 this.scrollToFormFeedback(); 680 this.toggleDisableButton(false); 681 } 682 683 this.postFormSubmission(); 684 }, 685 686 /* Allows extended views to add custom 687 * code after form submission 688 */ 689 postFormSubmission: function() { 690 return true; 691 }, 692 693 resetValidationVariables: function() { 694 return true; 695 }, 696 697 clearFormErrors: function() { 698 var query = '.' + this.formErrorsJsHook; 699 this.clearFormFeedbackItems(query); 700 }, 701 702 clearFormFeedbackItems: function(query) { 703 var $items = this.$formFeedback.find(query); 704 if ($items.length > 0) { 705 $items.remove(); 706 } 707 }, 708 709 /** 710 * If a form button is defined for this form, this will disable the button on 711 * submit, and re-enable the button if an error occurs. 712 * 713 * Args: 714 * disabled (boolean): If set to TRUE, disable the button. 715 * 716 */ 717 toggleDisableButton: function(disabled) { 718 if (this.$submitButton) { 719 this.$submitButton.attr('disabled', disabled); 720 } 721 }, 722 723 scrollToFormFeedback: function() { 724 var self = this; 725 // Scroll to feedback container 726 $('html,body').animate({ 727 scrollTop: this.$formFeedback.offset().top 728 }, 'slow', function() { 729 self.resetValidationVariables(); 730 }); 731 732 // Focus on the feedback container to ensure screen readers see the messages. 733 this.$formFeedback.focus(); 734 }, 735 736 validate: function($el) { 737 return EdxUtilsValidate.validate($el); 738 }, 739 740 liveValidate: function($el, url, dataType, data, method, model) { 741 $.ajax({ 742 url: url, 743 dataType: dataType, 744 data: data, 745 method: method, 746 success: function(response) { 747 model.trigger('validation', $el, response); 748 } 749 }); 750 }, 751 752 inLiveValidationFields: function($el) { 753 var i, 754 name = $el.attr('name') || false; 755 for (i = 0; i < this.liveValidationFields.length; ++i) { 756 if (this.liveValidationFields[i] === name) { 757 return true; 758 } 759 } 760 return false;
761 } 762 }); 763 }); 764}).call(this, define || RequireJS.define); 765 766 767RequireJS.define('text!templates/student_account/form_success.underscore',[],function () { return '<div class="<%- jsHook %> status submission-success">\n <h4 class="message-title"><%- title %></h4>\n <div class="message-copy">\n <%= HtmlUtils.ensureHtml(messageHtml) %>\n </div>\n</div>\n';}); 768 769 770RequireJS.define('text!templates/student_account/form_status.underscore',[],function () { return '<div class="<%- jsHook %> status">\n <div class="message-copy">\n <%= HtmlUtils.ensureHtml(message) %>\n </p>\n</div>\n\n';}); 771 772(function(define) { 773 'use strict'; 774 RequireJS.define('js/student_account/views/LoginView',[ 775 'jquery', 776 'underscore', 777 'gettext', 778 'edx-ui-toolkit/js/utils/html-utils', 779 'edx-ui-toolkit/js/utils/string-utils', 780 'js/student_account/views/FormView', 781 'text!templates/student_account/form_success.underscore', 782 'text!templates/student_account/form_status.underscore' 783 ], function( 784 $, _, gettext, 785 HtmlUtils, 786 StringUtils, 787 FormView, 788 formSuccessTpl, 789 formStatusTpl 790 ) { 791 return FormView.extend({ 792 el: '#login-form', 793 tpl: '#login-tpl', 794 events: { 795 'click .js-login': 'submitForm', 796 'click .forgot-password': 'forgotPassword', 797 'click .login-provider': 'thirdPartyAuth', 798 'click .enterprise-login': 'enterpriseSlugLogin', 799 'click .login-help': 'toggleLoginHelp' 800 }, 801 formType: 'login', 802 requiredStr: '', 803 optionalStr: '', 804 submitButton: '.js-login', 805 formSuccessTpl: formSuccessTpl, 806 formStatusTpl: formStatusTpl, 807 authWarningJsHook: 'js-auth-warning', 808 passwordResetSuccessJsHook: 'js-password-reset-success', 809 defaultFormErrorsTitle: gettext('We couldn\'t sign you in.'), 810 isEnterpriseEnable: false, 811 812 preRender: function(data) { 813 this.providers = data.thirdPartyAuth.providers || []; 814 this.hasSecondaryProviders = ( 815 data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length 816 ); 817 this.currentProvider = data.thirdPartyAuth.currentProvider || ''; 818 this.syncLearnerProfileData = data.thirdPartyAuth.syncLearnerProfileData || false; 819 this.errorMessage = data.thirdPartyAuth.errorMessage || ''; 820 this.platformName = data.platformName; 821 this.resetModel = data.resetModel; 822 this.accountRecoveryModel = data.accountRecoveryModel; 823 this.supportURL = data.supportURL; 824 this.passwordResetSupportUrl = data.passwordResetSupportUrl; 825 this.createAccountOption = data.createAccountOption; 826 this.accountActivationMessages = data.accountActivationMessages; 827 this.accountRecoveryMessages = data.accountRecoveryMessages; 828 this.hideAuthWarnings = data.hideAuthWarnings; 829 this.pipelineUserDetails = data.pipelineUserDetails; 830 this.enterpriseName = data.enterpriseName; 831 this.enterpriseSlugLoginURL = data.enterpriseSlugLoginURL; 832 this.isEnterpriseEnable = data.isEnterpriseEnable; 833 this.is_require_third_party_auth_enabled = data.is_require_third_party_auth_enabled || false; 834 835 this.listenTo(this.model, 'sync', this.saveSuccess); 836 this.listenTo(this.resetModel, 'sync', this.resetEmail); 837 this.listenTo(this.accountRecoveryModel, 'sync', this.resetEmail); 838 }, 839 840 render: function(html) { 841 var fields = html || ''; 842 843 HtmlUtils.setHtml( 844 $(this.el), 845 HtmlUtils.HTML( 846 _.template(this.tpl)({ 847 // We pass the context object to the template so that 848 // we can perform variable interpolation using sprintf 849 HtmlUtils: HtmlUtils, 850 context: { 851 fields: fields, 852 currentProvider: this.currentProvider, 853 syncLearnerProfileData: this.syncLearnerProfileData, 854 providers: this.providers, 855 hasSecondaryProviders: this.hasSecondaryProviders, 856 platformName: this.platformName, 857 createAccountOption: this.createAccountOption, 858 pipelineUserDetails: this.pipelineUserDetails, 859 enterpriseName: this.enterpriseName, 860 is_require_third_party_auth_enabled: this.is_require_third_party_auth_enabled 861 } 862 }) 863 ) 864 ); 865 this.postRender(); 866 867 return this; 868 }, 869 870 postRender: function() { 871 var formErrorsTitle; 872 this.$container = $(this.el); 873 this.$form = this.$container.find('form'); 874 this.$formFeedback = this.$container.find('.js-form-feedback'); 875 this.$submitButton = this.$container.find(this.submitButton); 876 877 if (this.errorMessage) { 878 formErrorsTitle = _.sprintf( 879 gettext('An error occurred when signing you in to %s.'), 880 this.platformName 881 ); 882 this.renderErrors(formErrorsTitle, [this.errorMessage]); 883 } else if (this.currentProvider) { 884 /* If we're already authenticated with a third-party 885 * provider, try logging in. The easiest way to do this 886 * is to simply submit the form. 887 */ 888 this.model.save(); 889 } 890 891 // Display account activation success or error messages. 892 this.renderAccountActivationMessages(); 893 this.renderAccountRecoveryMessages(); 894 }, 895 896 renderAccountActivationMessages: function() { 897 _.each(this.accountActivationMessages, this.renderMessage, this); 898 }, 899 900 renderAccountRecoveryMessages: function() { 901 _.each(this.accountRecoveryMessages, this.renderMessage, this); 902 }, 903 904 renderMessage: function(message) { 905 this.renderFormFeedback(this.formStatusTpl, { 906 jsHook: message.tags, 907 message: HtmlUtils.HTML(message.message) 908 }); 909 }, 910 911 forgotPassword: function(event) { 912 event.preventDefault(); 913 914 this.trigger('password-help'); 915 this.clearPasswordResetSuccess(); 916 }, 917 918 toggleLoginHelp: function(event) { 919 var $help; 920 event.preventDefault(); 921 $help = $('#login-help'); 922 this.toggleHelp(event, $help); 923 }, 924 925 enterpriseSlugLogin: function(event) { 926 event.preventDefault(); 927 if (this.enterpriseSlugLoginURL) { 928 window.location.href = this.enterpriseSlugLoginURL; 929 } 930 }, 931 932 postFormSubmission: function() { 933 this.clearPasswordResetSuccess(); 934 }, 935 936 resetEmail: function() { 937 var email = $('#password-reset-email').val(), 938 successTitle = gettext('Check Your Email'), 939 successMessageHtml = HtmlUtils.interpolateHtml( 940 gettext('{paragraphStart}You entered {boldStart}{email}{boldEnd}. If this email address is associated with your {platform_name} account, we will send a message with password recovery instructions to this email address.{paragraphEnd}' + // eslint-disable-line max-len 941 '{paragraphStart}If you do not receive a password reset message after 1 minute, verify that you entered the correct email address, or check your spam folder.{paragraphEnd}' + // eslint-disable-line max-len 942 '{paragraphStart}
942If you need further assistance, {anchorStart}contact technical support{anchorEnd}.{paragraphEnd}'), { // eslint-disable-line max-len 943 boldStart: HtmlUtils.HTML('<b data-hj-suppress>'), 944 boldEnd: HtmlUtils.HTML('</b>'), 945 paragraphStart: HtmlUtils.HTML('<p>'), 946 paragraphEnd: HtmlUtils.HTML('</p>'), 947 email: email, 948 platform_name: this.platformName, 949 anchorStart: HtmlUtils.HTML( 950 StringUtils.interpolate( 951 '<a href="{passwordResetSupportUrl}">', { 952 passwordResetSupportUrl: this.passwordResetSupportUrl 953 } 954 ) 955 ), 956 anchorEnd: HtmlUtils.HTML('</a>') 957 } 958 ); 959 960 this.clearFormErrors(); 961 this.clearPasswordResetSuccess(); 962 963 this.renderFormFeedback(this.formSuccessTpl, { 964 jsHook: this.passwordResetSuccessJsHook, 965 title: successTitle, 966 messageHtml: successMessageHtml 967 }); 968 }, 969 970 thirdPartyAuth: function(event) { 971 var providerUrl = $(event.currentTarget).data('provider-url') || ''; 972 973 if (providerUrl) { 974 window.location.href = providerUrl; 975 } 976 }, 977 978 saveSuccess: function() { 979 this.trigger('auth-complete'); 980 this.clearPasswordResetSuccess(); 981 }, 982 983 saveError: function(error) { 984 var errorCode; 985 var msg; 986 if (error.status === 0) { 987 msg = gettext('An error has occurred. Check your Internet connection and try again.'); 988 } else if (error.status === 500) { 989 msg = gettext('An error has occurred. Try refreshing the page, or check your Internet connection.'); // eslint-disable-line max-len 990 } else if (error.responseJSON !== undefined && error.responseJSON.error_code === 'inactive-user') { 991 msg = HtmlUtils.interpolateHtml( 992 gettext('In order to sign in, you need to activate your account.{line_break}{line_break}' + 993 'We just sent an activation link to {strong_start} {email} {strong_end}. If ' + 994 ' you do not receive an email, check your spam folders or ' + 995 ' {anchorStart}contact {platform_name} Support{anchorEnd}.'), 996 { 997 email: error.responseJSON.email, 998 platform_name: this.platform_name, 999 line_break: HtmlUtils.HTML('<br/>'), 1000 strong_start: HtmlUtils.HTML('<strong>'), 1001 strong_end: HtmlUtils.HTML('</strong>'), 1002 anchorStart: HtmlUtils.HTML( 1003 StringUtils.interpolate( 1004 '<a href="{SupportUrl}">', { 1005 SupportUrl: this.supportURL, 1006 } 1007 ) 1008 ), 1009 anchorEnd: HtmlUtils.HTML('</a>') 1010 } 1011 ); 1012 } else if (error.responseJSON !== undefined) { 1013 msg = error.responseJSON.value; 1014 errorCode = error.responseJSON.error_code; 1015 } else { 1016 msg = gettext('An unexpected error has occurred.'); 1017 } 1018 1019 this.errors = [ 1020 StringUtils.interpolate( 1021 '<li>{msg}</li>', { 1022 msg: msg 1023 } 1024 ) 1025 ]; 1026 this.clearPasswordResetSuccess(); 1027 1028 /* If the user successfully authenticated with a third-party provider, but they haven't 1029 * linked the accounts, instruct the user on how to link the accounts. 1030 */ 1031 if (errorCode === 'third-party-auth-with-no-linked-account' && this.currentProvider) { 1032 if (!this.hideAuthWarnings) { 1033 this.clearFormErrors(); 1034 this.renderThirdPartyAuthWarning(); 1035 } 1036 } else { 1037 this.renderErrors(this.defaultFormErrorsTitle, this.errors); 1038 } 1039 this.toggleDisableButton(false); 1040 }, 1041 1042 renderThirdPartyAuthWarning: function() { 1043 var message = _.sprintf( 1044 gettext('You have successfully signed into %(currentProvider)s, but your %(currentProvider)s' + 1045 ' account does not have a linked %(platformName)s account. To link your accounts,' + 1046 ' sign in now using your %(platformName)s password.'), 1047 {currentProvider: this.currentProvider, platformName: this.platformName} 1048 ); 1049 1050 this.clearAuthWarning(); 1051 this.renderFormFeedback(this.formStatusTpl, { 1052 jsHook: this.authWarningJsHook, 1053 message: message 1054 }); 1055 }, 1056 1057 clearPasswordResetSuccess: function() { 1058 var query = '.' + this.passwordResetSuccessJsHook; 1059 this.clearFormFeedbackItems(query); 1060 }, 1061 1062 clearAuthWarning: function() { 1063 var query = '.' + this.authWarningJsHook; 1064 this.clearFormFeedbackItems(query); 1065 } 1066 }); 1067 }); 1068}).call(this, define || RequireJS.define); 1069 1070(function(define) { 1071 'use strict'; 1072 RequireJS.define('js/student_account/views/PasswordResetView',[ 1073 'jquery', 1074 'js/student_account/views/FormView' 1075 ], 1076 function($, FormView) { 1077 return FormView.extend({ 1078 el: '#password-reset-form', 1079 1080 tpl: '#password_reset-tpl', 1081 1082 events: { 1083 'click .js-reset': 'submitForm', 1084 'click .reset-help': 'toggleResetHelp' 1085 }, 1086 1087 formType: 'password-reset', 1088 1089 requiredStr: '', 1090 optionalStr: '', 1091 1092 submitButton: '.js-reset', 1093 1094 preRender: function() { 1095 this.element.show($(this.el)); 1096 this.element.show($(this.el).parent()); 1097 this.listenTo(this.model, 'sync', this.saveSuccess); 1098 }, 1099 1100 toggleResetHelp: function(event) { 1101 var $help; 1102 event.preventDefault(); 1103 $help = $('#reset-help'); 1104 this.toggleHelp(event, $help); 1105 }, 1106 1107 saveSuccess: function() { 1108 this.trigger('password-email-sent'); 1109 1110 // Destroy the view (but not el) and unbind events 1111 this.$el.empty().off(); 1112 this.stopListening(); 1113 } 1114 }); 1115 }); 1116}).call(this, define || RequireJS.define); 1117 1118(function(define) { 1119 'use strict'; 1120 RequireJS.define('js/student_account/views/RegisterView',[ 1121 'jquery', 1122 'underscore', 1123 'gettext', 1124 'edx-ui-toolkit/js/utils/string-utils', 1125 'edx-ui-toolkit/js/utils/html-utils', 1126 'js/student_account/views/FormView', 1127 'text!templates/student_account/form_status.underscore' 1128 ], 1129 function( 1130 $, _, gettext, 1131 StringUtils, 1132 HtmlUtils, 1133 FormView, 1134 formStatusTpl 1135 ) { 1136 return FormView.extend({ 1137 el: '#register-form', 1138 tpl: '#register-tpl', 1139 validationUrl: '/api/user/v1/validation/registration', 1140 events: { 1141 'click .js-register': 'submitForm', 1142 'click .login-provider': 'thirdPartyAuth', 1143 'click input[required][type="checkbox"]': 'liveValidateHandler', 1144 'blur input[required], textarea[required], select[required]': 'liveValidateHandler',
1145 'focus input[required], textarea[required], select[required]': 'handleRequiredInputFocus' 1146 }, 1147 liveValidationFields: [ 1148 'name', 1149 'username', 1150 'password', 1151 'email', 1152 'confirm_email', 1153 'country', 1154 'honor_code', 1155 'terms_of_service' 1156 ], 1157 formType: 'register', 1158 formFields: '.form-fields', 1159 formStatusTpl: formStatusTpl, 1160 authWarningJsHook: 'js-auth-warning', 1161 defaultFormErrorsTitle: gettext('We couldn\'t create your account.'), 1162 submitButton: '.js-register', 1163 positiveValidationIcon: 'fa-check', 1164 negativeValidationIcon: 'fa-exclamation', 1165 successfulValidationDisplaySeconds: 3, 1166 // These are reset to true on form submission. 1167 positiveValidationEnabled: true, 1168 negativeValidationEnabled: true, 1169 1170 preRender: function(data) { 1171 this.providers = data.thirdPartyAuth.providers || []; 1172 this.hasSecondaryProviders = ( 1173 data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length 1174 ); 1175 this.currentProvider = data.thirdPartyAuth.currentProvider || ''; 1176 this.syncLearnerProfileData = data.thirdPartyAuth.syncLearnerProfileData || false; 1177 this.errorMessage = data.thirdPartyAuth.errorMessage || ''; 1178 this.platformName = data.platformName; 1179 this.autoSubmit = data.thirdPartyAuth.autoSubmitRegForm; 1180 this.hideAuthWarnings = data.hideAuthWarnings; 1181 this.autoRegisterWelcomeMessage = data.thirdPartyAuth.autoRegisterWelcomeMessage || ''; 1182 this.registerFormSubmitButtonText = 1183 data.thirdPartyAuth.registerFormSubmitButtonText || _('Create Account'); 1184 this.is_require_third_party_auth_enabled = data.is_require_third_party_auth_enabled; 1185 this.enableCoppaCompliance = data.enableCoppaCompliance; 1186 1187 this.listenTo(this.model, 'sync', this.saveSuccess); 1188 this.listenTo(this.model, 'validation', this.renderLiveValidations); 1189 }, 1190 1191 1192 renderFields: function(fields, className) { 1193 var html = [], 1194 i, 1195 fieldTpl = this.fieldTpl; 1196 1197 html.push(HtmlUtils.joinHtml( 1198 HtmlUtils.HTML('<div class="'), 1199 className, 1200 HtmlUtils.HTML('">') 1201 )); 1202 for (i = 0; i < fields.length; i++) { 1203 html.push(HtmlUtils.template(fieldTpl)($.extend(fields[i], { 1204 form: this.formType, 1205 requiredStr: this.requiredStr, 1206 optionalStr: fields[i].name === 'marketing_emails_opt_in' ? '' : this.optionalStr, 1207 supplementalText: fields[i].supplementalText || '', 1208 supplementalLink: fields[i].supplementalLink || '' 1209 }))); 1210 } 1211 html.push('</div>'); 1212 return html; 1213 }, 1214 1215 buildForm: function(data) { 1216 var html = [], 1217 i, 1218 field, 1219 len = data.length, 1220 requiredFields = [], 1221 optionalFields = [], 1222 exposedOptionalFields = []; 1223 1224 this.fields = data; 1225 1226 this.hasOptionalFields = false; 1227 for (i = 0; i < len; i++) { 1228 field = data[i]; 1229 if (field.errorMessages) { 1230 // eslint-disable-next-line no-param-reassign 1231 field.errorMessages = this.escapeStrings(field.errorMessages); 1232 } 1233 1234 if (field.required) { 1235 requiredFields.push(field); 1236 } else { 1237 if (field.type !== 'hidden') { 1238 // For the purporse of displaying the optional field toggle, 1239 // the form should be considered to have optional fields 1240 // only if all of the optional fields are being rendering as 1241 // input elements that are visible on the page. 1242 this.hasOptionalFields = true; 1243 } 1244 1245 if (field.exposed) { 1246 exposedOptionalFields.push(field); 1247 } else { 1248 optionalFields.push(field); 1249 } 1250 } 1251 } 1252 1253 html = this.renderFields(requiredFields, 'required-fields'); 1254 1255 html.push.apply(html, this.renderFields(exposedOptionalFields, 'exposed-optional-fields')); 1256 html.push.apply(html, this.renderFields( 1257 optionalFields, `optional-fields ${!this.enableCoppaCompliance ? '' : 'full-length-fields'}` 1258 )); 1259 1260 this.render(html.join('')); 1261 }, 1262 1263 render: function(html) { 1264 var fields = html || '', 1265 formErrorsTitle = gettext('An error occurred.'), 1266 renderHtml = _.template(this.tpl)({ 1267 /* We pass the context object to the template so that 1268 * we can perform variable interpolation using sprintf 1269 */ 1270 context: { 1271 fields: fields, 1272 currentProvider: this.currentProvider, 1273 syncLearnerProfileData: this.syncLearnerProfileData, 1274 providers: this.providers, 1275 hasSecondaryProviders: this.hasSecondaryProviders, 1276 platformName: this.platformName, 1277 autoRegisterWelcomeMessage: this.autoRegisterWelcomeMessage, 1278 registerFormSubmitButtonText: this.registerFormSubmitButtonText, 1279 is_require_third_party_auth_enabled: this.is_require_third_party_auth_enabled 1280 } 1281 }); 1282 1283 HtmlUtils.setHtml($(this.el), HtmlUtils.HTML(renderHtml)); 1284 1285 this.postRender(); 1286 1287 // Must be called after postRender, since postRender sets up $formFeedback. 1288 if (this.errorMessage) { 1289 this.renderErrors(formErrorsTitle, [this.errorMessage]); 1290 } else if (this.currentProvider && !this.hideAuthWarnings) { 1291 this.renderAuthWarning(); 1292 } 1293 1294 if (this.autoSubmit) { 1295 $(this.el).hide(); 1296 $('#register-honor_code, #register-terms_of_service').prop('checked', true);
1297 this.submitForm(); 1298 } 1299 1300 return this; 1301 }, 1302 1303 postRender: function() { 1304 var inputs = this.$('.form-field'), 1305 inputSelectors = 'input, select, textarea', 1306 inputTipSelectors = ['tip error', 'tip tip-input'], 1307 inputTipSelectorsHidden = ['tip error hidden', 'tip tip-input hidden'], 1308 onInputFocus = function() { 1309 // Apply on focus styles to input 1310 $(this).find('label').addClass('focus-in') 1311 .removeClass('focus-out'); 1312 1313 // Show each input tip 1314 $(this).children().each(function() { 1315 if (inputTipSelectorsHidden.indexOf($(this).attr('class')) >= 0) { 1316 $(this).removeClass('hidden'); 1317 } 1318 }); 1319 }, 1320 onInputFocusOut = function() { 1321 // If input has no text apply focus out styles 1322 if ($(this).find(inputSelectors).val().length === 0) { 1323 $(this).find('label').addClass('focus-out') 1324 .removeClass('focus-in'); 1325 } 1326 1327 // Hide each input tip 1328 $(this).children().each(function() { 1329 // This is a 1 instead of 0 so the error message for a field is not 1330 // hidden on blur and only the help tip is hidden. 1331 if (inputTipSelectors.indexOf($(this).attr('class')) >= 1) { 1332 $(this).addClass('hidden'); 1333 } 1334 }); 1335 }, 1336 handleInputBehavior = function(input) { 1337 // Initially put label in input 1338 if (input.find(inputSelectors).val().length === 0) { 1339 input.find('label').addClass('focus-out') 1340 .removeClass('focus-in'); 1341 } 1342 1343 // Initially hide each input tip 1344 input.children().each(function() { 1345 if (inputTipSelectors.indexOf($(this).attr('class')) >= 0) { 1346 $(this).addClass('hidden'); 1347 } 1348 }); 1349 1350 input.focusin(onInputFocus); 1351 input.focusout(onInputFocusOut); 1352 }, 1353 handleAutocomplete = function() { 1354 $(inputs).each(function() { 1355 var $input = $(this), 1356 isCheckbox = $input.attr('class').indexOf('checkbox') !== -1; 1357 1358 if (!isCheckbox) { 1359 if ($input.find(inputSelectors).val().length === 0 1360 && !$input.is(':-webkit-autofill')) { 1361 $input.find('label').addClass('focus-out') 1362 .removeClass('focus-in'); 1363 } else { 1364 $input.find('label').addClass('focus-in') 1365 .removeClass('focus-out'); 1366 } 1367 } 1368 }); 1369 }; 1370 1371 FormView.prototype.postRender.call(this); 1372 $('.optional-fields').addClass('hidden'); 1373 $('#toggle_optional_fields').change(function() { 1374 window.analytics.track('edx.bi.user.register.optional_fields_selected'); 1375 $('.optional-fields').toggleClass('hidden'); 1376 }); 1377 1378 // Since the honor TOS text has a composed css selector, it is more future proof 1379 // to insert the not toggled optional fields before .honor_tos_combined's parent 1380 // that is the container for the honor TOS text and checkbox. 1381 // xss-lint: disable=javascript-jquery-insert-into-target 1382 $('.exposed-optional-fields').insertBefore( 1383 $('.honor_tos_combined').parent() 1384 ); 1385 1386 // We are swapping the order of these elements here because the honor code agreement 1387 // is a required checkbox field and the optional fields toggle is a cosmetic 1388 // improvement so that we don't have to show all the optional fields. 1389 // xss-lint: disable=javascript-jquery-insert-into-target 1390 $('.checkbox-optional_fields_toggle').insertAfter('.required-fields'); 1391 if (!this.hasOptionalFields) { 1392 $('.checkbox-optional_fields_toggle').addClass('hidden'); 1393 } 1394 // xss-lint: disable=javascript-jquery-insert-into-target 1395 $('.checkbox-honor_code').insertAfter('.optional-fields'); 1396 // xss-lint: disable=javascript-jquery-insert-into-target 1397 $('.checkbox-terms_of_service').insertAfter('.optional-fields'); 1398 1399 // Clicking on links inside a label should open that link. 1400 $('label a').click(function(ev) { 1401 ev.stopPropagation(); 1402 ev.preventDefault(); 1403 window.open($(this).attr('href'), $(this).attr('target'), 'noopener'); 1404 }); 1405 $('.form-field').each(function() { 1406 $(this).find('option:first').html(''); 1407 }); 1408 $(inputs).each(function() { 1409 var $input = $(this), 1410 isCheckbox = $input.attr('class').indexOf('checkbox') !== -1; 1411 if ($input.length > 0 && !isCheckbox) { 1412 handleInputBehavior($input); 1413 } 1414 }); 1415 $('#register-confirm_email').bind('cut copy paste', function(e) { 1416 e.preventDefault(); 1417 }); 1418 setTimeout(handleAutocomplete, 1000); 1419 }, 1420
1421 hideRequiredMessageExceptOnError: function($el) { 1422 // We only handle blur if not in an error state. 1423 if (!$el.hasClass('error')) { 1424 this.hideRequiredMessage($el); 1425 } 1426 }, 1427 1428 hideRequiredMessage: function($el) { 1429 this.doOnInputLabel($el, function($label) { 1430 $label.addClass('hidden'); 1431 }); 1432 }, 1433 1434 doOnInputLabel: function($el, action) { 1435 var $label = this.getRequiredTextLabel($el); 1436 action($label); 1437 }, 1438 1439 handleRequiredInputFocus: function(event) { 1440 var $el = $(event.currentTarget); 1441 // Avoid rendering for required checkboxes. 1442 if ($el.attr('type') !== 'checkbox') { 1443 this.renderRequiredMessage($el); 1444 } 1445 if ($el.hasClass('error')) { 1446 this.doOnInputLabel($el, function($label) { 1447 $label.addClass('error'); 1448 }); 1449 } 1450 }, 1451 1452 renderRequiredMessage: function($el) { 1453 this.doOnInputLabel($el, function($label) { 1454 $label.removeClass('hidden').text(gettext('(required)')); 1455 }); 1456 }, 1457 1458 getRequiredTextLabel: function($el) { 1459 return $('#' + $el.attr('id') + '-required-label'); 1460 }, 1461 1462 renderLiveValidations: function($el, decisions) { 1463 var $label = this.getLabel($el), 1464 $requiredTextLabel = this.getRequiredTextLabel($el), 1465 $icon = this.getIcon($el), 1466 $errorTip = this.getErrorTip($el), 1467 name = $el.attr('name'), 1468 type = $el.attr('type'), 1469 isCheckbox = type === 'checkbox', 1470 hasError = decisions.validation_decisions[name] !== '', 1471 error = isCheckbox ? '' : decisions.validation_decisions[name]; 1472 1473 if (hasError && this.negativeValidationEnabled) { 1474 this.addValidationErrorMsgForScreenReader($el); 1475 this.renderLiveValidationError($el, $label, $requiredTextLabel, $icon, $errorTip, error); 1476 } else if (this.positiveValidationEnabled) { 1477 this.removeValidationErrorMsgForScreenReader($el); 1478 this.renderLiveValidationSuccess($el, $label, $requiredTextLabel, $icon, $errorTip); 1479 } 1480 }, 1481 1482 getLabel: function($el) { 1483 return this.$form.find('label[for=' + $el.attr('id') + ']'); 1484 }, 1485 1486 getIcon: function($el) { 1487 return $('#' + $el.attr('id') + '-validation-icon'); 1488 }, 1489 1490 addValidationErrorMsgForScreenReader: function($el) { 1491 var $validation_node = this.$form.find('#' + $el.attr('id') + '-validation-error'); 1492 $validation_node.find('.sr-only').text('ERROR:'); 1493 }, 1494 1495 removeValidationErrorMsgForScreenReader: function($el) { 1496 var $validation_node = this.$form.find('#' + $el.attr('id') + '-validation-error'); 1497 $validation_node.find('.sr-only').text(''); 1498 }, 1499 1500 getErrorTip: function($el) { 1501 return $('#' + $el.attr('id') + '-validation-error-msg'); 1502 }, 1503 1504 getFieldTimeout: function($el) { 1505 return $('#' + $el.attr('id')).attr('timeout-id') || null; 1506 }, 1507 1508 setFieldTimeout: function($el, time, action) { 1509 $el.attr('timeout-id', setTimeout(action, time)); 1510 }, 1511 1512 clearFieldTimeout: function($el) { 1513 var timeout = this.getFieldTimeout($el); 1514 if (timeout) { 1515 clearTimeout(this.getFieldTimeout($el)); 1516 $el.removeAttr('timeout-id'); 1517 } 1518 }, 1519 1520 renderLiveValidationError: function($el, $label, $req, $icon, $tip, error) { 1521 this.removeLiveValidationIndicators( 1522 $el, $label, $req, $icon, 1523 'success', this.positiveValidationIcon 1524 ); 1525 this.addLiveValidationIndicators( 1526 $el, $label, $req, $icon, $tip, 1527 'error', this.negativeValidationIcon, error 1528 ); 1529 this.renderRequiredMessage($el); 1530 }, 1531 1532 renderLiveValidationSuccess: function($el, $label, $req, $icon, $tip) { 1533 var self = this, 1534 validationFadeTime = this.successfulValidationDisplaySeconds * 1000; 1535 this.removeLiveValidationIndicators( 1536 $el, $label, $req, $icon, 1537 'error', this.negativeValidationIcon 1538 ); 1539 this.addLiveValidationIndicators( 1540 $el, $label, $req, $icon, $tip, 1541 'success', this.positiveValidationIcon, '' 1542 ); 1543 this.hideRequiredMessage($el); 1544 1545 // Hide success indicators after some time. 1546 this.clearFieldTimeout($el); 1547 this.setFieldTimeout($el, validationFadeTime, function() { 1548 self.removeLiveValidationIndicators( 1549 $el, $label, $req, $icon, 1550 'success', self.positiveValidationIcon 1551 ); 1552 self.clearFieldTimeout($el); 1553 }); 1554 }, 1555 1556 addLiveValidationIndicators: function($el, $label, $req, $icon, $tip, indicator, icon, msg) { 1557 $el.addClass(indicator); 1558 $label.addClass(indicator); 1559 $req.addClass(indicator); 1560 $icon.addClass(indicator + ' ' + icon); 1561 if (['username', 'email'].indexOf($el.attr('name')) > -1) { 1562 $tip.addClass(' data-hj-suppress'); 1563 } 1564 $tip.text(msg); 1565 }, 1566 1567 removeLiveValidationIndicators: function($el, $label, $req, $icon, indicator, icon) { 1568 $el.removeClass(indicator); 1569 $label.removeClass(indicator); 1570 $req.removeClass(indicator); 1571 $icon.removeClass(indicator + ' ' + icon); 1572 }, 1573 1574 thirdPartyAuth: function(event) { 1575 var providerUrl = $(event.currentTarget).data('provider-url') || ''; 1576 1577 if (providerUrl) { 1578 window.location.href = providerUrl; 1579 } 1580 }, 1581 1582 saveSuccess: function() { 1583 this.trigger('auth-complete'); 1584 }, 1585 1586 saveError: function(error) { 1587 $(this.el).show(); // Show in case the form was hidden for auto-submission 1588 this.errors = _.flatten( 1589 _.map( 1590 // Something is passing this 'undefined'. Protect against this. 1591 JSON.parse(error.responseText || '[]'), 1592 function(errorList, key) { 1593 if (key === 'error_code') { 1594 return null; 1595 } else { 1596 return _.map( 1597 errorList, 1598 function(errorItem) { 1599 if (errorItem.user_message) { 1600 return StringUtils.interpolate('<li {suppressAttr} >{error}</li>', { 1601 error: errorItem.user_message, 1602 suppressAttr: ( 1603 key === 'email' || key === 'username' 1604 ) ? 'data-hj-suppress' : '' 1605 }); 1606 } 1607 } 1608 ); 1609 } 1610 } 1611 ) 1612 ); 1613 this.renderErrors(this.defaultFormErrorsTitle, this.errors); 1614 this.scrollToFormFeedback(); 1615 this.toggleDisableButton(false); 1616 }, 1617 1618 postFormSubmission: function() { 1619 if (_.compact(this.errors).length) {
1620 // The form did not get submitted due to validation errors. 1621 $(this.el).show(); // Show in case the form was hidden for auto-submission 1622 } 1623 }, 1624 1625 resetValidationVariables: function() { 1626 this.positiveValidationEnabled = true; 1627 this.negativeValidationEnabled = true; 1628 }, 1629 1630 renderAuthWarning: function() { 1631 var msgPart1 = gettext('You\'ve successfully signed into %(currentProvider)s.'), 1632 msgPart2 = gettext( 1633 'We just need a little more information before you start learning with %(platformName)s.' 1634 ), 1635 fullMsg = _.sprintf( 1636 msgPart1 + ' ' + msgPart2, 1637 {currentProvider: this.currentProvider, platformName: this.platformName} 1638 ); 1639 1640 this.renderFormFeedback(this.formStatusTpl, { 1641 jsHook: this.authWarningJsHook, 1642 message: fullMsg 1643 }); 1644 $(this.formFields).removeClass('hidden'); 1645 }, 1646 1647 submitForm: function(event) { // eslint-disable-line no-unused-vars 1648 var elements = this.$form[0].elements, 1649 $el, 1650 i; 1651 1652 // As per requirements, disable positive validation for submission. 1653 this.positiveValidationEnabled = false; 1654 1655 for (i = 0; i < elements.length; i++) { 1656 $el = $(elements[i]); 1657 1658 // Simulate live validation. 1659 if ($el.attr('required')) { 1660 $el.blur(); 1661 } 1662 } 1663 1664 FormView.prototype.submitForm.apply(this, arguments); 1665 }, 1666 1667 getFormData: function() { 1668 var obj = FormView.prototype.getFormData.apply(this, arguments), 1669 $emailElement = this.$form.find('input[name=email]'), 1670 $confirmEmail = this.$form.find('input[name=confirm_email]'); 1671 1672 if ($confirmEmail.length) { 1673 if (!$confirmEmail.val() || ($emailElement.val() !== $confirmEmail.val())) { 1674 this.errors.push(StringUtils.interpolate('<li>{error}</li>', { 1675 error: $confirmEmail.data('errormsg-required') 1676 })); 1677 } 1678 obj.confirm_email = $confirmEmail.val(); 1679 } 1680 1681 return obj; 1682 }, 1683 1684 liveValidateHandler: function(event) { 1685 var $el = $(event.currentTarget); 1686 // Until we get a back-end that can handle all available 1687 // registration fields, we do some generic validation here. 1688 if (this.inLiveValidationFields($el)) { 1689 if ($el.attr('type') === 'checkbox') { 1690 this.liveValidateCheckbox($el); 1691 } else { 1692 this.liveValidate($el); 1693 } 1694 } else { 1695 this.genericLiveValidateHandler($el); 1696 } 1697 // On blur, we do exactly as the function name says, no matter which input. 1698 this.hideRequiredMessageExceptOnError($el); 1699 }, 1700 1701 liveValidate: function($el) { 1702 var data = {}, 1703 field, 1704 i; 1705 for (i = 0; i < this.liveValidationFields.length; ++i) { 1706 field = this.liveValidationFields[i]; 1707 data[field] = $('#register-' + field).val(); 1708 } 1709 FormView.prototype.liveValidate( 1710 $el, this.validationUrl, 'json', data, 'POST', this.model 1711 ); 1712 }, 1713 1714 liveValidateCheckbox: function($checkbox) {
1715 var validationDecisions = {validation_decisions: {}}, 1716 decisions = validationDecisions.validation_decisions, 1717 name = $checkbox.attr('name'), 1718 checked = $checkbox.is(':checked'), 1719 error = $checkbox.data('errormsg-required'); 1720 decisions[name] = checked ? '' : error; 1721 this.renderLiveValidations($checkbox, validationDecisions); 1722 }, 1723 1724 genericLiveValidateHandler: function($el) { 1725 var elementType = $el.attr('type'); 1726 if (elementType === 'checkbox') { 1727 // We are already validating checkboxes in a generic way. 1728 this.liveValidateCheckbox($el); 1729 } else { 1730 this.genericLiveValidate($el); 1731 } 1732 }, 1733 1734 genericLiveValidate: function($el) { 1735 var validationDecisions = {validation_decisions: {}}, 1736 decisions = validationDecisions.validation_decisions, 1737 name = $el.attr('name'), 1738 error = $el.data('errormsg-required'); 1739 decisions[name] = $el.val() ? '' : error; 1740 this.renderLiveValidations($el, validationDecisions); 1741 } 1742 }); 1743 }); 1744}).call(this, define || RequireJS.define); 1745 1746(function(define) { 1747 'use strict'; 1748 RequireJS.define('js/student_account/views/InstitutionLoginView',['jquery', 'underscore', 'backbone', 'edx-ui-toolkit/js/utils/html-utils'], 1749 function($, _, Backbone, HtmlUtils) { 1750 return Backbone.View.extend({ 1751 el: '#institution_login-form', 1752 1753 initialize: function(data) { 1754 var tpl = data.mode == 'register' ? '#institution_register-tpl' : '#institution_login-tpl'; 1755 this.tpl = $(tpl).html(); 1756 this.providers = data.thirdPartyAuth.secondaryProviders || []; 1757 this.platformName = data.platformName; 1758 }, 1759 1760 render: function() { 1761 HtmlUtils.setHtml( 1762 $(this.el), 1763 HtmlUtils.template(this.tpl)({ 1764 // We pass the context object to the template so that 1765 // we can perform variable interpolation using sprintf 1766 providers: this.providers, 1767 platformName: this.platformName 1768 }) 1769 ); 1770 return this; 1771 } 1772 }); 1773 }); 1774}).call(this, define || RequireJS.define); 1775 1776(function(define) { 1777 'use strict'; 1778 RequireJS.define('js/student_account/views/HintedLoginView',['jquery', 'underscore', 'backbone', 'edx-ui-toolkit/js/utils/html-utils'], 1779 function($, _, Backbone, HtmlUtils) { 1780 return Backbone.View.extend({ 1781 el: '#hinted-login-form', 1782 1783 tpl: '#hinted_login-tpl', 1784 1785 events: { 1786 'click .proceed-button': 'proceedWithHintedAuth' 1787 }, 1788 1789 formType: 'hinted-login', 1790 1791 initialize: function(data) { 1792 this.tpl = $(this.tpl).html(); 1793 this.hintedProvider = ( 1794 _.findWhere(data.thirdPartyAuth.providers, {id: data.hintedProvider}) || 1795 _.findWhere(data.thirdPartyAuth.secondaryProviders, {id: data.hintedProvider}) 1796 ); 1797 }, 1798 1799 render: function() { 1800 HtmlUtils.setHtml( 1801 $(this.el), 1802 HtmlUtils.template(this.tpl)({ 1803 hintedProvider: this.hintedProvider 1804 }) 1805 ); 1806 return this; 1807 }, 1808 1809 proceedWithHintedAuth: function(event) { 1810 this.redirect(this.hintedProvider.loginUrl); 1811 }, 1812 1813 /** 1814 * Redirect to a URL. Mainly useful for mocking out in tests. 1815 * @param {string} url The URL to redirect to. 1816 */ 1817 redirect: function(url) { 1818 window.location.href = url; 1819 } 1820 }); 1821 }); 1822}).call(this, define || RequireJS.define); 1823 1824(function(define) { 1825 'use strict'; 1826 RequireJS.define('js/student_account/utils',['jquery'], function($) { 1827 var edxUserCookieUtils = { 1828 userFromEdxUserCookie: function(edxUserInfoCookieName) { 1829 var cookie, user, userCookie; 1830 1831 cookie = document.cookie.match('(^|;)\\s*' + edxUserInfoCookieName + '\\s*=\\s*([^;]+)'); 1832 userCookie = cookie ? cookie.pop() : $.cookie(edxUserInfoCookieName); 1833 1834 if (!userCookie) { 1835 return {}; 1836 } 1837 1838 // returns the user object from cookie. Replaces '054' with ',' and removes '\' 1839 user = userCookie.replace(/\\/g, '').replace(/054/g, ','); 1840 user = user.substring(1, user.length - 1); 1841 return JSON.parse(user); 1842 } 1843 }; 1844 1845 return edxUserCookieUtils; 1846 }); 1847}).call(this, define || RequireJS.define); 1848 1849(function(define) { 1850 'use strict'; 1851 RequireJS.define('js/student_account/multiple_enterprise',['jquery', 'js/student_account/utils', 'jquery.cookie'], function($, Utils) { 1852 var MultipleEnterpriseInterface = { 1853 1854 urls: { 1855 learners: '/enterprise/api/v1/enterprise-learner/', 1856 multipleEnterpriseUrl: '/enterprise/select/active/?success_url=', 1857 enterpriseActivationUrl: '/enterprise/select/active' 1858 }, 1859 1860 headers: { 1861 'X-CSRFToken': $.cookie('csrftoken') 1862 }, 1863 1864 /** 1865 * Fetch the learner data, then redirect the user to a enterprise selection page if multiple 1866 * enterprises were found. 1867 * @param {string} nextUrl The URL to redirect to after multiple enterprise selection or incase 1868 * the selection page is bypassed e.g. when dealing with direct enrolment urls. 1869 */ 1870 check: function(nextUrl, edxUserInfoCookieName) { 1871 var view = this; 1872 var selectionPageUrl = this.urls.multipleEnterpriseUrl + encodeURIComponent(nextUrl); 1873 var username = Utils.userFromEdxUserCookie(edxUserInfoCookieName).username; 1874 var next = nextUrl || '/'; 1875 var enterpriseInUrl = this.getEnterpriseFromUrl(nextUrl); 1876 var userInEnterprise = false;
1877 var userWithMultipleEnterprises = false; 1878 $.ajax({ 1879 url: this.urls.learners + '?username=' + username, 1880 type: 'GET', 1881 contentType: 'application/json; charset=utf-8', 1882 headers: this.headers, 1883 context: this 1884 }).fail(function() { 1885 view.redirect(next); 1886 }).done(function(response) { 1887 userWithMultipleEnterprises = (response.count > 1); 1888 if (userWithMultipleEnterprises) { 1889 if (enterpriseInUrl) { 1890 userInEnterprise = view.checkEnterpriseExists(response, enterpriseInUrl); 1891 if (userInEnterprise) { 1892 view.activate(enterpriseInUrl).fail(function() { 1893 view.redirect(selectionPageUrl); 1894 }).done(function() { 1895 view.redirect(next); 1896 }); 1897 } else { 1898 view.redirect(selectionPageUrl); 1899 } 1900 } else { 1901 view.redirect(selectionPageUrl); 1902 } 1903 } else { 1904 view.redirect(next); 1905 } 1906 }); 1907 }, 1908 1909 redirect: function(url) { 1910 window.location.href = url; 1911 }, 1912 1913 activate: function(enterprise) { 1914 return $.ajax({ 1915 url: this.urls.enterpriseActivationUrl, 1916 method: 'POST', 1917 headers: {'X-CSRFToken': $.cookie('csrftoken')}, 1918 data: {enterprise: enterprise} 1919 }); 1920 }, 1921 1922 getEnterpriseFromUrl: function(url) { 1923 var regex; 1924 regex = RegExp('/enterprise/.*/course/.*/enroll'); 1925 if (typeof url !== 'string' || !regex.test(url)) { 1926 return void(0); 1927 } 1928 return url.split('/')[2]; 1929 }, 1930 1931 checkEnterpriseExists: function(response, enterprise) { 1932 return response.results.some(function(item) { 1933 return item.enterprise_customer.uuid === enterprise; 1934 }); 1935 } 1936 }; 1937 1938 return MultipleEnterpriseInterface; 1939 }); 1940}).call(this, define || RequireJS.define); 1941 1942/* History.js v1.8b2 https://github.com/browserstate/history.js */ 1943typeof JSON!="object"&&(JSON={}),function(){"use strict";function f(e){return e<10?"0"+e:e}function quote(e){return escapable.lastIndex=0,escapable.test(e)?'"'+e.replace(escapable,function(e){var t=meta[e];return typeof t=="string"?t:"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+e+'"'}function str(e,t){var n,r,i,s,o=gap,u,a=t[e];a&&typeof a=="object"&&typeof a.toJSON=="function"&&(a=a.toJSON(e)),typeof rep=="function"&&(a=rep.call(t,e,a));switch(typeof a){case"string":return quote(a);case"number":return isFinite(a)?String(a):"null";case"boolean":case"null":return String(a);case"object":if(!a)return"null";gap+=indent,u=[];if(Object.prototype.toString.apply(a)==="[object Array]"){s=a.length;for(n=0;n<s;n+=1)u[n]=str(n,a)||"null";return i=u.length===0?"[]":gap?"[\n"+gap+u.join(",\n"+gap)+"\n"+o+"]":"["+u.join(",")+"]",gap=o,i}if(rep&&typeof rep=="object"){s=rep.length;for(n=0;n<s;n+=1)typeof rep[n]=="string"&&(r=rep[n],i=str(r,a),i&&u.push(quote(r)+(gap?": ":":")+i))}else for(r in a)Object.prototype.hasOwnProperty.call(a,r)&&(i=str(r,a),i&&u.push(quote(r)+(gap?": ":":")+i));return i=u.length===0?"{}":gap?"{\n"+gap+u.join(",\n"+gap)+"\n"+o+"}":"{"+u.join(",")+"}",gap=o,i}}typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(e){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(e){return this.valueOf()});var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b"," ":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;typeof JSON.stringify!="function"&&(JSON.stringify=function(e,t,n){var r;gap="",indent="";if(typeof n=="number")for(r=0;r<n;r+=1)indent+=" ";else typeof n=="string"&&(indent=n);rep=t;if(!t||typeof t=="function"||typeof t=="object"&&typeof t.length=="number")return str("",{"":e});throw new Error("JSON.stringify")}),typeof JSON.parse!="function"&&(JSON.parse=function(text,reviver){function walk(e,t){var n,r,i=e[t];if(i&&typeof i=="object")for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(r=walk(i,n),r!==undefined?i[n]=r:delete i[n]);return reviver.call(e,t,i)}var j;text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(e){return"\\u"+("0000"+e.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),typeof reviver=="function"?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}(),function(e,t){"use strict";var n=e.History=e.History||{},r=e.jQuery;if(typeof n.Adapter!="undefined")throw new Error("History.js Adapter has already been loaded...");n.Adapter={bind:function(e,t,n){r(e).bind(t,n)},trigger:function(e,t,n){r(e).trigger(t,n)},extractEventData:function(e,n,r){var i=n&&n.originalEvent&&n.originalEvent[e]||r&&r[e]||t;return i},onDomLoad:function(e){r(e)}},typeof n.init!="undefined"&&n.init()}(window),function(e,t){"use strict";var n=e.document,r=e.setTimeout||r,i=e.clearTimeout||i,s=e.setInterval||s,o=e.History=e.History||{};if(typeof o.initHtml4!="undefined")throw new Error("History.js HTML4 Support has already been loaded...");o.initHtml4=function(){if(typeof o.initHtml4.initialized!="undefined")return!1;o.initHtml4.initialized=!0,o.enabled=!0,o.savedHashes=[],o.isLastHash=function(e){var t=o.getHashByIndex(),n;return n=e===t,n},o.isHashEqual=function(e,t){return e=encodeURIComponent(e).replace(/%25/g,"%"),t=encodeURIComponent(t).replace(/%25/g,"%"),e===t},o.saveHash=function(e){return o.isLastHash(e)?!1:(o.savedHashes.push(e),!0)},o.getHashByIndex=function(e){var t=null;return typeof e=="undefined"?t=o.savedHashes[o.savedHashes.length-1]:e<0?t=o.savedHashes[o.savedHashes.length+e]:t=o.savedHashes[e],t},o.discardedHashes={},o.discardedStates={},o.discardState=function(e,t,n){var r=o.getHashByState(e),i;return i={discardedState:e,backState:n,forwardState:t},o.discardedStates[r]=i,!0},o.discardHash=function(e,t,n){var r={discardedHash:e,backState:n,forwardState:t};return o.discardedHashes[e]=r,!0},o.discardedState=function(e){var t=o.getHashByState(e),n;return n=o.discardedStates[t]||!1,n},o.discardedHash=function(e){var t=o.discardedHashes[e]||!1;return t},o.recycleState=function(e){var t=o.getHashByState(e);return o.discardedState(e)&&delete o.discardedStates[t],!0},o.emulated.hashChange&&(o.hashChangeInit=function(){o.checkerFunction=null;
1943var t="",r,i,u,a,f=Boolean(o.getHash());return o.isInternetExplorer()?(r="historyjs-iframe",i=n.createElement("iframe"),i.setAttribute("id",r),i.setAttribute("src","#"),i.style.display="none",n.body.appendChild(i),i.contentWindow.document.open(),i.contentWindow.document.close(),u="",a=!1,o.checkerFunction=function(){if(a)return!1;a=!0;var n=o.getHash(),r=o.getHash(i.contentWindow.document);return n!==t?(t=n,r!==n&&(u=r=n,i.contentWindow.document.open(),i.contentWindow.document.close(),i.contentWindow.document.location.hash=o.escapeHash(n)),o.Adapter.trigger(e,"hashchange")):r!==u&&(u=r,f&&r===""?o.back():o.setHash(r,!1)),a=!1,!0}):o.checkerFunction=function(){var n=o.getHash()||"";return n!==t&&(t=n,o.Adapter.trigger(e,"hashchange")),!0},o.intervalList.push(s(o.checkerFunction,o.options.hashChangeInterval)),!0},o.Adapter.onDomLoad(o.hashChangeInit)),o.emulated.pushState&&(o.onHashChange=function(t){var n=t&&t.newURL||o.getLocationHref(),r=o.getHashByUrl(n),i=null,s=null,u=null,a;return o.isLastHash(r)?(o.busy(!1),!1):(o.doubleCheckComplete(),o.saveHash(r),r&&o.isTraditionalAnchor(r)?(o.Adapter.trigger(e,"anchorchange"),o.busy(!1),!1):(i=o.extractState(o.getFullUrl(r||o.getLocationHref()),!0),o.isLastSavedState(i)?(o.busy(!1),!1):(s=o.getHashByState(i),a=o.discardedState(i),a?(o.getHashByIndex(-2)===o.getHashByState(a.forwardState)?o.back(!1):o.forward(!1),!1):(o.pushState(i.data,i.title,encodeURI(i.url),!1),!0))))},o.Adapter.bind(e,"hashchange",o.onHashChange),o.pushState=function(t,n,r,i){r=encodeURI(r).replace(/%25/g,"%");if(o.getHashByUrl(r))throw new Error("History.js does not support states with fragment-identifiers (hashes/anchors).");if(i!==!1&&o.busy())return o.pushQueue({scope:o,callback:o.pushState,args:arguments,queue:i}),!1;o.busy(!0);var s=o.createStateObject(t,n,r),u=o.getHashByState(s),a=o.getState(!1),f=o.getHashByState(a),l=o.getHash(),c=o.expectedStateId==s.id;return o.storeState(s),o.expectedStateId=s.id,o.recycleState(s),o.setTitle(s),u===f?(o.busy(!1),!1):(o.saveState(s),c||o.Adapter.trigger(e,"statechange"),!o.isHashEqual(u,l)&&!o.isHashEqual(u,o.getShortUrl(o.getLocationHref()))&&o.setHash(u,!1),o.busy(!1),!0)},o.replaceState=function(t,n,r,i){r=encodeURI(r).replace(/%25/g,"%");if(o.getHashByUrl(r))throw new Error("History.js does not support states with fragment-identifiers (hashes/anchors).");if(i!==!1&&o.busy())return o.pushQueue({scope:o,callback:o.replaceState,args:arguments,queue:i}),!1;o.busy(!0);var s=o.createStateObject(t,n,r),u=o.getHashByState(s),a=o.getState(!1),f=o.getHashByState(a),l=o.getStateByIndex(-2);return o.discardState(a,s,l),u===f?(o.storeState(s),o.expectedStateId=s.id,o.recycleState(s),o.setTitle(s),o.saveState(s),o.Adapter.trigger(e,"statechange"),o.busy(!1)):o.pushState(s.data,s.title,s.url,!1),!0}),o.emulated.pushState&&o.getHash()&&!o.emulated.hashChange&&o.Adapter.onDomLoad(function(){o.Adapter.trigger(e,"hashchange")})},typeof o.init!="undefined"&&o.init()}(window),function(e,t){"use strict";var n=e.console||t,r=e.document,i=e.navigator,s=!1,o=e.setTimeout,u=e.clearTimeout,a=e.setInterval,f=e.clearInterval,l=e.JSON,c=e.alert,h=e.History=e.History||{},p=e.history;try{s=e.sessionStorage,s.setItem("TEST","1"),s.removeItem("TEST")}catch(d){s=!1}l.stringify=l.stringify||l.encode,l.parse=l.parse||l.decode;if(typeof h.init!="undefined")throw new Error("History.js Core has already been loaded...");h.init=function(e){return typeof h.Adapter=="undefined"?!1:(typeof h.initCore!="undefined"&&h.initCore(),typeof h.initHtml4!="undefined"&&h.initHtml4(),!0)},h.initCore=function(d){if(typeof h.initCore.initialized!="undefined")return!1;h.initCore.initialized=!0,h.options=h.options||{},h.options.hashChangeInterval=h.options.hashChangeInterval||100,h.options.safariPollInterval=h.options.safariPollInterval||500,h.options.doubleCheckInterval=h.options.doubleCheckInterval||500,h.options.disableSuid=h.options.disableSuid||!1,h.options.storeInterval=h.options.storeInterval||1e3,h.options.busyDelay=h.options.busyDelay||250,h.options.debug=h.options.debug||!1,h.options.initialTitle=h.options.initialTitle||r.title,h.options.html4Mode=h.options.html4Mode||!1,h.options.delayInit=h.options.delayInit||!1,h.intervalList=[],h.
1943clearAllIntervals=function(){var e,t=h.intervalList;if(typeof t!="undefined"&&t!==null){for(e=0;e<t.length;e++)f(t[e]);h.intervalList=null}},h.debug=function(){(h.options.debug||!1)&&h.log.apply(h,arguments)},h.log=function(){var e=typeof n!="undefined"&&typeof n.log!="undefined"&&typeof n.log.apply!="undefined",t=r.getElementById("log"),i,s,o,u,a;e?(u=Array.prototype.slice.call(arguments),i=u.shift(),typeof n.debug!="undefined"?n.debug.apply(n,[i,u]):n.log.apply(n,[i,u])):i="\n"+arguments[0]+"\n";for(s=1,o=arguments.length;s<o;++s){a=arguments[s];if(typeof a=="object"&&typeof l!="undefined")try{a=l.stringify(a)}catch(f){}i+="\n"+a+"\n"}return t?(t.value+=i+"\n-----\n",t.scrollTop=t.scrollHeight-t.clientHeight):e||c(i),!0},h.getInternetExplorerMajorVersion=function(){var e=h.getInternetExplorerMajorVersion.cached=typeof h.getInternetExplorerMajorVersion.cached!="undefined"?h.getInternetExplorerMajorVersion.cached:function(){var e=3,t=r.createElement("div"),n=t.getElementsByTagName("i");while((t.innerHTML="<!--[if gt IE "+ ++e+"]><i></i><![endif]-->")&&n[0]);return e>4?e:!1}();return e},h.isInternetExplorer=function(){var e=h.isInternetExplorer.cached=typeof h.isInternetExplorer.cached!="undefined"?h.isInternetExplorer.cached:Boolean(h.getInternetExplorerMajorVersion());return e},h.options.html4Mode?h.emulated={pushState:!0,hashChange:!0}
1943:h.emulated={pushState:!Boolean(e.history&&e.history.pushState&&e.history.replaceState&&!/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i.test(i.userAgent)&&!/AppleWebKit\/5([0-2]|3[0-2])/i.test(i.userAgent)),hashChange:Boolean(!("onhashchange"in e||"onhashchange"in r)||h.isInternetExplorer()&&h.getInternetExplorerMajorVersion()<8)},h.enabled=!h.emulated.pushState,h.bugs={setHash:Boolean(!h.emulated.pushState&&i.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(i.userAgent)),safariPoll:Boolean(!h.emulated.pushState&&i.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(i.userAgent)),ieDoubleCheck:Boolean(h.isInternetExplorer()&&h.getInternetExplorerMajorVersion()<8),hashEscape:Boolean(h.isInternetExplorer()&&h.getInternetExplorerMajorVersion()<7)},h.isEmptyObject=function(e){for(var t in e)if(e.hasOwnProperty(t))return!1;return!0},h.cloneObject=function(e){var t,n;return e?(t=l.stringify(e),n=l.parse(t)):n={},n},h.getRootUrl=function(){var e=r.location.protocol+"//"+(r.location.hostname||r.location.host);if(r.location.port||!1)e+=":"+r.location.port;return e+="/",e},h.getBaseHref=function(){var e=r.getElementsByTagName("base"),t=null,n="";return e.length===1&&(t=e[0],n=t.href.replace(/[^\/]+$/,"")),n=n.replace(/\/+$/,""),n&&(n+="/"),n},h.getBaseUrl=function(){var e=h.getBaseHref()||h.getBasePageUrl()||h.getRootUrl();return e},h.getPageUrl=function(){var e=h.getState(!1,!1),t=(e||{}).url||h.getLocationHref(),n;return n=t.replace(/\/+$/,"").replace(/[^\/]+$/,function(e,t,n){return/\./.test(e)?e:e+"/"}),n},h.getBasePageUrl=function(){var e=h.getLocationHref().replace(/[#\?].*/,"").replace(/[^\/]+$/,function(e,t,n){return/[^\/]$/.test(e)?"":e}).replace(/\/+$/,"")+"/";return e},h.getFullUrl=function(e,t){var n=e,r=e.substring(0,1);return t=typeof t=="undefined"?!0:t,/[a-z]+\:\/\//.test(e)||(r==="/"?n=h.getRootUrl()+e.replace(/^\/+/,""):r==="#"?n=h.getPageUrl().replace(/#.*/,"")+e:r==="?"?n=h.getPageUrl().replace(/[\?#].*/,"")+e:t?n=h.getBaseUrl()+e.replace(/^(\.\/)+/,""):n=h.getBasePageUrl()+e.replace(/^(\.\/)+/,"")),n.replace(/\#$/,"")},h.getShortUrl=function(e){var t=e,n=h.getBaseUrl(),r=h.getRootUrl();return h.emulated.pushState&&(t=t.replace(n,"")),t=t.replace(r,"/"),h.isTraditionalAnchor(t)&&(t="./"+t),t=t.replace(/^(\.\/)+/g,"./").replace(/\#$/,""),t},h.getLocationHref=function(e){return e=e||r,e.URL===e.location.href?e.location.href:e.location.href===decodeURIComponent(e.URL)?e.URL:e.location.hash&&decodeURIComponent(e.location.href.replace(/^[^#]+/,""))===e.location.hash?e.location.href:e.URL.indexOf("#")==-1&&e.location.href.indexOf("#")!=-1?e.location.href:e.URL||e.location.href},h.store={},h.idToState=h.idToState||{},h.stateToId=h.stateToId||{},h.urlToId=h.urlToId||{},h.storedStates=h.storedStates||[],h.savedStates=h.savedStates||[],h.normalizeStore=function(){h.store.idToState=h.store.idToState||{},h.store.urlToId=h.store.urlToId||{},h.store.stateToId=h.store.stateToId||{}},h.getState=function(e,t){typeof e=="undefined"&&(e=!0),typeof t=="undefined"&&(t=!0);var n=h.getLastSavedState();return!n&&t&&(n=h.createStateObject()),e&&(n=h.cloneObject(n),n.url=n.cleanUrl||n.url),n},h.getIdByState=function(e){var t=h.extractId(e.url),n;if(!t){n=h.getStateString(e);if(typeof h.stateToId[n]!="undefined")t=h.stateToId[n];else if(typeof h.store.stateToId[n]!="undefined")t=h.store.stateToId[n];else{for(;;){t=(new Date).getTime()+String(Math.random()).replace(/\D/g,"");if(typeof h.idToState[t]=="undefined"&&typeof h.store.idToState[t]=="undefined")break}h.stateToId[n]=t,h.idToState[t]=e}}return t},h.normalizeState=function(e){var t,n;if(!e||typeof e!="object")e={};if(typeof e.normalized!="undefined")return e;if(!e.data||typeof e.data!="object")e.data={};return t={},t.normalized=!0,t.title=e.title||"",t.url=h.getFullUrl(e.url?e.url:h.getLocationHref()),t.hash=h.getShortUrl(t.url),t.data=h.cloneObject(e.data),t.id=h.getIdByState(t),t.cleanUrl=t.url.replace(/\??\&_suid.*/,""),t.url=t.cleanUrl,n=!h.isEmptyObject(t.data),(t.title||n)&&h.options.disableSuid!==!0&&(t.hash=h.getShortUrl(t.url).replace(/\??\&_suid.*/,""),/\?/.test(t.hash)||(t.hash+="?"),t.hash+="&_suid="+t.id),t.hashedUrl=h.getFullUrl(t.hash),(h.emulated.pushState||h.bugs.safariPoll)&&h.hasUrlDuplicate(t)&&(t.url=t.hashedUrl),t},h.createStateObject=function(e,t,n){var r={data:e,title:t,url:n};return r=h.normalizeState(r),r},h.getStateById=function(e){e=String(e);var n=h.idToState[e]||h.store.idToState[e]||t;return n},h.getStateString=function(e){var t,n,r;return t=h.normalizeState(e),n={data:t.data,title:e.title,url:e.url},r=l.stringify(n),r},h.getStateId=function(e){var t,n;return t=h.normalizeState(e),n=t.id,n},h.getHashByState=function(e){var t,n;return t=h.normalizeState(e),n=t.hash,n},h.extractId=function(e){var t,n,r,i;return e.indexOf("#")!=-1?i=e.split("#")[0]:i=e,n=/(.*)\&_suid=([0-9]+)$/.exec(i),r=n?n[1]||e:e,t=n?String(n[2]||""):"",t||!1},h.isTraditionalAnchor=function(e){var t=!/[\/\?\.]/.test(e);return t},h.extractState=function(e,t){var n=null,r,i;return t=t||!1,r=h.extractId(e),r&&(n=h.getStateById(r)),n||(i=h.getFullUrl(e),r=h.getIdByUrl(i)||!1,r&&(n=h.getStateById(r)),!n&&t&&!h.isTraditionalAnchor(e)&&(n=h.createStateObject(null,null,i))),n},h.getIdByUrl=function(e){var n=h.urlToId[e]||h.store.urlToId[e]||t;return n},h.getLastSavedState=function(){return h.savedStates[h.savedStates.length-1]||t},h.getLastStoredState=function(){return h.storedStates[h.storedStates.length-1]||t},h.hasUrlDuplicate=function(e){var t=!1,n;return n=h.extractState(e.url),t=n&&n.id!==e.id,t},h.storeState=function(e){return h.urlToId[e.url]=e.id,h.storedStates.push(h.cloneObject(e)),e},h.isLastSavedState=function(e){var t=!1,n,r,i;return h.savedStates.length&&(n=e.id,r=h.getLastSavedState(),i=r.id,t=n===i),t},h.saveState=function(e){return h.isLastSavedState(e)?!1:(h.savedStates.push(h.cloneObject(e)),!0)},h.getStateByIndex=function(e){var t=null;return typeof e=="undefined"?t=h.savedStates[h.savedStates.length-1]:e<0?t=h.savedStates[h.savedStates.length+e]:t=h.savedStates[e],t},h.getCurrentIndex=function(){var e=null;return h.savedStates.length<1?e=0:e=h.savedStates.length-1,e},h.getHash=function(e){var t=h.getLocationHref(e),n;return n=h.getHashByUrl(t),n},h.unescapeHash=function(e){var t=h.normalizeHash(e);return t=decodeURIComponent(t),t},h.normalizeHash=function(e){var t=e.replace(/[^#]*#/,"").replace(/#.*/,"");return t},h.setHash=function(e,t){var n,i;return t!==!1&&h.busy()?(h.pushQueue({scope:h,callback:h.setHash,args:arguments,queue:t}),!1):(h.busy(!0),n=h.extractState(e,!0),n&&!h.emulated.pushState?h.pushState(n.data,n.title,n.url,!1):h.getHash()!==e&&(h.bugs.setHash?(i=h.getPageUrl(),h.pushState(null,null,i+"#"+e,!1)):r.location.hash=e),h)},h.escapeHash=function(t){var n=h.normalizeHash(t);return n=e.encodeURIComponent(n),h.bugs.hashEscape||(n=n.replace(/\%21/g,"!").replace(/\%26/g,"&").replace(/\%3D/g,"=").replace(/\%3F/g,"?")),n},h.getHashByUrl=function(e){var t=String(e).replace(/([^#]*)#?([^#]*)#?(.*)/,"$2");return t=h.unescapeHash(t),t},h.setTitle=function(e){var t=e.title,n;t||(n=h.getStateByIndex(0),n&&n.url===e.url&&(t=n.title||h.options.initialTitle));try{r.getElementsByTagName("title")[0].innerHTML=t.replace("<","<").replace(">",">").replace(" & "," & ")}catch(i){}return r.title=t,h},h.queues=[],h.busy=function(e){typeof e!="undefined"?h.busy.flag=e:typeof h.busy.flag=="undefined"&&(h.busy.flag=!1);if(!h.busy.flag){u(h.busy.timeout);var t=function(){var e,n,r;if(h.busy.flag)return;for(e=h.queues.length-1;e>=0;--e){n=h.queues[e];if(n.length===0)continue;r=n.shift(),h.fireQueueItem(r),h.busy.timeout=o(t,h.options.busyDelay)}};h.busy.timeout=o(t,h.options.busyDelay)}return h.busy.flag},h.busy.flag=!1,h.fireQueueItem=function(e){return e.callback.apply(e.scope||h,e.args||[])},h.pushQueue=function(e){return h.queues[e.queue||0]=h.queues[e.queue||0]||[],h.queues[e.queue||0].push(e),h},h.queue=function(e,t){return typeof e=="function"&&(e={callback:e}),typeof t!="undefined"&&(e.queue=t),h.busy()?h.pushQueue(e):h.fireQueueItem(e),h},h.clearQueue=function(){return h.busy.flag=!1,h.queues=[],h},h.stateChanged=!1,h.doubleChecker=!1,h.doubleCheckComplete=function(){return h.stateChanged=!0,h.doubleCheckClear(),h},h.doubleCheckClear=function(){return h.doubleChecker&&(u(h.doubleChecker),h.doubleChecker=!1),h},h.doubleCheck=function(e){return h.stateChanged=!1,h.doubleCheckClear(),h.bugs.ieDoubleCheck&&(h.doubleChecker=o(function(){return h.doubleCheckClear(),h.stateChanged||e(),!0},h.options.doubleCheckInterval)),h},h.safariStatePoll=function(){var t=h.extractState(h.getLocationHref()),n;if(!h.isLastSavedState(t))return n=t,n||(n=h.createStateObject()),h.Adapter.trigger(e,"popstate"),h;return},h.back=function(e){return e!==!1&&h.busy()?(h.pushQueue({scope:h,callback:h.back,args:arguments,queue:e}),!1):(h.busy(!0),h.doubleCheck(function(){h.back(!1)}),p.go(-1),!0)},h.forward=function(e){return e!==!1&&h.busy()?(h.pushQueue({scope:h,callback:h.forward,args:arguments,queue:e}),!1):(h.busy(!0),h.doubleCheck(function(){h.forward(!1)}),p.go(1),!0)},h.go=function(e,t){var n;if(e>0)for(n=1;n<=e;++n)h.forward(t);else{if(!(e<0))throw new Error("History.go: History.go requires a positive or negative integer passed.");for(n=-1;n>=e;--n)h.back(t)}return h};if(h.emulated.pushState){var v=function(){};h.pushState=h.pushState||v,h.replaceState=h.replaceState||v}else h.onPopState=function(t,n){var r=!1,i=!1,s,o;return h.doubleCheckComplete(),s=h.getHash(),s?(o=h.extractState(s||h.getLocationHref(),!0),o?h.replaceState(o.data,o.title,o.url,!1):(h.Adapter.trigger(e,"anchorchange"),h.busy(!1)),h.expectedStateId=!1,!1):(r=h.Adapter.extractEventData("state",t,n)||!1,r?i=h.getStateById(r):h.expectedStateId?i=h.getStateById(h.expectedStateId):i=h.extractState(h.getLocationHref()),i||(i=h.createStateObject(null,null,h.getL
1943ocationHref())),h.expectedStateId=!1,h.isLastSavedState(i)?(h.busy(!1),!1):(h.storeState(i),h.saveState(i),h.setTitle(i),h.Adapter.trigger(e,"statechange"),h.busy(!1),!0))},h.Adapter.bind(e,"popstate",h.onPopState),h.pushState=function(t,n,r,i){if(h.getHashByUrl(r)&&h.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(i!==!1&&h.busy())return h.pushQueue({scope:h,callback:h.pushState,args:arguments,queue:i}),!1;h.busy(!0);var s=h.createStateObject(t,n,r);return h.isLastSavedState(s)?h.busy(!1):(h.storeState(s),h.expectedStateId=s.id,p.pushState(s.id,s.title,s.url),h.Adapter.trigger(e,"popstate")),!0},h.replaceState=function(t,n,r,i){if(h.getHashByUrl(r)&&h.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(i!==!1&&h.busy())return h.pushQueue({scope:h,callback:h.replaceState,args:arguments,queue:i}),!1;h.busy(!0);var s=h.createStateObject(t,n,r);return h.isLastSavedState(s)?h.busy(!1):(h.storeState(s),h.expectedStateId=s.id,p.replaceState(s.id,s.title,s.url),h.Adapter.trigger(e,"popstate")),!0};if(s){try{h.store=l.parse(s.getItem("History.store"))||{}}catch(m){h.store={}}h.normalizeStore()}else h.store={},h.normalizeStore();h.Adapter.bind(e,"unload",h.clearAllIntervals),h.saveState(h.storeState(h.extractState(h.getLocationHref(),!0))),s&&(h.onUnload=function(){var e,t,n;try{e=l.parse(s.getItem("History.store"))||{}}catch(r){e={}}e.idToState=e.idToState||{},e.urlToId=e.urlToId||{},e.stateToId=e.stateToId||{};for(t in h.idToState){if(!h.idToState.hasOwnProperty(t))continue;e.idToState[t]=h.idToState[t]}for(t in h.urlToId){if(!h.urlToId.hasOwnProperty(t))continue;e.urlToId[t]=h.urlToId[t]}for(t in h.stateToId){if(!h.stateToId.hasOwnProperty(t))continue;e.stateToId[t]=h.stateToId[t]}h.store=e,h.normalizeStore(),n=l.stringify(e);try{s.setItem("History.store",n)}catch(i){if(i.code!==DOMException.QUOTA_EXCEEDED_ERR)throw i;s.length&&(s.removeItem("History.store"),s.setItem("History.store",n))}},h.intervalList.push(a(h.onUnload,h.options.storeInterval)),h.Adapter.bind(e,"beforeunload",h.onUnload),h.Adapter.bind(e,"unload",h.onUnload));if(!h.emulated.pushState){h.bugs.safariPoll&&h.intervalList.push(a(h.safariStatePoll,h.options.safariPollInterval));if(i.vendor==="Apple Computer, Inc."||(i.appCodeName||"")==="Mozilla")h.Adapter.bind(e,"hashchange",function(){h.Adapter.trigger(e,"popstate")}),h.getHash()&&h.Adapter.onDomLoad(function(){h.Adapter.trigger(e,"hashchange")})}},(!h.options||!h.options.delayInit)&&h.init()}(window); 1944RequireJS.define("js/vendor/history", function(){}); 1945 1946(function(define) { 1947 'use strict'; 1948 RequireJS.define('js/student_account/views/AccessView',[ 1949 'jquery', 1950 'utility', 1951 'underscore', 1952 'underscore.string', 1953 'backbone', 1954 'js/student_account/models/LoginModel', 1955 'js/student_account/models/PasswordResetModel', 1956 'js/student_account/models/RegisterModel', 1957 'js/student_account/models/AccountRecoveryModel', 1958 'js/student_account/views/LoginView', 1959 'js/student_account/views/PasswordResetView', 1960 'js/student_account/views/RegisterView', 1961 'js/student_account/views/InstitutionLoginView', 1962 'js/student_account/views/HintedLoginView', 1963 'edx-ui-toolkit/js/utils/html-utils', 1964 'js/student_account/multiple_enterprise', 1965 'js/vendor/history' 1966 ], 1967 function($, utility, _, _s, Backbone, LoginModel, PasswordResetModel, RegisterModel, AccountRecoveryModel, 1968 LoginView, PasswordResetView, RegisterView, InstitutionLoginView, HintedLoginView, HtmlUtils, 1969 multipleEnterpriseInterface) { 1970 return Backbone.View.extend({ 1971 tpl: '#access-tpl', 1972 events: { 1973 'click .form-toggle': 'toggleForm' 1974 }, 1975 subview: { 1976 login: {}, 1977 register: {}, 1978 passwordHelp: {}, 1979 accountRecoveryHelp: {}, 1980 institutionLogin: {}, 1981 hintedLogin: {} 1982 }, 1983 nextUrl: '/dashboard', 1984 // The form currently loaded 1985 activeForm: '', 1986 1987 initialize: function(options) { 1988 /* Mix non-conflicting functions from underscore.string 1989 * (all but include, contains, and reverse) into the 1990 * Underscore namespace 1991 */ 1992 _.mixin(_s.exports()); 1993 this.tpl = $(this.tpl).html(); 1994 1995 this.activeForm = options.initial_mode || 'login'; 1996 1997 this.thirdPartyAuth = options.third_party_auth || { 1998 currentProvider: null, 1999 providers: [] 2000 }; 2001 2002 this.thirdPartyAuthHint = options.third_party_auth_hint || null; 2003 this.edxUserInfoCookieName = options.edx_user_info_cookie_name || 'edx-user-info'; 2004 2005 // Account activation messages 2006 this.accountActivationMessages = options.account_activation_messages || []; 2007 this.accountRecoveryMessages = options.account_recovery_messages || []; 2008 2009 if (options.login_redirect_url) { 2010 this.nextUrl = options.login_redirect_url; 2011 } 2012 2013 this.formDescriptions = { 2014 login: options.login_form_desc, 2015 register: options.registration_form_desc, 2016 reset: options.password_reset_form_desc, 2017 institution_login: null, 2018 hinted_login: null 2019 }; 2020 this.platformName = options.platform_name; 2021 this.supportURL = options.support_link; 2022 this.passwordResetSupportUrl = options.password_reset_support_link; 2023 this.createAccountOption = options.account_creation_allowed; 2024 this.hideAuthWarnings = options.hide_auth_warnings || false; 2025 this.pipelineUserDetails = options.third_party_auth.pipeline_user_details; 2026 this.enterpriseName = options.enterprise_name || ''; 2027 this.enterpriseSlugLoginURL = options.enterprise_slug_login_url || ''; 2028 this.isEnterpriseEnable = options.is_enterprise_enable || false; 2029 this.isAccountRecoveryFeatureEnabled = options.is_account_recovery_feature_enabled || false; 2030 this.is_require_third_party_auth_enabled = options.is_require_third_party_auth_enabled || false; 2031 this.enable_coppa_compliance = options.enable_coppa_compliance; 2032 2033 // The login view listens for 'sync' events from the reset model 2034 this.resetModel = new PasswordResetModel({}, { 2035 method: 'GET', 2036 url: '#' 2037 }); 2038 2039 this.accountRecoveryModel = new AccountRecoveryModel({}, { 2040 method: 'GET', 2041 url: '#' 2042 }); 2043 2044 this.render(); 2045 2046 // Once the third party error message has been shown once, 2047 // there is no need to show it again, if the user changes mode: 2048 this.thirdPartyAuth.errorMessage = null; 2049 2050 // Once the account activation/account recovery messages have been shown once, 2051 // there is no need to show it again, if the user changes mode: 2052 this.accountActivationMessages = []; 2053 this.accountRecoveryMessages = []; 2054 }, 2055 2056 render: function() { 2057 HtmlUtils.setHtml( 2058 $(this.el), 2059 HtmlUtils.HTML( 2060 _.template(this.tpl)({ 2061 mode: this.activeForm 2062 }) 2063 ) 2064 ) 2065 this.postRender(); 2066 2067 return this; 2068 }, 2069 2070 postRender: function() { 2071 // get & check current url hash part & load form accordingly 2072 if (Backbone.history.getHash() === 'forgot-password-modal') { 2073 this.resetPassword(); 2074 } 2075 this.loadForm(this.activeForm); 2076 }, 2077 2078 loadForm: function(type) { 2079 var loadFunc; 2080 if (type === 'reset') { 2081 loadFunc = _.bind(this.load.login, this); 2082 loadFunc(this.formDescriptions.login); 2083 } 2084 loadFunc = _.bind(this.load[type], this); 2085 loadFunc(this.formDescriptions[type]); 2086 }, 2087 2088 load: { 2089 login: function(data) { 2090 var model = new LoginModel({}, { 2091 method: data.method, 2092 url: data.submit_url 2093 }); 2094 var isTpaSaml = this.thirdPartyAuth && this.thirdPartyAuth.finishAuthUrl ? 2095 this.thirdPartyAuth.finishAuthUrl.indexOf('tpa-saml') >= 0 : false;
2096 2097 this.subview.login = new LoginView({ 2098 fields: data.fields, 2099 model: model, 2100 resetModel: this.resetModel, 2101 accountRecoveryModel: this.accountRecoveryModel, 2102 thirdPartyAuth: this.thirdPartyAuth, 2103 accountActivationMessages: this.accountActivationMessages, 2104 accountRecoveryMessages: this.accountRecoveryMessages, 2105 platformName: this.platformName, 2106 supportURL: this.supportURL, 2107 passwordResetSupportUrl: this.passwordResetSupportUrl, 2108 createAccountOption: this.createAccountOption, 2109 hideAuthWarnings: this.hideAuthWarnings, 2110 pipelineUserDetails: this.pipelineUserDetails, 2111 enterpriseName: this.enterpriseName, 2112 enterpriseSlugLoginURL: this.enterpriseSlugLoginURL, 2113 isEnterpriseEnable: this.isEnterpriseEnable, 2114 is_require_third_party_auth_enabled: this.is_require_third_party_auth_enabled 2115 }); 2116 2117 // Listen for 'password-help' event to toggle sub-views 2118 this.listenTo(this.subview.login, 'password-help', this.resetPassword); 2119 2120 // Listen for 'auth-complete' event so we can enroll/redirect the user appropriately. 2121 if (!isTpaSaml) { 2122 this.listenTo(this.subview.login, 'auth-complete', this.loginComplete); 2123 } else { 2124 this.listenTo(this.subview.login, 'auth-complete', this.authComplete); 2125 } 2126 }, 2127 2128 reset: function(data) { 2129 this.resetModel.ajaxType = data.method; 2130 this.resetModel.urlRoot = data.submit_url; 2131 2132 this.subview.passwordHelp = new PasswordResetView({ 2133 fields: data.fields, 2134 model: this.resetModel 2135 }); 2136 2137 // Listen for 'password-email-sent' event to toggle sub-views 2138 this.listenTo(this.subview.passwordHelp, 'password-email-sent', this.passwordEmailSent); 2139 2140 // Focus on the form 2141 $('.password-reset-form').focus(); 2142 }, 2143 2144 register: function(data) { 2145 var model = new RegisterModel({}, { 2146 method: data.method, 2147 url: data.submit_url, 2148 nextUrl: this.nextUrl 2149 }); 2150 2151 this.subview.register = new RegisterView({ 2152 fields: data.fields, 2153 model: model, 2154 thirdPartyAuth: this.thirdPartyAuth, 2155 platformName: this.platformName, 2156 hideAuthWarnings: this.hideAuthWarnings, 2157 is_require_third_party_auth_enabled: this.is_require_third_party_auth_enabled, 2158 enableCoppaCompliance: this.enable_coppa_compliance, 2159 }); 2160 2161 // Listen for 'auth-complete' event so we can enroll/redirect the user appropriately. 2162 this.listenTo(this.subview.register, 'auth-complete', this.authComplete); 2163 }, 2164 2165 institution_login: function(unused) { 2166 this.subview.institutionLogin = new InstitutionLoginView({ 2167 thirdPartyAuth: this.thirdPartyAuth, 2168 platformName: this.platformName, 2169 mode: this.activeForm 2170 }); 2171 2172 this.subview.institutionLogin.render(); 2173 }, 2174 2175 hinted_login: function(unused) { 2176 this.subview.hintedLogin = new HintedLoginView({ 2177 thirdPartyAuth: this.thirdPartyAuth, 2178 hintedProvider: this.thirdPartyAuthHint, 2179 platformName: this.platformName 2180 }); 2181 2182 this.subview.hintedLogin.render(); 2183 } 2184 }, 2185 2186 passwordEmailSent: function() { 2187 var $loginAnchorElement = $('#login-form'); 2188 this.element.hide($(this.el).find('#password-reset-anchor')); 2189 this.element.show($loginAnchorElement); 2190 this.element.scrollTop($loginAnchorElement); 2191 }, 2192 2193 resetPassword: function() { 2194 window.analytics.track('edx.bi.password_reset_form.viewed', { 2195 category: 'user-engagement' 2196 }); 2197 2198 this.element.hide($(this.el).find('#login-form')); 2199 this.loadForm('reset'); 2200 this.element.scrollTop($('#password-reset-anchor')); 2201 }, 2202 2203 toggleForm: function(e) { 2204 var type = $(e.currentTarget).data('type'), 2205 $form = $('#' + type + '-form'), 2206 scrollX = window.scrollX, 2207 scrollY = window.scrollY, 2208 queryParams = url('?'), 2209 queryStr = queryParams.length > 0 ? '?' + queryParams : ''; 2210 2211 e.preventDefault(); 2212 2213 window.analytics.track('edx.bi.' + type + '_form.toggled', { 2214 category: 'user-engagement' 2215 }); 2216 2217 // Load the form. Institution login is always refreshed since it changes based on the previous form. 2218 if (!this.form.isLoaded($form) || type == 'institution_login') { 2219 2220 // We need a special case for loading reset form as there is mismatch of form id 2221 // value ie 'password-reset' vs load function name ie 'reset' 2222 if (type === 'password-reset') { 2223 type = 'reset'; 2224 } 2225 this.loadForm(type); 2226 } 2227 this.activeForm = type; 2228 2229 this.element.hide($(this.el).find('.submission-success')); 2230 this.element.hide($(this.el).find('.form-wrapper')); 2231 this.element.show($form); 2232 2233 // Update url without reloading page 2234 if (type != 'institution_login' && type != 'reset') { 2235 History.pushState(null, document.title, '/' + type + queryStr); 2236 } 2237 analytics.page('login_and_registration', type); 2238 2239 // Focus on the form 2240 $('#' + type).focus(); 2241 2242 // Maintain original scroll position 2243 window.scrollTo(scrollX, scrollY); 2244 }, 2245 2246 /** 2247 * Once authentication has completed successfully: 2248 * 2249 * If we're in a third party auth pipeline, we must complete the pipeline. 2250 * Otherwise, redirect to the specified next step. 2251 * 2252 */ 2253 authComplete: function() { 2254 if (this.thirdPartyAuth && this.thirdPartyAuth.finishAuthUrl) { 2255 this.redirect(this.thirdPartyAuth.finishAuthUrl); 2256 // Note: the third party auth URL likely contains another redirect URL embedded inside 2257 } else { 2258 this.redirect(this.nextUrl); 2259 } 2260 }, 2261 2262 /** 2263 /** 2264 * Take a learner attached to multiple enterprises to the enterprise selection page: 2265 * 2266 */ 2267 loginComplete: function() { 2268 if (this.thirdPartyAuth && this.thirdPartyAuth.finishAuthUrl) { 2269 multipleEnterpriseInterface.check( 2270 this.thirdPartyAuth.finishAuthUrl, 2271 this.edxUserInfoCookieName 2272 ); 2273 // Note: the third party auth URL likely contains another redirect URL embedded inside 2274 } else { 2275 multipleEnterpriseInterface.check(this.nextUrl, this.edxUserInfoCookieName); 2276 } 2277 }, 2278 2279 /** 2280 * Redirect to a URL. Mainly useful for mocking out in tests. 2281 * @param {string} url The URL to redirect to. 2282 */ 2283 redirect: function(url) { 2284 window.location.replace(url); 2285 }, 2286 2287 form: { 2288 isLoaded: function($form) { 2289 return $form.html().length > 0; 2290 } 2291 }, 2292 2293 /* Helper method to toggle display 2294 * including accessibility considerations 2295 */ 2296 element: { 2297 hide: function($el) { 2298 $el.addClass('hidden'); 2299 }, 2300 2301 scrollTop: function($el) { 2302 // Scroll to top of selected element 2303 $('html,body').animate({ 2304 scrollTop: $el.offset().top 2305 }, 'slow'); 2306 }, 2307 2308 show: function($el) { 2309 $el.removeClass('hidden'); 2310 } 2311 } 2312 }); 2313 }); 2314}).call(this, define || RequireJS.define); 2315 2316(function(define) { 2317 'use strict'; 2318 RequireJS.define('js/student_account/logistration_factory',[ 2319 'jquery', 2320 'js/student_account/views/AccessView' 2321 ], 2322 function($, AccessView) { 2323 return function(options) { 2324 var $logistrationElement = $('#login-and-registration-container'); 2325 2326 new AccessView(_.extend(options, {el: $logistrationElement})); 2327 }; 2328 } 2329 ); 2330}).call(this, define || RequireJS.define); 2331
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.