1+
2+
3+ function describeEnumType ( element : Element ) : { vals : Record < string , string > } {
4+ const vals : Record < string , string > = { } ;
5+
6+ const sortedEnumVals = Array . from ( element . children )
7+ . filter ( ( child ) => child . tagName === "EnumVal" )
8+ . sort (
9+ ( v1 , v2 ) =>
10+ parseInt ( v1 . getAttribute ( "ord" ) ! , 10 ) -
11+ parseInt ( v2 . getAttribute ( "ord" ) ! , 10 ) ,
12+ ) ;
13+ for ( const val of sortedEnumVals )
14+ vals [ val . getAttribute ( "ord" ) ! ] = val . textContent ?? "" ;
15+
16+ return { vals } ;
17+ }
18+
19+ function describeDAType ( element : Element ) : {
20+ bdas : Record < string , Record < string , string | null > > ;
21+ } {
22+ const bdas : Record < string , Record < string , string | null > > = { } ;
23+ for ( const bda of Array . from ( element . children )
24+ . filter ( ( child ) => child . tagName === "BDA" )
25+ . sort ( ( c1 , c2 ) => c1 . outerHTML . localeCompare ( c2 . outerHTML ) ) ) {
26+ const [ bType , type , dchg , dupd , qchg ] = [
27+ "bType" ,
28+ "type" ,
29+ "dchg" ,
30+ "dupd" ,
31+ "qchg" ,
32+ ] . map ( ( attr ) => bda . getAttribute ( attr ) ) ;
33+ bdas [ bda . getAttribute ( "name" ) ! ] = { bType, type, dchg, dupd, qchg } ;
34+ }
35+ return { bdas } ;
36+ }
37+
38+ function describeDOType ( element : Element ) {
39+ const sdos : Record < string , Record < string , string | null > > = { } ;
40+ for ( const sdo of Array . from ( element . children )
41+ . filter ( ( child ) => child . tagName === "SDO" )
42+ . sort ( ( c1 , c2 ) => c1 . outerHTML . localeCompare ( c2 . outerHTML ) ) ) {
43+ const [ name , type , transient ] = [ "name" , "type" , "transient" ] . map ( ( attr ) =>
44+ sdo . getAttribute ( attr ) ,
45+ ) ;
46+ sdos [ name ! ] = { type, transient } ;
47+ }
48+ const das : Record < string , Record < string , string | null > > = { } ;
49+ for ( const da of Array . from ( element . children )
50+ . filter ( ( child ) => child . tagName === "DA" )
51+ . sort ( ( c1 , c2 ) => c1 . outerHTML . localeCompare ( c2 . outerHTML ) ) ) {
52+ const [ name , fc , bType , type , dchg , dupd , qchg ] = [
53+ "name" ,
54+ "fc" ,
55+ "bType" ,
56+ "type" ,
57+ "dchg" ,
58+ "dupd" ,
59+ "qchg" ,
60+ ] . map ( ( attr ) => da . getAttribute ( attr ) ) ;
61+ das [ name ! ] = {
62+ fc,
63+ bType,
64+ type,
65+ dchg,
66+ dupd,
67+ qchg,
68+ } ;
69+ }
70+ return {
71+ sdos,
72+ das,
73+ cdc : element . getAttribute ( "cdc" ) ,
74+ } ;
75+ }
76+
77+ function describeLNodeType ( element : Element ) {
78+ const dos : Record < string , Record < string , string | null > > = { } ;
79+ for ( const doElement of Array . from ( element . children )
80+ . filter ( ( child ) => child . tagName === "DO" )
81+ . sort ( ( c1 , c2 ) => c1 . outerHTML . localeCompare ( c2 . outerHTML ) ) ) {
82+ const [ name , type , transient ] = [ "name" , "type" , "transient" ] . map ( ( attr ) =>
83+ doElement . getAttribute ( attr ) ,
84+ ) ;
85+ dos [ name ! ] = { type, transient } ;
86+ }
87+ return {
88+ dos,
89+ lnClass : element . getAttribute ( "lnClass" ) ,
90+ } ;
91+ }
92+
93+ const typeDescriptions = {
94+ EnumType : describeEnumType ,
95+ DAType : describeDAType ,
96+ DOType : describeDOType ,
97+ LNodeType : describeLNodeType ,
98+ } as Partial < Record < string , ( e : Element ) => object > > ;
99+
100+ function describeElement ( element : Element ) : object {
101+ const describe = typeDescriptions [ element . tagName ] ! ;
102+
103+ return describe ( element ) ;
104+ }
105+
106+ export function hashElement ( element : Element ) : string {
107+ /** A direct copy from www.github.com/openscd/open-scd-core/foundation/cyrb64.ts */
108+
109+ /**
110+ * Hashes `str` using the cyrb64 variant of
111+ * https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
112+ * @returns digest - a rather insecure hash, very quickly
113+ */
114+ function cyrb64 ( str : string ) : string {
115+ /* eslint-disable no-bitwise */
116+ let h1 = 0xdeadbeef ;
117+ let h2 = 0x41c6ce57 ;
118+ /* eslint-disable-next-line no-plusplus */
119+ for ( let i = 0 , ch ; i < str . length ; i ++ ) {
120+ ch = str . charCodeAt ( i ) ;
121+ h1 = Math . imul ( h1 ^ ch , 2654435761 ) ;
122+ h2 = Math . imul ( h2 ^ ch , 1597334677 ) ;
123+ }
124+ h1 =
125+ Math . imul ( h1 ^ ( h1 >>> 16 ) , 2246822507 ) ^
126+ Math . imul ( h2 ^ ( h2 >>> 13 ) , 3266489909 ) ;
127+ h2 =
128+ Math . imul ( h2 ^ ( h2 >>> 16 ) , 2246822507 ) ^
129+ Math . imul ( h1 ^ ( h1 >>> 13 ) , 3266489909 ) ;
130+ return (
131+ ( h2 >>> 0 ) . toString ( 16 ) . padStart ( 8 , "0" ) +
132+ ( h1 >>> 0 ) . toString ( 16 ) . padStart ( 8 , "0" )
133+ ) ;
134+ /* eslint-enable no-bitwise */
135+ }
136+
137+ return cyrb64 ( JSON . stringify ( describeElement ( element ) ) ) ;
138+ }
139+
140+ export function isEqualNode ( ours : Element , theirs : Element ) : boolean {
141+ return JSON . stringify ( describeElement ( ours ) ) === JSON . stringify ( describeElement ( theirs ) ) ;
142+ }
0 commit comments