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