diff --git a/.gitignore b/.gitignore index 4f4740d..b36a0c9 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ node_modules/ *DS_Store* .vscode/ *.bak +*.mda diff --git a/.jshintrc b/.jshintrc index f73b569..2bbba13 100644 --- a/.jshintrc +++ b/.jshintrc @@ -7,6 +7,14 @@ "quotmark" : "double", "eqeqeq" : true, "undef" : true, + "maxstatements" : 15, + "maxdepth" : 2, + "maxcomplexity" : 5, + "nonbsp" : true, + "nonem" : true, + "unparam" : true, + "plusplus" : true, + "regexp" : true, "globals" : { "mendix" : false, "mx" : false, diff --git a/README.md b/README.md index acf0fe9..2676f6c 100644 --- a/README.md +++ b/README.md @@ -23,27 +23,29 @@ For more information on contributing to this repository visit [Contributing to a ### Display * *Username placeholder* - Standard text to be displayed in username field * *Password placeholder* - Standard text to be displayed in password field -* *'Login' button caption* - Text to be displayed on login button -* *Progress bar caption* - Text to display in progress bar, displayed while login request executes +* *Username Label* - Username label caption +* *Password Label* - Password label caption +* *Login button caption* - Text to be displayed on login button * *Empty username/password message* - Feedback message shown when a user didn't provide all credentials -* *'Forgot password' link text* - Text to use in Forgot password link +* *Forgot password link text* - Text to use in the Forgot password link ### Behaviour * *Show progress bar* - Display a progress bar while signing in * *Forgot password microflow* - Microflow being triggered in case of 'forgot password' onclick * *Focus username input field* - Move focus to username field when the form is showed (only use this if it is not done automatically) - +* *Login Failure Text* - Warning message to show when login fails twice ### Mobile * *Auto capitalize* - Enables/disables auto capitalize functionality on username input field for mobile devices * *Auto correct* - Enables/disables auto correct functionality on username input field for mobile devices +* *Username Keyboard Type* - The keyboard type to display when the user is entering their username ### Password * *Show/mask password toggle* - Adds a toggle button to show/mask password.* -* *'Show password' button caption* - The caption that is used for the show button for the value of the password -* *'Mask password' button caption* - The caption that is used for the hide button for the value of the password -* *'Show password' button image* - Optional image to be displayed in 'Show password' button -* *'Mask password' button image* - Optional image to be displayed in 'Mask password' button +* *Show password button caption* - The caption that is used for the show button for the value of the password +* *Mask password button caption* - The caption that is used for the hide button for the value of the password +* *Show password button image* - Optional image to be displayed in 'Show password' button +* *Mask password button image* - Optional image to be displayed in 'Mask password' button ### Casehandling * Convert to lower/uppercase - this option will convert user name input to upper/lower case. Using this functionality only makes practical sense when all usernames defined in your application are either upper or lower cased. diff --git a/package.json b/package.json index a834f09..1fef73d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "LoginForm", - "version": "3.4.0", + "version": "3.5.0", "description": "A custom login form which can be used as an alternative to the default Mendix login page.", "license": "", "author": "", diff --git a/src/LoginForm/LoginForm.xml b/src/LoginForm/LoginForm.xml index dcf01e5..4473118 100644 --- a/src/LoginForm/LoginForm.xml +++ b/src/LoginForm/LoginForm.xml @@ -1,4 +1,7 @@ - + LoginForm Login form @@ -17,80 +20,100 @@ - Username placeholder + Username Placeholder Display Input box sample value - Password placeholder + Password Placeholder Display Input box sample value - - 'Login' button caption + + Show Labels + Display + Show username and password labels + + + Username Label Display - Caption of login button + Username label caption (Only used if Show Labels is Yes) - - Progress bar caption + + Password Label Display - Text to display in progress bar + Password label caption (Only used if Show Labels is Yes) + + + Login Button Caption + Display + Login button caption - Empty username or password text + Empty Username or Password Text Display - Text to display in case of form being submitted without username or password + Displayed when Username or Password is empty - 'Forgot password' link text + Forgot Password Link Text Display - Text to display in 'forgot password' link + Text in Forgot Password link - Show progress bar + Show Progress Bar Behavior Display a progress bar while signing in + + Login Failure Warning + Behavior + Alert that username will be blocked for 5 minutes if login fails 3 times + + + Login Failure Text + Behavior + Warning message to show when login fails twice + + + Forgot Password Microflow + Behavior + Triggered on clicking the Forgot Password link + + + + Focus Username Input Field + Behavior + When form is shown, username filed takes focus (If not automatic) + - Show/mask password toggle + Show/Mask Password Toggle Password - Adds a toggle button to show/mask password. + Toggle button to show/mask password. - 'Show password' button caption + Show Password Button Caption Password - The caption that is used for the show button for the value of the password. + Caption for the button that shows the password - 'Mask password' button caption + Mask Password Button Caption Password - The caption that is used for the hide button for the value of the password. + Caption for the button that masks the password - 'Show password' button image + Show Password Button Image Password - Optional image to be displayed in 'Show password' button. + Optional image to be displayed on Show Password button. - 'Mask password' button image + Mask Password Button Image Password - Optional image to be displayed in 'Mask password' button. - - - Forgot password microflow - Behavior - Microflow being triggered in case of 'forgot password' onclick - - - - Focus username input field - Behavior - Focus the username field when the form is showed (only use this if it is not done automatically) + Optional image to be displayed on Mask Password button. Convert to lowercase/uppercase Casehandling - Enables/disables automatic conversion of username input field to lowercase/uppercase + Automatic conversion of Username to lowercase/uppercase None Convert to lowercase @@ -98,14 +121,25 @@ - Auto correct + Auto-correct Mobile - Enables/disables auto correct functionality on username input field for mobile devices + Auto-correct Username - Auto capitalize + Auto-capitalize Mobile - Enables/disables auto capitalize functionality on username input field for mobile devices + Auto-capitalize Username + + + Username Keyboard Type + Mobile + The keyboard type to display when the user is entering their username + + Text + Email + Telephone Number + Number + diff --git a/src/LoginForm/widget/LoginForm.js b/src/LoginForm/widget/LoginForm.js index a6e02f5..26287ab 100644 --- a/src/LoginForm/widget/LoginForm.js +++ b/src/LoginForm/widget/LoginForm.js @@ -1,299 +1,389 @@ -require([ - - "mxui/widget/_WidgetBase", "dijit/_Widget", "dijit/_TemplatedMixin", - "mxui/dom", "dojo/dom", "dojo/query", "dojo/dom-prop", "dojo/dom-geometry", "dojo/dom-class", "dojo/dom-construct", "dojo/dom-style", "dojo/on", "dojo/_base/lang", "dojo/_base/declare", "dojo/text", "dojo/dom-attr", "dojo/request/xhr", "dojo/_base/json", - "dojo/_base/event", "dojo/_base/window", - "dojo/text!LoginForm/widget/templates/LoginForm.html" - -], function (_WidgetBase, _Widget, _TemplatedMixin, domMx, dom, domQuery, domProp, domGeom, domClass, domConstruct, domStyle, on, lang, declare, text, attr, xhr, dojoJSON, event, win, template) { - +/*jslint browser: true, devel:true, nomen:true, unparam:true, plusplus: true, regexp: true*/ +/*global logger, mendix, define, mx, dojo*/ +/** + LoginForm + ======================== + @file : LoginForm.js + @version : 3.5.0 + @author : Mendix + @date : 7/26/2016 + @copyright : {{copyright}} + @license : Apache 2.0 + Documentation + ======================== + A custom login form which can be used as an alternative to the default Mendix login page. + */ +define([ + + "mxui/widget/_WidgetBase", "dijit/_TemplatedMixin", "mxui/dom", + "dojo/dom", "dojo/query", "dojo/dom-class", + "dojo/dom-construct", "dojo/dom-style", "dojo/on", + "dojo/_base/lang", "dojo/_base/declare", "dojo/text", + "dojo/dom-attr", "dojo/request/xhr", "dojo/json", + "dojo/_base/event", "dojo/html", "dojo/has", + "dojo/text!LoginForm/widget/templates/LoginForm.html", + "dojo/text!LoginForm/widget/templates/LoginFormWithoutShowPassword.html", "dojo/sniff" + +], function (_WidgetBase, _TemplatedMixin, dom, + dojoDom, dojoQuery, domClass, + domConstruct, domStyle, dojoOn, + dojoLang, declare, text, + domAttr, dojoXhr, dojoJSON, + dojoEvent, dojoHtml, dojoHas, + template, templateWithoutShowPassword) { + "use strict"; // Declare widget. - return declare("LoginForm.widget.LoginForm", [ _WidgetBase, _Widget, _TemplatedMixin ], { - - // Template path - templateString: template, + return declare ("LoginForm.widget.LoginForm", [ _WidgetBase, _TemplatedMixin ], { + + // Template path, set in the postMixInProperties function + templateString: "", + + // DOM Elements + loginFormNode: null, + alertMessageNode: null, + usernameInputNode: null, + passwordContainerNode: null, + passwordInputNode: null, + passwordVisibilityToggleButtonNode: null, + submitButtonNode: null, + forgotPasswordNode: null, + forgotPasswordLinkNode: null, + usernameLabelNode: null, + passwordLabelNode: null, + + // Parameters configured in the Modeler. + /** + * Display + */ + userexample: "Username", + passexample: "Password", + logintext: "Login", + progresstext: "", + emptytext: "No username or password given", + forgottext: "Forgot your password?", + showLabels: false, + usernameLabel: "User name", + passwordLabel: "Password", + /** + * Behaviour + */ + showprogress: false, + forgotmf: "", + dofocus: false, + showLoginFailureWarning: false, + loginFailureText: "Your account will be blocked for 5 minutes if login with the same username fails thrice!", + /** + * Password + */ + showPasswordView: true, + showButtonCaption: "Show", + hideButtonCaption: "Mask", + showImage: "", + hideImage: "", + /** + * Mobile + */ + autoCorrect: false, + autoCapitalize: false, + keyboardType: "text", + /** + * Case Handling + */ + convertCase: "none", + // Internal variables. Non-primitives created in the prototype are shared between all widget instances. _handle: null, _userInput : null, _passInput : null, - _captionShow : null, - _captionHide : null, - + _captionShow : "", + _captionHide : "", _indicator : null, _i18nmap : null, _setup: false, - + _loginForm_FailedAttempts: 0, + // dijit._WidgetBase.postMixInProperties is called before rendering occurs, and before any dom nodes are created. + postMixInProperties: function () { + logger.debug(this.id + ".postMixInProperties"); + if (this.showPasswordView === true) { + this.templateString = template; + } else { + this.templateString = templateWithoutShowPassword; + } + }, + // dijit._WidgetBase.postCreate is called after constructing the widget. Implement to do extra setup work postCreate: function () { - this._setupWidget(); + logger.debug(this.id + ".postCreate"); + this._getI18NMap(); + this._updateRendering(); this._setupEvents(); }, - - update: function (obj, callback) { - if (!this._setup) { - this._setupWidget(callback); - } else { - callback(); + // Rerender the interface. + _updateRendering: function () { + logger.debug(this.id + "._updateRendering"); + domClass.add(this.alertMessageNode, "hidden"); + this._addMobileOptions(); + this._showLabels(); + // Check if user wants to display the show-password toggle + if (this.showPasswordView === true) { + this._styleMaskPasswordButton(); + this._styleShowPasswordButton(); + domConstruct.place(this._captionShow, this.passwordVisibilityToggleButtonNode, "only"); } - }, - _setupWidget: function (callback) { + this._setUsernameInputAttributes(); + this._forgotPasswordLink(); - if (this.showImage) { - this._captionShow = ""; - } else { - this._captionShow = ""; - } - if (this.hideImage) { - this._captionHide = ""; - } else { - this._captionHide = ""; + // Captures focus for the input node. (Already automatically set, so can only if not done automatically) + if (this.dofocus) { + this._focusNode(); } - if (this.showButtonCaption !== "") { - if (this.showImage) { - this._captionShow += " "; - } - this._captionShow += this.showButtonCaption; + }, + /** + * Conditionally sets the icon and caption of the show-password button + * @private + */ + _styleShowPasswordButton: function () { + if (this.showImage) { + this._captionShow = ''; } - if (this.hideButtonCaption !== "") { - if (this.hideImage) { - this._captionHide += " "; - } - this._captionHide += this.hideButtonCaption; + if (this.showButtonCaption.trim() !== "") { + this._captionShow += "" + this.showButtonCaption + ""; } - - var templateWithView = [ - "
", - " ", - "
" + this._captionShow + "
", - "
" - ].join(""); - var templateWithoutView = ""; - - //Setup controls - this._userInput = this.usernameInput; - attr.set(this._userInput, "placeholder", this.userexample); - - if (this.showPasswordView === false) { - this.passwordContainer.innerHTML = templateWithoutView; - } else { - this.passwordContainer.innerHTML = templateWithView; - this.connect(dom.byId(this.id + "_view"), "click", dojo.hitch(this, function () { - if (attr.get(this._passInput, "type") === "password") { - attr.set(this._passInput, "type", "text"); - dom.byId(this.id + "_view").innerHTML = this._captionHide; - } else { - attr.set(this._passInput, "type", "password"); - dom.byId(this.id + "_view").innerHTML = this._captionShow; - } - })); + }, + /** + * Conditionally sets the icon and caption of the mask-password button + * @private + */ + _styleMaskPasswordButton: function () { + if (this.hideImage) { + this._captionHide = ''; } - this._passInput = dom.byId(this.id + "_password"); - - attr.set(this._passInput, "placeholder", this.passexample); + if (this.hideButtonCaption.trim() !== "") { + this._captionHide += "" + this.hideButtonCaption + ""; + } + }, + /** + * Conditionally sets the Username node input attributes + * e.g autocorrect, autocapitalize, text-transform + * @private + */ + _setUsernameInputAttributes: function () { if (this.autoCorrect) { - attr.set(this._userInput, "autocorrect", "on"); + domAttr.set(this.usernameInputNode, "autocorrect", "on"); } if (this.autoCapitalize && this.convertCase !== "none") { - attr.set(this._userInput, "autocapitalize", "on"); + domAttr.set(this.usernameInputNode, "autocapitalize", "on"); } if (this.convertCase === "toLowerCase") { - domStyle.set(this._userInput, "text-transform", "lowercase"); + domStyle.set(this.usernameInputNode, "text-transform", "lowercase"); } else if (this.convertCase === "toUpperCase") { - domStyle.set(this._userInput, "text-transform", "uppercase"); - } - - - // Setup text input elements - this.submitButton.value = this.logintext; - + domStyle.set(this.usernameInputNode, "text-transform", "uppercase"); + }// Else this.convertCase === "None", in which case, do nothing + }, + /** + * Shows or hides the forgot-password link + * @private + */ + _forgotPasswordLink: function () { if (this.forgotmf) { - this.forgotLink.innerHTML = this.forgottext; + dojoHtml.set(this.forgotPasswordLinkNode, this.forgottext); } else { - domStyle.set(this.forgotPane, "display", "none"); + domClass.add(this.forgotPasswordNode, "hidden"); + } + }, + /** + * Controls what happens when Login Fails based on the passed in response code + * @param code + * @private + */ + _loginFailed : function (code) { + logger.debug(this.id + "._loginFailed"); + var message = ""; + + if (this._indicator) { + mx.ui.hideProgress(this._indicator); } - domStyle.set(this.messageNode, "display", "none"); - - this._getI18NMap(); + message = this.getStatusMessage(code); + logger.warn("Login has failed with Code: " + code + " and Message: " + message); - if (this.showprogress) { - this._indicator = mx.ui.getProgress(this.progresstext); + if (this.showLoginFailureWarning) { + if (this._loginForm_FailedAttempts === 1) { + message += "
" + this.loginFailureText; + } + this._loginForm_FailedAttempts = this._loginForm_FailedAttempts + 1; } - if (typeof this.dofocus !== "undefined" && this.dofocus) { - this._focusNode(); - } - mendix.lang.nullExec(callback); + dojoHtml.set(this.alertMessageNode, message); + domClass.remove(this.alertMessageNode, "hidden"); }, - + /** + * Retrieves the matching value from the internationalization object + * @param str + * @returns {*} + */ + translate: function (str) { + return window.i18nMap[str]; + }, + /** + * Gets Status message based on the login response code + * @param code + * @returns {*} + */ + getStatusMessage: function (code) { + return this.translate("http" + code) || this.translate("httpdefault"); + }, + // Attach events to HTML dom elements _setupEvents: function () { + logger.debug(this.id + "._setupEvents"); + this.own(dojoOn(this.loginFormNode, "submit", dojoLang.hitch(this, this._loginUser))); - on(this.loginForm, "submit", lang.hitch(this, function(e) { - var user = null, - pass = null, - promise = null; - - domStyle.set(this.messageNode, "display", "none"); + if (this.forgotmf) { + this.own(dojoOn(this.forgotPasswordLinkNode, "click", dojoLang.hitch(this, this._forgotPassword))); + } - if (attr.get(this._passInput, "type") === "text") { - attr.set(this._passInput, "type", "password"); - dom.byId(this.id + "_view").innerHTML = this._captionShow; - } + if (this.passwordVisibilityToggleButtonNode) { + this.own(dojoOn(this.passwordVisibilityToggleButtonNode, "click", dojoLang.hitch(this, this.togglePasswordVisibility))); + } + }, - logger.debug(this.id + ".submitForm"); + /** + * Widget Interaction Methods. + * ====================== + */ - user = this._userInput.value; - pass = this._passInput.value; + /** + * Attempts to login the user. Takes in an Event Parameter + * @param e + * @private + */ + _loginUser: function (e) { + logger.debug(this.id + "._loginUser"); - if (user && pass) { - if (typeof this._indicator !== "undefined" && this._indicator) { - this._indicator.start(); - } + domClass.add(this.alertMessageNode, "hidden"); - // local testing requires you to login with MxAdmin, which is case sensitive - if (user.toLowerCase() === "mxadmin") { - user = "MxAdmin"; - } + if (domAttr.get(this.passwordInputNode, "type") === "text") { + this.togglePasswordVisibility(); + } - dojo.rawXhrPost({ - url : mx.baseUrl, - mimetype : "application/json", - contentType : "application/json", - handleAs : "json", - headers : { - "csrfToken" : mx.session.getCSRFToken() - }, - postData : dojoJSON.toJson({ - action : "login", - params : { - username : user, - password : pass, - locale : "" - } - }), - handle : lang.hitch(this, this._validate) - }); + var username = this._changeCase(this.usernameInputNode.value); + var password = this.passwordInputNode.value; - } else { - domStyle.set(this.messageNode, "display", "block"); - this.messageNode.innerHTML = this.emptytext; + if (username && password) { + if (this.showprogress) { + logger.debug("Showing Progress!!"); + this._indicator = mx.ui.showProgress(); } - event.stop(e); - - return false; - - })); - - if (this.forgotmf) { - on(this.forgotLink, "click", lang.hitch(this, function(e) { - logger.debug(this.id + ".forgotPwd"); - - var action = this.forgotmf; - - if (action) { - mx.data.action({ - params : { - actionname : action - }, - callback : function() { - // ok - }, - error : function() { - logger.error(this.id + ".forgotPwd: Error while calling microflow"); - } - }); + mx.login(username, password, dojoLang.hitch(this, function (response) { + // Login Successful + if (this._indicator) { + mx.ui.hideProgress(this._indicator); } + }), dojoLang.hitch(this, this._loginFailed)); - event.stop(e); - })); + } else { + domClass.remove(this.alertMessageNode, "hidden"); + this.alertMessageNode.innerHTML = this.emptytext; } - on(this._userInput, "keyup", lang.hitch(this, function(e) { - if (this.convertCase === "toUpperCase") { - this._userInput.value = this._userInput.value.toUpperCase(); - } else if (this.convertCase === "toLowerCase") { - this._userInput.value = this._userInput.value.toLowerCase(); - } - })); + dojoEvent.stop(e); }, - - /** - * Interaction widget methods. - * ====================== - */ - _loadData: function () { - // TODO, get aditional data from mendix. + * Triggers Forgot-Password Microflow. Takes in an event parameter + * @param e + * @private + */ + _forgotPassword: function (e) { + logger.debug(this.id + "._forgotPassword"); + mx.data.action({ + params: { + actionname : this.forgotmf + }, + callback: dojoLang.hitch(this, function () { + logger.debug(this.id, "Forgot Password Microflow Triggered Successfully!"); + }), + error: dojoLang.hitch(this, function (error) { + logger.error(this.id + "._forgotPassword: Error while calling microflow " + this.forgotmf, error); + }) + }); + + dojoEvent.stop(e); }, - - _validate : function(response, ioArgs) { - var i18nmap = null, - span = null, - message = " "; - - if (typeof this._indicator !== "undefined" && this._indicator) { - this._indicator.stop(); - } - - i18nmap = this._i18nmap; - - switch(ioArgs.xhr.status) { - case 200 : - mx.login(); - return; - case 400 : - case 401 : - message += i18nmap.http401; - break; - case 402 : - case 403 : - message += i18nmap.http401; - break; - case 404 : - message += i18nmap.http404; - break; - case 500 : - message += i18nmap.http500; - break; - case 503 : - message += i18nmap.http503; - break; - default : - message += i18nmap.httpdefault; - break; + /** + * Show/hide the Password + */ + togglePasswordVisibility : function () { + if (domAttr.get(this.passwordInputNode, "type") === "password") { + domAttr.set(this.passwordInputNode, "type", "text"); + dojoHtml.set(this.passwordVisibilityToggleButtonNode, this._captionHide); + } else { + domAttr.set(this.passwordInputNode, "type", "password"); + dojoHtml.set(this.passwordVisibilityToggleButtonNode, this._captionShow); } - - this.messageNode.innerHTML = message; // is only reached when xhrstatus !== 200 - domStyle.set(this.messageNode, "display", "block"); }, - - _getI18NMap : function() { - logger.debug(this.id + ".injectI18NMap"); - + /** + * Retrieves internalization mapping + * @private + */ + _getI18NMap : function () { + logger.debug(this.id + "._getI18NMap"); if (!window.i18n) { - dojo.xhrGet({ - url : mx.appUrl + "js/login_i18n.js", - handleAs : "javascript", - sync : true - }); + dojoXhr(mx.appUrl + "js/login_i18n.js", { + handleAs: "javascript" + }).then(dojoLang.hitch(this, function (data) { + this._i18nmap = window.i18nMap; + }), dojoLang.hitch(this, function (err) { + logger.debug(this.id + "._getI18Map: Failed to get i18NMap!", err); + })); } - - this._i18nmap = window.i18nMap; }, - - _focusNode : function() { - logger.debug(this.id + ".focusNode"); - - setTimeout(lang.hitch(this, function() { - this.usernameInput.focus(); - }), 0); + /** + * Changes the Case of the passed in username to either uppercase or lowercase + * @param username + * @private + */ + _changeCase: function (username) { + if (this.convertCase === "toUpperCase") { + return username.toUpperCase(); + } + if (this.convertCase === "toLowerCase") { + return username.toLowerCase(); + } + return username; + }, + /** + * Sets focus to the username input node if not the default + * @private + */ + _focusNode : function () { + logger.debug(this.id + "._focusNode"); + //Even with timeout set to 0, function code is made asynchronous + setTimeout(dojoLang.hitch(this, this.usernameInputNode.focus()), 0); + }, + /** + * Detects if widget is running on mobile device and sets the available options e.g Keyboard Type + * @private + */ + _addMobileOptions: function () { + if (dojoHas("ios") || dojoHas("android") || dojoHas("bb")) { + domAttr.set(this.usernameInputNode, "type", this.keyboardType); + } + }, + /** + * Handles whether to show/hide the Username and Password Labels + * @private + */ + _showLabels: function () { + if (this.showLabels) { + domClass.remove(this.usernameLabelNode, "hidden"); + domClass.remove(this.passwordLabelNode, "hidden"); + } } - }); }); diff --git a/src/LoginForm/widget/templates/LoginForm.html b/src/LoginForm/widget/templates/LoginForm.html index 48e4122..13ac597 100644 --- a/src/LoginForm/widget/templates/LoginForm.html +++ b/src/LoginForm/widget/templates/LoginForm.html @@ -1,12 +1,19 @@ -
-
+ +
- + +
-
- - - +
+
+ + +
+
+
+ + + diff --git a/src/LoginForm/widget/templates/LoginFormWithoutShowPassword.html b/src/LoginForm/widget/templates/LoginFormWithoutShowPassword.html new file mode 100644 index 0000000..ac4a295 --- /dev/null +++ b/src/LoginForm/widget/templates/LoginFormWithoutShowPassword.html @@ -0,0 +1,16 @@ + diff --git a/src/LoginForm/widget/ui/LoginForm.css b/src/LoginForm/widget/ui/LoginForm.css new file mode 100644 index 0000000..9be1ccb --- /dev/null +++ b/src/LoginForm/widget/ui/LoginForm.css @@ -0,0 +1,3 @@ +.login-form-widget .toggle span { + padding-left: 5px; +} \ No newline at end of file diff --git a/src/package.xml b/src/package.xml index b601a0a..2e7222a 100644 --- a/src/package.xml +++ b/src/package.xml @@ -1,6 +1,6 @@ - + diff --git a/test/LoginForm Test.mpr b/test/LoginForm Test.mpr index ff3bd62..8ac754e 100644 Binary files a/test/LoginForm Test.mpr and b/test/LoginForm Test.mpr differ diff --git a/test/widgets/LoginForm.mpk b/test/widgets/LoginForm.mpk index d02d30b..9655033 100644 Binary files a/test/widgets/LoginForm.mpk and b/test/widgets/LoginForm.mpk differ diff --git a/xsd/widget.xsd b/xsd/widget.xsd index 4af981e..3f4c734 100644 --- a/xsd/widget.xsd +++ b/xsd/widget.xsd @@ -1,156 +1,153 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +