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 });