2222 * @source : https://github.com/kogmbh/WebODF/
2323 */
2424
25- function aggregate ( callback ) {
26- return function ( collection , individual ) {
27- return collection . concat ( callback ( individual ) ) ;
28- } ;
29- }
25+ "use strict" ;
3026
31- function toArray ( nodeList ) {
32- "use strict" ;
33- return Array . prototype . slice . call ( nodeList ) ;
27+ var rngns = "http://relaxng.org/ns/structure/1.0" ;
28+
29+ /**
30+ * Check if a node is an element in the rng namespace and the given localName.
31+ * @param {!string } localName
32+ * @param {!Node } node
33+ * @return {!boolean }
34+ */
35+ function isRng ( localName , node ) {
36+ if ( node . nodeType !== 1 ) {
37+ return false ;
38+ }
39+ if ( node . namespaceURI !== rngns ) {
40+ return false ;
41+ }
42+ if ( node . localName !== localName ) {
43+ return false ;
44+ }
45+ return true ;
3446}
3547
36- function getName ( node ) {
37- return node && node . getAttribute ( "name" ) ;
48+ /**
49+ * @param {!Node } node
50+ * @return {?Element }
51+ */
52+ function getFirstElementNode ( node ) {
53+ node = node . firstChild ;
54+ while ( node && node . nodeType !== 1 ) {
55+ node = node . nextSibling ;
56+ }
57+ return node ;
3858}
3959
60+ /**
61+ * Return all explicit names for an <element/> or <attribute/> element.
62+ * <anyName/> and <nsName/> return nothing.
63+ *
64+ * @param {!Node } node
65+ * @return {!Array.<!string> }
66+ */
4067function getNames ( node ) {
41- var name = getName ( node ) ;
42- return name ? [ name ] : toArray ( node . getElementsByTagName ( "name" ) ) . map ( function ( node ) {
43- return node . textContent ;
44- } ) ;
68+ var names = [ ] ;
69+ if ( node . hasAttribute ( "name" ) ) {
70+ names . push ( node . getAttribute ( "name" ) ) ;
71+ } else {
72+ node = getFirstElementNode ( node ) ;
73+ if ( isRng ( "choice" , node ) ) {
74+ node = getFirstElementNode ( node ) ;
75+ }
76+ while ( node ) {
77+ if ( isRng ( "name" , node ) ) {
78+ names . push ( node . textContent ) ;
79+ }
80+ node = node . nextSibling ;
81+ }
82+ }
83+ return names ;
4584}
4685
86+ /**
87+ * Increase length of string by adding spaces.
88+ * @param {!string } str
89+ * @param {!number } length
90+ * @return {!string }
91+ */
4792function pad ( str , length ) {
4893 while ( str . length < length ) {
4994 str += " " ;
@@ -52,58 +97,171 @@ function pad(str, length) {
5297}
5398
5499/**
55- * Extract container node information out of the supplied RNG schema document.
56- * This only does extremely simplistic parsing.
57- *
58- * @constructor
59- * @param {!Document } document
100+ * Get all the <define/> elements from an RNG grammar.
101+ * @param {!Element } grammar
102+ * @return {!Object.<!string,!Element> }
60103 */
61- function ExtractContainerInfo ( document ) {
62- /**
63- * @param {!Node } node
64- * @return {!Array.<!Node> }
65- */
66- function findParentElements ( node ) {
67- var refs ;
68-
69- while ( node && / ( d e f i n e | e l e m e n t ) / . test ( node . localName ) === false ) {
70- node = node . parentNode ;
104+ function getDefines ( grammar ) {
105+ var defines = { } ,
106+ c = grammar . firstChild ;
107+ while ( c ) {
108+ if ( c . nodeType === 1 && isRng ( "define" , c ) ) {
109+ defines [ c . getAttribute ( "name" ) ] = c ;
71110 }
111+ c = c . nextSibling ;
112+ }
113+ return defines ;
114+ }
72115
73- if ( node ) {
74- if ( node . localName === "element" ) {
75- return [ node ] ;
116+ /**
117+ * Information about an attribute or element.
118+ * @constructor
119+ */
120+ function Info ( ) {
121+ /**@type {!Object.<!string,!string> }*/
122+ this . refs = { } ;
123+ /**@type {!boolean }*/
124+ this . text = false ;
125+ /**@type {!boolean }*/
126+ this . data = false ;
127+ /**@type {!boolean }*/
128+ this . value = false ;
129+ /**@type {!Array.<!Info }*/
130+ this . childElements = [ ] ;
131+ /**@type {!Array.<!Info }*/
132+ this . attributes = [ ] ;
133+ }
134+
135+ /**
136+ * Add information from a <define/> to that of <element/> or <attribute/>.
137+ * @param {!Info } info
138+ * @param {!string } ref
139+ * @param {!Object.<!string,!Info> } defines
140+ * @return {undefined }
141+ */
142+ function addDefine ( info , ref , defines ) {
143+ var define = defines [ ref ] ,
144+ c ;
145+ if ( define ) {
146+ info . text = info . text || define . text ;
147+ info . data = info . data || define . data ;
148+ info . value = info . value || define . value ;
149+ define . childElements . forEach ( function ( ce ) {
150+ if ( info . childElements . indexOf ( ce ) === - 1 ) {
151+ info . childElements . push ( ce ) ;
76152 }
77- refs = toArray ( document . querySelectorAll ( "ref[name='" + getName ( node ) + "']" ) ) ;
78- return refs . reduce ( aggregate ( findParentElements ) , [ ] ) ;
79- }
80- return [ ] ;
153+ } ) ;
154+ define . attributes . forEach ( function ( a ) {
155+ if ( info . attributes . indexOf ( a ) === - 1 ) {
156+ info . attributes . push ( a ) ;
157+ }
158+ } ) ;
81159 }
160+ }
82161
83- this . getTextElements = function ( ) {
84- return toArray ( document . getElementsByTagName ( "text" ) ) . reduce ( aggregate ( findParentElements ) , [ ] ) ;
85- } ;
162+ /**
163+ * Add information from <define/> elements to the set of <element/> or
164+ * <attribute/> elements.
165+ * @param {!Object.<!string,!Info> } infos
166+ * @param {!Object.<!string,!Info> } defines
167+ * @return {undefined }
168+ */
169+ function resolveDefines ( infos , defines ) {
170+ Object . keys ( infos ) . forEach ( function ( name ) {
171+ var info = infos [ name ] ;
172+ Object . keys ( info . refs ) . forEach ( function ( ref ) {
173+ addDefine ( info , ref , defines ) ;
174+ } ) ;
175+ } ) ;
176+ }
177+
178+ /**
179+ * Recursively collect information from all elements in an RNG grammar.
180+ * If a <ref/> is encountered, the corresponding <define/> is traversed.
181+ * This is done only once for each <define/>.
182+ *
183+ * @param {!Element } e
184+ * @param {!Object.<!string,!Element } defs
185+ * @param {?Info } current
186+ * @param {!Object.<!string,!Info } elements
187+ * @param {!Object.<!string,!Info } attributes
188+ * @param {!Object.<!string,!Info } defines
189+ * @return {undefined }
190+ */
191+ function handleChildElements ( e , defs , current , elements , attributes , defines ) {
192+ var c = e . firstChild ,
193+ def ,
194+ info ,
195+ name ;
196+ while ( c ) {
197+ if ( isRng ( "ref" , c ) ) {
198+ name = c . getAttribute ( "name" ) ;
199+ if ( current ) {
200+ current . refs [ name ] = name ;
201+ }
202+ def = defs [ name ] ;
203+ if ( def ) {
204+ delete defs [ name ] ;
205+ info = new Info ( ) ;
206+ defines [ name ] = info ;
207+ handleChildElements ( def , defs , info , elements , attributes , defines ) ;
208+ }
209+ } else if ( isRng ( "element" , c ) ) {
210+ info = new Info ( ) ;
211+ getNames ( c ) . forEach ( function ( name ) {
212+ elements [ name ] = info ;
213+ } ) ;
214+ if ( current ) {
215+ current . childElements . push ( info ) ;
216+ }
217+ handleChildElements ( c , defs , info , elements , attributes , defines ) ;
218+ } else if ( isRng ( "attribute" , c ) ) {
219+ info = new Info ( ) ;
220+ getNames ( c ) . forEach ( function ( name ) {
221+ attributes [ name ] = info ;
222+ } ) ;
223+ if ( current ) {
224+ current . attributes . push ( info ) ;
225+ }
226+ handleChildElements ( c , defs , info , elements , attributes , defines ) ;
227+ } else if ( isRng ( "text" , c ) ) {
228+ current . text = true ;
229+ } else if ( isRng ( "data" , c ) ) {
230+ current . data = true ;
231+ } else if ( isRng ( "value" , c ) ) {
232+ current . value = true ;
233+ } else {
234+ handleChildElements ( c , defs , current , elements , attributes , defines ) ;
235+ }
236+ c = c . nextSibling ;
237+ }
86238}
87239
88240function onLoadRng ( err , document ) {
89241 if ( err ) {
90242 console . log ( "\nError: " + err + "\n" ) ;
91- runtime . exit ( 1 ) ;
92- } else {
93- var containerFinder = new ExtractContainerInfo ( document ) ,
94- textElements ,
95- elementNames ,
96- doc ;
97-
98- textElements = containerFinder . getTextElements ( ) ;
99- elementNames = textElements . reduce ( aggregate ( getNames ) , [ ] ) . sort ( ) ;
100- doc = elementNames . map ( function ( elementName ) {
101- return "[" + pad ( '"' + elementName + '"' , 40 ) + ", TODO]" ;
102- } ) . join ( ",\n" ) ;
103-
104- console . log ( doc + "\n" ) ;
105- runtime . exit ( 0 ) ;
243+ return runtime . exit ( 1 ) ;
106244 }
245+ var grammar = document . documentElement ,
246+ start = document . getElementsByTagNameNS ( rngns , "start" ) . item ( 0 ) ,
247+ defs = getDefines ( grammar ) ,
248+ elements = { } ,
249+ attributes = { } ,
250+ defines = { } ,
251+ elementNames ,
252+ doc ;
253+
254+ handleChildElements ( start , defs , null , elements , attributes , defines ) ;
255+ resolveDefines ( elements , defines ) ;
256+ resolveDefines ( attributes , defines ) ;
257+
258+ elementNames = Object . keys ( elements ) . sort ( ) ;
259+ doc = elementNames . map ( function ( elementName ) {
260+ return "[" + pad ( '"' + elementName + '"' , 40 ) + ", TODO]" ;
261+ } ) . join ( ",\n" ) ;
262+
263+ console . log ( doc + "\n" ) ;
264+ runtime . exit ( 0 ) ;
107265}
108266
109267function main ( args ) {
@@ -117,4 +275,4 @@ function main(args) {
117275 runtime . loadXML ( rngPath , onLoadRng ) ;
118276 }
119277}
120- main ( String ( typeof arguments ) !== "undefined" && Array . prototype . slice . call ( arguments ) ) ;
278+ main ( String ( typeof arguments ) !== "undefined" && Array . prototype . slice . call ( arguments ) ) ;
0 commit comments