diff --git a/CHANGELOG b/CHANGELOG index 469c01bd16..dec0d0f9ad 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Use separate XML parser in Views * Add range selection with Shift-click * Use between numbers in filter box * Fill 'to' with 'from' value on filter box diff --git a/src/view.js b/src/view.js index 292399c884..fa9753c8cd 100644 --- a/src/view.js +++ b/src/view.js @@ -4,12 +4,17 @@ 'use strict'; Sao.View = Sao.class_(Object, { + view_type: null, + el: null, + mnemonic_widget: null, + view_id: null, + editable: null, + children_field: null, + xml_parser: null, init: function(screen, xml) { this.screen = screen; - this.view_type = null; - this.el = null; - this.view_id = null; - this.fields = {}; + this.widgets = {}; + this.state_widgets = []; var attributes = xml.children()[0].attributes; this.attributes = {}; for (var i = 0, len = attributes.length; i < len; i++) { @@ -17,6 +22,18 @@ this.attributes[attribute.name] = attribute.value; } screen.set_on_write(this.attributes.on_write); + + var field_attrs = {}; + for (var name in this.screen.model.fields) { + field_attrs[name] = this.screen.model.fields[name].description; + } + new this.xml_parser( + this, this.screen.exclude_field, field_attrs) + .parse(xml.children()[0]); + }, + set_value: function() { + }, + get_fields: function() { }, get record() { return this.screen.current_record; @@ -27,17 +44,12 @@ get group() { return this.screen.group; }, - set_value: function() { - }, - get_fields: function() { - return Object.keys(this.fields); - }, get selected_records() { return []; }, get_buttons: function() { return []; - } + }, }); Sao.View.idpath2path = function(tree, idpath) { @@ -70,4 +82,68 @@ return new Sao.View.Calendar(screen, xml); } }; + + Sao.View.XMLViewParser = Sao.class_(Object, { + init: function(view, exclude_field, field_attrs) { + this.view = view; + this.exclude_field = exclude_field; + this.field_attrs = field_attrs; + }, + _node_attributes: function(node) { + var node_attrs = {}; + for (var i = 0; i < node.attributes.length; i++) { + var attribute = node.attributes[i]; + node_attrs[attribute.name] = attribute.value; + } + + var field = {}; + if (node_attrs.name) { + field = this.field_attrs[node_attrs.name] || {}; + } + + ['readonly', 'homogeneous'].forEach(function(name) { + if (node_attrs[name]) { + node_attrs[name] = node_attrs[name] == 1; + } + }); + ['yexpand', 'yfill', 'xexpand', 'xfill', 'colspan', + 'position'].forEach(function(name) { + if (node_attrs[name]) { + node_attrs[name] = Number(node_attrs[name]); + } + }); + ['xalign', 'yalign'].forEach(function(name) { + if (node_attrs[name]) { + node_attrs[name] = Number(node_attrs[name]); + } + }); + + if (!jQuery.isEmptyObject(field)) { + if (!node_attrs.widget) { + node_attrs.widget = field.type; + } + if ((node.tagName == 'label') && (!node_attrs.string)) { + node_attrs.string = field.string + Sao.i18n.gettext(':'); + } + if ((node.tagName == 'field') && (!node_attrs.help)) { + node_attrs.help = field.help; + } + + ['relation', 'domain', 'selection', 'string', 'states', + 'relation_field', 'views', 'invisible', 'add_remove', + 'sort', 'context', 'size', 'filename', 'autocomplete', + 'translate', 'create', 'delete', 'selection_change_with', + 'schema_model'].forEach(function(name) { + if (field[name] && !node_attrs[name]) { + node_attrs[name] = field[name]; + } + }); + } + return node_attrs; + }, + parse: function(node) { + var attributes = this._node_attributes(node); + this['_parse_' + node.tagName](node, attributes); + }, + }); }()); diff --git a/src/view/calendar.js b/src/view/calendar.js index ef806b801e..71a674e22c 100644 --- a/src/view/calendar.js +++ b/src/view/calendar.js @@ -3,27 +3,27 @@ (function() { 'use strict'; - Sao.View.Calendar = Sao.class_(Sao.View, { - /* Fullcalendar works with utc date, the default week start day depends on - the user language, the events dates are handled by moment object. */ - init: function(screen, xml) { - Sao.View.Calendar._super.init.call(this, screen, xml); - // Used to check if the events are still processing - this.processing = true; - this.view_type = 'calendar'; - this.el = jQuery('
', { - 'class': 'calendar' - }); - var lang = Sao.i18n.getlang(); - lang = lang.slice(0, 2); + Sao.View.CalendarXMLViewParser = Sao.class_(Sao.View.XMLViewParser, { + _parse_calendar: function(node, attributes) { + [].forEach.call(node.childNodes, function(child) { + this.parse(child); + }.bind(this)); + + var view_week; + if (this.view.screen.model.fields[attributes.dtstart] + .description.type == "datetime") { + view_week = 'agendaWeek'; + } else { + view_week = 'basicWeek'; + } var defaultview = 'month'; - if (this.attributes.mode == 'week') { - defaultview = this.get_week_view(); + if (attributes.mode == 'week') { + defaultview = view_week; } var header = { left: 'today prev,next', center: 'title', - right: 'month,' + this.get_week_view(), + right: 'month,' + view_week, }; if (Sao.i18n.rtl) { var header_rtl = jQuery.extend({}, header); @@ -31,12 +31,12 @@ header_rtl.right = header.left; header = header_rtl; } - this.el.fullCalendar({ + this.view.el.fullCalendar({ defaultView: defaultview, header: header, timeFormat: 'H:mm', - events: this.get_events.bind(this), - locale: lang, + events: this.view.get_events.bind(this.view), + locale: Sao.i18n.getlang().slice(0, 2), isRTL: Sao.i18n.rtl, themeSystem: 'bootstrap3', bootstrapGlyphicons: { @@ -48,19 +48,33 @@ 'month': Sao.i18n.gettext("Month"), 'week': Sao.i18n.gettext("Week"), }, - eventRender: this.event_render.bind(this), - eventResize: this.event_resize.bind(this), - eventDrop: this.event_drop.bind(this), - eventClick: this.event_click.bind(this), - dayClick: this.day_click.bind(this), + eventRender: this.view.event_render.bind(this.view), + eventResize: this.view.event_resize.bind(this.view), + eventDrop: this.view.event_drop.bind(this.view), + eventClick: this.view.event_click.bind(this.view), + dayClick: this.view.day_click.bind(this.view), }); + }, + _parse_field: function(node, attributes) { + this.view.fields.push(attributes.name); + }, + }); + + Sao.View.Calendar = Sao.class_(Sao.View, { + /* Fullcalendar works with utc date, the default week start day depends on + the user language, the events dates are handled by moment object. */ + editable: false, + view_type: 'calendar', + xml_parser: Sao.View.CalendarXMLViewParser, + init: function(screen, xml) { + // Used to check if the events are still processing + this.processing = true; this.fields = []; - xml.find('calendar').children().each(function(pos, child){ - if (child.tagName == 'field') { - this.fields.push(child.attributes.name.value); - } - }.bind(this)); - this.el.fullCalendar('changeView', defaultview); + this.el = jQuery('
', { + 'class': 'calendar' + }); + Sao.View.Calendar._super.init.call(this, screen, xml); + //this.el.fullCalendar('changeView', defaultview); }, get_colors: function(record) { var colors = {}; @@ -158,14 +172,6 @@ }.bind(this)); }.bind(this)); }, - get_week_view: function() { - if (this.screen.model.fields[this.attributes.dtstart] - .description.type == "datetime") { - return 'agendaWeek'; - } else { - return 'basicWeek'; - } - }, event_click: function(calEvent, jsEvent, view) { // Prevent opening the wrong event while the calendar event clicked // when loading diff --git a/src/view/form.js b/src/view/form.js index 42069a6afd..9b7ca79e39 100644 --- a/src/view/form.js +++ b/src/view/form.js @@ -14,236 +14,56 @@ function eval_pyson(value){ (function() { 'use strict'; - Sao.View.Form = Sao.class_(Sao.View, { - editable: true, - init: function(screen, xml) { - Sao.View.Form._super.init.call(this, screen, xml); - this.view_type = 'form'; - this.el = jQuery('
', { - 'class': 'form' - }); - this.widgets = {}; - this.widget_id = 0; - this.state_widgets = []; - this.containers = []; - this.notebooks = []; - this.expandables = []; + Sao.View.FormXMLViewParser = Sao.class_(Sao.View.XMLViewParser, { + init: function(view, exclude_field, field_attrs) { + Sao.View.FormXMLViewParser._super.init.call( + this, view, exclude_field, field_attrs); + this._containers = []; + this._mnemonics = {}; + }, + get container() { + if (this._containers.length > 0) { + return this._containers[this._containers.length - 1]; + } + return null; + }, + _parse_form: function(node, attributes) { + var container = new Sao.View.Form.Container( + Number(node.getAttribute('col') || 4)); + this.view.containers.push(container); + this.view.el.append(container.el); + this.parse_child(node, container); + if (this._containers.length > 0) { + throw 'AssertionError'; + } + }, + parse_child: function(node, container) { + if (container) { + this._containers.push(container); + } + [].forEach.call(node.childNodes, function(child) { + this.parse(child); + }.bind(this)); + if (container) { + this._containers.pop(); + } + }, + _parse_field: function(node, attributes) { + var name = attributes.name; + if (name && (name == this.exclude_field)) { + this.container.add(null, attributes); + return; + } + var WidgetFactory = Sao.View.FormXMLViewParser.WIDGETS[ + attributes.widget]; + var widget = new WidgetFactory(this.view, attributes); + if (!this.view.widgets[name]) { + this.view.widgets[name] = []; + } + this.view.widgets[name].push(widget); + widget.position = this.view.widget_id += 1; - var root = xml.children()[0]; - var container = this.parse(screen.model, root); - this.el.append(container.el); - }, - _parse_node: function(model, child, container, attributes, labels) { - var widget; - switch (child.tagName) { - case 'image': - this._parse_image( - model, child, container, attributes); - break; - case 'separator': - this._parse_separator( - model, child, container, attributes); - break; - case 'label': - widget = this._parse_label( - model, child, container, attributes); - if (attributes.name && widget) { - labels[attributes.name] = widget; - } - break; - case 'newline': - container.add_row(); - break; - case 'button': - this._parse_button(child, container, attributes); - break; - case 'notebook': - this._parse_notebook( - model, child, container, attributes); - break; - case 'page': - this._parse_page(model, child, container, attributes); - break; - case 'field': - widget = this._parse_field( - model, child, container, attributes); - if ((attributes.name in labels) && - widget && - widget.labelled) { - var label = labels[attributes.name]; - label.el.uniqueId(); - widget.labelled.uniqueId(); - widget.labelled.attr( - 'aria-labelledby', label.el.attr('id')); - label.el.attr('for', widget.labelled.attr('id')); - } - break; - case 'group': - this._parse_group(model, child, container, attributes); - break; - case 'hpaned': - this._parse_paned(model, child, container, attributes, - 'horizontal'); - break; - case 'vpaned': - this._parse_paned(model, child, container, attributes, - 'vertical'); - break; - case 'child': - this._parse_child(model, child, container, attributes); - break; - } - }, - parse: function(model, node, container) { - if (container === undefined) { - container = new Sao.View.Form.Container( - Number(node.getAttribute('col') || 4)); - this.containers.push(container); - } - var labels = {}; - var _parse = function(index, child) { - var attributes = {}; - for (var i = 0, len = child.attributes.length; i < len; i++) { - var attribute = child.attributes[i]; - attributes[attribute.name] = attribute.value; - } - ['readonly', 'invisible'].forEach(function(name) { - if (attributes[name]) { - attributes[name] = attributes[name] == 1; - } - }); - ['yexpand', 'yfill', 'xexpand', 'xfill', 'colspan'].forEach( - function(name) { - if (attributes[name]) { - attributes[name] = Number(attributes[name]); - } - }); - this._parse_node(model, child, container, attributes, labels); - }; - jQuery(node).children().each(_parse.bind(this)); - return container; - }, - _parse_image: function(model, node, container, attributes) { - var image = new Sao.View.Form.Image_(attributes); - this.state_widgets.push(image); - container.add(attributes, image); - }, - _parse_separator: function(model, node, container, attributes) { - var name = attributes.name; - var text = attributes.string; - if (name in model.fields) { - if (!attributes.states && (name in model.fields)) { - attributes.states = model.fields[name].description.states; - } - if (!text) { - text = model.fields[name].description.string; - } - } - var separator = new Sao.View.Form.Separator(text, attributes); - this.state_widgets.push(separator); - container.add(attributes, separator); - }, - _parse_label: function(model, node, container, attributes) { - var name = attributes.name; - if (attributes.xexpand === undefined) { - attributes.xexpand = 0; - } - if (name in model.fields) { - if (name == this.screen.exclude_field) { - container.add(attributes); - return; - } - if (!attributes.states && (name in model.fields)) { - attributes.states = model.fields[name].description.states; - } - if (attributes.string === undefined) { - attributes.string = model.fields[name] - .description.string + Sao.i18n.gettext(':'); - } - } - if (attributes.xalign === undefined) { - attributes.xalign = 1.0; - } - var label = new Sao.View.Form.Label(attributes.string, attributes); - this.state_widgets.push(label); - container.add(attributes, label); - return label; - }, - _parse_button: function(node, container, attributes) { - var button = new Sao.common.Button(attributes); - this.state_widgets.push(button); - container.add(attributes, button); - button.el.click(button, this.button_clicked.bind(this)); - }, - _parse_notebook: function(model, node, container, attributes) { - if (attributes.colspan === undefined) { - attributes.colspan = 4; - } - var notebook = new Sao.View.Form.Notebook(attributes); - if (attributes.height) { - notebook.el.css('min-height', attributes.height + 'px'); - } - if (attributes.width) { - notebook.el.css('min-width', attributes.width + 'px'); - } - this.notebooks.push(notebook); - this.state_widgets.push(notebook); - container.add(attributes, notebook); - this.parse(model, node, notebook); - }, - _parse_page: function(model, node, container, attributes) { - var text = attributes.string; - if (attributes.name in model.fields) { - var field = model.fields[attributes.name]; - if (attributes.name == this.screen.exclude_field) { - return; - } - ['states', 'string'].forEach(function(attr) { - if ((attributes[attr] === undefined) && - (field.description[attr] !== undefined)) { - attributes[attr] = field.description[attr]; - } - }); - } - var page = this.parse(model, node); - page = new Sao.View.Form.Page( - container.add( - page.el, attributes.string, attributes.icon), - attributes); - this.state_widgets.push(page); - }, - _parse_field: function(model, node, container, attributes) { - var name = attributes.name; - if (!(name in model.fields) || name == this.screen.exclude_field) { - container.add(attributes); - return; - } - if (!attributes.widget) { - attributes.widget = model.fields[name] - .description.type; - } - var attribute_names = ['relation', 'domain', 'selection', 'help', - 'relation_field', 'string', 'views', 'add_remove', 'sort', - 'context', 'size', 'filename', 'autocomplete', 'translate', - 'create', 'delete', 'selection_change_with', 'schema_model']; - for (var i in attribute_names) { - var attr = attribute_names[i]; - if ((attr in model.fields[name].description) && - (node.getAttribute(attr) === null)) { - attributes[attr] = model.fields[name] - .description[attr]; - } - } - var WidgetFactory = Sao.View.form_widget_get( - attributes.widget); - if (!WidgetFactory) { - container.add(attributes); - return; - } - var widget = new WidgetFactory(name, model, attributes); - widget.position = this.widget_id += 1; - widget.view = this; - if (WidgetFactory.prototype.expand) { + if (widget.expand) { if (attributes.yexpand === undefined) { attributes.yexpand = true; } @@ -251,62 +71,137 @@ function eval_pyson(value){ attributes.yfill = true; } } - if (attributes.height) { + + if (attributes.height !== undefined) { widget.el.css('min-height', attributes.height + 'px'); } if (attributes.width !== undefined) { widget.el.css('min-width', attributes.width + 'px'); } - container.add(attributes, widget); - if (this.widgets[name] === undefined) { - this.widgets[name] = []; + + this.container.add(widget, attributes); + + if (this._mnemonics[name] && widget.labelled) { + var label = this._mnemonics[name]; + label.el.uniqueId(); + widget.labelled.uniqueId(); + widget.labelled.attr('aria-labelledby', label.el.attr('id')); + label.el.attr('for', widget.labelled.attr('id')); } - this.widgets[name].push(widget); - this.fields[name] = true; - return widget; }, - _parse_group: function(model, node, container, attributes) { - if (attributes.name !== undefined) { - var field = model.fields[attributes.name]; - if (attributes.name == this.screen.exclude_field) { - container.add(attributes); - return; - } - ['states', 'string'].forEach(function(attr) { - if (!(attr in attributes) && (attr in field.description)) { - attributes[attr] = field.description[attr]; - } - }); + _parse_button: function(node, attributes) { + var button = new Sao.common.Button(attributes); + button.el.click(button, this.view.button_clicked.bind(this.view)); + this.view.state_widgets.push(button); + this.container.add(button, attributes); + }, + _parse_image: function(node, attributes) { + var image = new Sao.View.Form.Image_(attributes); + this.view.state_widgets.push(image); + this.container.add(image, attributes); + }, + _parse_separator: function(node, attributes) { + var text = attributes.string; + var separator = new Sao.View.Form.Separator(text, attributes); + this.view.state_widgets.push(separator); + this.container.add(separator, attributes); + }, + _parse_label: function(node, attributes) { + var name = attributes.name; + if (name && (name == this.exclude_field)) { + this.container.add(null, attributes); + return; + } + if (attributes.xexpand === undefined) { + attributes.xexpand = 0; + } + if (attributes.xalign === undefined) { + attributes.xalign = 1.0; + } + var label = new Sao.View.Form.Label(attributes.string, attributes); + this.view.state_widgets.push(label); + this.container.add(label, attributes); + if (name) { + this._mnemonics[name] = label; + } + }, + _parse_newline: function(node, attributes) { + this.container.add_row(); + }, + _parse_notebook: function(node, attributes) { + if (attributes.colspan === undefined) { + attributes.colspan = 4; + } + var notebook = new Sao.View.Form.Notebook(attributes); + if (attributes.height !== undefined) { + notebook.el.css('min-height', attributes.height + 'px'); + } + if (attributes.width !== undefined) { + notebook.el.css('min-width', attributes.width + 'px'); + } + this.view.state_widgets.push(notebook); + this.view.notebooks.push(notebook); + this.container.add(notebook, attributes); + this.parse_child(node, notebook); + }, + _parse_page: function(node, attributes) { + if (attributes.name && (attributes.name == this.exclude_field)) { + return; + } + var container = new Sao.View.Form.Container( + Number(node.getAttribute('col') || 4)); + this.view.containers.push(container); + this.parse_child(node, container); + var page = new Sao.View.Form.Page( + this.container.add( + container.el, attributes.string, attributes.icon), + attributes); + this.view.state_widgets.push(page); + }, + _parse_group: function(node, attributes) { + var group = new Sao.View.Form.Container( + Number(node.getAttribute('col') || 4)); + this.view.containers.push(group); + this.parse_child(node, group); + + if (attributes.name && (attributes.name == this.exclude_field)) { + this.container.add(null, attributes); + return; } var widget; if (attributes.expandable !== undefined) { widget = new Sao.View.Form.Expander(attributes); widget.set_expanded(attributes.expandable === '1'); - this.expandables.push(widget); + this.view.expandables.push(widget); } else { widget = new Sao.View.Form.Group(attributes); } - widget.add(this.parse(model, node)); + widget.add(group); - this.state_widgets.push(widget); - container.add(attributes, widget); + this.view.state_widgets.push(widget); + this.container.add(widget, attributes); }, - _parse_paned: function(model, node, container, attributes, - orientation) { - if (attributes.yexpand === undefined) { - attributes.yexpand = true; - } - if (attributes.yfill === undefined) { - attributes.yfill = true; - } + _parse_hpaned: function(node, attributes) { + this._parse_paned(node, attributes, 'horizontal'); + }, + _parse_vpaned: function(node, attributes) { + this._parse_paned(node, attributes, 'vertical'); + }, + _parse_paned: function(node, attributes, orientation) { var paned = new Sao.common.Paned(orientation); // TODO position - container.add(attributes, paned); - this.parse(model, node, paned); + this.view.containers.push(paned); + this.container.add(paned, attributes); + this.parse_child(node, paned); }, - _parse_child: function(model, node, paned, attributes) { - var container = this.parse(model, node); + _parse_child: function(node, attributes) { + var paned = this.container; + var container = new Sao.View.Form.Container( + Number(node.getAttribute('col') || 4)); + this.view.containers.push(container); + this.parse_child(node, container); + var child; if (!paned.get_child1().children().length) { child = paned.get_child1(); @@ -315,6 +210,22 @@ function eval_pyson(value){ } child.append(container.el); }, + }); + + Sao.View.Form = Sao.class_(Sao.View, { + editable: true, + view_type: 'form', + xml_parser: Sao.View.FormXMLViewParser, + init: function(screen, xml) { + this.el = jQuery('
', { + 'class': 'form' + }); + this.notebooks = []; + this.expandables = []; + this.containers = []; + this.widget_id = 0; + Sao.View.Form._super.init.call(this, screen, xml); + }, get_buttons: function() { var buttons = []; for (var j in this.state_widgets) { @@ -521,7 +432,7 @@ function eval_pyson(value){ row: function() { return this.rows().last(); }, - add: function(attributes, widget) { + add: function(widget, attributes) { var colspan = attributes.colspan; if (colspan === undefined) colspan = 1; var xfill = attributes.xfill; @@ -958,75 +869,10 @@ function eval_pyson(value){ } }); - Sao.View.form_widget_get = function(type) { - switch (type) { - case 'char': - return Sao.View.Form.Char; - case 'password': - return Sao.View.Form.Password; - case 'date': - return Sao.View.Form.Date; - case 'datetime': - case 'timestamp': - return Sao.View.Form.DateTime; - case 'time': - return Sao.View.Form.Time; - case 'timedelta': - return Sao.View.Form.TimeDelta; - case 'integer': - case 'biginteger': - return Sao.View.Form.Integer; - case 'float': - case 'numeric': - return Sao.View.Form.Float; - case 'selection': - return Sao.View.Form.Selection; - case 'boolean': - return Sao.View.Form.Boolean; - case 'text': - return Sao.View.Form.Text; - case 'richtext': - return Sao.View.Form.RichText; - case 'many2one': - return Sao.View.Form.Many2One; - case 'one2one': - return Sao.View.Form.One2One; - case 'reference': - return Sao.View.Form.Reference; - case 'one2many': - return Sao.View.Form.One2Many; - case 'many2many': - return Sao.View.Form.Many2Many; - case 'binary': - return Sao.View.Form.Binary; - case 'multiselection': - return Sao.View.Form.MultiSelection; - case 'image': - return Sao.View.Form.Image; - case 'url': - return Sao.View.Form.URL; - case 'email': - return Sao.View.Form.Email; - case 'callto': - return Sao.View.Form.CallTo; - case 'sip': - return Sao.View.Form.SIP; - case 'progressbar': - return Sao.View.Form.ProgressBar; - case 'dict': - return Sao.View.Form.Dict; - case 'pyson': - return Sao.View.Form.PYSON; - } - }; - - Sao.View.Form.Widget = Sao.class_(Object, { expand: false, - init: function(field_name, model, attributes) { - this.field_name = field_name; - this.model = model; - this.view = null; // Filled later + init: function(view, attributes) { + this.view = view; this.attributes = attributes; this.el = null; this.position = 0; @@ -1104,6 +950,15 @@ function eval_pyson(value){ _invalid_el: function() { return this.el; }, + get field_name() { + return this.attributes.name; + }, + get model_name() { + return this.view.screen.model_name; + }, + get model() { + return this.view.screen.model; + }, get record() { return this.view.record; }, @@ -1353,9 +1208,8 @@ function eval_pyson(value){ Sao.View.Form.Char = Sao.class_(Sao.View.Form.Widget, { class_: 'form-char', - init: function(field_name, model, attributes) { - Sao.View.Form.Char._super.init.call(this, field_name, model, - attributes); + init: function(view, attributes) { + Sao.View.Form.Char._super.init.call(this, view, attributes); Sao.View.Form.TranslateMixin.init.call(this); this.el = jQuery('
', { 'class': this.class_ @@ -1454,9 +1308,8 @@ function eval_pyson(value){ Sao.View.Form.Password = Sao.class_(Sao.View.Form.Char, { class_: 'form-password', - init: function(field_name, model, attributes) { - Sao.View.Form.Password._super.init.call(this, field_name, model, - attributes); + init: function(view, attributes) { + Sao.View.Form.Password._super.init.call(this, view, attributes); this.input.prop('type', 'password'); this.button = jQuery('