/* Playground Demo: Login */ /* This playground demo is still in production I will improve this shortly and add the options to recover and reset the password Let me know if you have questions or ideas how to improve this login modal: stephanwagner.me@gmail.com */ // We are ssetting up a global variable where we can adjust html and texts var jBoxLogin = { jBox: null, // The html of each of the content containers html: { login: '
', register: '
', passwordRecovery: '', // TODO '
', passwordReset: '' // TODO '
' }, // Corresponding titles for content elements title: { login: 'Login', register: 'Create new account', // TODO passwordRecovery: 'Recover password', // TODO passwordReset: 'Reset password' }, // These tooltips will show when a textelemet gets focus textfieldTooltips: { loginUsername: 'For this demo the username is "username"', loginPassword: 'For this demo the password is "password"', registerUsername: 'Choose a unique username', registerEmail: 'Your email address', registerPassword: 'Be tricky, use numbers and special characters' } }; $(document).ready(function() { // On domready create the login modal jBoxLogin.jBox = new jBox('Modal', { // Unique id for CSS access id: 'jBoxLogin', // Dimensions width: 320, // TODO move to global var height: 350, // Attach to elements attach: '#DemoLogin', // Create the content with the html provided in global var content: '
' + jBoxLogin.html.login + jBoxLogin.html.register + jBoxLogin.html.passwordRecovery + jBoxLogin.html.passwordReset + '
', // When the jBox is being initialized add internal functions onInit: function () { // Internal function to show content this.showContent = function (id, force) { // Abort if an ajax call is loading if (!force && $('#LoginWrapper').hasClass('request-running')) return null; // Set the title depending on id this.setTitle(jBoxLogin.title[id]); // Show content depending on id $('.login-container.active').removeClass('active'); $('#LoginContainer-' + id).addClass('active'); // Remove error tooltips // TODO only loop through active elements or store tooltips in global var rather than on the element $.each(jBoxLogin.textfieldTooltips, function (id, tt) { $('#' + id).data('jBoxTextfieldError') && $('#' + id).data('jBoxTextfieldError').close(); }); }; // Initially show content for login this.showContent('login', true); // Add focus and blur events to textfields $.each(jBoxLogin.textfieldTooltips, function (id, tt) { // Focus an textelement $('#' + id).on('focus', function () { // When there is an error tooltip close it $(this).data('jBoxTextfieldError') && $(this).data('jBoxTextfieldError').close(); // Remove the error state from the textfield $(this).removeClass('textfield-error'); // Store the tooltip jBox in the elements data if (!$(this).data('jBoxTextfieldTooltip')) { // TODO create a small jbox plugin $(this).data('jBoxTextfieldTooltip', new jBox('Tooltip', { width: 310, // TODO use modal width - 10 theme: 'TooltipSmall', addClass: 'LoginTooltipSmall', target: $(this), position: { x: 'left', y: 'top' }, outside: 'y', offset: { y: 6, x: 8 }, pointer: 'left:17', content: tt, animation: 'move' })); } $(this).data('jBoxTextfieldTooltip').open(); // Loose focus of textelement }).on('blur', function () { $(this).data('jBoxTextfieldTooltip').close(); }); }); // Internal function to show errors this.showError = function (element, message) { if (!element.data('errorTooltip')) { // TODO add the error class here element.data('errorTooltip', new jBox('Tooltip', { width: 310, theme: 'TooltipError', addClass: 'LoginTooltipError', target: element, position: { x: 'left', y: 'top' }, outside: 'y', offset: { y: 6 }, pointer: 'left:9', content: message, animation: 'move' })); } element.data('errorTooltip').open(); }; // Internal function to change checkbox state this.toggleCheckbox = function () { // Abort if an ajax call is loading if ($('#LoginWrapper').hasClass('request-running')) return null; $('.login-checkbox').toggleClass('login-checkbox-active'); }; // Add checkbox events to checkbox and label $('.login-checkbox, .login-checkbox-label').on('click', function () { this.toggleCheckbox(); }.bind(this)); // Parse an ajax repsonse this.parseResponse = function(response) { try { response = JSON.parse(response.responseText || response); } catch (e) {} return response; }; // Show a global error this.globalError = function () { new jBox('Notice', { color: 'red', content: 'Oops, something went wrong.', attributes: { x: 'right', y: 'bottom' } }); }; // Internal function to disable or enable the form while request is running this.startRequest = function() { this.toggleRequest(); }.bind(this); this.completeRequest = function() { this.toggleRequest(true); }.bind(this); this.toggleRequest = function (enable) { $('#LoginWrapper')[enable ? 'removeClass' : 'addClass']('request-running'); $('#LoginWrapper button')[enable ? 'removeClass' : 'addClass']('loading-bar'); $('#LoginWrapper input, #LoginWrapper button').attr('disabled', enable ? false : 'disabled'); }.bind(this); // Bind ajax login function to login button $('#LoginContainer-login button').on('click', function () { $.ajax({ url: 'https://stephanwagner.me/PlaygroundLogin/login', data: { username: $('#loginUsername').val(), password: $('#loginPassword').val(), remember: $('.login-checkbox').hasClass('login-checkbox-active') ? 1 : 0 }, method: 'post', beforeSend: function () { this.startRequest(); }.bind(this), // Ajax call successfull success: function (response) { this.completeRequest(); response = this.parseResponse(response); // Login successfull if (response.success) { this.close(); new jBox('Notice', { color: 'green', content: 'You are now logged in', attributes: { x: 'right', y: 'bottom' } }); // Redirect or own login behavior here // Login failed } else { // Shake submit button this.animate('shake', {element: $('#LoginContainer-login button')}); if (response.errors) { // Show error on textfields, for login no error tooltips neccessary, username or password is wrong $('#loginUsername, #loginPassword').addClass('textfield-error'); } else { // Backend error this.globalError(); } } }.bind(this), // Ajax call failed error: function () { this.completeRequest(); this.animate('shake', {element: $('#LoginContainer-login button')}); this.globalError(); }.bind(this) }); }.bind(this)); // Bind ajax register function to register button $('#LoginContainer-register button').on('click', function () { $.ajax({ url: 'https://stephanwagner.me/PlaygroundLogin/register', data: { username: $('#registerUsername').val(), email: $('#registerEmail').val(), password: $('#registerPassword').val() }, method: 'post', beforeSend: function () { this.startRequest(); }.bind(this), success: function (response) { this.completeRequest(); response = this.parseResponse(response); // Registration successfull if (response.success) { this.close(); new jBox('Notice', { color: 'green', content: 'Your account was created', attributes: { x: 'right', y: 'bottom' } }); // Redirect or own register behavior here // Registration failed } else { // Shake submit button this.animate('shake', {element: $('#LoginContainer-register button')}); if (response.errors) { // Loop through errors and open tooltips $.each(response.errors, function (id, error) { // TODO Only one tooltip at a time var id_selector = 'register' + (id).substr(0,1).toUpperCase() + (id).substr(1); $('#' + id_selector).addClass('textfield-error'); if (!$('#' + id_selector).data('jBoxTextfieldError')) { $('#' + id_selector).data('jBoxTextfieldError', new jBox('Tooltip', { width: 310, theme: 'TooltipError', addClass: 'LoginTooltipError', target: $('#' + id_selector), position: { x: 'left', y: 'top' }, outside: 'y', offset: { y: 6, x: 8 }, pointer: 'left:17', //content: error, animation: 'move' })); } $('#' + id_selector).data('jBoxTextfieldError').setContent(error).open(); }); // Backend error } else { this.globalError(); } } }.bind(this), // Ajax call failed error: function () { this.completeRequest(); this.animate('shake', {element: $('#RegisterContainer-login button')}); this.globalError(); }.bind(this) }); }.bind(this)); }, onOpen: function () { // Go back to login when we open the modal this.showContent('login', true); }, onClose: function () { // TODO reset form completely // TODO only close jBox with close button, not on overlay click // Remove error tooltips // TODO Better to reset the form, this loop is also in showContent $.each(jBoxLogin.textfieldTooltips, function (id, tt) { $('#' + id).data('jBoxTextfieldError') && $('#' + id).data('jBoxTextfieldError').close(); }); } }); });