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