1 /** 2 * Copyright (C) 2012-2013 KO GmbH <copyright@kogmbh.com> 3 * 4 * @licstart 5 * This file is part of WebODF. 6 * 7 * WebODF is free software: you can redistribute it and/or modify it 8 * under the terms of the GNU Affero General Public License (GNU AGPL) 9 * as published by the Free Software Foundation, either version 3 of 10 * the License, or (at your option) any later version. 11 * 12 * WebODF is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU Affero General Public License for more details. 16 * 17 * You should have received a copy of the GNU Affero General Public License 18 * along with WebODF. If not, see <http://www.gnu.org/licenses/>. 19 * @licend 20 * 21 * @source: http://www.webodf.org/ 22 * @source: https://github.com/kogmbh/WebODF/ 23 */ 24 25 /*jslint nomen: true, bitwise: true, emptyblock: true, unparam: true */ 26 /*global window, XMLHttpRequest, require, console, DOMParser, document, 27 process, __dirname, setTimeout, Packages, print, 28 readFile, quit, Buffer, ArrayBuffer, Uint8Array, 29 navigator, VBArray, alert, now, clearTimeout, webodf_version */ 30 31 /** 32 * Three implementations of a runtime for browser, node.js and rhino. 33 */ 34 35 /** 36 * Abstraction of the runtime environment. 37 * @class 38 * @interface 39 */ 40 functionRuntime(){"use strict";} 41 42 /** 43 * @param {!string} name 44 * @return {*} 45 */ 46 Runtime.prototype.getVariable=function(name){"use strict";}; 47 48 /** 49 * @param {*} anything 50 * @return {!string} 51 */ 52 Runtime.prototype.toJson=function(anything){"use strict";}; 53 54 /** 55 * @param {!string} jsonstr 56 * @return {*} 57 */ 58 Runtime.prototype.fromJson=function(jsonstr){"use strict";}; 59 60 /** 61 * @param {!string} string 62 * @param {!string} encoding 63 * @return {!Uint8Array} 64 */ 65 Runtime.prototype.byteArrayFromString=function(string,encoding){"use strict";}; 66 /** 67 * @param {!Uint8Array} bytearray 68 * @param {!string} encoding 69 * @return {!string} 70 */ 71 Runtime.prototype.byteArrayToString=function(bytearray,encoding){"use strict";}; 72 /** 73 * Read part of a binary file. 74 * @param {!string} path 75 * @param {!number} offset 76 * @param {!number} length 77 * @param {!function(?string,?Uint8Array):undefined} callback 78 * @return {undefined} 79 */ 80 Runtime.prototype.read=function(path,offset,length,callback){"use strict";}; 81 /** 82 * Read the contents of a file. Returns the result via a callback. If the 83 * encoding is 'binary', the result is returned as a Uint8Array, 84 * otherwise, it is returned as a string. 85 * @param {!string} path 86 * @param {!string} encoding text encoding or 'binary' 87 * @param {!function(?string,?(string|Uint8Array)):undefined} callback 88 * @return {undefined} 89 */ 90 Runtime.prototype.readFile=function(path,encoding,callback){"use strict";}; 91 /** 92 * Read a file completely, throw an exception if there is a problem. 93 * @param {!string} path 94 * @param {!string} encoding text encoding or 'binary' 95 * @return {!string|!Uint8Array} 96 */ 97 Runtime.prototype.readFileSync=function(path,encoding){"use strict";}; 98 /** 99 * @param {!string} path 100 * @param {!function(?string,?Document):undefined} callback 101 * @return {undefined} 102 */ 103 Runtime.prototype.loadXML=function(path,callback){"use strict";}; 104 /** 105 * @param {!string} path 106 * @param {!Uint8Array} data 107 * @param {!function(?string):undefined} callback 108 * @return {undefined} 109 */ 110 Runtime.prototype.writeFile=function(path,data,callback){"use strict";}; 111 /** 112 * @param {!string} path 113 * @param {!function(?string):undefined} callback 114 * @return {undefined} 115 */ 116 Runtime.prototype.deleteFile=function(path,callback){"use strict";}; 117 /** 118 * @param {!string} msgOrCategory 119 * @param {!string=} msg 120 * @return {undefined} 121 */ 122 Runtime.prototype.log=function(msgOrCategory,msg){"use strict";}; 123 /** 124 * @param {!function():undefined} callback 125 * @param {!number} milliseconds 126 * @return {!number} 127 */ 128 Runtime.prototype.setTimeout=function(callback,milliseconds){"use strict";}; 129 /** 130 * @param {!number} timeoutID 131 * @return {undefined} 132 */ 133 Runtime.prototype.clearTimeout=function(timeoutID){"use strict";}; 134 /** 135 * @return {!Array.<string>} 136 */ 137 Runtime.prototype.libraryPaths=function(){"use strict";}; 138 /** 139 * @return {!string} 140 */ 141 Runtime.prototype.currentDirectory=function(){"use strict";}; 142 /** 143 * @param {!string} dir 144 * @return {undefined} 145 */ 146 Runtime.prototype.setCurrentDirectory=function(dir){"use strict";}; 147 /** 148 * @return {string} 149 */ 150 Runtime.prototype.type=function(){"use strict";}; 151 /** 152 * @return {?DOMImplementation} 153 */ 154 Runtime.prototype.getDOMImplementation=function(){"use strict";}; 155 /** 156 * @param {!string} xml 157 * @return {?Document} 158 */ 159 Runtime.prototype.parseXML=function(xml){"use strict";}; 160 /** 161 * @param {!number} exitCode 162 */ 163 Runtime.prototype.exit=function(exitCode){"use strict";}; 164 /** 165 * @return {?Window} 166 */ 167 Runtime.prototype.getWindow=function(){"use strict";}; 168 /** 169 * @param {!function():undefined} callback 170 * @return {!number} 171 */ 172 Runtime.prototype.requestAnimationFrame=function(callback){"use strict";}; 173 /** 174 * @param {!number} requestId 175 * @return {undefined} 176 */ 177 Runtime.prototype.cancelAnimationFrame=function(requestId){"use strict";}; 178 /** 179 * @param {!boolean} condition 180 * @param {!string} message 181 * @return {undefined} 182 */ 183 Runtime.prototype.assert=function(condition,message){"use strict";}; 184 /*jslint emptyblock: false, unparam: false */ 185 186 /** @define {boolean} */ 187 varIS_COMPILED_CODE=false; 188 189 /** 190 * @this {Runtime} 191 * @param {!Uint8Array} bytearray 192 * @param {!string} encoding 193 * @return {!string} 194 */ 195 Runtime.byteArrayToString=function(bytearray,encoding){ 196 "use strict"; 197 /** 198 * @param {!Uint8Array} bytearray 199 * @return {!string} 200 */ 201 functionbyteArrayToString(bytearray){ 202 vars="",i,l=bytearray.length; 203 for(i=0;i<l;i+=1){ 204 s+=String.fromCharCode(bytearray[i]&0xff); 205 } 206 returns; 207 } 208 /** 209 * @param {!Uint8Array} bytearray 210 * @return {!string} 211 */ 212 functionutf8ByteArrayToString(bytearray){ 213 vars="",startPos,i,l=bytearray.length, 214 chars=[], 215 c0,c1,c2,c3,codepoint; 216 217 // skip a possible UTF-8 BOM 218 if(l>=3&&bytearray[0]===0xef&&bytearray[1]===0xbb&&bytearray[2]===0xbf){ 219 startPos=3; 220 }else{ 221 startPos=0; 222 } 223 224 for(i=startPos;i<l;i+=1){ 225 c0=/**@type{!number}*/(bytearray[i]); 226 if(c0<0x80){ 227 chars.push(c0); 228 }else{ 229 i+=1; 230 c1=/**@type{!number}*/(bytearray[i]); 231 if(c0>=0xc2&&c0<0xe0){ 232 chars.push(((c0&0x1f)<<6)|(c1&0x3f)); 233 }else{ 234 i+=1; 235 c2=/**@type{!number}*/(bytearray[i]); 236 if(c0>=0xe0&&c0<0xf0){ 237 chars.push(((c0&0x0f)<<12)|((c1&0x3f)<<6)|(c2&0x3f)); 238 }else{ 239 i+=1; 240 c3=/**@type{!number}*/(bytearray[i]); 241 if(c0>=0xf0&&c0<0xf5){ 242 codepoint=((c0&0x07)<<18)|((c1&0x3f)<<12)|((c2&0x3f)<<6)|(c3&0x3f); 243 codepoint-=0x10000; 244 chars.push((codepoint>>10)+0xd800,(codepoint&0x3ff)+0xdc00); 245 } 246 } 247 } 248 } 249 if(chars.length>=1000){ 250 // more than 2 chars can be added in the above logic, so the length might exceed 1000 251 252 // Char-to-string conversion is done using apply as it provides a roughly %30 improvement vs. 253 // converting the characters 1-by-1, and improves memory usage significantly as well. 254 255 // However, the apply function has an upper limit on the size of the arguments array. If it is exceeded, 256 // most browsers with throw a RangeError. Avoid this problem by converting no more than 1000(ish) 257 // characters per call. 258 s+=String.fromCharCode.apply(null,chars); 259 chars.length=0; 260 } 261 } 262 // Based on the above chars.length check, there is guaranteed to be less than 1000 chars left in the array 263 returns+String.fromCharCode.apply(null,chars); 264 } 265 varresult; 266 if(encoding==="utf8"){ 267 result=utf8ByteArrayToString(bytearray); 268 }else{ 269 if(encoding!=="binary"){ 270 this.log("Unsupported encoding: "+encoding); 271 } 272 result=byteArrayToString(bytearray); 273 } 274 returnresult; 275 }; 276 277 /** 278 * @param {!string} name 279 * @return {*} 280 */ 281 Runtime.getVariable=function(name){ 282 "use strict"; 283 /*jslint evil: true*/ 284 try{ 285 returneval(name); 286 }catch(e){ 287 returnundefined; 288 } 289 /*jslint evil: false*/ 290 }; 291 292 /** 293 * @param {*} anything 294 * @return {!string} 295 */ 296 Runtime.toJson=function(anything){ 297 "use strict"; 298 returnJSON.stringify(anything); 299 }; 300 301 /** 302 * @param {!string} jsonstr 303 * @return {*} 304 */ 305 Runtime.fromJson=function(jsonstr){ 306 "use strict"; 307 returnJSON.parse(jsonstr); 308 }; 309 310 /** 311 * @param {!Function} f 312 * @return {?string} 313 */ 314 Runtime.getFunctionName=functiongetFunctionName(f){ 315 "use strict"; 316 varm; 317 if(f.name===undefined){ 318 m=newRegExp("function\\s+(\\w+)").exec(f); 319 returnm&&m[1]; 320 } 321 returnf.name; 322 }; 323 324 /** 325 * @this {Runtime} 326 * @param {!boolean} condition 327 * @param {!string} message 328 * @return {undefined} 329 */ 330 Runtime.assert=function(condition,message){ 331 "use strict"; 332 if(!condition){ 333 this.log("alert","ASSERTION FAILED:\n"+message); 334 thrownewError(message);// interrupt execution and provide a backtrace 335 } 336 }; 337 338 /** 339 * @class 340 * @constructor 341 * @augments Runtime 342 * @implements {Runtime} 343 */ 344 functionBrowserRuntime(){ 345 "use strict"; 346 varself=this; 347 348 /** 349 * Return the number of bytes a string would take up when encoded as utf-8. 350 * @param {string} string 351 * @return {number} 352 */ 353 functiongetUtf8LengthForString(string){ 354 varl=string.length,i,n,j=0; 355 for(i=0;i<l;i+=1){ 356 n=string.charCodeAt(i); 357 j+=1+(n>0x80)+(n>0x800); 358 if(n>0xd700&&n<0xe000){// first of a surrogate pair 359 j+=1; 360 i+=1;// skip second half of in surrogate pair 361 } 362 } 363 returnj; 364 } 365 366 /** 367 * Convert UCS-2 string to UTF-8 array. 368 * @param {string} string 369 * @param {number} length the length of the resulting array 370 * @param {boolean} addBOM whether or not to start with a BOM 371 * @return {!Uint8Array} 372 */ 373 functionutf8ByteArrayFromString(string,length,addBOM){ 374 varl=string.length,bytearray,i,n, 375 j; 376 // allocate a buffer and convert to a utf8 array 377 bytearray=newUint8Array(newArrayBuffer(length)); 378 if(addBOM){ 379 bytearray[0]=0xef; 380 bytearray[1]=0xbb; 381 bytearray[2]=0xbf; 382 j=3; 383 }else{ 384 j=0; 385 } 386 for(i=0;i<l;i+=1){ 387 n=string.charCodeAt(i); 388 if(n<0x80){ 389 bytearray[j]=n; 390 j+=1; 391 }elseif(n<0x800){ 392 bytearray[j]=0xc0|(n>>>6); 393 bytearray[j+1]=0x80|(n&0x3f); 394 j+=2; 395 }elseif(n<=0xd700||n>=0xe000){ 396 bytearray[j]=0xe0|((n>>>12)&0x0f); 397 bytearray[j+1]=0x80|((n>>>6)&0x3f); 398 bytearray[j+2]=0x80|(n&0x3f); 399 j+=3; 400 }else{// surrogate pair 401 i+=1; 402 n=(((n-0xd800)<<10)|(string.charCodeAt(i)-0xdc00)) 403 +0x10000; 404 bytearray[j]=0xf0|(n>>>18&0x07); 405 bytearray[j+1]=0x80|(n>>>12&0x3f); 406 bytearray[j+2]=0x80|(n>>>6&0x3f); 407 bytearray[j+3]=0x80|(n&0x3f); 408 j+=4; 409 } 410 } 411 returnbytearray; 412 } 413 /** 414 * Convert UCS-2 string to UTF-8 array. 415 * wishLength is the desired length, if it is 3 bytes longer than 416 * forsee by the string data, a BOM is prepended. 417 * @param {string} string 418 * @param {(number|string)=} wishLength 419 * @return {!Uint8Array|undefined} 420 */ 421 functionutf8ByteArrayFromXHRString(string,wishLength){ 422 varaddBOM=false, 423 length=getUtf8LengthForString(string); 424 if(typeofwishLength==="number"){ 425 if(wishLength!==length&&wishLength!==length+3){ 426 // the desired length does not match the content of the string 427 returnundefined; 428 } 429 addBOM=length+3===wishLength; 430 length=wishLength; 431 } 432 returnutf8ByteArrayFromString(string,length,addBOM); 433 } 434 /** 435 * @param {!string} string 436 * @return {!Uint8Array} 437 */ 438 functionbyteArrayFromString(string){ 439 // ignore encoding for now 440 varl=string.length, 441 a=newUint8Array(newArrayBuffer(l)), 442 i; 443 for(i=0;i<l;i+=1){ 444 a[i]=string.charCodeAt(i)&0xff; 445 } 446 returna; 447 } 448 /** 449 * @param {!string} string 450 * @param {!string} encoding 451 * @return {!Uint8Array} 452 */ 453 this.byteArrayFromString=function(string,encoding){ 454 varresult; 455 if(encoding==="utf8"){ 456 result=utf8ByteArrayFromString(string, 457 getUtf8LengthForString(string),false); 458 }else{ 459 if(encoding!=="binary"){ 460 self.log("unknown encoding: "+encoding); 461 } 462 result=byteArrayFromString(string); 463 } 464 returnresult; 465 }; 466 this.byteArrayToString=Runtime.byteArrayToString; 467 468 /** 469 * @param {!string} name 470 * @return {*} 471 */ 472 this.getVariable=Runtime.getVariable; 473 474 475 /** 476 * @param {!string} jsonstr 477 * @return {*} 478 */ 479 this.fromJson=Runtime.fromJson; 480 /** 481 * @param {*} anything 482 * @return {!string} 483 */ 484 this.toJson=Runtime.toJson; 485 486 /** 487 * @param {!string} msgOrCategory 488 * @param {string=} msg 489 * @return {undefined} 490 */ 491 functionlog(msgOrCategory,msg){ 492 varcategory; 493 if(msg!==undefined){ 494 category=msgOrCategory; 495 }else{ 496 msg=msgOrCategory; 497 } 498 console.log(msg); 499 if(self.enableAlerts&&category==="alert"){ 500 alert(msg); 501 } 502 } 503 504 /** 505 * @param {!Array.<!number>} buffer 506 * @return {!Uint8Array} 507 */ 508 functionarrayToUint8Array(buffer){ 509 varl=buffer.length,i, 510 a=newUint8Array(newArrayBuffer(l)); 511 for(i=0;i<l;i+=1){ 512 a[i]=buffer[i]; 513 } 514 returna; 515 } 516 /** 517 * Convert the text received by XHR to a byte array. 518 * An XHR request can send a text as a response even though binary content 519 * was requested. This text string should be converted to a byte array. 520 * If the length of the text is equal to the reported length of the content 521 * then each character becomes one byte. 522 * If the length is different, which can happen on WebKit and Blink 523 * browsers, the string should be converted while taking into account the 524 * encoding. Currently, only utf8 is supported for this procedure. 525 * @param {!XMLHttpRequest} xhr 526 * @return {!Uint8Array} 527 */ 528 functionstringToBinaryWorkaround(xhr){ 529 varcl,data; 530 cl=xhr.getResponseHeader("Content-Length"); 531 if(cl){ 532 cl=parseInt(cl,10); 533 } 534 // If Content-Length was found and is a valid number that is not equal 535 // to the length of the string, the byte array should be reconstructed 536 // from the encoding. 537 if(cl&&cl!==xhr.responseText.length){ 538 // The text is not simple ascii, so we assume it is utf8 and try to 539 // reconstruct the text from that. 540 data=utf8ByteArrayFromXHRString(xhr.responseText,cl); 541 } 542 if(data===undefined){ 543 data=byteArrayFromString(xhr.responseText); 544 } 545 returndata; 546 } 547 /** 548 * @param {!string} path 549 * @param {!string} encoding 550 * @param {!XMLHttpRequest} xhr 551 * @return {!{err:?string,data:(?string|?Uint8Array)}} 552 */ 553 functionhandleXHRResult(path,encoding,xhr){ 554 varr,d,a, 555 /**@type{!Uint8Array|!string}*/ 556 data; 557 if(xhr.status===0&&!xhr.responseText){ 558 // for local files there is no difference between missing 559 // and empty files, so empty files are considered as errors 560 r={err:"File "+path+" is empty.",data:null}; 561 }elseif(xhr.status===200||xhr.status===0){ 562 // report file 563 if(xhr.response&&typeofxhr.response!=="string"){ 564 // w3c compliant way http://www.w3.org/TR/XMLHttpRequest2/#the-response-attribute 565 if(encoding==="binary"){ 566 d=/**@type{!ArrayBuffer}*/(xhr.response); 567 data=newUint8Array(d); 568 }else{ 569 data=String(xhr.response); 570 } 571 }elseif(encoding==="binary"){ 572 if(xhr.responseBody!==null 573 &&String(typeofVBArray)!=="undefined"){ 574 // fallback for IE <= 10 575 a=(newVBArray(xhr.responseBody)).toArray(); 576 data=arrayToUint8Array(a); 577 }else{ 578 data=stringToBinaryWorkaround(xhr); 579 } 580 }else{ 581 // if we just want text, it's simple 582 data=xhr.responseText; 583 } 584 r={err:null,data:data}; 585 }else{ 586 // report error 587 r={err:xhr.responseText||xhr.statusText,data:null}; 588 } 589 returnr; 590 } 591 /** 592 * @param {!string} path 593 * @param {!string} encoding 594 * @param {!boolean} async 595 * @return {!XMLHttpRequest} 596 */ 597 functioncreateXHR(path,encoding,async){ 598 varxhr=newXMLHttpRequest(); 599 xhr.open('GET',path,async); 600 if(xhr.overrideMimeType){ 601 if(encoding!=="binary"){ 602 xhr.overrideMimeType("text/plain; charset="+encoding); 603 }else{ 604 xhr.overrideMimeType("text/plain; charset=x-user-defined"); 605 } 606 } 607 returnxhr; 608 } 609 /** 610 * Read the contents of a file. Returns the result via a callback. If the 611 * encoding is 'binary', the result is returned as a Uint8Array, 612 * otherwise, it is returned as a string. 613 * @param {!string} path 614 * @param {!string} encoding text encoding or 'binary' 615 * @param {!function(?string,?(string|Uint8Array)):undefined} callback 616 * @return {undefined} 617 */ 618 functionreadFile(path,encoding,callback){ 619 varxhr=createXHR(path,encoding,true); 620 functionhandleResult(){ 621 varr; 622 if(xhr.readyState===4){ 623 r=handleXHRResult(path,encoding,xhr); 624 callback(r.err,r.data); 625 } 626 } 627 xhr.onreadystatechange=handleResult; 628 try{ 629 xhr.send(null); 630 }catch(/**@type{!Error}*/e){ 631 callback(e.message,null); 632 } 633 } 634 /** 635 * @param {!string} path 636 * @param {!number} offset 637 * @param {!number} length 638 * @param {!function(?string,?Uint8Array):undefined} callback 639 * @return {undefined} 640 */ 641 functionread(path,offset,length,callback){ 642 readFile(path,"binary",function(err,result){ 643 varr=null; 644 if(result){ 645 if(typeofresult==="string"){ 646 throw"This should not happen."; 647 } 648 r=/**@type{!Uint8Array}*/(result.subarray(offset, 649 offset+length)); 650 } 651 callback(err,r); 652 }); 653 } 654 /** 655 * @param {!string} path 656 * @param {!string} encoding text encoding or 'binary' 657 * @return {!string|!Uint8Array} 658 */ 659 functionreadFileSync(path,encoding){ 660 varxhr=createXHR(path,encoding,false), 661 r; 662 try{ 663 xhr.send(null); 664 r=handleXHRResult(path,encoding,xhr); 665 if(r.err){ 666 throwr.err; 667 } 668 if(r.data===null){ 669 throw"No data read from "+path+"."; 670 } 671 }catch(/**@type{!Error}*/e){ 672 throwe; 673 } 674 returnr.data; 675 } 676 /** 677 * @param {!string} path 678 * @param {!Uint8Array} data 679 * @param {!function(?string):undefined} callback 680 * @return {undefined} 681 */ 682 functionwriteFile(path,data,callback){ 683 varxhr=newXMLHttpRequest(), 684 /**@type{!string|!ArrayBuffer}*/ 685 d; 686 functionhandleResult(){ 687 if(xhr.readyState===4){ 688 if(xhr.status===0&&!xhr.responseText){ 689 // for local files there is no difference between missing 690 // and empty files, so empty files are considered as errors 691 callback("File "+path+" is empty."); 692 }elseif((xhr.status>=200&&xhr.status<300)|| 693 xhr.status===0){ 694 // report success 695 callback(null); 696 }else{ 697 // report error 698 callback("Status "+String(xhr.status)+": "+ 699 xhr.responseText||xhr.statusText); 700 } 701 } 702 } 703 xhr.open('PUT',path,true); 704 xhr.onreadystatechange=handleResult; 705 // ArrayBufferView will have an ArrayBuffer property, in WebKit, XHR 706 // can send() an ArrayBuffer, In Firefox, one must use sendAsBinary with 707 // a string 708 if(data.buffer&&!xhr.sendAsBinary){ 709 d=data.buffer;// webkit supports sending an ArrayBuffer 710 }else{ 711 // encode into a string, this works in FireFox>= 3 712 d=self.byteArrayToString(data,"binary"); 713 } 714 try{ 715 if(xhr.sendAsBinary){ 716 xhr.sendAsBinary(d); 717 }else{ 718 xhr.send(d); 719 } 720 }catch(/**@type{!Error}*/e){ 721 self.log("HUH? "+e+" "+data); 722 callback(e.message); 723 } 724 } 725 /** 726 * @param {!string} path 727 * @param {!function(?string):undefined} callback 728 * @return {undefined} 729 */ 730 functiondeleteFile(path,callback){ 731 varxhr=newXMLHttpRequest(); 732 xhr.open('DELETE',path,true); 733 xhr.onreadystatechange=function(){ 734 if(xhr.readyState===4){ 735 if(xhr.status<200&&xhr.status>=300){ 736 callback(xhr.responseText); 737 }else{ 738 callback(null); 739 } 740 } 741 }; 742 xhr.send(null); 743 } 744 /** 745 * @param {!string} path 746 * @param {!function(?string,?Document):undefined} callback 747 * @return {undefined} 748 */ 749 functionloadXML(path,callback){ 750 varxhr=newXMLHttpRequest(); 751 functionhandleResult(){ 752 if(xhr.readyState===4){ 753 if(xhr.status===0&&!xhr.responseText){ 754 callback("File "+path+" is empty.",null); 755 }elseif(xhr.status===200||xhr.status===0){ 756 // report file 757 callback(null,xhr.responseXML); 758 }else{ 759 // report error 760 callback(xhr.responseText,null); 761 } 762 } 763 } 764 xhr.open("GET",path,true); 765 if(xhr.overrideMimeType){ 766 xhr.overrideMimeType("text/xml"); 767 } 768 xhr.onreadystatechange=handleResult; 769 try{ 770 xhr.send(null); 771 }catch(/**@type{!Error}*/e){ 772 callback(e.message,null); 773 } 774 } 775 this.readFile=readFile; 776 this.read=read; 777 this.readFileSync=readFileSync; 778 this.writeFile=writeFile; 779 this.deleteFile=deleteFile; 780 this.loadXML=loadXML; 781 this.log=log; 782 this.enableAlerts=true; 783 this.assert=Runtime.assert; 784 /** 785 * @param {!function():undefined} f 786 * @param {!number} msec 787 * @return {!number} 788 */ 789 this.setTimeout=function(f,msec){ 790 returnsetTimeout(function(){ 791 f(); 792 },msec); 793 }; 794 /** 795 * @param {!number} timeoutID 796 * @return {undefined} 797 */ 798 this.clearTimeout=function(timeoutID){ 799 clearTimeout(timeoutID); 800 }; 801 /** 802 * @return {!Array.<!string>} 803 */ 804 this.libraryPaths=function(){ 805 return["lib"];// TODO: find a good solution 806 // probably let html app specify it 807 }; 808 /*jslint emptyblock: true*/ 809 this.setCurrentDirectory=function(){ 810 }; 811 /*jslint emptyblock: false*/ 812 /** 813 * @return {!string} 814 */ 815 this.currentDirectory=function(){ 816 return""; 817 }; 818 this.type=function(){ 819 return"BrowserRuntime"; 820 }; 821 this.getDOMImplementation=function(){ 822 returnwindow.document.implementation; 823 }; 824 /** 825 * @param {!string} xml 826 * @return {?Document} 827 */ 828 this.parseXML=function(xml){ 829 varparser=newDOMParser(); 830 returnparser.parseFromString(xml,"text/xml"); 831 }; 832 /** 833 * @param {!number} exitCode 834 */ 835 this.exit=function(exitCode){ 836 log("Calling exit with code "+String(exitCode)+ 837 ", but exit() is not implemented."); 838 }; 839 /** 840 * @return {!Window} 841 */ 842 this.getWindow=function(){ 843 returnwindow; 844 }; 845 /** 846 * @param {!function():undefined} callback 847 * @return {!number} 848 */ 849 this.requestAnimationFrame=function(callback){ 850 varrAF=window.requestAnimationFrame 851 ||window.webkitRequestAnimationFrame 852 ||window.mozRequestAnimationFrame 853 ||window.msRequestAnimationFrame, 854 requestId=0; 855 856 if(rAF){ 857 // This code is to satisfy Closure, which expects 858 // that the `this` of rAF should be window. 859 rAF.bind(window); 860 requestId=/**@type{function(!function():undefined):!number}*/(rAF)(callback); 861 }else{ 862 returnsetTimeout(callback,15); 863 } 864 865 returnrequestId; 866 }; 867 /** 868 * @param {!number} requestId 869 * @return {undefined} 870 */ 871 this.cancelAnimationFrame=function(requestId){ 872 varcAF=window.cancelAnimationFrame 873 ||window.webkitCancelAnimationFrame 874 ||window.mozCancelAnimationFrame 875 ||window.msCancelAnimationFrame; 876 877 if(cAF){ 878 cAF.bind(window); 879 /**@type{function(!number)}*/(cAF)(requestId); 880 }else{ 881 clearTimeout(requestId); 882 } 883 }; 884 } 885 886 /** 887 * @constructor 888 * @implements {Runtime} 889 */ 890 functionNodeJSRuntime(){ 891 "use strict"; 892 varself=this, 893 fs=require('fs'), 894 pathmod=require('path'), 895 /**@type{!string}*/ 896 currentDirectory="", 897 /**@type{!DOMParser}*/ 898 parser, 899 domImplementation; 900 901 /** 902 * @param {!Buffer} buffer 903 * @return {!Uint8Array} 904 */ 905 functionbufferToUint8Array(buffer){ 906 varl=buffer.length,i, 907 a=newUint8Array(newArrayBuffer(l)); 908 for(i=0;i<l;i+=1){ 909 a[i]=buffer[i]; 910 } 911 returna; 912 } 913 /** 914 * @param {!string} string 915 * @param {!string} encoding 916 * @return {!Uint8Array} 917 */ 918 this.byteArrayFromString=function(string,encoding){ 919 varbuf=newBuffer(string,encoding),i,l=buf.length, 920 a=newUint8Array(newArrayBuffer(l)); 921 for(i=0;i<l;i+=1){ 922 a[i]=buf[i]; 923 } 924 returna; 925 }; 926 927 this.byteArrayToString=Runtime.byteArrayToString; 928 929 /** 930 * @param {!string} name 931 * @return {*} 932 */ 933 this.getVariable=Runtime.getVariable; 934 935 /** 936 * @param {!string} jsonstr 937 * @return {*} 938 */ 939 this.fromJson=Runtime.fromJson; 940 /** 941 * @param {*} anything 942 * @return {!string} 943 */ 944 this.toJson=Runtime.toJson; 945 946 /** 947 * Read the contents of a file. Returns the result via a callback. If the 948 * encoding is 'binary', the result is returned as a Uint8Array, 949 * otherwise, it is returned as a string. 950 * @param {!string} path 951 * @param {!string} encoding text encoding or 'binary' 952 * @param {!function(?string,?(string|Uint8Array)):undefined} callback 953 * @return {undefined} 954 */ 955 functionreadFile(path,encoding,callback){ 956 /** 957 * @param {?string} err 958 * @param {?Buffer|?string} data 959 * @return {undefined} 960 */ 961 functionconvert(err,data){ 962 if(err){ 963 returncallback(err,null); 964 } 965 if(!data){ 966 returncallback("No data for "+path+".",null); 967 } 968 vard; 969 if(typeofdata==="string"){ 970 d=/**@type{!string}*/(data); 971 returncallback(err,d); 972 } 973 d=/**@type{!Buffer}*/(data); 974 callback(err,bufferToUint8Array(d)); 975 } 976 path=pathmod.resolve(currentDirectory,path); 977 if(encoding!=="binary"){ 978 fs.readFile(path,encoding,convert); 979 }else{ 980 fs.readFile(path,null,convert); 981 } 982 } 983 this.readFile=readFile; 984 /** 985 * @param {!string} path 986 * @param {!function(?string,?Document):undefined} callback 987 * @return {undefined} 988 */ 989 functionloadXML(path,callback){ 990 readFile(path,"utf-8",function(err,data){ 991 if(err){ 992 returncallback(err,null); 993 } 994 if(!data){ 995 returncallback("No data for "+path+".",null); 996 } 997 vard=/**@type{!string}*/(data); 998 callback(null,self.parseXML(d)); 999 }); 1000 } 1001 this.loadXML=loadXML; 1002 /** 1003 * @param {!string} path 1004 * @param {!Uint8Array} data 1005 * @param {!function(?string):undefined} callback 1006 * @return {undefined} 1007 */ 1008 this.writeFile=function(path,data,callback){ 1009 varbuf=newBuffer(data); 1010 path=pathmod.resolve(currentDirectory,path); 1011 fs.writeFile(path,buf,"binary",function(err){ 1012 callback(err||null); 1013 }); 1014 }; 1015 /** 1016 * @param {!string} path 1017 * @param {!function(?string):undefined} callback 1018 * @return {undefined} 1019 */ 1020 this.deleteFile=function(path,callback){ 1021 path=pathmod.resolve(currentDirectory,path); 1022 fs.unlink(path,callback); 1023 }; 1024 /** 1025 * @param {!string} path 1026 * @param {!number} offset 1027 * @param {!number} length 1028 * @param {!function(?string,?Uint8Array):undefined} callback 1029 * @return {undefined} 1030 */ 1031 this.read=function(path,offset,length,callback){ 1032 path=pathmod.resolve(currentDirectory,path); 1033 fs.open(path,"r+",666,function(err,fd){ 1034 if(err){ 1035 callback(err,null); 1036 return; 1037 } 1038 varbuffer=newBuffer(length); 1039 fs.read(fd,buffer,0,length,offset,function(err){ 1040 fs.close(fd); 1041 callback(err,bufferToUint8Array(buffer)); 1042 }); 1043 }); 1044 }; 1045 /** 1046 * @param {!string} path 1047 * @param {!string} encoding text encoding or 'binary' 1048 * @return {!string|!Uint8Array} 1049 */ 1050 this.readFileSync=function(path,encoding){ 1051 vars, 1052 enc=(encoding==="binary")?null:encoding, 1053 r=fs.readFileSync(path,enc); 1054 if(r===null){ 1055 throw"File "+path+" could not be read."; 1056 } 1057 if(encoding==="binary"){ 1058 s=/**@type{!Buffer}*/(r); 1059 s=bufferToUint8Array(s); 1060 }else{ 1061 s=/**@type{!string}*/(r); 1062 } 1063 returns; 1064 }; 1065 /** 1066 * @param {!string} msgOrCategory 1067 * @param {string=} msg 1068 * @return {undefined} 1069 */ 1070 functionlog(msgOrCategory,msg){ 1071 varcategory; 1072 if(msg!==undefined){ 1073 category=msgOrCategory; 1074 }else{ 1075 msg=msgOrCategory; 1076 } 1077 if(category==="alert"){ 1078 process.stderr.write("\n!!!!! ALERT !!!!!"+'\n'); 1079 } 1080 process.stderr.write(msg+'\n'); 1081 if(category==="alert"){ 1082 process.stderr.write("!!!!! ALERT !!!!!"+'\n'); 1083 } 1084 } 1085 this.log=log; 1086 1087 this.assert=Runtime.assert; 1088 1089 /** 1090 * @param {!function():undefined} f 1091 * @param {!number} msec 1092 * @return {!number} 1093 */ 1094 this.setTimeout=function(f,msec){ 1095 returnsetTimeout(function(){ 1096 f(); 1097 },msec); 1098 }; 1099 /** 1100 * @param {!number} timeoutID 1101 * @return {undefined} 1102 */ 1103 this.clearTimeout=function(timeoutID){ 1104 clearTimeout(timeoutID); 1105 }; 1106 /** 1107 * @return {!Array.<!string>} 1108 */ 1109 this.libraryPaths=function(){ 1110 return[__dirname]; 1111 }; 1112 /** 1113 * @param {!string} dir 1114 * @return {undefined} 1115 */ 1116 this.setCurrentDirectory=function(dir){ 1117 currentDirectory=dir; 1118 }; 1119 this.currentDirectory=function(){ 1120 returncurrentDirectory; 1121 }; 1122 this.type=function(){ 1123 return"NodeJSRuntime"; 1124 }; 1125 this.getDOMImplementation=function(){ 1126 returndomImplementation; 1127 }; 1128 /** 1129 * @param {!string} xml 1130 * @return {?Document} 1131 */ 1132 this.parseXML=function(xml){ 1133 returnparser.parseFromString(xml,"text/xml"); 1134 }; 1135 this.exit=process.exit; 1136 this.getWindow=function(){ 1137 returnnull; 1138 }; 1139 /** 1140 * @param {!function():undefined} callback 1141 * @return {!number} 1142 */ 1143 this.requestAnimationFrame=function(callback){ 1144 returnsetTimeout(callback,15); 1145 }; 1146 /** 1147 * @param {!number} requestId 1148 * @return {undefined} 1149 */ 1150 this.cancelAnimationFrame=function(requestId){ 1151 clearTimeout(requestId); 1152 }; 1153 functioninit(){ 1154 var/**@type{function(new:DOMParser)}*/ 1155 DOMParser=require('xmldom').DOMParser; 1156 parser=newDOMParser(); 1157 domImplementation=self.parseXML("<a/>").implementation; 1158 } 1159 init(); 1160 } 1161 1162 /** 1163 * @constructor 1164 * @implements {Runtime} 1165 */ 1166 functionRhinoRuntime(){ 1167 "use strict"; 1168 varself=this, 1169 Packages={}, 1170 dom=Packages.javax.xml.parsers.DocumentBuilderFactory.newInstance(), 1171 /**@type{!Packages.javax.xml.parsers.DocumentBuilder}*/ 1172 builder, 1173 entityresolver, 1174 /**@type{!string}*/ 1175 currentDirectory=""; 1176 dom.setValidating(false); 1177 dom.setNamespaceAware(true); 1178 dom.setExpandEntityReferences(false); 1179 dom.setSchema(null); 1180 /*jslint unparam: true */ 1181 entityresolver=Packages.org.xml.sax.EntityResolver({ 1182 /** 1183 * @param {!string} publicId 1184 * @param {!string} systemId 1185 * @return {!Packages.org.xml.sax.InputSource} 1186 */ 1187 resolveEntity:function(publicId,systemId){ 1188 varfile; 1189 /** 1190 * @param {!string} path 1191 * @return {!Packages.org.xml.sax.InputSource} 1192 */ 1193 functionopen(path){ 1194 varreader=newPackages.java.io.FileReader(path), 1195 source=newPackages.org.xml.sax.InputSource(reader); 1196 returnsource; 1197 } 1198 file=systemId; 1199 //file = /[^\/]*$/.exec(systemId); // what should this do? 1200 returnopen(file); 1201 } 1202 }); 1203 /*jslint unparam: false */ 1204 //dom.setEntityResolver(entityresolver); 1205 builder=dom.newDocumentBuilder(); 1206 builder.setEntityResolver(entityresolver); 1207 1208 /*jslint unparam: true*/ 1209 /** 1210 * @param {!string} string 1211 * @param {!string} encoding 1212 * @return {!Uint8Array} 1213 */ 1214 this.byteArrayFromString=function(string,encoding){ 1215 // ignore encoding for now 1216 vari, 1217 l=string.length, 1218 a=newUint8Array(newArrayBuffer(l)); 1219 for(i=0;i<l;i+=1){ 1220 a[i]=string.charCodeAt(i)&0xff; 1221 } 1222 returna; 1223 }; 1224 /*jslint unparam: false*/ 1225 this.byteArrayToString=Runtime.byteArrayToString; 1226 1227 /** 1228 * @param {!string} name 1229 * @return {*} 1230 */ 1231 this.getVariable=Runtime.getVariable; 1232 1233 /** 1234 * @param {!string} jsonstr 1235 * @return {*} 1236 */ 1237 this.fromJson=Runtime.fromJson; 1238 /** 1239 * @param {*} anything 1240 * @return {!string} 1241 */ 1242 this.toJson=Runtime.toJson; 1243 1244 /** 1245 * @param {!string} path 1246 * @param {!function(?string,?Document):undefined} callback 1247 * @return {undefined} 1248 */ 1249 functionloadXML(path,callback){ 1250 varfile=newPackages.java.io.File(path), 1251 xmlDocument=null; 1252 try{ 1253 xmlDocument=builder.parse(file); 1254 }catch(/**@type{!string}*/err){ 1255 print(err); 1256 returncallback(err,null); 1257 } 1258 callback(null,xmlDocument); 1259 } 1260 /** 1261 * @param {!string} path 1262 * @param {!string} encoding text encoding or 'binary' 1263 * @param {!function(?string,?(string|Uint8Array)):undefined} callback 1264 * @return {undefined} 1265 */ 1266 functionruntimeReadFile(path,encoding,callback){ 1267 if(currentDirectory){ 1268 path=currentDirectory+"/"+path; 1269 } 1270 varfile=newPackages.java.io.File(path), 1271 data, 1272 // read binary, seems hacky but works 1273 rhinoencoding=(encoding==="binary")?"latin1":encoding; 1274 if(!file.isFile()){ 1275 callback(path+" is not a file.",null); 1276 }else{ 1277 data=readFile(path,rhinoencoding); 1278 if(data&&encoding==="binary"){ 1279 data=self.byteArrayFromString(data,"binary"); 1280 } 1281 callback(null,data); 1282 } 1283 } 1284 /** 1285 * @param {!string} path 1286 * @param {!string} encoding 1287 * @return {?string} 1288 */ 1289 functionruntimeReadFileSync(path,encoding){ 1290 varfile=newPackages.java.io.File(path); 1291 if(!file.isFile()){ 1292 returnnull; 1293 } 1294 if(encoding==="binary"){ 1295 encoding="latin1";// read binary, seems hacky but works 1296 } 1297 returnreadFile(path,encoding); 1298 } 1299 this.loadXML=loadXML; 1300 this.readFile=runtimeReadFile; 1301 /** 1302 * @param {!string} path 1303 * @param {!Uint8Array} data 1304 * @param {!function(?string):undefined} callback 1305 * @return {undefined} 1306 */ 1307 this.writeFile=function(path,data,callback){ 1308 if(currentDirectory){ 1309 path=currentDirectory+"/"+path; 1310 } 1311 varout=newPackages.java.io.FileOutputStream(path), 1312 i, 1313 l=data.length; 1314 for(i=0;i<l;i+=1){ 1315 out.write(data[i]); 1316 } 1317 out.close(); 1318 callback(null); 1319 }; 1320 /** 1321 * @param {!string} path 1322 * @param {!function(?string):undefined} callback 1323 * @return {undefined} 1324 */ 1325 this.deleteFile=function(path,callback){ 1326 if(currentDirectory){ 1327 path=currentDirectory+"/"+path; 1328 } 1329 varfile=newPackages.java.io.File(path), 1330 otherPath=path+Math.random(), 1331 other=newPackages.java.io.File(otherPath); 1332 // 'delete' cannot be used with closure compiler, so we use a workaround 1333 if(file.rename(other)){ 1334 other.deleteOnExit(); 1335 callback(null); 1336 }else{ 1337 callback("Could not delete "+path); 1338 } 1339 }; 1340 /** 1341 * @param {!string} path 1342 * @param {!number} offset 1343 * @param {!number} length 1344 * @param {!function(?string,?Uint8Array):undefined} callback 1345 * @return {undefined} 1346 */ 1347 this.read=function(path,offset,length,callback){ 1348 // TODO: adapt to read only a part instead of the whole file 1349 if(currentDirectory){ 1350 path=currentDirectory+"/"+path; 1351 } 1352 vardata=runtimeReadFileSync(path,"binary"); 1353 if(data){ 1354 callback(null,this.byteArrayFromString( 1355 data.substring(offset,offset+length), 1356 "binary" 1357 )); 1358 }else{ 1359 callback("Cannot read "+path,null); 1360 } 1361 }; 1362 /** 1363 * @param {!string} path 1364 * @param {!string} encoding text encoding or 'binary' 1365 * @return {!string} 1366 */ 1367 this.readFileSync=function(path,encoding){ 1368 if(!encoding){ 1369 return""; 1370 } 1371 vars=readFile(path,encoding); 1372 if(s===null){ 1373 throw"File could not be read."; 1374 } 1375 returns; 1376 }; 1377 /** 1378 * @param {!string} msgOrCategory 1379 * @param {string=} msg 1380 * @return {undefined} 1381 */ 1382 functionlog(msgOrCategory,msg){ 1383 varcategory; 1384 if(msg!==undefined){ 1385 category=msgOrCategory; 1386 }else{ 1387 msg=msgOrCategory; 1388 } 1389 if(category==="alert"){ 1390 print("\n!!!!! ALERT !!!!!"); 1391 } 1392 print(msg); 1393 if(category==="alert"){ 1394 print("!!!!! ALERT !!!!!"); 1395 } 1396 } 1397 this.log=log; 1398 1399 this.assert=Runtime.assert; 1400 1401 /** 1402 * @param {!function():undefined} f 1403 * @return {!number} 1404 */ 1405 this.setTimeout=function(f){ 1406 f(); 1407 return0; 1408 }; 1409 /*jslint emptyblock: true */ 1410 /** 1411 * @return {undefined} 1412 */ 1413 this.clearTimeout=function(){ 1414 }; 1415 /*jslint emptyblock: false */ 1416 /** 1417 * @return {!Array.<!string>} 1418 */ 1419 this.libraryPaths=function(){ 1420 return["lib"]; 1421 }; 1422 /** 1423 * @param {!string} dir 1424 */ 1425 this.setCurrentDirectory=function(dir){ 1426 currentDirectory=dir; 1427 }; 1428 this.currentDirectory=function(){ 1429 returncurrentDirectory; 1430 }; 1431 this.type=function(){ 1432 return"RhinoRuntime"; 1433 }; 1434 this.getDOMImplementation=function(){ 1435 returnbuilder.getDOMImplementation(); 1436 }; 1437 /** 1438 * @param {!string} xml 1439 * @return {?Document} 1440 */ 1441 this.parseXML=function(xml){ 1442 varreader=newPackages.java.io.StringReader(xml), 1443 source=newPackages.org.xml.sax.InputSource(reader); 1444 returnbuilder.parse(source); 1445 }; 1446 this.exit=quit; 1447 this.getWindow=function(){ 1448 returnnull; 1449 }; 1450 /** 1451 * @param {!function():undefined} callback 1452 * @return {!number} 1453 */ 1454 this.requestAnimationFrame=function(callback){ 1455 callback(); 1456 return0; 1457 }; 1458 /*jslint emptyblock: true */ 1459 /** 1460 * @return {undefined} 1461 */ 1462 this.cancelAnimationFrame=function(){ 1463 }; 1464 /*jslint emptyblock: false */ 1465 } 1466 1467 /** 1468 * @return {!Runtime} 1469 */ 1470 Runtime.create=functioncreate(){ 1471 "use strict"; 1472 var/**@type{!Runtime}*/ 1473 result; 1474 if(String(typeofwindow)!=="undefined"){ 1475 result=newBrowserRuntime(); 1476 }elseif(String(typeofrequire)!=="undefined"){ 1477 result=newNodeJSRuntime(); 1478 }else{ 1479 result=newRhinoRuntime(); 1480 } 1481 returnresult; 1482 }; 1483 1484 /** 1485 * @const 1486 * @type {!Runtime} 1487 */ 1488 varruntime=Runtime.create(); 1489 1490 /** 1491 * @namespace The core package. 1492 */ 1493 varcore={}; 1494 /** 1495 * @namespace The gui package. 1496 */ 1497 vargui={}; 1498 /** 1499 * @namespace The xmldom package. 1500 */ 1501 varxmldom={}; 1502 /** 1503 * @namespace The ODF package. 1504 */ 1505 varodf={}; 1506 /** 1507 * @namespace The editing operations 1508 */ 1509 varops={}; 1510 1511 /** 1512 * @namespace The webodf namespace 1513 */ 1514 varwebodf={}; 1515 1516 (function(){ 1517 "use strict"; 1518 /** 1519 * @return {string} 1520 */ 1521 functiongetWebODFVersion(){ 1522 varversion=(String(typeofwebodf_version)!=="undefined" 1523 ?webodf_version 1524 :"From Source" 1525 ); 1526 returnversion; 1527 } 1528 /** 1529 * @const 1530 * @type {!string} 1531 */ 1532 webodf.Version=getWebODFVersion(); 1533 }()); 1534 1535 /*jslint sloppy: true*/ 1536 (function(){ 1537 /** 1538 * @param {string} dir Needs to be non-empty, use "." to denote same directory 1539 * @param {!Object.<string,!{dir:string, deps:!Array.<string>}>} dependencies 1540 * @param {!boolean=} expectFail Set to true if it is not known if there is a manifest 1541 */ 1542 functionloadDependenciesFromManifest(dir,dependencies,expectFail){ 1543 "use strict"; 1544 varpath=dir+"/manifest.json", 1545 content, 1546 list, 1547 manifest, 1548 /**@type{string}*/ 1549 m; 1550 runtime.log("Loading manifest: "+path); 1551 try{ 1552 content=runtime.readFileSync(path,"utf-8"); 1553 }catch(/**@type{string}*/e){ 1554 if(expectFail){ 1555 runtime.log("No loadable manifest found."); 1556 }else{ 1557 console.log(String(e)); 1558 throwe; 1559 } 1560 return; 1561 } 1562 list=JSON.parse(/**@type{string}*/(content)); 1563 manifest=/**@type{!Object.<!Array.<string>>}*/(list); 1564 for(minmanifest){ 1565 if(manifest.hasOwnProperty(m)){ 1566 dependencies[m]={dir:dir,deps:manifest[m]}; 1567 } 1568 } 1569 } 1570 /** 1571 * @return {!Object.<string,!{dir:string, deps:!Array.<string>}>} 1572 */ 1573 functionloadDependenciesFromManifests(){ 1574 "use strict"; 1575 var/**@type{!Object.<string,!{dir:string, deps:!Array.<string>}>}*/ 1576 dependencies=[], 1577 paths=runtime.libraryPaths(), 1578 i; 1579 // Convenience: try to load any possible manifest in the current directory 1580 // but only if it not also included in the library paths 1581 if(runtime.currentDirectory()&&paths.indexOf(runtime.currentDirectory())===-1){ 1582 // there is no need to have a manifest there, so allow loading to fail 1583 loadDependenciesFromManifest(runtime.currentDirectory(),dependencies,true); 1584 } 1585 for(i=0;i<paths.length;i+=1){ 1586 loadDependenciesFromManifest(paths[i],dependencies); 1587 } 1588 returndependencies; 1589 } 1590 /** 1591 * @param {string} dir 1592 * @param {string} className 1593 * @return {string} 1594 */ 1595 functiongetPath(dir,className){ 1596 "use strict"; 1597 returndir+"/"+className.replace(".","/")+".js"; 1598 } 1599 /** 1600 * Create a list of required classes from a list of desired classes. 1601 * A new list is created that lists all classes that still need to be loaded 1602 * to load the list of desired classes. 1603 * @param {!Array.<string>} classNames 1604 * @param {!Object.<string,!{dir:string, deps:!Array.<string>}>} dependencies 1605 * @param {function(string):boolean} isDefined 1606 * @return {!Array.<string>} 1607 */ 1608 functiongetLoadList(classNames,dependencies,isDefined){ 1609 "use strict"; 1610 varloadList=[], 1611 stack={}, 1612 /**@type{!Object.<string,boolean>}*/ 1613 visited={}; 1614 /** 1615 * @param {string} n 1616 */ 1617 functionvisit(n){ 1618 if(visited[n]||isDefined(n)){ 1619 return; 1620 } 1621 if(stack[n]){ 1622 throw"Circular dependency detected for "+n+"."; 1623 } 1624 stack[n]=true; 1625 if(!dependencies[n]){ 1626 throw"Missing dependency information for class "+n+"."; 1627 } 1628 vard=dependencies[n],deps=d.deps,i,l=deps.length; 1629 for(i=0;i<l;i+=1){ 1630 visit(deps[i]); 1631 } 1632 stack[n]=false; 1633 visited[n]=true; 1634 loadList.push(getPath(d.dir,n)); 1635 } 1636 classNames.forEach(visit); 1637 returnloadList; 1638 } 1639 /** 1640 * @param {string} path 1641 * @param {string} content 1642 * @return {string} 1643 */ 1644 functionaddContent(path,content){ 1645 "use strict"; 1646 content+="\n//# sourceURL="+path; 1647 returncontent; 1648 } 1649 /** 1650 * @param {!Array.<string>} paths 1651 */ 1652 functionloadFiles(paths){ 1653 // this function is not strict, so eval can assign to globals 1654 vari, 1655 content; 1656 for(i=0;i<paths.length;i+=1){ 1657 content=runtime.readFileSync(paths[i],"utf-8"); 1658 content=addContent(paths[i],/**@type{string}*/(content)); 1659 /*jslint evil: true*/ 1660 eval(content); 1661 /*jslint evil: false*/ 1662 } 1663 } 1664 /** 1665 * Load scripts by adding <script/> elements to the DOM. 1666 * The new script tags are added after the <script/> tag for runtime.js. 1667 * The scripts are added with async = false so that they are executed in the 1668 * right order. The scripts are executed when control returns to the browser 1669 * from the current stack. 1670 * If a callback is provided, it is executed after the last script has run. 1671 * @param {!Array.<string>} paths array with one or more script paths 1672 * @param {!Function=} callback 1673 */ 1674 functionloadFilesInBrowser(paths,callback){ 1675 "use strict"; 1676 vare=document.currentScript||document.documentElement.lastChild, 1677 df=document.createDocumentFragment(), 1678 script, 1679 i; 1680 for(i=0;i<paths.length;i+=1){ 1681 script=document.createElement("script"); 1682 script.type="text/javascript"; 1683 script.charset="utf-8"; 1684 script.async=false;// execute the scripts in order 1685 script.setAttribute("src",paths[i]); 1686 df.appendChild(script); 1687 } 1688 if(callback){ 1689 script.onload=callback; 1690 } 1691 e.parentNode.insertBefore(df,e); 1692 } 1693 var/**@type{!Object.<string,!{dir:string, deps:!Array.<string>}>}*/ 1694 dependencies, 1695 packages={ 1696 core:core, 1697 gui:gui, 1698 xmldom:xmldom, 1699 odf:odf, 1700 ops:ops 1701 }; 1702 /** 1703 * Check if a class has been defined. 1704 * For class "core.sub.Name", this checks if there is an entry 1705 * packages.core.sub.Name. 1706 * @param {string} classname 1707 * @return {boolean} 1708 */ 1709 functionisDefined(classname){ 1710 "use strict"; 1711 varparts=classname.split("."),i, 1712 /**@type{Object}*/ 1713 p=packages, 1714 l=parts.length; 1715 for(i=0;i<l;i+=1){ 1716 if(!p.hasOwnProperty(parts[i])){ 1717 returnfalse; 1718 } 1719 p=/**@type{Object}*/(p[parts[i]]); 1720 } 1721 returntrue; 1722 } 1723 /** 1724 * @param {!Array.<string>} classnames 1725 * @param {function():undefined=} callback 1726 * @returns {undefined} 1727 */ 1728 runtime.loadClasses=function(classnames,callback){ 1729 "use strict"; 1730 if(IS_COMPILED_CODE||classnames.length===0){ 1731 returncallback&&callback(); 1732 } 1733 dependencies=dependencies||loadDependenciesFromManifests(); 1734 classnames=getLoadList(classnames,dependencies,isDefined); 1735 if(classnames.length===0){ 1736 returncallback&&callback(); 1737 } 1738 if(runtime.type()==="BrowserRuntime"&&callback){ 1739 loadFilesInBrowser(classnames,callback); 1740 }else{ 1741 loadFiles(classnames); 1742 if(callback){ 1743 callback(); 1744 } 1745 } 1746 }; 1747 /** 1748 * @param {string} classname 1749 * @param {function():undefined=} callback 1750 * @return {undefined} 1751 * @export 1752 */ 1753 runtime.loadClass=function(classname,callback){ 1754 "use strict"; 1755 runtime.loadClasses([classname],callback); 1756 }; 1757 }()); 1758 /*jslint sloppy: false*/ 1759 1760 (function(){ 1761 "use strict"; 1762 /** 1763 * @param {!string} string 1764 * @return {!string} 1765 */ 1766 vartranslator=function(string){ 1767 returnstring; 1768 }; 1769 /*jslint emptyblock: false*/ 1770 1771 /** 1772 * Translator function. Takes the original string 1773 * and returns the translation if it exists, else 1774 * returns the original. 1775 * @param {!string} original 1776 * @return {!string} 1777 */ 1778 functiontr(original){ 1779 varresult=translator(original); 1780 if(!result||(String(typeofresult)!=="string")){ 1781 returnoriginal; 1782 } 1783 returnresult; 1784 } 1785 1786 /** 1787 * Gets the custom translator function 1788 * @return {!function(!string):!string} 1789 */ 1790 runtime.getTranslator=function(){ 1791 returntranslator; 1792 }; 1793 /** 1794 * Set an external translator function 1795 * @param {!function(!string):!string} translatorFunction 1796 * @return {undefined} 1797 */ 1798 runtime.setTranslator=function(translatorFunction){ 1799 translator=translatorFunction; 1800 }; 1801 /** 1802 * @param {string} original 1803 * @return {string} 1804 */ 1805 runtime.tr=tr; 1806 }()); 1807 1808 /*jslint sloppy: true*/ 1809 (function(args){ 1810 if(args){ 1811 args=Array.prototype.slice.call(/**@type{{length:number}}*/(args)); 1812 }else{ 1813 args=[]; 1814 } 1815 1816 /*jslint unvar: true, defined: true*/ 1817 /** 1818 * @param {!Array.<!string>} argv 1819 */ 1820 functionrun(argv){ 1821 if(!argv.length){ 1822 return; 1823 } 1824 varscript=argv[0]; 1825 runtime.readFile(script,"utf8",function(err,code){ 1826 varpath="", 1827 pathEndIndex=script.lastIndexOf("/"), 1828 codestring=/**@type{string}*/(code); 1829 1830 if(pathEndIndex!==-1){ 1831 path=script.substring(0,pathEndIndex); 1832 }else{ 1833 path="."; 1834 } 1835 runtime.setCurrentDirectory(path); 1836 functioninner_run(){ 1837 varscript,path,args,argv,result;// hide variables 1838 // execute script and make arguments available via argv 1839 /*jslint evil: true*/ 1840 result=/**@type{!number}*/(eval(codestring)); 1841 /*jslint evil: false*/ 1842 if(result){ 1843 runtime.exit(result); 1844 } 1845 return; 1846 } 1847 if(err){ 1848 runtime.log(err); 1849 runtime.exit(1); 1850 }elseif(codestring===null){ 1851 runtime.log("No code found for "+script); 1852 runtime.exit(1); 1853 }else{ 1854 // run the script with arguments bound to arguments parameter 1855 inner_run.apply(null,argv); 1856 } 1857 }); 1858 } 1859 /*jslint unvar: false, defined: false*/ 1860 // if rhino or node.js, run the scripts provided as arguments 1861 if(runtime.type()==="NodeJSRuntime"){ 1862 run(process.argv.slice(2)); 1863 }elseif(runtime.type()==="RhinoRuntime"){ 1864 run(args); 1865 }else{ 1866 run(args.slice(1)); 1867 } 1868 }(String(typeofarguments)!=="undefined"&&arguments)); 1869