/
TextField.py
252 lines (204 loc) · 7.43 KB
/
TextField.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# -*- coding: utf-8 -*-
# CTK: Cherokee Toolkit
#
# Authors:
# Alvaro Lopez Ortega <alvaro@alobbs.com>
#
# Copyright (C) 2009 Alvaro Lopez Ortega
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
from cgi import escape
from RawHTML import RawHTML
from Widget import Widget
from Server import cfg
from util import to_utf8
from Server import get_server
HEADER = [
'<script type="text/javascript" src="/CTK/js/jquery.form-defaults.js"></script>'
]
class TextField (Widget):
"""
Widget for the base <input type="text"> element. Arguments are optional.
Arguments:
props: dictionary with properties for the HTML element,
such as {'name': 'foo', 'id': 'bar', 'class': 'noauto
required', optional: 'True', optional_string: 'Whatever
works...'}.
noauto: form will not be submitted instantly upon
edition of the field.
optional: True|False, defines whether a grayed out
'Optional' string should be overlayed on the input field.
optional_string: if optional flag is enabled, show
this string instead of 'Optional'.
Examples:
field1 = CTK.TextField({'name': 'email1', 'class': 'noauto'})
field2 = CTK.TextField({'name': 'email2', 'class': 'noauto',
'optional': True, 'optional_string':
'Providing this field is optional}})
"""
def __init__ (self, props=None):
Widget.__init__ (self)
self.type = "text"
self.id = 'widget%d'%(self.uniq_id)
if props:
self._props = props
else:
self._props = {}
if not 'id' in self._props:
self._props['id'] = self.id
def __get_input_props (self):
render = ''
for prop in self._props:
render += " %s" %(prop)
value = self._props[prop]
if value:
val = to_utf8(value)
render += '="%s"' %(val)
return render
def __get_error_div_props (self):
render = 'id="error_%s"' % (self.id)
name = to_utf8 (self._props.get('name'))
if name:
render += ' key="%s"' %(name)
return render
def Render (self):
# Watermark
js = ''
if self._props.get('optional'):
optional_string = self._props.get('optional_string', _("Optional"))
js += "$('#%s').DefaultValue('optional-grey','%s');" %(self.id, optional_string)
if not self._props.get('class'):
self._props['class'] = 'optional'
else:
self._props['class'] += ' optional'
# Render the text field
html = '<input type="%s"%s />' %(self.type, self.__get_input_props())
# Render the error reporting field
html += '<div class="error" %s></div>' %(self.__get_error_div_props())
render = Widget.Render (self)
render.html += html
render.js += js
render.headers += HEADER
return render
def JS_to_clean (self):
return "$('#%s').attr('value', '');" %(self.id)
def JS_to_focus (self):
return "$('#%s').blur(); $('#%s').focus();" %(self.id, self.id)
class TextFieldPassword (TextField):
"""
Widget for password fields. Behaves exactly like a TextField, but
renders a <input type="password> element instead.
"""
def __init__ (self, *a, **kw):
TextField.__init__ (self, *a, **kw)
self.type = "password"
class TextCfg (TextField):
"""
Configuration-Tree based TextField widget. Populates the input
field with the value of the configuration tree given by key
argument if it exists. It can be set as optional, and accepts
properties to pass to the base TextField object.
"""
def __init__ (self, key, optional=False, props={}):
# Sanity checks
assert type(key) == str
assert type(optional) == bool
assert type(props) in (type(None), dict)
props = props.copy()
# Read the key value
val = cfg.get_val(key)
if val:
props['value'] = escape(val, quote=True)
if optional:
props['optional'] = True
# Other properties
props['name'] = key
# Init parent
TextField.__init__ (self, props)
class TextCfgPassword (TextCfg):
"""
Configuration-Tree based TextPassword widget. Populates the input
field with the value of the configuration tree given by key
argument if it exists. It can be set as optional, and accepts
properties to pass to the base TextPassword object.
"""
def __init__ (self, *a, **kw):
TextCfg.__init__ (self, *a, **kw)
self.type = "password"
JS = """
$("#%(id)s")
.bind ("blur", function (event){
var self = $(this);
var value = self.val();
if (value != self.data('last_value')) {
$("#activity").show();
$.ajax ({type: 'POST',
dataType: 'json',
url: '%(url)s',
data: '%(key)s='+value,
success: function (data) {
self.data('last_value', value);
var name = self.attr('name');
for (var key in data['updates']) {
if (key == name) {
self.val (data['updates'][key]);
break;
}
}
},
error: function() {
event.stopPropagation();
self.val (self.data('last_value'));
},
complete: function (XMLHttpRequest, textStatus) {
$("#activity").fadeOut('fast');
}
});
}
})
.data ('last_value', "%(value)s")
.bind ("keypress", function(event){
if (event.keyCode == 13) {
focus_next_input (this);
}
});
"""
class TextCfgAuto (TextCfg):
"""
Automatic variant of TextCfg widget. It needs a key for an entry
in the Configuration tree, and an url to which data is sent by
HTTP POST. It can be set as optional, and accepts properties to
pass to the base TextField object.
The field is automatically populated by the value of the
configuration entry given by key, and the configuration entry is
updated automatically when the input field is submitted.
"""
def __init__ (self, key, url, optional=False, props=None):
self.key = key
self.url = url
TextCfg.__init__ (self, key, optional, props)
# Secure submit
srv = get_server()
if srv.use_sec_submit:
self.url += '?key=%s' %(srv.sec_submit)
def Render (self):
value = cfg.get_val (self.key, '')
render = TextCfg.Render(self)
render.js += JS %({'id': self.id,
'url': self.url,
'key': self.key,
'value': value})
return render