      1 /*
      2     Copyright 2008-2013
      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 <http://www.gnu.org/licenses/>
     29     and <http://opensource.org/licenses/MIT/>.
     30  */
     31
     32
     33 /*global JXG: true, define: true*/
     34 /*jslint nomen: true, plusplus: true*/
     35
     36 /* depends:
     37  jxg
     38  utils/type
     39  */
     40
     41 /**
     42  * @fileoverview The JXG.Dump namespace provides methods to save a board to javascript.
     43  */
     44
     45 define(['jxg', 'utils/type'], function (JXG, Type) {
     46
     47     "use strict";
     48
     49     /**
     50      * The JXG.Dump namespace provides classes and methods to save a board to javascript.
     51      * @namespace
     52      */
     53     JXG.Dump = {
     54
     55         /**
     56          * Adds markers to every element of the board
     57          * @param {JXG.Board} board
     58          * @param {Array|String} markers
     59          * @param {Array} values
     60          */
     61         addMarkers: function (board, markers, values) {
     62             var e, l, i;
     63
     64             if (!Type.isArray(markers)) {
     65                 markers = [markers];
     66             }
     67
     68             if (!Type.isArray(values)) {
     69                 values = [values];
     70             }
     71
     72             l = Math.min(markers.length, values.length);
     73
     74             markers.length = l;
     75             values.length = l;
     76
     77             for (e in board.objects) {
     78                 if (board.objects.hasOwnProperty(e)) {
     79                     for (i = 0; i < l; i++) {
     80                         board.objects[e][markers[i]] = values[i];
     81                     }
     82                 }
     83             }
     84         },
     85
     86         /**
     87          * Removes markers from every element on the board.
     88          * @param {JXG.Board} board
     89          * @param {Array|String} markers
     90          */
     91         deleteMarkers: function (board, markers) {
     92             var e, l, i;
     93
     94             if (!Type.isArray(markers)) {
     95                 markers = [markers];
     96             }
     97
     98             l = markers.length;
     99
    100             markers.length = l;
    101
    102             for (e in board.objects) {
    103                 if (board.objects.hasOwnProperty(e)) {
    104                     for (i = 0; i < l; i++) {
    105                         delete board.objects[e][markers[i]];
    106                     }
    107                 }
    108             }
    109         },
    110
    111         /**
    112          * Stringifies a string, i.e. puts some quotation marks around <tt>s</tt> if it is of type string.
    113          * @param {*} s
    114          * @returns {String} " + s + "
    115          */
    116         str: function (s) {
    117             if (typeof s === 'string' && s.substr(0, 7) !== 'function') {
    118                 s = '\'' + s + '\'';
    119             }
    120
    121             return s;
    122         },
    123
    124         /**
    125          * Eliminate default values given by {@link JXG.Options} from the attributes object.
    126          * @param {Object} instance Attribute object of the element
    127          * @param {Object} s Arbitrary number of objects <tt>instance</tt> will be compared to. Usually these are
    128          * sub-objects of the {@link JXG.Board#options} structure.
    129          * @returns {Object} Minimal attributes object
    130          */
    131         minimizeObject: function (instance, s) {
    132             var p, pl, i,
    133                 def = {},
    134                 copy = Type.deepCopy(instance),
    135                 defaults = [];
    136
    137             for (i = 1; i < arguments.length; i++) {
    138                 defaults.push(arguments[i]);
    139             }
    140
    141             for (i = defaults.length; i > 0; i--) {
    142                 def = Type.deepCopy(def, defaults[i - 1], true);
    143             }
    144
    145             for (p in def) {
    146                 if (def.hasOwnProperty(p)) {
    147                     pl = p.toLowerCase();
    148
    149                     if (typeof def[p] !== 'object' && def[p] === copy[pl]) {
    150                         delete copy[pl];
    151                     }
    152                 }
    153             }
    154
    155             return copy;
    156         },
    157
    158         /**
    159          * Prepare the attributes object for an element.
    160          * @param {JXG.Board} board
    161          * @param {JXG.GeometryElement} obj Geometry element which attributes object is generated
    162          * @returns {Object} An attributes object.
    163          */
    164         prepareAttributes: function (board, obj) {
    165             var a, s;
    166
    167             a = this.minimizeObject(obj.getAttributes(), JXG.Options[obj.elType]);
    168
    169             for (s in obj.subs) {
    170                 if (obj.subs.hasOwnProperty(s)) {
    171                     a[s] = this.minimizeObject(obj.subs[s].getAttributes(), JXG.Options[obj.elType][s], JXG.Options[obj.subs[s].elType]);
    172                     a[s].id = obj.subs[s].id;
    173                     a[s].name = obj.subs[s].name;
    174                 }
    175             }
    176
    177             a.id = obj.id;
    178             a.name = obj.name;
    179
    180             return a;
    181         },
    182
    183         /**
    184          * Generate a save-able structure with all elements. This is used by {@link JXG.Dump#toJessie} and {@link JXG.Dump#toJavaScript}
    185          * to generate the script.
    186          * @param {JXG.Board} board
    187          * @returns {Array} An array with all metadata necessary to save the construction.
    188          */
    189         dump: function (board) {
    190             var e, obj, element, s,
    191                 props = [],
    192                 methods = [],
    193                 elementList = [],
    194                 len = board.objectsList.length;
    195
    196             this.addMarkers(board, 'dumped', false);
    197
    198             methods.push({
    199                 obj: '$board',
    200                 method: 'setBoundingBox',
    201                 params: [board.getBoundingBox(), true]
    202             });
    203
    204             for (e = 0; e < len; e++) {
    205                 obj = board.objectsList[e];
    206                 element = {};
    207
    208                 if (!obj.dumped && obj.dump) {
    209                     element.type = obj.getType();
    210                     element.parents = obj.getParents();
    211
    212                     if (element.type === 'point' && element.parents[0] === 1) {
    213                         element.parents = element.parents.slice(1);
    214                     }
    215
    216                     for (s = 0; s < element.parents.length; s++) {
    217                         if (typeof element.parents[s] === 'string') {
    218                             element.parents[s] = '\'' + element.parents[s] + '\'';
    219                         }
    220                     }
    221
    222                     element.attributes = this.prepareAttributes(board, obj);
    223                     if (element.type === 'glider' && obj.onPolygon) {
    224                         props.push({
    225                             obj: obj.id,
    226                             prop: 'onPolygon',
    227                             val: true
    228                         });
    229                     }
    230
    231                     elementList.push(element);
    232                 }
    233             }
    234
    235             this.deleteMarkers(board, 'dumped');
    236
    237             return {
    238                 elements: elementList,
    239                 props: props,
    240                 methods: methods
    241             };
    242         },
    243
    244         /**
    245          * Converts an array of different values into a parameter string that can be used by the code generators.
    246          * @param {Array} a
    247          * @param {function} converter A function that is used to transform the elements of <tt>a</tt>. Usually
    248          * {@link JXG.toJSON} or {@link JXG.Dump.toJSAN} are used.
    249          * @returns {String}
    250          */
    251         arrayToParamStr: function (a, converter) {
    252             var i,
    253                 s = [];
    254
    255             for (i = 0; i < a.length; i++) {
    256                 s.push(converter.call(this, a[i]));
    257             }
    258
    259             return s.join(', ');
    260         },
    261
    262         /**
    263          * Converts a JavaScript object into a JSAN (JessieScript Attribute Notation) string.
    264          * @param {Object} obj A JavaScript object, functions will be ignored.
    265          * @returns {String} The given object stored in a JSAN string.
    266          */
    267         toJSAN: function (obj) {
    268             var s, i, list, prop;
    269
    270             switch (typeof obj) {
    271             case 'object':
    272                 if (obj) {
    273                     list = [];
    274
    275                     if (Type.isArray(obj)) {
    276                         for (i = 0; i < obj.length; i++) {
    277                             list.push(this.toJSAN(obj[i]));
    278                         }
    279
    280                         return '[' + list.join(',') + ']';
    281                     }
    282
    283                     for (prop in obj) {
    284                         if (obj.hasOwnProperty(prop)) {
    285                             list.push(prop + ': ' + this.toJSAN(obj[prop]));
    286                         }
    287                     }
    288
    289                     return '<<' + list.join(', ') + '>> ';
    290                 }
    291                 return 'null';
    292             case 'string':
    293                 return '\'' + obj.replace(/(["'])/g, '\\$1') + '\'';
    294             case 'number':
    295             case 'boolean':
    296                 return obj.toString();
    297             case 'null':
    298                 return 'null';
    299             }
    300         },
    301
    302         /**
    303          * Saves the construction in <tt>board</tt> to JessieScript.
    304          * @param {JXG.Board} board
    305          * @returns {String} JessieScript
    306          */
    307         toJessie: function (board) {
    308             var i, elements,
    309                 dump = this.dump(board),
    310                 script = [];
    311
    312             elements = dump.elements;
    313
    314             for (i = 0; i < elements.length; i++) {
    315                 if (elements[i].attributes.name.length > 0) {
    316                     script.push('// ' + elements[i].attributes.name);
    317                 }
    318
    319                 script.push('s' + i + ' = ' + elements[i].type + '(' + elements[i].parents.join(', ') + ') ' + this.toJSAN(elements[i].attributes).replace(/\n/, '\\n') + ';');
    320                 script.push('');
    321             }
    322
    323             for (i = 0; i < dump.methods.length; i++) {
    324                 script.push(dump.methods[i].obj + '.' + dump.methods[i].method + '(' + this.arrayToParamStr(dump.methods[i].params, this.toJSAN) + ');');
    325                 script.push('');
    326             }
    327
    328             for (i = 0; i < dump.props.length; i++) {
    329                 script.push(dump.props[i].obj + '.' + dump.props[i].prop + ' = ' + this.toJSAN(dump.props[i].val) + ';');
    330                 script.push('');
    331             }
    332
    333             return script.join('\n');
    334         },
    335
    336         /**
    337          * Saves the construction in <tt>board</tt> to JavaScript.
    338          * @param {JXG.Board} board
    339          * @returns {String} JavaScript
    340          */
    341         toJavaScript: function (board) {
    342             var i, elements,
    343                 dump = this.dump(board),
    344                 script = [];
    345
    346             elements = dump.elements;
    347
    348             for (i = 0; i < elements.length; i++) {
    349                 script.push('board.create("' + elements[i].type + '", [' + elements[i].parents.join(', ') + '], ' + Type.toJSON(elements[i].attributes) + ');');
    350             }
    351
    352             for (i = 0; i < dump.methods.length; i++) {
    353                 script.push(dump.methods[i].obj + '.' + dump.methods[i].method + '(' + this.arrayToParamStr(dump.methods[i].params, Type.toJSON) + ');');
    354                 script.push('');
    355             }
    356
    357             for (i = 0; i < dump.props.length; i++) {
    358                 script.push(dump.props[i].obj + '.' + dump.props[i].prop + ' = ' + Type.toJSON(dump.props[i].val) + ';');
    359                 script.push('');
    360             }
    361
    362             return script.join('\n');
    363         }
    364     };
    365
    366     return JXG.Dump;
    367 });