Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F121642955
cleanupIDs.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sat, Jul 12, 18:29
Size
6 KB
Mime Type
text/html
Expires
Mon, Jul 14, 18:29 (2 d)
Engine
blob
Format
Raw Data
Handle
27365541
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
cleanupIDs.js
View Options
'use strict'
;
/**
* @typedef {import('../lib/types').XastElement} XastElement
*/
const
{
visitSkip
}
=
require
(
'../lib/xast.js'
);
const
{
referencesProps
}
=
require
(
'./_collections.js'
);
exports
.
type
=
'visitor'
;
exports
.
name
=
'cleanupIDs'
;
exports
.
active
=
true
;
exports
.
description
=
'removes unused IDs and minifies used'
;
const
regReferencesUrl
=
/\burl\(("|')?#(.+?)\1\)/
;
const
regReferencesHref
=
/^#(.+?)$/
;
const
regReferencesBegin
=
/(\w+)\./
;
const
generateIDchars
=
[
'a'
,
'b'
,
'c'
,
'd'
,
'e'
,
'f'
,
'g'
,
'h'
,
'i'
,
'j'
,
'k'
,
'l'
,
'm'
,
'n'
,
'o'
,
'p'
,
'q'
,
'r'
,
's'
,
't'
,
'u'
,
'v'
,
'w'
,
'x'
,
'y'
,
'z'
,
'A'
,
'B'
,
'C'
,
'D'
,
'E'
,
'F'
,
'G'
,
'H'
,
'I'
,
'J'
,
'K'
,
'L'
,
'M'
,
'N'
,
'O'
,
'P'
,
'Q'
,
'R'
,
'S'
,
'T'
,
'U'
,
'V'
,
'W'
,
'X'
,
'Y'
,
'Z'
,
];
const
maxIDindex
=
generateIDchars
.
length
-
1
;
/**
* Check if an ID starts with any one of a list of strings.
*
* @type {(string: string, prefixes: Array<string>) => boolean}
*/
const
hasStringPrefix
=
(
string
,
prefixes
)
=>
{
for
(
const
prefix
of
prefixes
)
{
if
(
string
.
startsWith
(
prefix
))
{
return
true
;
}
}
return
false
;
};
/**
* Generate unique minimal ID.
*
* @type {(currentID: null | Array<number>) => Array<number>}
*/
const
generateID
=
(
currentID
)
=>
{
if
(
currentID
==
null
)
{
return
[
0
];
}
currentID
[
currentID
.
length
-
1
]
+=
1
;
for
(
let
i
=
currentID
.
length
-
1
;
i
>
0
;
i
--
)
{
if
(
currentID
[
i
]
>
maxIDindex
)
{
currentID
[
i
]
=
0
;
if
(
currentID
[
i
-
1
]
!==
undefined
)
{
currentID
[
i
-
1
]
++
;
}
}
}
if
(
currentID
[
0
]
>
maxIDindex
)
{
currentID
[
0
]
=
0
;
currentID
.
unshift
(
0
);
}
return
currentID
;
};
/**
* Get string from generated ID array.
*
* @type {(arr: Array<number>, prefix: string) => string}
*/
const
getIDstring
=
(
arr
,
prefix
)
=>
{
return
prefix
+
arr
.
map
((
i
)
=>
generateIDchars
[
i
]).
join
(
''
);
};
/**
* Remove unused and minify used IDs
* (only if there are no any <style> or <script>).
*
* @author Kir Belevich
*
* @type {import('../lib/types').Plugin<{
* remove?: boolean,
* minify?: boolean,
* prefix?: string,
* preserve?: Array<string>,
* preservePrefixes?: Array<string>,
* force?: boolean,
* }>}
*/
exports
.
fn
=
(
_root
,
params
)
=>
{
const
{
remove
=
true
,
minify
=
true
,
prefix
=
''
,
preserve
=
[],
preservePrefixes
=
[],
force
=
false
,
}
=
params
;
const
preserveIDs
=
new
Set
(
Array
.
isArray
(
preserve
)
?
preserve
:
preserve
?
[
preserve
]
:
[]
);
const
preserveIDPrefixes
=
Array
.
isArray
(
preservePrefixes
)
?
preservePrefixes
:
preservePrefixes
?
[
preservePrefixes
]
:
[];
/**
* @type {Map<string, XastElement>}
*/
const
nodeById
=
new
Map
();
/**
* @type {Map<string, Array<{element: XastElement, name: string, value: string }>>}
*/
const
referencesById
=
new
Map
();
let
deoptimized
=
false
;
return
{
element
:
{
enter
:
(
node
)
=>
{
if
(
force
==
false
)
{
// deoptimize if style or script elements are present
if
(
(
node
.
name
===
'style'
||
node
.
name
===
'script'
)
&&
node
.
children
.
length
!==
0
)
{
deoptimized
=
true
;
return
;
}
// avoid removing IDs if the whole SVG consists only of defs
if
(
node
.
name
===
'svg'
)
{
let
hasDefsOnly
=
true
;
for
(
const
child
of
node
.
children
)
{
if
(
child
.
type
!==
'element'
||
child
.
name
!==
'defs'
)
{
hasDefsOnly
=
false
;
break
;
}
}
if
(
hasDefsOnly
)
{
return
visitSkip
;
}
}
}
for
(
const
[
name
,
value
]
of
Object
.
entries
(
node
.
attributes
))
{
if
(
name
===
'id'
)
{
// collect all ids
const
id
=
value
;
if
(
nodeById
.
has
(
id
))
{
delete
node
.
attributes
.
id
;
// remove repeated id
}
else
{
nodeById
.
set
(
id
,
node
);
}
}
else
{
// collect all references
/**
* @type {null | string}
*/
let
id
=
null
;
if
(
referencesProps
.
includes
(
name
))
{
const
match
=
value
.
match
(
regReferencesUrl
);
if
(
match
!=
null
)
{
id
=
match
[
2
];
// url() reference
}
}
if
(
name
===
'href'
||
name
.
endsWith
(
':href'
))
{
const
match
=
value
.
match
(
regReferencesHref
);
if
(
match
!=
null
)
{
id
=
match
[
1
];
// href reference
}
}
if
(
name
===
'begin'
)
{
const
match
=
value
.
match
(
regReferencesBegin
);
if
(
match
!=
null
)
{
id
=
match
[
1
];
// href reference
}
}
if
(
id
!=
null
)
{
let
refs
=
referencesById
.
get
(
id
);
if
(
refs
==
null
)
{
refs
=
[];
referencesById
.
set
(
id
,
refs
);
}
refs
.
push
({
element
:
node
,
name
,
value
});
}
}
}
},
},
root
:
{
exit
:
()
=>
{
if
(
deoptimized
)
{
return
;
}
/**
* @type {(id: string) => boolean}
**/
const
isIdPreserved
=
(
id
)
=>
preserveIDs
.
has
(
id
)
||
hasStringPrefix
(
id
,
preserveIDPrefixes
);
/**
* @type {null | Array<number>}
*/
let
currentID
=
null
;
for
(
const
[
id
,
refs
]
of
referencesById
)
{
const
node
=
nodeById
.
get
(
id
);
if
(
node
!=
null
)
{
// replace referenced IDs with the minified ones
if
(
minify
&&
isIdPreserved
(
id
)
===
false
)
{
/**
* @type {null | string}
*/
let
currentIDString
=
null
;
do
{
currentID
=
generateID
(
currentID
);
currentIDString
=
getIDstring
(
currentID
,
prefix
);
}
while
(
isIdPreserved
(
currentIDString
));
node
.
attributes
.
id
=
currentIDString
;
for
(
const
{
element
,
name
,
value
}
of
refs
)
{
if
(
value
.
includes
(
'#'
))
{
// replace id in href and url()
element
.
attributes
[
name
]
=
value
.
replace
(
`#
$
{
id
}
`
,
`#
$
{
currentIDString
}
`
);
}
else
{
// replace id in begin attribute
element
.
attributes
[
name
]
=
value
.
replace
(
`
$
{
id
}.
`
,
`
$
{
currentIDString
}.
`
);
}
}
}
// keep referenced node
nodeById
.
delete
(
id
);
}
}
// remove non-referenced IDs attributes from elements
if
(
remove
)
{
for
(
const
[
id
,
node
]
of
nodeById
)
{
if
(
isIdPreserved
(
id
)
===
false
)
{
delete
node
.
attributes
.
id
;
}
}
}
},
},
};
};
Event Timeline
Log In to Comment