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