PageSourceSearch

https://learn.totem-project.org/static/js/student_account/logistration_factory.5cfe73759a4f.js?raw

js totem-project.org collected 2026-09-28 05:50:19 UTC 123,632 bytes, 2,331 lines download raw bytes

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("<","&lt;").replace(">","&gt;").replace(" & "," &amp; ")}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.