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 var priv = {
 44     ButtonClickEventHandler: function () {
 45         if (this._handler) {
 46             this._handler();
 47         }
 48         this.board.update();
 49     }
 50 };
 51 
 52 /**
 53  * @class This element is used to provide a constructor for special texts containing a
 54  * form button element.
 55  * For this element, the attribute "display" has to have the value 'html' (which is the default).
 56  *
 57  * <p><b>Setting a CSS class:</b> The attribute <tt>cssClass</tt> affects the HTML div element that contains the button element. To change the CSS properties of the HTML button element a selector of the form
 58  * <tt>.mybutton > button { ... }</tt> has to be used. See the example below.
 59  *
 60  * <p><b>Access the button element with JavaScript:</b>
 61  * The underlying HTML button element can be accessed through the sub-object 'rendNodeButton', e.g. to
 62  * add event listeners.
 63  *
 64  * @pseudo
 65  * @name Button
 66  * @augments Text
 67  * @constructor
 68  * @type JXG.Text
 69  *
 70  * @param {number,function_number,function_String,function_function} x,y,label,handler Parent elements for button elements.
 71  *  <p>
 72  *  x and y are the coordinates of the lower left corner of the text box.
 73  *   The position of the text is fixed,
 74  *  x and y are numbers. The position is variable if x or y are functions.
 75  *  <p>
 76  *  The label of the input element may be given  as string.
 77  *  <p>
 78  *  The (optional) handler function which is called when the button is pressed.
 79  *
 80  * @example
 81  *  var p = board.create('point', [0.5, 0.5], {id: 'p1'});
 82  *
 83  *  // Create a button element at position [1,2].
 84  *  var button1 = board.create('button', [1, 2, 'Change Y with JavaScript', function() {
 85  *      p.moveTo([p.X(), p.Y() + 0.5], 100);
 86  *  }], {});
 87  *
 88  *  // Create a button element at position [1,4].
 89  *  var button2 = board.create('button', [1, 4, 'Change Y with JessieCode',
 90  *      "$('p1').Y = $('p1').Y() - 0.5;"
 91  *  ], {});
 92  *
 93  * </pre><div class="jxgbox" id="JXGf19b1bce-dd00-4e35-be97-ff1817d11514" style="width: 500px; height: 300px;"></div>
 94  * <script type="text/javascript">
 95  *  var t1_board = JXG.JSXGraph.initBoard('JXGf19b1bce-dd00-4e35-be97-ff1817d11514', {boundingbox: [-3, 6, 5, -3], axis: true, showcopyright: false, shownavigation: false});
 96  *  var p = t1_board.create('point', [0, -1], {id: 'p1'});
 97  *
 98  *  // Create a button element at position [1,2].
 99  *  var button1 = t1_board.create('button', [1, 2, 'Change Y with JavaScript', function() {
100  *      p.moveTo([p.X(), p.Y() + 0.5], 100);
101  *  }], {});
102  *
103  *  // Create a button element at position [1,4].
104  *  var button2 = t1_board.create('button', [1, 4, 'Change Y with JessieCode',
105  *      "$('p1').Y = $('p1').Y() - 0.5;"
106  *  ], {});
107  *
108  * </script><pre>
109  *
110  * @example
111  * // A toggle button
112  * var butt = board.create('button', [-2, -2, 'Off', function() {
113  *   var txt;
114  *   butt.value = !butt.value;
115  *   if (butt.value) {
116  *   	txt = 'On';
117  *   } else {
118  *   	txt = 'Off';
119  *   }
120  * 	butt.rendNodeButton.innerHTML = txt;
121  * }]);
122  *
123  * // Set initial value for the button
124  * if (!JXG.exists(butt.value)) {
125  * 	butt.value = false;
126  * }
127  *
128  * var p = board.create('point', [2, -2], {
129  * 	visible: () => butt.value
130  * });
131  *
132  *
133  *
134  * </pre><div id="JXGa1eaab8f-c73b-4660-96ce-4ca17bcac4d6" class="jxgbox" style="width: 300px; height: 300px;"></div>
135  * <script type="text/javascript">
136  *     (function() {
137  *         var board = JXG.JSXGraph.initBoard('JXGa1eaab8f-c73b-4660-96ce-4ca17bcac4d6',
138  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
139  *     var butt = board.create('button', [-2, -2, 'Off', function() {
140  *       var txt;
141  *       butt.value = !butt.value;
142  *       if (butt.value) {
143  *       	txt = 'On';
144  *       } else {
145  *       	txt = 'Off';
146  *       }
147  *     	butt.rendNodeButton.innerHTML = txt;
148  *     }]);
149  *
150  *     // Set initial value for the button
151  *     if (!JXG.exists(butt.value)) {
152  *     	butt.value = false;
153  *     }
154  *
155  *     var p = board.create('point', [2, -2], {
156  *     	visible: () => butt.value
157  *     });
158  *
159  *     })();
160  *
161  * </script><pre>
162  *
163  * @example
164  * var i1 = board.create('input', [-3, 4, 'sin(x)', 'f(x)='], {cssStyle: 'width:4em', maxlength: 2});
165  * var c1 = board.create('checkbox', [-3, 2, 'label 1'], {});
166  * var b1 = board.create('button', [-3, -1, 'Change texts', function () {
167  *         i1.setText('g(x)');
168  *         i1.set('cos(x)');
169  *         c1.setText('label 2');
170  *         b1.setText('Texts are changed');
171  *     }],
172  *     {cssStyle: 'width:200px'});
173  *
174  * </pre><div id="JXG11cac8ff-2354-47e7-9da4-eb928e53de05" class="jxgbox" style="width: 300px; height: 300px;"></div>
175  * <script type="text/javascript">
176  *     (function() {
177  *         var board = JXG.JSXGraph.initBoard('JXG11cac8ff-2354-47e7-9da4-eb928e53de05',
178  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
179  *             var i1 = board.create('input', [-3, 4, 'sin(x)', 'f(x)='], {cssStyle: 'width:4em', maxlength: 2});
180  *             var c1 = board.create('checkbox', [-3, 2, 'label 1'], {});
181  *             var b1 = board.create('button', [-3, -1, 'Change texts', function () {
182  *                     i1.setText('g(x)');
183  *                     i1.set('cos(x)');
184  *                     c1.setText('label 2');
185  *                     b1.setText('Texts are changed');
186  *                 }],
187  *                 {cssStyle: 'width:200px'});
188  *
189  *     })();
190  *
191  * </script><pre>
192  *
193  * @example
194  * // Set the CSS class of the button
195  *
196  * // CSS:
197  * <style>
198  * .mybutton > button {
199  *   background-color: #04AA6D;
200  *   border: none;
201  *   color: white;
202  *   padding: 1px 3px;
203  *   text-align: center;
204  *   text-decoration: none;
205  *   display: inline-block;
206  *   font-size: 16px;
207  * }
208  * </style>
209  *
210  * // JavaScript:
211  * var button = board.create('button',
212  *     [1, 4, 'answers', function () {}],
213  *     {cssClass:'mybutton', highlightCssClass: 'mybutton'});
214  *
215  * </pre>
216  * <style>
217  * .mybutton > button {
218  *   background-color: #04AA6D;
219  *   border: none;
220  *   color: white;
221  *   padding: 1px 3px;
222  *   text-align: center;
223  *   text-decoration: none;
224  *   display: inline-block;
225  *   font-size: 16px;
226  * }
227  * </style>
228  * <div id="JXG2da6cf73-8c2e-495c-bd31-42de43b71cf8" class="jxgbox" style="width: 300px; height: 300px;"></div>
229  * <script type="text/javascript">
230  *     (function() {
231  *         var board = JXG.JSXGraph.initBoard('JXG2da6cf73-8c2e-495c-bd31-42de43b71cf8',
232  *             {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false});
233  *       var button = board.create('button', [1, 4, 'answers', function () {
234  *       }], {cssClass:'mybutton', highlightCssClass: 'mybutton'});
235  *
236  *     })();
237  *
238  * </script><pre>
239  *
240  */
241 JXG.createButton = function (board, parents, attributes) {
242     var t,
243         par,
244         attr = Type.copyAttributes(attributes, board.options, "button");
245 
246     //if (parents.length < 3) {
247     //throw new Error("JSXGraph: Can't create button with parent types '" +
248     //    (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." +
249     //    "\nPossible parents are: [x, y, label, handler]");
250     //}
251 
252     // 1. Create empty button
253     par = [parents[0], parents[1], '<button type="button" style="width:100%;"></button>'];
254     t = board.create("text", par, attr);
255     t.type = Type.OBJECT_TYPE_BUTTON;
256 
257     t.rendNodeButton = t.rendNode.childNodes[0];
258     t.rendNodeButton.id = t.rendNode.id + "_button";
259     // t.rendNodeButton.innerHTML = parents[2];
260 
261     t.rendNodeTag = t.rendNodeButton; // Needed for unified treatment in setAttribute
262     t.rendNodeTag.disabled = !!attr.disabled;
263 
264     // 2. Set parents[2] (string|function) as content of the button.
265     // abstract.js selects the correct DOM element for the update
266     t.setText(parents[2]);
267 
268     // This sets the font-size of the button text
269     t.visPropOld.fontsize = "0px";
270     board.renderer.updateTextStyle(t, false);
271 
272     if (parents[3]) {
273         if (Type.isString(parents[3])) {
274             t._jc = new JXG.JessieCode();
275             t._jc.use(board);
276             t._handler = function () {
277                 t._jc.parse(parents[3]);
278             };
279         } else {
280             t._handler = parents[3];
281         }
282     }
283 
284     Env.addEvent(t.rendNodeButton, "click", priv.ButtonClickEventHandler, t);
285     Env.addEvent(
286         t.rendNodeButton,
287         "mousedown",
288         function (evt) {
289             if (Type.exists(evt.stopPropagation)) {
290                 evt.stopPropagation();
291             }
292         },
293         t
294     );
295     Env.addEvent(
296         t.rendNodeButton,
297         "touchstart",
298         function (evt) {
299             if (Type.exists(evt.stopPropagation)) {
300                 evt.stopPropagation();
301             }
302         },
303         t
304     );
305     Env.addEvent(
306         t.rendNodeButton,
307         "pointerdown",
308         function (evt) {
309             if (Type.exists(evt.stopPropagation)) {
310                 evt.stopPropagation();
311             }
312         },
313         t
314     );
315 
316     return t;
317 };
318 
319 JXG.registerElement("button", JXG.createButton);
320 
321 // export default {
322 //     createButton: JXG.createButton
323 // };
324