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 to be dumped as JavaScript or JessieCode code. 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(), 172 JXG.Options[obj.elType][s], 173 JXG.Options[obj.subs[s].elType]); 174 a[s].id = obj.subs[s].id; 175 a[s].name = obj.subs[s].name; 176 } 177 } 178 179 a.id = obj.id; 180 a.name = obj.name; 181 182 return a; 183 }, 184 185 setBoundingBox: function(methods, board, boardVarName) { 186 methods.push({ 187 obj: boardVarName, 188 method: 'setBoundingBox', 189 params: [board.getBoundingBox(), true] 190 }); 191 192 return methods; 193 }, 194 195 /** 196 * Generate a save-able structure with all elements. This is used by {@link JXG.Dump#toJessie} and 197 * {@link JXG.Dump#toJavaScript} to generate the script. 198 * @param {JXG.Board} board 199 * @returns {Array} An array with all metadata necessary to save the construction. 200 */ 201 dump: function (board) { 202 var e, obj, element, s, 203 props = [], 204 methods = [], 205 elementList = [], 206 len = board.objectsList.length; 207 208 this.addMarkers(board, 'dumped', false); 209 210 // This has been moved to toJavaScript and toJessie 211 /* 212 methods.push({ 213 obj: '$board', 214 method: 'setBoundingBox', 215 params: [board.getBoundingBox(), true] 216 }); 217 */ 218 219 for (e = 0; e < len; e++) { 220 obj = board.objectsList[e]; 221 element = {}; 222 223 if (!obj.dumped && obj.dump) { 224 element.type = obj.getType(); 225 element.parents = obj.getParents().slice(); 226 227 // Extract coordinates of a point 228 if (element.type === 'point' && element.parents[0] === 1) { 229 element.parents = element.parents.slice(1); 230 } 231 232 for (s = 0; s < element.parents.length; s++) { 233 if (Type.isString(element.parents[s]) && 234 element.parents[s][0] !== "'" && 235 element.parents[s][0] !== '"') { 236 237 element.parents[s] = '"' + element.parents[s] + '"'; 238 } else if (Type.isArray( element.parents[s]) ) { 239 element.parents[s] = '[' + element.parents[s].toString() + ']'; 240 } 241 } 242 243 element.attributes = this.prepareAttributes(board, obj); 244 if (element.type === 'glider' && obj.onPolygon) { 245 props.push({ 246 obj: obj.id, 247 prop: 'onPolygon', 248 val: true 249 }); 250 } 251 252 elementList.push(element); 253 } 254 } 255 256 this.deleteMarkers(board, 'dumped'); 257 258 return { 259 elements: elementList, 260 props: props, 261 methods: methods 262 }; 263 }, 264 265 /** 266 * Converts an array of different values into a parameter string that can be used by the code generators. 267 * @param {Array} a 268 * @param {function} converter A function that is used to transform the elements of <tt>a</tt>. Usually 269 * {@link JXG.toJSON} or {@link JXG.Dump.toJCAN} are used. 270 * @returns {String} 271 */ 272 arrayToParamStr: function (a, converter) { 273 var i, 274 s = []; 275 276 for (i = 0; i < a.length; i++) { 277 s.push(converter.call(this, a[i])); 278 } 279 280 return s.join(', '); 281 }, 282 283 /** 284 * Converts a JavaScript object into a JCAN (JessieCode Attribute Notation) string. 285 * @param {Object} obj A JavaScript object, functions will be ignored. 286 * @returns {String} The given object stored in a JCAN string. 287 */ 288 toJCAN: function (obj) { 289 var s, i, list, prop; 290 291 switch (typeof obj) { 292 case 'object': 293 if (obj) { 294 list = []; 295 296 if (Type.isArray(obj)) { 297 for (i = 0; i < obj.length; i++) { 298 list.push(this.toJCAN(obj[i])); 299 } 300 301 return '[' + list.join(',') + ']'; 302 } 303 304 for (prop in obj) { 305 if (obj.hasOwnProperty(prop)) { 306 list.push(prop + ': ' + this.toJCAN(obj[prop])); 307 } 308 } 309 310 return '<<' + list.join(', ') + '>> '; 311 } 312 return 'null'; 313 case 'string': 314 return '\'' + obj.replace(/(["'])/g, '\\$1') + '\''; 315 case 'number': 316 case 'boolean': 317 return obj.toString(); 318 case 'null': 319 return 'null'; 320 } 321 }, 322 323 /** 324 * Saves the construction in <tt>board</tt> to JessieCode. 325 * @param {JXG.Board} board 326 * @returns {String} JessieCode 327 */ 328 toJessie: function (board) { 329 var i, elements, 330 dump = this.dump(board), 331 script = []; 332 333 dump.methods = this.setBoundingBox(dump.methods, board, '$board'); 334 335 elements = dump.elements; 336 337 for (i = 0; i < elements.length; i++) { 338 if (elements[i].attributes.name.length > 0) { 339 script.push('// ' + elements[i].attributes.name); 340 } 341 342 script.push('s' + i + ' = ' + elements[i].type + '(' + elements[i].parents.join(', ') + ') ' + this.toJCAN(elements[i].attributes).replace(/\n/, '\\n') + ';'); 343 script.push(''); 344 } 345 346 for (i = 0; i < dump.methods.length; i++) { 347 script.push(dump.methods[i].obj + '.' + dump.methods[i].method + '(' + this.arrayToParamStr(dump.methods[i].params, this.toJCAN) + ');'); 348 script.push(''); 349 } 350 351 for (i = 0; i < dump.props.length; i++) { 352 script.push(dump.props[i].obj + '.' + dump.props[i].prop + ' = ' + this.toJCAN(dump.props[i].val) + ';'); 353 script.push(''); 354 } 355 356 return script.join('\n'); 357 }, 358 359 /** 360 * Saves the construction in <tt>board</tt> to JavaScript. 361 * @param {JXG.Board} board 362 * @returns {String} JavaScript 363 */ 364 toJavaScript: function (board) { 365 var i, elements, 366 dump = this.dump(board), 367 script = []; 368 369 dump.methods = this.setBoundingBox(dump.methods, board, 'board'); 370 371 elements = dump.elements; 372 373 for (i = 0; i < elements.length; i++) { 374 script.push('board.create("' + elements[i].type + '", [' + elements[i].parents.join(', ') + '], ' + Type.toJSON(elements[i].attributes) + ');'); 375 } 376 377 for (i = 0; i < dump.methods.length; i++) { 378 script.push(dump.methods[i].obj + '.' + dump.methods[i].method + '(' + this.arrayToParamStr(dump.methods[i].params, Type.toJSON) + ');'); 379 script.push(''); 380 } 381 382 for (i = 0; i < dump.props.length; i++) { 383 script.push(dump.props[i].obj + '.' + dump.props[i].prop + ' = ' + Type.toJSON(dump.props[i].val) + ';'); 384 script.push(''); 385 } 386 387 return script.join('\n'); 388 } 389 }; 390 391 return JXG.Dump; 392 }); 393