1 /*
  2     Copyright 2008-2022
  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 <http://www.gnu.org/licenses/>
 29     and <http://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 
 33 /*global JXG: true, define: true, window: true*/
 34 /*jslint nomen: true, plusplus: true*/
 35 
 36 /* depends:
 37  jxg
 38  utils/env
 39  utils/type
 40  */
 41 
 42 /**
 43  * @fileoverview In this file the Text element is defined.
 44  */
 45 
 46 define([
 47     'jxg', 'utils/env', 'utils/type'
 48 ], function (JXG, Env, Type) {
 49 
 50     "use strict";
 51 
 52     var priv = {
 53             InputInputEventHandler: function (evt) {
 54                 this._value = this.rendNodeInput.value;
 55                 this.board.update();
 56             }
 57         };
 58 
 59     /**
 60      * @class This element is used to provide a constructor for special texts containing a
 61      * HTML form input element.
 62      * <p>
 63      * If the width of element is set with the attribute "cssStyle", the width of the
 64      * label must be added.
 65      * <p>
 66      * For this element, the attribute "display" has to have the value 'html' (which is the default).
 67      * @pseudo
 68      * @description
 69      * @name Input
 70      * @augments Text
 71      * @constructor
 72      * @type JXG.Text
 73      *
 74      * @param {number,function_number,function_String_String,function} x,y,value,label Parent elements for input elements.
 75      *                     <p>
 76      *                     x and y are the coordinates of the lower left corner of the text box. The position of the text is fixed,
 77      *                     x and y are numbers. The position is variable if x or y are functions.
 78      *                     <p>
 79      *                     The default value of the input element must be given as string.
 80      *                     <p>
 81      *                     The label of the input element may be given as string or function.
 82      *
 83      * @example
 84      *  // Create an input element at position [1,4].
 85      *  var input = board.create('input', [0, 1, 'sin(x)*x', 'f(x)='], {cssStyle: 'width: 100px'});
 86      *  var f = board.jc.snippet(input.Value(), true, 'x', false);
 87      *  var graph = board.create('functiongraph',[f,
 88      *          function() {
 89      *            var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[0,0],board);
 90      *            return c.usrCoords[1];
 91      *          },
 92      *          function() {
 93      *            var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[board.canvasWidth,0],board);
 94      *            return c.usrCoords[1];
 95      *          }
 96      *        ]);
 97      *
 98      *  board.create('text', [1, 3, '<button onclick="updateGraph()">Update graph</button>']);
 99      *
100      *  var updateGraph = function() {
101      *      graph.Y = board.jc.snippet(input.Value(), true, 'x', false);
102      *      graph.updateCurve();
103      *      board.update();
104      *  }
105      * </pre><div class="jxgbox" id="JXGc70f55f1-21ba-4719-a37d-a93ae2943faa" style="width: 500px; height: 300px;"></div>
106      * <script type="text/javascript">
107      *   var t1_board = JXG.JSXGraph.initBoard('JXGc70f55f1-21ba-4719-a37d-a93ae2943faa', {boundingbox: [-3, 6, 5, -3], axis: true, showcopyright: false, shownavigation: false});
108      *   var input = t1_board.create('input', [1, 4, 'sin(x)*x', 'f(x)='], {cssStyle: 'width: 100px'});
109      *   var f = t1_board.jc.snippet(input.Value(), true, 'x', false);
110      *   var graph = t1_board.create('functiongraph',[f,
111      *          function() {
112      *            var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[0,0],t1_board);
113      *            return c.usrCoords[1];
114      *          },
115      *          function() {
116      *            var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[t1_board.canvasWidth,0],t1_board);
117      *            return c.usrCoords[1];
118      *          }
119      *        ]);
120      *
121      *  t1_board.create('text', [1, 3, '<button onclick="updateGraph()">Update graph</button>']);
122      *
123      *  var updateGraph = function() {
124      *      graph.Y = t1_board.jc.snippet(input.Value(), true, 'x', false);
125      *      graph.updateCurve();
126      *      t1_board.update();
127      *  }
128      * </script><pre>
129      */
130     JXG.createInput = function (board, parents, attributes) {
131         var t, par,
132             attr = Type.copyAttributes(attributes, board.options, 'input');
133 
134         par = [parents[0], parents[1],
135             '<span style="display:inline; white-space:nowrap; padding:0px;">' +
136             '<span></span><input type="text" maxlength="' +
137                 attr.maxlength +
138                 '" style="width:100%"/>' +
139             '</span>'
140             ];
141 
142         // 1. Create input element with empty label
143         t = board.create('text', par, attr);
144         t.type = Type.OBJECT_TYPE_INPUT;
145 
146         t.rendNodeLabel = t.rendNode.childNodes[0].childNodes[0];
147         t.rendNodeInput = t.rendNode.childNodes[0].childNodes[1];
148         // t.rendNodeLabel.innerHTML = parents[3];
149         t.rendNodeInput.value = parents[2];
150         t.rendNodeTag = t.rendNodeInput; // Needed for unified treatment in setAttribute
151         t.rendNodeTag.disabled = !!attr.disabled;
152         t.rendNodeLabel.id = t.rendNode.id + '_label';
153         t.rendNodeInput.id = t.rendNode.id + '_input';
154 
155         // 2. Set parents[3] (string|function) as label of the input element.
156         // abstract.js selects the correct DOM element for the update
157         t.setText(parents[3]);
158 
159         t._value = parents[2];
160         t.update = function () {
161             if (this.needsUpdate) {
162                 JXG.Text.prototype.update.call(this);
163                 this._value = this.rendNodeInput.value;
164             }
165             return this;
166         };
167 
168         /**
169          * Returns the value (content) of the input element
170          * @name Value
171          * @memberOf Input.prototype
172          * @function
173          * @returns {String} content of the input field.
174          */
175         t.Value = function () {
176             return this._value;
177         };
178 
179         /**
180          * Sets value of the input element.
181          * @name set
182          * @memberOf Input.prototype
183          * @function
184          *
185          * @param {String} val
186          * @returns {JXG.GeometryElement} Reference to the element.
187          *
188          * @example
189          *         var i1 = board.create('input', [-3, 4, 'sin(x)', 'f(x)='], {cssStyle: 'width:4em', maxlength: 2});
190          *         var c1 = board.create('checkbox', [-3, 2, 'label 1'], {});
191          *         var b1 = board.create('button', [-3, -1, 'Change texts', function () {
192          *                 i1.setText('g(x)');
193          *                 i1.set('cos(x)');
194          *                 c1.setText('label 2');
195          *                 b1.setText('Texts are changed');
196          *             }],
197          *             {cssStyle: 'width:400px'});
198          *
199          * </pre><div id="JXG11cac8ff-2354-47e7-9da4-eb298e53de05" class="jxgbox" style="width: 300px; height: 300px;"></div>
200          * <script type="text/javascript">
201          *     (function() {
202          *         var board = JXG.JSXGraph.initBoard('JXG11cac8ff-2354-47e7-9da4-eb298e53de05',
203          *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
204          *             var i1 = board.create('input', [-3, 4, 'sin(x)', 'f(x)='], {cssStyle: 'width:4em', maxlength: 2});
205          *             var c1 = board.create('checkbox', [-3, 2, 'label 1'], {});
206          *             var b1 = board.create('button', [-3, -1, 'Change texts', function () {
207          *                     i1.setText('g(x)');
208          *                     i1.set('cos(x)');
209          *                     c1.setText('label 2');
210          *                     b1.setText('Texts are changed');
211          *                 }],
212          *                 {cssStyle: 'width:400px'});
213          *
214          *     })();
215          *
216          * </script><pre>
217          *
218          */
219         t.set = function (val) {
220             this._value = val;
221             this.rendNodeInput.value = val;
222             return this;
223         };
224 
225         Env.addEvent(t.rendNodeInput, 'input', priv.InputInputEventHandler, t);
226         Env.addEvent(t.rendNodeInput, 'mousedown', function(evt) { if (Type.exists(evt.stopPropagation)) { evt.stopPropagation(); } }, t);
227         Env.addEvent(t.rendNodeInput, 'touchstart', function(evt) { if (Type.exists(evt.stopPropagation)) { evt.stopPropagation(); } }, t);
228         Env.addEvent(t.rendNodeInput, 'pointerdown', function(evt) { if (Type.exists(evt.stopPropagation)) { evt.stopPropagation(); } }, t);
229 
230         // This sets the font-size of the input HTML element
231         t.visPropOld.fontsize = "0px";
232         board.renderer.updateTextStyle(t, false);
233 
234         return t;
235     };
236 
237     JXG.registerElement('input', JXG.createInput);
238 
239     return {
240         createInput: JXG.createInput
241     };
242 });
243