/[suikacvs]/webroot/www/style/ui/widget-values.js.u8
Suika

Contents of /webroot/www/style/ui/widget-values.js.u8

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.3 - (show annotations) (download)
Tue Jan 6 07:05:50 2009 UTC (16 years, 7 months ago) by wakaba
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +20 -3 lines
Relicensed under the terms that are compatibile with Perl's license

1 function WidgetValues (c) {
2 if (c) {
3 this.container = c;
4 this._init ();
5 }
6 } // WidgetValues
7
8 WidgetValues.prototype._init = function () {
9 var ta = this.container.getElementsByTagName ('textarea')[0];
10 if (!ta) return;
11
12 this.name = this.container.getAttribute ('data-widget-name') || ta.name || '';
13
14 this.separatorPattern
15 = new RegExp (this.container.getAttribute ('data-widget-separator')
16 || '\\s+');
17
18 this.defaultValues = ta.defaultValue.length
19 ? ta.defaultValue.split (this.separatorPattern) : [];
20 this.initialValues = ta.value.length
21 ? ta.value.split (this.separatorPattern) : [];
22
23 this.clear ();
24 this.appendValues (this.initialValues);
25 }; // _init
26
27 WidgetValues.prototype._clear = function () {
28 this.firstField = null;
29 this.lastField = null;
30 this.container.innerHTML = '';
31 }; // _clear
32
33 WidgetValues.prototype.appendValues = function (values) {
34 var self = this;
35 for (var i = 0; i < values.length; i++) {
36 var field = this.createField (values[i]);
37 field.wvRemove = function () { self._removeField (this) };
38 field.wvMoveUp = function () { self._moveUpField (this) };
39 field.wvMoveDown = function () { self._moveDownField (this) };
40 var parent = this.lastField ? this.lastField.parentNode : null;
41 parent = parent ? parent : this.fieldsParent;
42 parent.insertBefore (field, this.lastField ? this.lastField.nextSibling : null);
43 this.lastField = field;
44 this.firstField = this.firstField || field;
45 }
46 }; // appendValues
47
48 WidgetValues.prototype.reset = function () {
49 this.clear ();
50 this.appendValues (this.defaultValues);
51 }; // reset
52
53 WidgetValues.prototype._removeField = function (field) {
54 if (this.firstField == field) {
55 this.firstField = this._getNextField (field);
56 }
57 if (this.lastField == field) {
58 this.lastField = this._getPreviousField (field);
59 }
60 field.parentNode.removeChild (field);
61 }; // _removeField
62
63 WidgetValues.prototype._moveUpField = function (field) {
64 this._swapFields (field, this._getPreviousField (field));
65 }; // _moveUpField
66
67 WidgetValues.prototype._moveDownField = function (field) {
68 this._swapFields (field, this._getNextField (field));
69 }; // _moveDownField
70
71 WidgetValues.prototype._swapFields = function (f1, f2) {
72 if (!f1 || !f2) return;
73 var ph = document.createElement (f1.tagName || 'x');
74 f1.parentNode.replaceChild (ph, f1);
75 f2.parentNode.replaceChild (f1, f2);
76 ph.parentNode.replaceChild (f2, ph);
77 }; // _swapFields
78
79 WidgetValues.prototype._getNextField = function (field) {
80 var n = field.nextSibling;
81 while (n != null) {
82 if (this.isField (n)) {
83 return n;
84 }
85 n = n.nextSibling;
86 }
87 return null;
88 }; // _getNextField
89
90 WidgetValues.prototype._getPreviousField = function (field) {
91 var n = field.previousSibling;
92 while (n != null) {
93 if (this.isField (n)) {
94 return n;
95 }
96 n = n.previousSibling;
97 }
98 return null;
99 }; // _getPreviousField
100
101 WidgetValues.prototype.createButton = function () {
102 var el;
103
104 try {
105 el = document.createElement ('button');
106 el.setAttribute('type', 'button');
107 } catch (e) {
108 el = document.createElement ('<button type=button>');
109 }
110
111 return el;
112 }; // createButton
113
114 WidgetValues.prototype.isField = function (n) {
115 return n.wvIsField;
116 }; // isField
117
118
119
120
121 WidgetValues.prototype.createField = function (value) {
122 var field = document.createElement ('div');
123 field.wvIsField = true;
124
125 var input = document.createElement ('input');
126 input.name = this.name;
127 input.value = value;
128 field.appendChild (input);
129
130 var removeButton = this.createButton ();
131 removeButton.innerHTML = 'Remove';
132 removeButton.onclick = function () { field.wvRemove () };
133 field.appendChild (removeButton);
134
135 var moveUpButton = this.createButton ();
136 moveUpButton.innerHTML = 'Move up';
137 moveUpButton.onclick = function () { field.wvMoveUp () };
138 field.appendChild (moveUpButton);
139
140 var moveDownButton = this.createButton ();
141 moveDownButton.innerHTML = 'Move down';
142 moveDownButton.onclick = function () { field.wvMoveDown () };
143 field.appendChild (moveDownButton);
144
145 return field;
146 }; // createField
147
148 WidgetValues.prototype.clear = function () {
149 var self = this;
150
151 this._clear ();
152
153 var fields = document.createElement ('div');
154 this.container.appendChild (fields);
155 this.fieldsParent = fields;
156
157 var addButton = this.createButton ();
158 addButton.innerHTML = 'Add';
159 addButton.onclick = function () { self.appendValues (['']) };
160 this.container.appendChild (addButton);
161
162 var resetButton = this.createButton ();
163 resetButton.innerHTML = 'Reset';
164 resetButton.onclick = function () { self.reset () };
165 this.container.appendChild (resetButton);
166 }; // clear
167
168 if (window.WidgetValuesOnLoad) WidgetValuesOnLoad ();
169
170 /*
171
172 Usage:
173
174 <script src="http://suika.fam.cx/www/style/ui/widget-values.js.u8" charset=utf-8></script>
175 <script>
176 window.onload = function () {
177 new WidgetValues (document.getElementById ('c'));
178 };
179 </script>
180
181 <div id=c data-widget-name=test-value data-widget-separator="[\r\n]+">
182 <textarea name=test-values>
183 default
184 values
185 separated
186 by
187 newlines
188 </textarea>
189 </div>
190
191 The |WidgetValues| constructor takes an argument, which represents the
192 container element. The container element MUST contain a |textarea|
193 element, which represents the default values. The content of the
194 container element is replaced by indivisual input fields (called
195 "fields" in this script) and additional contents such as an "add"
196 button.
197
198 The |data-widget-name| attribute of the container element represents
199 the name of the control, used by the form controls in the fields as
200 the |name| attribute value. This attribute is REQURIED.
201
202 The |data-widget-separator| attribute of the container element
203 represents a JavaScript regular expression that matches to substrings
204 used to separate values in the |textarea| element. This attribute is
205 optional; the default separator is /\s+/ (i.e. spaces).
206
207 You can customize how fields and additional contents are organized in
208 the container element by creating a subclass and inheriting
209 |createField| and |clear| methods, like the example below:
210
211 // <http://suika.fam.cx/~wakaba/-temp/test/misc/jswidgets/values/values-2.html>
212
213 function MyNumbersWidget (c) {
214 WidgetValues.apply (this, [c]);
215 }
216
217 MyNumbersWidget.prototype = new WidgetValues;
218
219 MyNumbersWidget.prototype.createField = function (value) {
220 var field = document.createElement ('span');
221 field.wvIsField = true;
222
223 var input = document.createElement ('input');
224 input.setAttribute ('type', 'number');
225 input.setAttribute ('min', 1);
226 input.setAttribute ('max', 10);
227 field.appendChild (input);
228
229 return field;
230 };
231
232 MyNumbersWidget.prototype.clear = function () {
233 var self = this;
234
235 this._clear ();
236
237 var fields = document.createElement ('span');
238 this.container.appendChild (fields);
239 this.fieldsParent = fields;
240
241 var addButton = this.createButton ();
242 addButton.innerHTML = '+';
243 addButton.onclick = function () { self.appendValues ([5]) };
244 this.container.appendChild (addButton);
245 };
246
247 Latest version of this script is available at
248 <http://suika.fam.cx/www/style/ui/widget-values.js.u8>. Old versions
249 of this script are available from
250 <http://suika.fam.cx/www/style/ui/widget-values.js.u8,cvslog>.
251
252 */
253
254 /* ***** BEGIN LICENSE BLOCK *****
255 * Copyright 2008 Wakaba <w@suika.fam.cx>. All rights reserved.
256 *
257 * This program is free software; you can redistribute it and/or
258 * modify it under the same terms as Perl itself.
259 *
260 * Alternatively, the contents of this file may be used
261 * under the following terms (the "MPL/GPL/LGPL"),
262 * in which case the provisions of the MPL/GPL/LGPL are applicable instead
263 * of those above. If you wish to allow use of your version of this file only
264 * under the terms of the MPL/GPL/LGPL, and not to allow others to
265 * use your version of this file under the terms of the Perl, indicate your
266 * decision by deleting the provisions above and replace them with the notice
267 * and other provisions required by the MPL/GPL/LGPL. If you do not delete
268 * the provisions above, a recipient may use your version of this file under
269 * the terms of any one of the Perl or the MPL/GPL/LGPL.
270 *
271 * "MPL/GPL/LGPL":
272 *
273 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
274 *
275 * The contents of this file are subject to the Mozilla Public License Version
276 * 1.1 (the "License"); you may not use this file except in compliance with
277 * the License. You may obtain a copy of the License at
278 * <http://www.mozilla.org/MPL/>
279 *
280 * Software distributed under the License is distributed on an "AS IS" basis,
281 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
282 * for the specific language governing rights and limitations under the
283 * License.
284 *
285 * The Original Code is WidgetValues code.
286 *
287 * The Initial Developer of the Original Code is Wakaba.
288 * Portions created by the Initial Developer are Copyright (C) 2008
289 * the Initial Developer. All Rights Reserved.
290 *
291 * Contributor(s):
292 * Wakaba <w@suika.fam.cx>
293 *
294 * Alternatively, the contents of this file may be used under the terms of
295 * either the GNU General Public License Version 2 or later (the "GPL"), or
296 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
297 * in which case the provisions of the GPL or the LGPL are applicable instead
298 * of those above. If you wish to allow use of your version of this file only
299 * under the terms of either the GPL or the LGPL, and not to allow others to
300 * use your version of this file under the terms of the MPL, indicate your
301 * decision by deleting the provisions above and replace them with the notice
302 * and other provisions required by the LGPL or the GPL. If you do not delete
303 * the provisions above, a recipient may use your version of this file under
304 * the terms of any one of the MPL, the GPL or the LGPL.
305 *
306 * ***** END LICENSE BLOCK ***** */

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24