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 

AltStyle によって変換されたページ (->オリジナル) /