      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