      1 /*
      2     Copyright 2008-2011
      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: you can redistribute it and/or modify
     13     it under the terms of the GNU Lesser General Public License as published by
     14     the Free Software Foundation, either version 3 of the License, or
     15     (at your option) any later version.
     16
     17     JSXGraph is distributed in the hope that it will be useful,
     18     but WITHOUT ANY WARRANTY; without even the implied warranty of
     19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     20     GNU Lesser General Public License for more details.
     21
     22     You should have received a copy of the GNU Lesser General Public License
     23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
     24 */
     25
     26 /**
     27  * @fileoverview The JXG.Dump namespace provides methods to save a board to javascript.
     28  */
     29
     30 /**
     31  * The JXG.Dump namespace provides classes and methods to save a board to javascript.
     32  * @namespace
     33  */
     34 JXG.Dump = {
     35
     36     /**
     37      * Adds markers to every element of the board
     38      * @param {JXG.Board} board
     39      * @param {Array|String} markers
     40      * @param {Array|%} values
     41      */
     42     addMarkers: function (board, markers, values) {
     43         var e, l, i;
     44
     45         if (!JXG.isArray(markers)) {
     46             markers = [markers];
     47         }
     48
     49         if (!JXG.isArray(values)) {
     50             values = [values];
     51         }
     52
     53         l = Math.min(markers.length, values.length);
     54
     55         markers.length = l;
     56         values.length = l;
     57
     58         for (e in board.objects) {
     59             for (i = 0; i < l; i++) {
     60                 board.objects[e][markers[i]] = values[i];
     61             }
     62         }
     63     },
     64
     65     /**
     66      * Removes markers from every element on the board.
     67      * @param {JXG.Board} board
     68      * @param {Array|String} markers
     69      */
     70     deleteMarkers: function (board, markers) {
     71         var e, l, i;
     72
     73         if (!JXG.isArray(markers)) {
     74             markers = [markers];
     75         }
     76
     77         l = markers.length;
     78
     79         markers.length = l;
     80
     81         for (e in board.objects) {
     82             for (i = 0; i < l; i++) {
     83                 delete board.objects[e][markers[i]];
     84             }
     85         }
     86     },
     87
     88     /**
     89      * Stringifies a string, i.e. puts some quotation marks around <tt>s</tt> if it is of type string.
     90      * @param {%} s
     91      * @returns {String} " + s + "
     92      */
     93     str: function (s) {
     94         if (typeof s === 'string' && s.substr(0, 7) !== 'function') {
     95             s = '\'' + s + '\'';
     96         }
     97
     98         return s;
     99     },
    100
    101     /**
    102      * Eliminate default values given by {@link JXG.Options} from the attributes object.
    103      * @param {Object} instance Attribute object of the element
    104      * @param {Object} % Arbitrary number of objects <tt>instance</tt> will be compared to. Usually these are
    105      * sub-objects of the {@link JXG.Board#options} structure.
    106      * @returns {Object} Minimal attributes object
    107      */
    108     minimizeObject: function (instance) {
    109         var p, pl, copy = JXG.deepCopy(instance),
    110             defaults = [], i;
    111
    112         for (i = 1; i < arguments.length; i++) {
    113             defaults.push(arguments[i]);
    114         }
    115
    116         for (i = 0; i < defaults.length; i++) {
    117             for (p in defaults[i]) {
    118                 pl = p.toLowerCase();
    119
    120                 if (typeof defaults[i][p] !== 'object' && defaults[i][p] == copy[pl]) {
    121                     delete copy[pl];
    122                 }
    123             }
    124         }
    125
    126         return copy;
    127     },
    128
    129     /**
    130      * Prepare the attributes object for an element.
    131      * @param {JXG.Board} board
    132      * @param {JXG.GeometryElement} obj Geometry element which attributes object is generated
    133      * @returns {Object} An attributes object.
    134      */
    135     prepareAttributes: function (board, obj) {
    136         var a, s;
    137
    138         a = this.minimizeObject(obj.getAttributes(), board.options[obj.elType], board.options.elements);
    139
    140         for (s in obj.subs) {
    141             a[s] = this.minimizeObject(obj.subs[s].getAttributes(), board.options[obj.elType][s], board.options[obj.subs[s].elType], board.options.elements);
    142             a[s].id = obj.subs[s].id;
    143             a[s].name = obj.subs[s].name;
    144         }
    145
    146         a.id = obj.id;
    147         a.name = obj.name;
    148
    149         return a;
    150     },
    151
    152     /**
    153      * Generate a save-able structure with all elements. This is used by {@link JXG.Dump#toJessie} and {@link JXG.Dump#toJavaScript}
    154      * to generate the script.
    155      * @param {JXG.Board} board
    156      * @returns {Array} An array with all metadata necessary to save the construction.
    157      */
    158     dump: function (board) {
    159         var e, obj, element, s,
    160             elementList = [], i;
    161
    162         this.addMarkers(board, 'dumped', false);
    163
    164         for (e in board.objects) {
    165             obj = board.objects[e];
    166             element = {};
    167
    168             if (!obj.dumped && obj.dump) {
    169                 element.type = obj.getType();
    170                 element.parents = obj.getParents();
    171
    172                 if (element.type === 'point' && element.parents[0] == 1) {
    173                     element.parents = element.parents.slice(1);
    174                 }
    175
    176                 for (s = 0; s < element.parents.length; s++) {
    177                     if (typeof element.parents[s] === 'string') {
    178                         element.parents[s] = '\'' + element.parents[s] + '\'';
    179                     }
    180                 }
    181
    182                 element.attributes = this.prepareAttributes(board, obj);
    183
    184                 elementList.push(element);
    185             }
    186         }
    187
    188         this.deleteMarkers(board, 'dumped');
    189
    190         return elementList;
    191     },
    192
    193     /**
    194      * Converts a JavaScript object into a JSAN (JessieScript Attribute Notation) string.
    195      * @param {Object} obj A JavaScript object, functions will be ignored.
    196      * @returns {String} The given object stored in a JSAN string.
    197      */
    198     toJSAN: function (obj) {
    199         var s, i;
    200
    201         switch (typeof obj) {
    202             case 'object':
    203                 if (obj) {
    204                     var list = [];
    205                     if (obj instanceof Array) {
    206                         for (i = 0; i < obj.length; i++) {
    207                             list.push(this.toJSAN(obj[i]));
    208                         }
    209                         return '[' + list.join(',') + ']';
    210                     } else {
    211                         for (var prop in obj) {
    212                             list.push(prop + ': ' + this.toJSAN(obj[prop]));
    213                         }
    214                         return '<<' + list.join(', ') + '>> ';
    215                     }
    216                 } else {
    217                     return 'null';
    218                 }
    219             case 'string':
    220                 return '\'' + obj.replace(/(["'])/g, '\\$1') + '\'';
    221             case 'number':
    222             case 'boolean':
    223                 return new String(obj);
    224             case 'null':
    225                 return 'null';
    226         }
    227     },
    228
    229     /**
    230      * Saves the construction in <tt>board</tt> to JessieScript.
    231      * @param {JXG.Board} board
    232      * @returns {String} JessieScript
    233      */
    234     toJessie: function (board) {
    235         var elements = this.dump(board),
    236             script = [], i;
    237
    238         for (i = 0; i < elements.length; i++) {
    239             if (elements[i].attributes.name.length > 0) {
    240                 script.push('// ' + elements[i].attributes.name);
    241             }
    242
    243             script.push('s' + i + ' = ' + elements[i].type + '(' + elements[i].parents.join(', ') + ') ' + this.toJSAN(elements[i].attributes).replace(/\n/, '\\n') + ';');
    244             script.push('');
    245         }
    246
    247         return script.join('\n');
    248     },
    249
    250     /**
    251      * Saves the construction in <tt>board</tt> to JavaScript.
    252      * @param {JXG.Board} board
    253      * @returns {String} JavaScript
    254      */
    255     toJavaScript: function (board) {
    256         var elements = this.dump(board),
    257             script = [], i;
    258
    259         for (i = 0; i < elements.length; i++) {
    260             script.push('board.create("' + elements[i].type + '", [' + elements[i].parents.join(', ') + '], ' + JXG.toJSON(elements[i].attributes) + ');');
    261         }
    262
    263         return script.join('\n');
    264     }
    265 };