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