1 /** 2 * Copyright (C) 2014 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 /*global runtime, odf*/ 26 /*jslint emptyblock: false, unparam: false*/ 27 28 /** 29 * @constructor 30 * @param {?Element} element 31 * @param {!odf.PageLayoutCache} pageLayoutCache 32 */ 33 odf.MasterPage = function (element, pageLayoutCache) { 34 "use strict"; 35 var self = this; 36 /** 37 * @type {!odf.PageLayout} 38 */ 39 this.pageLayout; 40 function init() { 41 var pageLayoutName; 42 if (element) { 43 pageLayoutName = element.getAttributeNS(odf.Namespaces.stylens, 44 "page-layout-name"); 45 self.pageLayout = pageLayoutCache.getPageLayout(pageLayoutName); 46 } else { 47 self.pageLayout = pageLayoutCache.getDefaultPageLayout(); 48 } 49 } 50 init(); 51 }; 52 /*jslint emptyblock: true, unparam: true*/ 53 /** 54 * @interface 55 */ 56 odf.MasterPageCache = function () {"use strict"; }; 57 /** 58 * @param {!string} name 59 * @return {?odf.MasterPage} 60 */ 61 odf.MasterPageCache.prototype.getMasterPage = function (name) {"use strict"; }; 62 /*jslint emptyblock: false, unparam: false*/ 63 /** 64 * @constructor 65 * @param {!Element} element 66 * @param {!odf.StyleParseUtils} styleParseUtils 67 * @param {!odf.MasterPageCache} masterPageCache 68 * @param {!odf.StylePileEntry=} parent 69 */ 70 odf.StylePileEntry = function (element, styleParseUtils, masterPageCache, parent) { 71 "use strict"; 72 /** 73 * @type {!odf.TextProperties|undefined} 74 */ 75 this.text; 76 /** 77 * @type {!odf.ParagraphProperties|undefined} 78 */ 79 this.paragraph; 80 /** 81 * @type {!odf.GraphicProperties|undefined} 82 */ 83 this.graphic; 84 /** 85 * @return {?odf.MasterPage} 86 */ 87 this.masterPage = function () { 88 var masterPageName = element.getAttributeNS(odf.Namespaces.stylens, 89 "master-page-name"), 90 masterPage = null; 91 if (masterPageName) { 92 masterPage = masterPageCache.getMasterPage(masterPageName); 93 } 94 return masterPage; 95 }; 96 /** 97 * @param {!odf.StylePileEntry} self 98 * @return {undefined} 99 */ 100 function init(self) { 101 var stylens = odf.Namespaces.stylens, 102 family = element.getAttributeNS(stylens, "family"), 103 e = null; 104 if (family === "graphic" || family === "chart") { 105 self.graphic = parent === undefined ? undefined : parent.graphic; 106 e = styleParseUtils.getPropertiesElement("graphic-properties", element, e); 107 if (e !== null) { 108 self.graphic = new odf.GraphicProperties(e, styleParseUtils, 109 self.graphic); 110 } 111 } 112 if (family === "paragraph" || family === "table-cell" 113 || family === "graphic" || family === "presentation" 114 || family === "chart") { 115 self.paragraph = parent === undefined ? undefined : parent.paragraph; 116 e = styleParseUtils.getPropertiesElement("paragraph-properties", element, e); 117 if (e !== null) { 118 self.paragraph = new odf.ParagraphProperties(e, styleParseUtils, 119 self.paragraph); 120 } 121 } 122 if (family === "text" || family === "paragraph" 123 || family === "table-cell" || family === "graphic" 124 || family === "presentation" || family === "chart") { 125 self.text = parent === undefined ? undefined : parent.text; 126 e = styleParseUtils.getPropertiesElement("text-properties", element, e); 127 if (e !== null) { 128 self.text = new odf.TextProperties(e, styleParseUtils, self.text); 129 } 130 } 131 } 132 init(this); 133 }; 134 /** 135 * Collection of all the styles in the document for one style family. 136 * There are separate style piles for family 'text', 'paragraph', 'graphic' etc. 137 * @constructor 138 * @param {!odf.StyleParseUtils} styleParseUtils 139 * @param {!odf.MasterPageCache} masterPageCache 140 */ 141 odf.StylePile = function (styleParseUtils, masterPageCache) { 142 "use strict"; 143 var stylens = odf.Namespaces.stylens, 144 /**@type{!Object.<!string,!Element>}*/ 145 commonStyles = {}, 146 /**@type{!Object.<!string,!Element>}*/ 147 automaticStyles = {}, 148 /**@type{!odf.StylePileEntry|undefined}*/ 149 defaultStyle, 150 /**@type{!Object.<!string,!odf.StylePileEntry>}*/ 151 parsedCommonStyles = {}, 152 /**@type{!Object.<!string,!odf.StylePileEntry>}*/ 153 parsedAutomaticStyles = {}, 154 /**@type{!function(!string,!Array.<!string>):(!odf.StylePileEntry|undefined)}*/ 155 getCommonStyle; 156 /** 157 * @param {!Element} element 158 * @param {!Array.<!string>} visitedStyles track visited styles to avoid loops 159 * @return {!odf.StylePileEntry} 160 */ 161 function parseStyle(element, visitedStyles) { 162 var parent, 163 parentName, 164 style; 165 if (element.hasAttributeNS(stylens, "parent-style-name")) { 166 parentName = element.getAttributeNS(stylens, "parent-style-name"); 167 if (visitedStyles.indexOf(parentName) === -1) { 168 parent = getCommonStyle(parentName, visitedStyles); 169 } 170 } 171 style = new odf.StylePileEntry(element, styleParseUtils, masterPageCache, parent); 172 return style; 173 } 174 /** 175 * @param {!string} styleName 176 * @param {!Array.<!string>} visitedStyles track visited styles to avoid loops 177 * @return {!odf.StylePileEntry|undefined} 178 */ 179 getCommonStyle = function (styleName, visitedStyles) { 180 var style = parsedCommonStyles[styleName], 181 element; 182 if (!style) { 183 element = commonStyles[styleName]; 184 if (element) { 185 visitedStyles.push(styleName); 186 style = parseStyle(element, visitedStyles); 187 parsedCommonStyles[styleName] = style; 188 } 189 } 190 return style; 191 }; 192 /** 193 * @param {!string} styleName 194 * @return {!odf.StylePileEntry|undefined} 195 */ 196 function getStyle(styleName) { 197 var style = parsedAutomaticStyles[styleName] 198 || parsedCommonStyles[styleName], 199 element, 200 visitedStyles = []; 201 if (!style) { 202 element = automaticStyles[styleName]; 203 if (!element) { 204 element = commonStyles[styleName]; 205 if (element) { 206 visitedStyles.push(styleName); 207 } 208 } 209 if (element) { 210 style = parseStyle(element, visitedStyles); 211 } 212 } 213 return style; 214 } 215 this.getStyle = getStyle; 216 /** 217 * @param {!Element} style 218 * @return {undefined} 219 */ 220 this.addCommonStyle = function (style) { 221 var name; 222 if (style.hasAttributeNS(stylens, "name")) { 223 name = style.getAttributeNS(stylens, "name"); 224 if (!commonStyles.hasOwnProperty(name)) { 225 commonStyles[name] = style; 226 } 227 } 228 }; 229 /** 230 * @param {!Element} style 231 * @return {undefined} 232 */ 233 this.addAutomaticStyle = function (style) { 234 var name; 235 if (style.hasAttributeNS(stylens, "name")) { 236 name = style.getAttributeNS(stylens, "name"); 237 if (!automaticStyles.hasOwnProperty(name)) { 238 automaticStyles[name] = style; 239 } 240 } 241 }; 242 /** 243 * @param {!Element} style 244 * @return {undefined} 245 */ 246 this.setDefaultStyle = function (style) { 247 if (defaultStyle === undefined) { 248 defaultStyle = parseStyle(style, []); 249 } 250 }; 251 /** 252 * @return {!odf.StylePileEntry|undefined} 253 */ 254 this.getDefaultStyle = function () { 255 return defaultStyle; 256 }; 257 }; 258 /** 259 * @constructor 260 */ 261 odf.ComputedGraphicStyle = function () { 262 "use strict"; 263 /** 264 * @type {!odf.ComputedTextProperties} 265 */ 266 this.text = new odf.ComputedTextProperties(); 267 /** 268 * @type {!odf.ComputedParagraphProperties} 269 */ 270 this.paragraph = new odf.ComputedParagraphProperties(); 271 /** 272 * @type {!odf.ComputedGraphicProperties} 273 */ 274 this.graphic = new odf.ComputedGraphicProperties(); 275 }; 276 /** 277 * @constructor 278 */ 279 odf.ComputedParagraphStyle = function () { 280 "use strict"; 281 /** 282 * @type {!odf.ComputedTextProperties} 283 */ 284 this.text = new odf.ComputedTextProperties(); 285 /** 286 * @type {!odf.ComputedParagraphProperties} 287 */ 288 this.paragraph = new odf.ComputedParagraphProperties(); 289 }; 290 /** 291 * @constructor 292 */ 293 odf.ComputedTextStyle = function () { 294 "use strict"; 295 /** 296 * @type {!odf.ComputedTextProperties} 297 */ 298 this.text = new odf.ComputedTextProperties(); 299 }; 300 /** 301 * Fast and type-safe access to styling properties of an ODF document. 302 * When the document changes, update() has to be called to update the 303 * information. 304 * 305 * This class gives access to computed styles. The term 'computed' is used 306 * similarly to its use in the DOM function window.getComputedStyle(). 307 * In ODF, as in CSS but differently, the evaluation of styles is influenced by 308 * the position of an element in a document. Specifically, the types and styles 309 * of the ancestor elements determine properties of a style. This is explained 310 * in chapter 16 of the ODF 1.2 specification. 311 * http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#__RefHeading__1416272_253892949 312 * 313 * Here is an example. Consider the following style and content: 314 <s:styles> 315 <s:default-style s:family="text">...</s:default-style> 316 <s:default-style s:family="paragraph">...</s:default-style> 317 <s:style s:name="Standard" s:family="text">...</s:style> 318 <s:style s:name="Bold" s:parent-style-name="Standard" s:family="text">...</s:style> 319 <s:style s:name="Standard" s:family="paragraph">...</s:style> 320 </s:styles> 321 <s:automatic-styles> 322 <s:style s:name="T1" s:parent-style-name="Standard" s:family="text">...</s:style> 323 <s:style s:name="T2" s:parent-style-name="Bold" s:family="text">...</s:style> 324 <s:style s:name="C1" s:parent-style-name="Standard" s:family="text">...</s:style> 325 <s:style s:name="C2" s:parent-style-name="Standard" s:family="text">...</s:style> 326 <s:style s:name="P1" s:parent-style-name="Standard" s:family="paragraph">...</s:style> 327 </s:automatic-styles> 328 ... 329 <text:p text:style-name="P1"> 330 <text:span text:style-name="T1" text:class-names="C1 C2"> 331 <text:span text:style-name="T2"> 332 hello 333 </text:span> 334 </text:span> 335 </text:p> 336 337 * The style properties for the word 'hello' are looked for, in order, in this 338 * list of styles: 339 * text:T2 340 * text:Bold 341 * text:Standard 342 * text:T1 343 * text:Standard 344 * text:C1 345 * text:Standard 346 * text:C2 347 * text:Standard 348 * paragraph:P1 349 * paragraph:Standard 350 * paragraph-document-default 351 * paragraph-implementation-default 352 * 353 * The style names can be concatenated into a key. The parent styles are not 354 * needed in the key. For the above example, the key is: 355 * text/T2/text/T1/text/C1/text/C2/paragraph/P1 356 * StyleCache creates computed style objects on demand and caches them. 357 * 358 * StyleCache also provides convenient access to page layout and master page 359 * information. 360 * Each property of the style objects has a valid value even if the underlying 361 * XML element or attribute is missing or invalid. 362 * 363 * @constructor 364 * @implements {odf.MasterPageCache} 365 * @implements {odf.PageLayoutCache} 366 * @param {!odf.ODFDocumentElement} odfroot 367 */ 368 odf.StyleCache = function (odfroot) { 369 "use strict"; 370 var self = this, 371 /**@type{!{text:!odf.StylePile,paragraph:!odf.StylePile}}*/ 372 stylePiles, 373 /**@type{!Object.<!string,!odf.ComputedTextStyle>}*/ 374 textStyleCache, 375 /**@type{!Object.<!string,!odf.ComputedParagraphStyle>}*/ 376 paragraphStyleCache, 377 /**@type{!Object.<!string,!odf.ComputedGraphicStyle>}*/ 378 graphicStyleCache, 379 /**@type{!odf.StylePile}*/ 380 textStylePile, 381 /**@type{!odf.StylePile}*/ 382 paragraphStylePile, 383 /**@type{!odf.StylePile}*/ 384 graphicStylePile, 385 textns = odf.Namespaces.textns, 386 stylens = odf.Namespaces.stylens, 387 styleInfo = new odf.StyleInfo(), 388 styleParseUtils = new odf.StyleParseUtils(), 389 /**@type{!Object.<!string,!Element>}*/ 390 masterPages, 391 /**@type{!Object.<!string,!odf.MasterPage>}*/ 392 parsedMasterPages, 393 /**@type{!odf.MasterPage}*/ 394 defaultMasterPage, 395 /**@type{!odf.PageLayout}*/ 396 defaultPageLayout, 397 /**@type{!Object.<!string,!Element>}*/ 398 pageLayouts, 399 /**@type{!Object.<!string,!odf.PageLayout>}*/ 400 parsedPageLayouts; 401 /** 402 * @param {!string} family 403 * @param {!string} ns 404 * @param {!Element} element 405 * @param {!Array.<!string>} chain 406 * @return {undefined} 407 */ 408 function appendClassNames(family, ns, element, chain) { 409 var names = element.getAttributeNS(ns, "class-names"), 410 stylename, 411 i; 412 if (names) { 413 names = names.split(" "); 414 for (i = 0; i < names.length; i += 1) { 415 stylename = names[i]; 416 if (stylename) { 417 chain.push(family); 418 chain.push(stylename); 419 } 420 } 421 } 422 } 423 /** 424 * @param {!Element} element 425 * @param {!Array.<!string>} chain 426 * @return {!Array.<!string>} 427 */ 428 function getGraphicStyleChain(element, chain) { 429 var stylename = styleInfo.getStyleName("graphic", element); 430 if (stylename !== undefined) { 431 chain.push("graphic"); 432 chain.push(stylename); 433 } 434 return chain; 435 } 436 /** 437 * @param {!Element} element 438 * @param {!Array.<!string>} chain 439 * @return {!Array.<!string>} 440 */ 441 function getParagraphStyleChain(element, chain) { 442 var stylename = styleInfo.getStyleName("paragraph", element); 443 if (stylename !== undefined) { 444 chain.push("paragraph"); 445 chain.push(stylename); 446 } 447 // text:p and text:h can have text:class-names 448 if (element.namespaceURI === textns && 449 (element.localName === "h" || element.localName === "p")) { 450 appendClassNames("paragraph", textns, element, chain); 451 } 452 return chain; 453 } 454 /** 455 * @param {!Array.<!string>} styleChain 456 * @param {!string} propertiesName 457 * @param {!string} defaultFamily 458 * @return {!Array.<!Object>} 459 */ 460 function createPropertiesChain(styleChain, propertiesName, defaultFamily) { 461 var chain = [], i, lastProperties, family, styleName, pile, style, 462 properties; 463 for (i = 0; i < styleChain.length; i += 2) { 464 family = styleChain[i]; 465 styleName = styleChain[i + 1]; 466 pile = /**@type{!odf.StylePile}*/(stylePiles[family]); 467 style = pile.getStyle(styleName); 468 if (style !== undefined) { 469 properties = /**@type{!Object|undefined}*/(style[propertiesName]); 470 if (properties !== undefined && properties !== lastProperties) { 471 chain.push(properties); 472 lastProperties = properties; 473 } 474 } 475 } 476 pile = /**@type{!odf.StylePile}*/(stylePiles[defaultFamily]); 477 style = pile.getDefaultStyle(); 478 if (style) { 479 properties = /**@type{!Object|undefined}*/(style[propertiesName]); 480 if (properties !== undefined && properties !== lastProperties) { 481 chain.push(properties); 482 } 483 } 484 return chain; 485 } 486 /** 487 * Return the paragraph style for the given content element. 488 * @param {!Element} element 489 * @return {!odf.ComputedGraphicStyle} 490 */ 491 this.getComputedGraphicStyle = function (element) { 492 var styleChain = getGraphicStyleChain(element, []), 493 key = styleChain.join("/"), 494 computedStyle = graphicStyleCache[key]; 495 runtime.assert(styleChain.length % 2 === 0, "Invalid style chain."); 496 if (computedStyle === undefined) { 497 computedStyle = new odf.ComputedGraphicStyle(); 498 computedStyle.graphic.setGraphicProperties(/**@type{!odf.GraphicProperties|undefined}*/( 499 createPropertiesChain(styleChain, "graphic", "graphic")[0] 500 )); 501 computedStyle.text.setStyleChain(/**@type{!Array.<!odf.TextProperties>}*/( 502 createPropertiesChain(styleChain, "text", "graphic") 503 )); 504 computedStyle.paragraph.setStyleChain(/**@type{!Array.<!odf.ParagraphProperties>}*/( 505 createPropertiesChain(styleChain, "paragraph", "graphic") 506 )); 507 graphicStyleCache[key] = computedStyle; 508 } 509 return computedStyle; 510 }; 511 /** 512 * Return the paragraph style for the given content element. 513 * @param {!Element} element 514 * @return {!odf.ComputedParagraphStyle} 515 */ 516 this.getComputedParagraphStyle = function (element) { 517 var styleChain = getParagraphStyleChain(element, []), 518 key = styleChain.join("/"), 519 computedStyle = paragraphStyleCache[key]; 520 runtime.assert(styleChain.length % 2 === 0, "Invalid style chain."); 521 if (computedStyle === undefined) { 522 computedStyle = new odf.ComputedParagraphStyle(); 523 computedStyle.text.setStyleChain(/**@type{!Array.<!odf.TextProperties>}*/( 524 createPropertiesChain(styleChain, "text", "paragraph") 525 )); 526 computedStyle.paragraph.setStyleChain(/**@type{!Array.<!odf.ParagraphProperties>}*/( 527 createPropertiesChain(styleChain, "paragraph", "paragraph") 528 )); 529 paragraphStyleCache[key] = computedStyle; 530 } 531 return computedStyle; 532 }; 533 /** 534 * @param {!Element} element 535 * @param {!Array.<!string>} chain 536 * @return {!Array.<!string>} 537 */ 538 function getTextStyleChain(element, chain) { 539 var stylename = styleInfo.getStyleName("text", element), 540 parent = /**@type{!Element}*/(element.parentNode); 541 if (stylename !== undefined) { 542 chain.push("text"); 543 chain.push(stylename); 544 } 545 // a text:span can have text:class-names 546 if (element.localName === "span" && element.namespaceURI === textns) { 547 appendClassNames("text", textns, element, chain); 548 } 549 if (!parent || parent === odfroot) { 550 return chain; 551 } 552 if (parent.namespaceURI === textns && 553 (parent.localName === "p" || parent.localName === "h")) { 554 getParagraphStyleChain(parent, chain); 555 } else { 556 getTextStyleChain(parent, chain); 557 } 558 return chain; 559 } 560 /** 561 * Return the text style for the given content element. 562 * @param {!Element} element 563 * @return {!odf.ComputedTextStyle} 564 */ 565 this.getComputedTextStyle = function (element) { 566 var styleChain = getTextStyleChain(element, []), 567 key = styleChain.join("/"), 568 computedStyle = textStyleCache[key]; 569 runtime.assert(styleChain.length % 2 === 0, "Invalid style chain."); 570 if (computedStyle === undefined) { 571 computedStyle = new odf.ComputedTextStyle(); 572 computedStyle.text.setStyleChain(/**@type{!Array.<!odf.TextProperties>}*/( 573 createPropertiesChain(styleChain, "text", "text") 574 )); 575 textStyleCache[key] = computedStyle; 576 } 577 return computedStyle; 578 }; 579 /** 580 * @param {!Element} element 581 * @return {!odf.StylePile|undefined} 582 */ 583 function getPileFromElement(element) { 584 var family = element.getAttributeNS(stylens, "family"); 585 return stylePiles[family]; 586 } 587 /** 588 * @param {!Element} element 589 * @return {undefined} 590 */ 591 function addMasterPage(element) { 592 var name = element.getAttributeNS(stylens, "name"); 593 if (name.length > 0 && !masterPages.hasOwnProperty(name)) { 594 masterPages[name] = element; 595 } 596 } 597 /** 598 * @param {!string} name 599 * @return {!odf.PageLayout} 600 */ 601 function getPageLayout(name) { 602 var pageLayout = parsedPageLayouts[name], e; 603 if (!pageLayout) { 604 e = pageLayouts[name]; 605 if (e) { 606 pageLayout = new odf.PageLayout(e, styleParseUtils, defaultPageLayout); 607 parsedPageLayouts[name] = pageLayout; 608 } else { 609 pageLayout = defaultPageLayout; 610 } 611 } 612 return pageLayout; 613 } 614 this.getPageLayout = getPageLayout; 615 /** 616 * @return {!odf.PageLayout} 617 */ 618 this.getDefaultPageLayout = function () { 619 return defaultPageLayout; 620 }; 621 /** 622 * @param {!string} name 623 * @return {?odf.MasterPage} 624 */ 625 function getMasterPage(name) { 626 var masterPage = parsedMasterPages[name], 627 element; 628 if (masterPage === undefined) { 629 element = masterPages[name]; 630 if (element) { 631 masterPage = new odf.MasterPage(element, self); 632 parsedMasterPages[name] = masterPage; 633 } else { 634 masterPage = null; 635 } 636 } 637 return masterPage; 638 } 639 this.getMasterPage = getMasterPage; 640 /** 641 * @return {!odf.MasterPage} 642 */ 643 this.getDefaultMasterPage = function () { 644 return defaultMasterPage; 645 }; 646 /** 647 * @return {undefined} 648 */ 649 function update() { 650 var e, 651 pile, 652 defaultPageLayoutElement = null, 653 defaultMasterPageElement = null; 654 textStyleCache = {}; 655 paragraphStyleCache = {}; 656 graphicStyleCache = {}; 657 masterPages = {}; 658 parsedMasterPages = {}; 659 parsedPageLayouts = {}; 660 pageLayouts = {}; 661 textStylePile = new odf.StylePile(styleParseUtils, self); 662 paragraphStylePile = new odf.StylePile(styleParseUtils, self); 663 graphicStylePile = new odf.StylePile(styleParseUtils, self); 664 stylePiles = { 665 text: textStylePile, 666 paragraph: paragraphStylePile, 667 graphic: graphicStylePile 668 }; 669 // go through <office:styles/> 670 e = odfroot.styles.firstElementChild; 671 while (e) { 672 if (e.namespaceURI === stylens) { 673 pile = getPileFromElement(e); 674 if (pile) { 675 if (e.localName === "style") { 676 pile.addCommonStyle(e); 677 } else if (e.localName === "default-style") { 678 pile.setDefaultStyle(e); 679 } 680 } else if (e.localName === "default-page-layout") { 681 defaultPageLayoutElement = e; 682 } 683 } 684 e = e.nextElementSibling; 685 } 686 defaultPageLayout = new odf.PageLayout(defaultPageLayoutElement, 687 styleParseUtils); 688 // go through <office:automatic-styles/> 689 e = odfroot.automaticStyles.firstElementChild; 690 while (e) { 691 if (e.namespaceURI === stylens) { 692 pile = getPileFromElement(e); 693 if (pile && e.localName === "style") { 694 pile.addAutomaticStyle(e); 695 } else if (e.localName === "page-layout") { 696 pageLayouts[e.getAttributeNS(stylens, "name")] = e; 697 } 698 } 699 e = e.nextElementSibling; 700 } 701 // go through <office:master-styles/> 702 e = odfroot.masterStyles.firstElementChild; 703 while (e) { 704 if (e.namespaceURI === stylens && e.localName === "master-page") { 705 defaultMasterPageElement = defaultMasterPageElement || e; 706 addMasterPage(e); 707 } 708 e = e.nextElementSibling; 709 } 710 defaultMasterPage = new odf.MasterPage(defaultMasterPageElement, self); 711 } 712 this.update = update; 713 }; 714