1 /* 2 Copyright 2008-2024 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.js"; 40 import Env from "../utils/env.js"; 41 import Type from "../utils/type.js"; 42 43 /** 44 * @class 45 * @ignore 46 */ 47 var priv = { 48 /** 49 * @class 50 * @ignore 51 */ 52 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 * For this element, the attribute "display" has to have the value 'html' (which is the default). 63 * If the width of element is set with the attribute "cssStyle", the width of the 64 * label must be added because cssStyle affects the surrounding div element. 65 * 66 * <p><b>Setting a CSS class:</b> The attribute <tt>cssClass</tt> affects the HTML div element that contains the input element. To change the CSS properties of the HTML input element a selector of the form 67 * <tt>.myinput > input { ... }</tt> has to be used. See the analog example for buttons: 68 * {@link Button}. 69 * 70 * <p><b>Access the input element with JavaScript:</b> 71 * The underlying HTML button element can be accessed through the sub-object 'rendNodeInput', e.g. to 72 * add event listeners. 73 * 74 * @pseudo 75 * @name Input 76 * @augments Text 77 * @constructor 78 * @type JXG.Text 79 * 80 * @param {number,function_number,function_String_String,function} x,y,value,label Parent elements for input elements. 81 * <p> 82 * x and y are the coordinates of the lower left corner of the text box. The position of the text is fixed, 83 * x and y are numbers. The position is variable if x or y are functions. 84 * <p> 85 * The default value of the input element must be given as string. 86 * <p> 87 * The label of the input element may be given as string or function. 88 * 89 * @example 90 * // Create an input element at position [1,4]. 91 * var input = board.create('input', [0, 1, 'sin(x)*x', 'f(x)='], {cssStyle: 'width: 100px'}); 92 * var f = board.jc.snippet(input.Value(), true, 'x', false); 93 * var graph = board.create('functiongraph',[f, 94 * function() { 95 * var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[0,0],board); 96 * return c.usrCoords[1]; 97 * }, 98 * function() { 99 * var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[board.canvasWidth,0],board); 100 * return c.usrCoords[1]; 101 * } 102 * ]); 103 * 104 * board.create('text', [1, 3, '<button onclick="updateGraph()">Update graph</button>']); 105 * 106 * var updateGraph = function() { 107 * graph.Y = board.jc.snippet(input.Value(), true, 'x', false); 108 * graph.updateCurve(); 109 * board.update(); 110 * } 111 * </pre><div class="jxgbox" id="JXGc70f55f1-21ba-4719-a37d-a93ae2943faa" style="width: 500px; height: 300px;"></div> 112 * <script type="text/javascript"> 113 * var t1_board = JXG.JSXGraph.initBoard('JXGc70f55f1-21ba-4719-a37d-a93ae2943faa', {boundingbox: [-3, 6, 5, -3], axis: true, showcopyright: false, shownavigation: false}); 114 * var input = t1_board.create('input', [1, 4, 'sin(x)*x', 'f(x)='], {cssStyle: 'width: 100px'}); 115 * var f = t1_board.jc.snippet(input.Value(), true, 'x', false); 116 * var graph = t1_board.create('functiongraph',[f, 117 * function() { 118 * var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[0,0],t1_board); 119 * return c.usrCoords[1]; 120 * }, 121 * function() { 122 * var c = new JXG.Coords(JXG.COORDS_BY_SCREEN,[t1_board.canvasWidth,0],t1_board); 123 * return c.usrCoords[1]; 124 * } 125 * ]); 126 * 127 * t1_board.create('text', [1, 3, '<button onclick="updateGraph()">Update graph</button>']); 128 * 129 * var updateGraph = function() { 130 * graph.Y = t1_board.jc.snippet(input.Value(), true, 'x', false); 131 * graph.updateCurve(); 132 * t1_board.update(); 133 * } 134 * </script><pre> 135 * 136 * @example 137 * // Add the `keyup` event to an input field 138 * var A = board.create('point', [3, -2]); 139 * var i = board.create('input', [-4, -4, "1", "x "]); 140 * 141 * i.rendNodeInput.addEventListener("keyup", ( function () { 142 * var x = parseFloat(this.value); 143 * if (!isNaN(x)) { 144 * A.moveTo([x, 3], 100); 145 * } 146 * })); 147 * 148 * </pre><div id="JXG81c84fa7-3f36-4874-9e0f-d4b9e93e755b" class="jxgbox" style="width: 300px; height: 300px;"></div> 149 * <script type="text/javascript"> 150 * (function() { 151 * var board = JXG.JSXGraph.initBoard('JXG81c84fa7-3f36-4874-9e0f-d4b9e93e755b', 152 * {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}); 153 * var A = board.create('point', [3, -2]); 154 * var i = board.create('input', [-4, -4, "1", "x "]); 155 * 156 * i.rendNodeInput.addEventListener("keyup", ( function () { 157 * var x = parseFloat(this.value); 158 * if (!isNaN(x)) { 159 * A.moveTo([x, 3], 100); 160 * } 161 * })); 162 * 163 * })(); 164 * 165 * </script><pre> 166 * 167 * @example 168 * // Add the `change` event to an input field 169 * var A = board.create('point', [3, -2]); 170 * var i = board.create('input', [-4, -4, "1", "x "]); 171 * 172 * i.rendNodeInput.addEventListener("change", ( function () { 173 * var x = parseFloat(i.Value()); 174 * A.moveTo([x, 2], 100); 175 * })); 176 * 177 * </pre><div id="JXG51c4d78b-a7ad-4c34-a983-b3ddae6192d7" class="jxgbox" style="width: 300px; height: 300px;"></div> 178 * <script type="text/javascript"> 179 * (function() { 180 * var board = JXG.JSXGraph.initBoard('JXG51c4d78b-a7ad-4c34-a983-b3ddae6192d7', 181 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 182 * var A = board.create('point', [3, -2]); 183 * var i = board.create('input', [-4, -4, "1", "x "]); 184 * 185 * i.rendNodeInput.addEventListener("change", ( function () { 186 * var x = parseFloat(i.Value()); 187 * A.moveTo([x, 2], 100); 188 * })); 189 * 190 * })(); 191 * 192 * </script><pre> 193 * 194 * 195 * @example 196 * Apply CSS classes to label and input tag 197 * <style> 198 * div.JXGtext_inp { 199 * font-weight: bold; 200 * } 201 * 202 * // Label 203 * div.JXGtext_inp > span > span { 204 * padding: 3px; 205 * } 206 * 207 * // Input field 208 * div.JXGtext_inp > span > input { 209 * width: 100px; 210 * border: solid 4px red; 211 * border-radius: 25px; 212 * } 213 * </style> 214 * 215 * var inp = board.create('input', [-6, 1, 'x', 'y'], { 216 * CssClass: 'JXGtext_inp', HighlightCssClass: 'JXGtext_inp' 217 * }); 218 * 219 * </pre> 220 * <style> 221 * div.JXGtext_inp { 222 * font-weight: bold; 223 * } 224 * 225 * div.JXGtext_inp > span > span { 226 * padding: 3px; 227 * } 228 * 229 * div.JXGtext_inp > span > input { 230 * width: 100px; 231 * border: solid 4px red; 232 * border-radius: 25px; 233 * } 234 * </style> 235 * <div id="JXGa3642ebd-a7dc-41ac-beb2-0c9e705ab8b4" class="jxgbox" style="width: 300px; height: 300px;"></div> 236 * <script type="text/javascript"> 237 * (function() { 238 * var board = JXG.JSXGraph.initBoard('JXGa3642ebd-a7dc-41ac-beb2-0c9e705ab8b4', 239 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 240 * var inp = board.create('input', [-6, 1, 'x', 'y'], {CssClass: 'JXGtext_inp', HighlightCssClass: 'JXGtext_inp'}); 241 * 242 * })(); 243 * </script><pre> 244 * 245 */ 246 JXG.createInput = function (board, parents, attributes) { 247 var t, 248 par, 249 attr = Type.copyAttributes(attributes, board.options, "input"); 250 251 par = [ 252 parents[0], 253 parents[1], 254 '<span style="display:inline; white-space:nowrap; padding:0px;">' + 255 '<span></span><input type="text" maxlength="' + 256 attr.maxlength + 257 '" style="width:100%"/>' + 258 "</span>" 259 ]; 260 261 // 1. Create input element with empty label 262 t = board.create("text", par, attr); 263 t.type = Type.OBJECT_TYPE_INPUT; 264 265 t.rendNodeLabel = t.rendNode.childNodes[0].childNodes[0]; 266 t.rendNodeInput = t.rendNode.childNodes[0].childNodes[1]; 267 // t.rendNodeLabel.innerHTML = parents[3]; 268 t.rendNodeInput.value = parents[2]; 269 t.rendNodeTag = t.rendNodeInput; // Needed for unified treatment in setAttribute 270 t.rendNodeTag.disabled = !!attr.disabled; 271 t.rendNodeLabel.id = t.rendNode.id + "_label"; 272 t.rendNodeInput.id = t.rendNode.id + "_input"; 273 274 // 2. Set parents[3] (string|function) as label of the input element. 275 // abstract.js selects the correct DOM element for the update 276 t.setText(parents[3]); 277 278 t._value = parents[2]; 279 280 /** 281 * @class 282 * @ignore 283 */ 284 t.update = function () { 285 if (this.needsUpdate) { 286 JXG.Text.prototype.update.call(this); 287 this._value = this.rendNodeInput.value; 288 } 289 return this; 290 }; 291 292 /** 293 * Returns the value (content) of the input element 294 * @name Value 295 * @memberOf Input.prototype 296 * @function 297 * @returns {String} content of the input field. 298 */ 299 t.Value = function () { 300 return this._value; 301 }; 302 303 /** 304 * Sets value of the input element. 305 * @name set 306 * @memberOf Input.prototype 307 * @function 308 * 309 * @param {String} val 310 * @returns {JXG.GeometryElement} Reference to the element. 311 * 312 * @example 313 * var i1 = board.create('input', [-3, 4, 'sin(x)', 'f(x)='], {cssStyle: 'width:4em', maxlength: 2}); 314 * var c1 = board.create('checkbox', [-3, 2, 'label 1'], {}); 315 * var b1 = board.create('button', [-3, -1, 'Change texts', function () { 316 * i1.setText('g(x)'); 317 * i1.set('cos(x)'); 318 * c1.setText('label 2'); 319 * b1.setText('Texts are changed'); 320 * }], 321 * {cssStyle: 'width:400px'}); 322 * 323 * </pre><div id="JXG11cac8ff-2354-47e7-9da4-eb298e53de05" class="jxgbox" style="width: 300px; height: 300px;"></div> 324 * <script type="text/javascript"> 325 * (function() { 326 * var board = JXG.JSXGraph.initBoard('JXG11cac8ff-2354-47e7-9da4-eb298e53de05', 327 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 328 * var i1 = board.create('input', [-3, 4, 'sin(x)', 'f(x)='], {cssStyle: 'width:4em', maxlength: 2}); 329 * var c1 = board.create('checkbox', [-3, 2, 'label 1'], {}); 330 * var b1 = board.create('button', [-3, -1, 'Change texts', function () { 331 * i1.setText('g(x)'); 332 * i1.set('cos(x)'); 333 * c1.setText('label 2'); 334 * b1.setText('Texts are changed'); 335 * }], 336 * {cssStyle: 'width:400px'}); 337 * 338 * })(); 339 * 340 * </script><pre> 341 * 342 */ 343 344 /** 345 * @class 346 * @ignore 347 */ 348 349 t.set = function (val) { 350 this._value = val; 351 this.rendNodeInput.value = val; 352 return this; 353 }; 354 355 Env.addEvent(t.rendNodeInput, "input", priv.InputInputEventHandler, t); 356 Env.addEvent( 357 t.rendNodeInput, 358 "mousedown", 359 function (evt) { 360 if (Type.exists(evt.stopPropagation)) { 361 evt.stopPropagation(); 362 } 363 }, 364 t 365 ); 366 Env.addEvent( 367 t.rendNodeInput, 368 "touchstart", 369 function (evt) { 370 if (Type.exists(evt.stopPropagation)) { 371 evt.stopPropagation(); 372 } 373 }, 374 t 375 ); 376 Env.addEvent( 377 t.rendNodeInput, 378 "pointerdown", 379 function (evt) { 380 if (Type.exists(evt.stopPropagation)) { 381 evt.stopPropagation(); 382 } 383 }, 384 t 385 ); 386 387 // This sets the font-size of the input HTML element 388 t.visPropOld.fontsize = "0px"; 389 board.renderer.updateTextStyle(t, false); 390 391 return t; 392 }; 393 394 JXG.registerElement("input", JXG.createInput); 395 396 // export default { 397 // createInput: JXG.createInput 398 // }; 399