1 /*
  2     Copyright 2008-2023
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Peter Wilfahrt
  9 
 10     This file is part of JSXGraph.
 11 
 12     JSXGraph is free software dual licensed under the GNU LGPL or MIT License.
 13 
 14     You can redistribute it and/or modify it under the terms of the
 15 
 16       * GNU Lesser General Public License as published by
 17         the Free Software Foundation, either version 3 of the License, or
 18         (at your option) any later version
 19       OR
 20       * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT
 21 
 22     JSXGraph is distributed in the hope that it will be useful,
 23     but WITHOUT ANY WARRANTY; without even the implied warranty of
 24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 25     GNU Lesser General Public License for more details.
 26 
 27     You should have received a copy of the GNU Lesser General Public License and
 28     the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/>
 29     and <https://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 /*global JXG: true, define: true, window: true*/
 33 /*jslint nomen: true, plusplus: true*/
 34 
 35 /**
 36  * @fileoverview In this file the Text element is defined.
 37  */
 38 
 39 import JXG from "../jxg";
 40 import Env from "../utils/env";
 41 import Type from "../utils/type";
 42 
 43 var priv = {
 44     InputInputEventHandler: function (evt) {
 45         this._value = this.rendNodeInput.value;
 46         this.board.update();
 47     }
 48 };
 49 
 50 /**
 51  * @class This element is used to provide a constructor for special texts containing a
 52  * HTML form input element.
 53  * <p>
 54  * If the width of element is set with the attribute "cssStyle", the width of the
 55  * label must be added.
 56  * <p>
 57  * For this element, the attribute "display" has to have the value 'html' (which is the default).
 58  * <p>
 59  * The underlying HTML input field can be accessed through the sub-object 'rendNodeInput', e.g. to
 60  * add event listeners.
 61  * 
 62  * @pseudo
 63  * @description
 64  * @name Input
 65  * @augments Text
 66  * @constructor
 67  * @type JXG.Text
 68  *
 69  * @param {number,function_number,function_String_String,function} x,y,value,label Parent elements for input elements.
 70  *   <p>
 71  *   x and y are the coordinates of the lower left corner of the text box. The position of the text is fixed,
 72  *   x and y are numbers. The position is variable if x or y are functions.
 73  *   <p>
 74  *   The default value of the input element must be given as string.
 75  *   <p>
 76  *   The label of the input element may be given as string or function.
 77  *
 78  * @example
 79  *  // Create an input element at position [1,4].
 80  *  var input = board.create('input', [0, 1, 'sin(x)*x', 'f(x)='], {cssStyle: 'width: 100px'});
 81  *  var f = board.jc.snippet(input.Value(), true, 'x', false);
 82  *  var graph = board.create('functiongraph',[f,
 83  *          function() {
 84  *            var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[0,0],board);
 85  *            return c.usrCoords[1];
 86  *          },
 87  *          function() {
 88  *            var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[board.canvasWidth,0],board);
 89  *            return c.usrCoords[1];
 90  *          }
 91  *        ]);
 92  *
 93  *  board.create('text', [1, 3, '<button onclick="updateGraph()">Update graph</button>']);
 94  *
 95  *  var updateGraph = function() {
 96  *      graph.Y = board.jc.snippet(input.Value(), true, 'x', false);
 97  *      graph.updateCurve();
 98  *      board.update();
 99  *  }
100  * </pre><div class="jxgbox" id="JXGc70f55f1-21ba-4719-a37d-a93ae2943faa" style="width: 500px; height: 300px;"></div>
101  * <script type="text/javascript">
102  *   var t1_board = JXG.JSXGraph.initBoard('JXGc70f55f1-21ba-4719-a37d-a93ae2943faa', {boundingbox: [-3, 6, 5, -3], axis: true, showcopyright: false, shownavigation: false});
103  *   var input = t1_board.create('input', [1, 4, 'sin(x)*x', 'f(x)='], {cssStyle: 'width: 100px'});
104  *   var f = t1_board.jc.snippet(input.Value(), true, 'x', false);
105  *   var graph = t1_board.create('functiongraph',[f,
106  *          function() {
107  *            var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[0,0],t1_board);
108  *            return c.usrCoords[1];
109  *          },
110  *          function() {
111  *            var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[t1_board.canvasWidth,0],t1_board);
112  *            return c.usrCoords[1];
113  *          }
114  *        ]);
115  *
116  *  t1_board.create('text', [1, 3, '<button onclick="updateGraph()">Update graph</button>']);
117  *
118  *  var updateGraph = function() {
119  *      graph.Y = t1_board.jc.snippet(input.Value(), true, 'x', false);
120  *      graph.updateCurve();
121  *      t1_board.update();
122  *  }
123  * </script><pre>
124  * 
125  * @example
126  * // Add the `keyup` event to an input field
127  * var A = board.create('point', [3, -2]);
128  * var i = board.create('input', [-4, -4, "1", "x "]);
129  * 
130  * i.rendNodeInput.addEventListener("keyup", ( function () {
131  *    var x = parseFloat(this.value);
132  *    if (!isNaN(x)) {
133  * 	   A.moveTo([x, 3], 100);
134  *    }
135  * }));
136  * 
137  * </pre><div id="JXG81c84fa7-3f36-4874-9e0f-d4b9e93e755b" class="jxgbox" style="width: 300px; height: 300px;"></div>
138  * <script type="text/javascript">
139  *     (function() {
140  *         var board = JXG.JSXGraph.initBoard('JXG81c84fa7-3f36-4874-9e0f-d4b9e93e755b',
141  *             {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false});
142  *     var A = board.create('point', [3, -2]);
143  *     var i = board.create('input', [-4, -4, "1", "x "]);
144  *     
145  *     i.rendNodeInput.addEventListener("keyup", ( function () {
146  *        var x = parseFloat(this.value);
147  *        if (!isNaN(x)) {
148  *     	    A.moveTo([x, 3], 100);
149  *        }
150  *     }));
151  * 
152  *     })();
153  * 
154  * </script><pre>
155  * 
156  * @example
157  * // Add the `change` event to an input field
158  * var A = board.create('point', [3, -2]);
159  * var i = board.create('input', [-4, -4, "1", "x "]);
160  * 
161  * i.rendNodeInput.addEventListener("change", ( function () {
162  *    var x = parseFloat(i.Value());
163  *    A.moveTo([x, 2], 100);
164  * }));
165  * 
166  * </pre><div id="JXG51c4d78b-a7ad-4c34-a983-b3ddae6192d7" class="jxgbox" style="width: 300px; height: 300px;"></div>
167  * <script type="text/javascript">
168  *     (function() {
169  *         var board = JXG.JSXGraph.initBoard('JXG51c4d78b-a7ad-4c34-a983-b3ddae6192d7',
170  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
171  *     var A = board.create('point', [3, -2]);
172  *     var i = board.create('input', [-4, -4, "1", "x "]);
173  *     
174  *     i.rendNodeInput.addEventListener("change", ( function () {
175  *        var x = parseFloat(i.Value());
176  *        A.moveTo([x, 2], 100);
177  *     }));
178  * 
179  *     })();
180  * 
181  * </script><pre>
182  * 
183  */
184 JXG.createInput = function (board, parents, attributes) {
185     var t,
186         par,
187         attr = Type.copyAttributes(attributes, board.options, "input");
188 
189     par = [
190         parents[0],
191         parents[1],
192         '<span style="display:inline; white-space:nowrap; padding:0px;">' +
193             '<span></span><input type="text" maxlength="' +
194             attr.maxlength +
195             '" style="width:100%"/>' +
196             "</span>"
197     ];
198 
199     // 1. Create input element with empty label
200     t = board.create("text", par, attr);
201     t.type = Type.OBJECT_TYPE_INPUT;
202 
203     t.rendNodeLabel = t.rendNode.childNodes[0].childNodes[0];
204     t.rendNodeInput = t.rendNode.childNodes[0].childNodes[1];
205     // t.rendNodeLabel.innerHTML = parents[3];
206     t.rendNodeInput.value = parents[2];
207     t.rendNodeTag = t.rendNodeInput; // Needed for unified treatment in setAttribute
208     t.rendNodeTag.disabled = !!attr.disabled;
209     t.rendNodeLabel.id = t.rendNode.id + "_label";
210     t.rendNodeInput.id = t.rendNode.id + "_input";
211 
212     // 2. Set parents[3] (string|function) as label of the input element.
213     // abstract.js selects the correct DOM element for the update
214     t.setText(parents[3]);
215 
216     t._value = parents[2];
217     t.update = function () {
218         if (this.needsUpdate) {
219             JXG.Text.prototype.update.call(this);
220             this._value = this.rendNodeInput.value;
221         }
222         return this;
223     };
224 
225     /**
226      * Returns the value (content) of the input element
227      * @name Value
228      * @memberOf Input.prototype
229      * @function
230      * @returns {String} content of the input field.
231      */
232     t.Value = function () {
233         return this._value;
234     };
235 
236     /**
237      * Sets value of the input element.
238      * @name set
239      * @memberOf Input.prototype
240      * @function
241      *
242      * @param {String} val
243      * @returns {JXG.GeometryElement} Reference to the element.
244      *
245      * @example
246      *         var i1 = board.create('input', [-3, 4, 'sin(x)', 'f(x)='], {cssStyle: 'width:4em', maxlength: 2});
247      *         var c1 = board.create('checkbox', [-3, 2, 'label 1'], {});
248      *         var b1 = board.create('button', [-3, -1, 'Change texts', function () {
249      *                 i1.setText('g(x)');
250      *                 i1.set('cos(x)');
251      *                 c1.setText('label 2');
252      *                 b1.setText('Texts are changed');
253      *             }],
254      *             {cssStyle: 'width:400px'});
255      *
256      * </pre><div id="JXG11cac8ff-2354-47e7-9da4-eb298e53de05" class="jxgbox" style="width: 300px; height: 300px;"></div>
257      * <script type="text/javascript">
258      *     (function() {
259      *         var board = JXG.JSXGraph.initBoard('JXG11cac8ff-2354-47e7-9da4-eb298e53de05',
260      *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
261      *             var i1 = board.create('input', [-3, 4, 'sin(x)', 'f(x)='], {cssStyle: 'width:4em', maxlength: 2});
262      *             var c1 = board.create('checkbox', [-3, 2, 'label 1'], {});
263      *             var b1 = board.create('button', [-3, -1, 'Change texts', function () {
264      *                     i1.setText('g(x)');
265      *                     i1.set('cos(x)');
266      *                     c1.setText('label 2');
267      *                     b1.setText('Texts are changed');
268      *                 }],
269      *                 {cssStyle: 'width:400px'});
270      *
271      *     })();
272      *
273      * </script><pre>
274      *
275      */
276     t.set = function (val) {
277         this._value = val;
278         this.rendNodeInput.value = val;
279         return this;
280     };
281 
282     Env.addEvent(t.rendNodeInput, "input", priv.InputInputEventHandler, t);
283     Env.addEvent(
284         t.rendNodeInput,
285         "mousedown",
286         function (evt) {
287             if (Type.exists(evt.stopPropagation)) {
288                 evt.stopPropagation();
289             }
290         },
291         t
292     );
293     Env.addEvent(
294         t.rendNodeInput,
295         "touchstart",
296         function (evt) {
297             if (Type.exists(evt.stopPropagation)) {
298                 evt.stopPropagation();
299             }
300         },
301         t
302     );
303     Env.addEvent(
304         t.rendNodeInput,
305         "pointerdown",
306         function (evt) {
307             if (Type.exists(evt.stopPropagation)) {
308                 evt.stopPropagation();
309             }
310         },
311         t
312     );
313 
314     // This sets the font-size of the input HTML element
315     t.visPropOld.fontsize = "0px";
316     board.renderer.updateTextStyle(t, false);
317 
318     return t;
319 };
320 
321 JXG.registerElement("input", JXG.createInput);
322 
323 // export default {
324 //     createInput: JXG.createInput
325 // };
326