our
$VERSION
=
'0.68'
;
our
$AUTOLOAD
;
sub
new {
my
$pkg
=
shift
;
return
$pkg
->__new_dialect(
@_
)
if
$pkg
eq __PACKAGE__;
my
$self
=
bless
{ },
$pkg
;
$self
->__load_attribute_specs();
$self
->__setup(
@_
);
return
$self
;
}
sub
__new_dialect {
my
(
$pkg
,
%opts
) =
@_
;
croak
"Required 'dialect' parameter is missing"
unless
$opts
{dialect};
my
@dialect_classes
= ( __PACKAGE__.
'::'
.
$opts
{dialect},
$opts
{dialect} );
foreach
my
$dialect_class
(
@dialect_classes
) {
return
$dialect_class
->new(
%opts
)
if
eval
"use $dialect_class; 1"
or
$dialect_class
->isa(
$pkg
);
}
my
$dc_list
=
join
', '
,
@dialect_classes
;
croak
"Dialect '$opts{dialect}' could not be loaded (tried $dc_list). Error: $@"
;
}
sub
__setup {
my
$self
=
shift
;
$self
->__setup_attributes(
@_
);
$self
->__setup_rules();
}
sub
__setup_attributes {
my
$self
=
shift
;
$self
->__attrs( {} );
$self
->__load_and_validate_attributes(
@_
);
}
sub
__setup_rules {
my
$self
=
shift
;
$self
->__load_rules();
$self
->__validate_rules();
}
sub
__original_attrs {
shift
->_attr( {
internal
=> 1 },
__original_attrs
=>
@_
) }
sub
__attrs {
shift
->_attr( {
internal
=> 1 },
__attrs
=>
@_
) }
sub
__attrs_changed {
shift
->_attr( {
internal
=> 1 },
__attrs_changed
=>
@_
) }
sub
__root {
shift
->_attr( {
internal
=> 1 },
__root
=>
@_
) }
sub
__rules {
shift
->_attr( {
internal
=> 1 },
__rules
=>
@_
) }
sub
__attribute_specs {
shift
->_attr( {
internal
=> 1 },
__attribute_specs
=>
@_
) }
sub
base_url {
shift
->__no_such(
attribute
=>
qw/ base_url base_uri /
) }
sub
wiki_url {
shift
->__no_such(
attribute
=>
qw/ wiki_url wiki_uri /
) }
sub
__no_such {
my
(
$self
,
$thing
,
$that
,
$this
) =
@_
;
croak
"'$that' is not a valid $thing. Perhaps you meant '$this'?"
;
}
sub
_attr {
my
(
$self
,
$opts
,
$param
,
@value
) =
ref
$_
[1] eq
'HASH'
?
@_
: ( +
shift
, {},
@_
);
my
$store
=
$opts
->{internal} ?
$self
:
$self
->__attrs;
if
(
@value
) {
eval
{ validate_pos(
@value
,
$self
->__attribute_specs->{
$param
} )
unless
$opts
->{internal} };
$self
->__attribute_error($@)
if
$@;
$store
->{
$param
} =
$value
[0];
$self
->__attrs_changed(1)
if
!
$opts
->{internal};
}
return
defined
$store
->{
$param
} ?
$store
->{
$param
} :
''
;
}
sub
AUTOLOAD {
my
$self
=
shift
;
(
my
$attr
=
$AUTOLOAD
) =~ s/.*://;
return
$self
->_attr(
$attr
=>
@_
)
if
exists
$self
->__attribute_specs->{
$attr
};
croak
"Can't locate method '$attr' in package "
.
ref
(
$self
);
}
sub
DESTROY { }
sub
__slurp {
my
(
$self
,
$file
) =
@_
;
eval
"use File::Slurp;"
;
return
$self
->__simple_slurp(
$file
)
if
$@;
return
scalar
File::Slurp::read_file(
$file
);
}
sub
__simple_slurp {
my
(
$self
,
$file
) =
@_
;
open
my
$fh
,
$file
or croak
"can't open file $file for reading: $!"
;
my
$text
=
do
{
local
$/; <
$fh
> };
close
$fh
;
return
$text
;
}
sub
html2wiki {
my
$self
=
shift
;
my
%args
=
@_
% 2 ? (
html
=> +
shift
,
@_
) :
@_
;
my
%common_arg_errors
= (
url
=>
'uri'
,
base_url
=>
'base_uri'
,
wiki_url
=>
'wiki_uri'
);
while
(
my
(
$bad
,
$good
) =
each
%common_arg_errors
) {
$self
->__no_such(
'argument to html2wiki()'
,
$bad
,
$good
)
if
exists
$args
{
$bad
};
}
my
@input_sources
=
grep
{
exists
$args
{
$_
} }
qw/ html file uri /
;
croak
"missing 'html', 'file', or 'uri' argument to html2wiki"
unless
@input_sources
;
croak
"more than one of 'html', 'file', or 'uri' provided, but only one input source allowed"
if
@input_sources
> 1;
my
$html
=
delete
$args
{html} ||
''
;
my
$file
=
delete
$args
{file} ||
''
;
my
$uri
=
delete
$args
{uri} ||
''
;
$html
=
$self
->__slurp(
$file
)
if
$file
&&
$self
->slurp;
$html
=
$self
->__fetch_html_from_uri(
$uri
)
if
$uri
;
$html
=
"<html>$html</html>"
if
$html
and
$self
->wrap_in_html;
$self
->__original_attrs( { %{
$self
->__attrs } } );
$self
->
$_
(
$args
{
$_
} )
foreach
keys
%args
;
$self
->__setup_rules()
if
$self
->__attrs_changed;
$html
= decode(
$self
->encoding,
$html
);
my
$tree
= new HTML::TreeBuilder();
$tree
->store_comments(1);
$tree
->p_strict(
$self
->p_strict );
$tree
->implicit_body_p_tag(1);
$tree
->ignore_unknown(0);
if
(
$html
) {
$self
->given_html(
$html
);
$tree
->parse(
$html
);
$tree
->
eof
();
}
else
{
$self
->given_html(
$self
->__slurp(
$file
) );
$tree
->parse_file(
$file
);
}
$self
->__root(
$tree
);
$self
->__preprocess_tree();
$self
->__root->deobjectify_text();
$self
->parsed_html(
$tree
->as_HTML(
undef
,
' '
, {}) );
$self
->__root->objectify_text();
my
$output
=
$self
->__wikify(
$tree
);
$self
->__postprocess_output(\
$output
);
$tree
->
delete
();
$output
= encode(
$self
->encoding,
$output
);
if
(
$self
->__attrs_changed ) {
$self
->__attrs( { %{
$self
->__original_attrs } } );
$self
->__setup_rules();
$self
->__attrs_changed(0);
}
return
$output
;
}
sub
__wikify {
my
(
$self
,
$node
) =
@_
;
$node
->normalize_content();
if
(
$node
->tag eq
'~text'
) {
return
$node
->attr(
'text'
);
}
elsif
(
$node
->tag eq
'~comment'
) {
return
'<!--'
.
$node
->attr(
'text'
) .
'-->'
;
}
else
{
my
$rules
=
$self
->rules_for_tag(
$node
->tag );
return
$self
->__subst(
$rules
->{replace},
$node
,
$rules
)
if
exists
$rules
->{replace};
if
(
$rules
->{preserve} ) {
$rules
->{__start} = \
&__preserve_start
,
$rules
->{__end} =
$rules
->{empty} ?
undef
:
'</'
.
$node
->tag.
'>'
;
}
elsif
(
$rules
->{passthrough} ) {
$rules
->{__start} =
''
;
$rules
->{__end} =
''
;
}
my
$output
=
$self
->get_elem_contents(
$node
);
my
$trim
=
$rules
->{trim} ||
'none'
;
$output
=~ s/^\s+//
if
$trim
eq
'both'
or
$trim
eq
'leading'
;
$output
=~ s/\s+$//
if
$trim
eq
'both'
or
$trim
eq
'trailing'
;
my
$lf
=
$rules
->{line_format} ||
'none'
;
$output
=~ s/^\s*\n/\n/gm
if
$lf
ne
'none'
;
if
(
$lf
eq
'blocks'
) {
$output
=~ s/\n{3,}/\n\n/g;
}
elsif
(
$lf
eq
'multi'
) {
$output
=~ s/\n{2,}/\n/g;
}
elsif
(
$lf
eq
'single'
) {
$output
=~ s/\n+/ /g;
}
elsif
(
$lf
eq
'none'
) {
}
$output
=~ s/^/
$self
->__subst(
$rules
->{line_prefix},
$node
,
$rules
)/gem
if
$rules
->{line_prefix};
$output
=
$self
->__subst(
$rules
->{__start},
$node
,
$rules
).
$output
if
$rules
->{__start};
$output
=
$output
.
$self
->__subst(
$rules
->{__end},
$node
,
$rules
)
if
$rules
->{__end};
$output
=
$self
->__subst(
$rules
->{start},
$node
,
$rules
).
$output
if
$rules
->{start};
$output
=
$output
.
$self
->__subst(
$rules
->{end},
$node
,
$rules
)
if
$rules
->{end};
$output
=
"\n\n$output\n\n"
if
$rules
->{block} &&
( !
$self
->elem_search_lineage(
$node
, {
block
=> 1 } ) or
$self
->elem_search_lineage(
$node
, {
line_format
=>
'blocks'
} ) );
$output
=
"\n$output"
if
$rules
->{block} and
$node
->parent->look_up(
_tag
=>
$node
->tag ) and
$trim
ne
'none'
;
return
$output
;
}
}
sub
elem_within_block {
my
(
$self
,
$node
) =
@_
;
foreach
my
$n
(
$node
->lineage ) {
return
$n
if
$self
->rules_for_tag(
$n
->tag ||
''
)->{block};
}
return
0;
}
sub
elem_search_lineage {
my
(
$self
,
$node
,
$search_rules
) =
@_
;
foreach
my
$n
(
$node
->lineage ) {
my
$rules
=
$self
->rules_for_tag(
$n
->tag );
my
$matched
= 1;
while
(
my
(
$k
,
$v
) =
each
%$search_rules
) {
my
$rule_value
=
$rules
->{
$k
} ||
''
;
$matched
= 0
unless
$v
eq
$rule_value
;
}
return
$n
if
$matched
;
}
return
undef
;
}
sub
__subst {
my
(
$self
,
$subst
,
$node
,
$rules
) =
@_
;
return
ref
$subst
eq
'CODE'
?
$subst
->(
$self
,
$node
,
$rules
) :
$subst
;
}
sub
__preserve_start {
my
(
$self
,
$node
,
$rules
) =
@_
;
my
$tag
=
$node
->tag;
my
@attrs
=
exists
$rules
->{attributes} ? @{
$rules
->{attributes}} : ( );
my
$attr_str
=
$self
->get_attr_str(
$node
,
@attrs
);
my
$slash
=
$rules
->{empty} ?
' /'
:
''
;
return
'<'
.
$tag
.
' '
.
$attr_str
.
$slash
.
'>'
if
$attr_str
;
return
'<'
.
$tag
.
$slash
.
'>'
;
}
my
%rel2abs
= (
a
=>
'href'
,
img
=>
'src'
);
my
%allowedEmptyTag
= (
%HTML::Tagset::emptyElement
,
'~comment'
=> 1,
'~text'
=> 1 );
my
%isKnownTag
=
%HTML::Tagset::isKnown
;
sub
__preprocess_tree {
my
$self
=
shift
;
$self
->__root->objectify_text();
$self
->preprocess_tree(
$self
->__root);
HTML::WikiConverter::Normalizer->new->normalize(
$self
->__root)
if
$self
->normalize;
my
%strip_tag
=
map
{
$_
=> 1 } @{
$self
->strip_tags || [] };
my
%passthrough_naked_tags
=
map
{
$_
=> 1 }
$self
->__passthrough_naked_tags;
foreach
my
$node
(
$self
->__root->descendents ) {
$node
->tag(
''
)
unless
$node
->tag;
$node
->
delete
,
next
if
$strip_tag
{
$node
->tag};
$node
->replace_with_content->
delete
,
next
if
$passthrough_naked_tags
{
$node
->tag} and !
$node
->all_external_attr_names;
$self
->__rm_invalid_text(
$node
);
$node
->
delete
,
next
if
$self
->strip_empty_tags and !
$allowedEmptyTag
{
$node
->tag} and
$self
->__elem_is_empty(
$node
);
$self
->__encode_entities(
$node
)
if
$node
->tag eq
'~text'
and
$self
->escape_entities;
$self
->__rel2abs(
$node
)
if
$self
->base_uri and
exists
$rel2abs
{
$node
->tag};
$self
->preprocess_node(
$node
);
}
$self
->__root->objectify_text();
$self
->preprocess->(
$self
->__root )
if
ref
$self
->preprocess;
}
sub
__passthrough_naked_tags {
my
$self
=
shift
;
my
@tags
;
if
(
ref
$self
->passthrough_naked_tags eq
'ARRAY'
) {
@tags
= @{
$self
->passthrough_naked_tags };
}
elsif
(
$self
->passthrough_naked_tags ) {
@tags
=
$self
->__default_passthrough_naked_tags;
}
else
{
@tags
= ( );
}
return
@tags
;
}
sub
__default_passthrough_naked_tags {
qw/ tbody thead span div font /
}
sub
__elem_is_empty {
my
(
$self
,
$node
) =
@_
;
my
$content
=
$self
->get_elem_contents(
$node
);
my
$has_nonwhitespace
=
$content
&&
length
$content
?
$content
=~ /\S/ : 0;
return
!
$has_nonwhitespace
;
}
sub
__fetch_html_from_uri {
my
(
$self
,
$uri
) =
@_
;
my
$ua
=
$self
->__user_agent;
my
$res
=
$ua
->get(
$uri
);
croak
"request for <$uri> failed"
unless
$res
->is_success;
my
$encoding
=
$self
->encoding ||
$self
->__guess_encoding(
$res
) ||
'utf-8'
;
my
$html
= encode(
$self
->encoding, decode(
$encoding
,
$res
->content ) );
return
$html
;
}
sub
__guess_encoding {
my
(
$self
,
$res
) =
@_
;
carp
"LWP::Charset is not installed but is required for determining the charset claimed by the content at the requested URI"
,
return
unless
eval
"use LWP::Charset; 1"
;
return
LWP::Charset::getCharset(
$res
);
}
sub
__user_agent {
my
$self
=
shift
;
$self
->user_agent(
$self
->__default_user_agent )
unless
$self
->user_agent;
return
$self
->user_agent;
}
sub
__default_user_agent {
croak
"LWP is not installed but is required for fetching URIs"
unless
eval
"use LWP::UserAgent; 1"
;
return
LWP::UserAgent->new(
agent
=>
shift
->__default_ua_string );
}
sub
__default_ua_string {
"html2wiki/$VERSION"
}
sub
__encode_entities {
my
(
$self
,
$node
) =
@_
;
my
$text
=
defined
$node
->attr(
'text'
) ?
$node
->attr(
'text'
) :
''
;
encode_entities(
$text
,
'<>&'
);
$node
->attr(
text
=>
$text
);
}
sub
__rel2abs {
my
(
$self
,
$node
) =
@_
;
my
$attr
=
$rel2abs
{
$node
->tag};
return
unless
$node
->attr(
$attr
);
$node
->attr(
$attr
=> uri_unescape( URI->new_abs(
$node
->attr(
$attr
),
$self
->base_uri )->as_string ) );
}
my
%containers
=
map
{
$_
=> 1 }
qw/ table tbody tr ul ol dl menu /
;
sub
__rm_invalid_text {
my
(
$self
,
$node
) =
@_
;
my
$tag
=
defined
$node
->tag ?
$node
->tag :
''
;
if
(
$containers
{
$tag
} ) {
$_
->
delete
for
grep
{
$_
->tag eq
'~text'
}
$node
->content_list;
}
}
sub
strip_aname {
my
(
$self
,
$node
) =
@_
;
return
if
$node
->attr(
'href'
);
$node
->replace_with_content->
delete
();
}
sub
caption2para {
my
(
$self
,
$node
) =
@_
;
my
$table
=
$node
->parent;
$node
->detach();
$table
->preinsert(
$node
);
$node
->tag(
'p'
);
}
sub
preprocess_tree { }
sub
preprocess_node { }
sub
__postprocess_output {
my
(
$self
,
$outref
) =
@_
;
$$outref
=~ s/\n[\s^\n]+\n/\n\n/g;
$$outref
=~ s/\n{2,}/\n\n/g;
$$outref
=~ s/^\n+//;
$$outref
=~ s/\s+$//;
$$outref
=~ s/[ \t]+$//gm;
$self
->postprocess_output(
$outref
);
}
sub
postprocess_output { }
sub
attributes { {} }
sub
__load_attribute_specs {
my
$self
=
shift
;
my
$default_specs
=
$self
->__default_attribute_specs;
my
@dialect_specs
=
$self
->attributes;
my
$dialect_specs
=
@dialect_specs
== 1 &&
ref
$dialect_specs
[0] eq
'HASH'
?
$dialect_specs
[0] : {
@dialect_specs
};
my
%attr_specs
=
%$default_specs
;
while
(
my
(
$attr
,
$spec
) =
each
%$dialect_specs
) {
$attr_specs
{
$attr
} =
$spec
;
}
$self
->__attribute_specs( \
%attr_specs
);
}
sub
__load_and_validate_attributes {
my
$self
=
shift
;
my
%attrs
=
eval
{ validate(
@_
,
$self
->__attribute_specs ) };
$self
->__attribute_error($@)
if
$@;
while
(
my
(
$attr
,
$value
) =
each
%attrs
) {
$self
->
$attr
(
$value
);
}
}
sub
__attribute_error {
my
(
$self
,
$error
) =
@_
;
(
my
$dialect
=
ref
$self
) =~ s/.*://;
$error
=
sprintf
"The attribute '%s' does not exist in the dialect '%s'."
, $1,
$dialect
if
$error
=~ /not listed in the validation options\: (\w+)/;
croak
$error
;
}
sub
rules { {} }
sub
__load_rules {
my
$self
=
shift
;
$self
->__rules(
$self
->rules );
}
my
%meta_rules
= (
trim
=> {
range
=> [
qw/ none both leading trailing /
] },
line_format
=> {
range
=> [
qw/ none single multi blocks /
] },
replace
=> {
singleton
=> 1 },
alias
=> {
singleton
=> 1 },
attributes
=> {
depends
=> [
qw/ preserve /
] },
empty
=> {
depends
=> [
qw/ preserve /
] },
passthrough
=> {
singleton
=> 1 },
);
sub
__validate_rules {
my
$self
=
shift
;
foreach
my
$tag
(
keys
%{
$self
->__rules } ) {
my
$rules
=
$self
->__rules->{
$tag
};
foreach
my
$opt
(
keys
%$rules
) {
my
$spec
=
$meta_rules
{
$opt
} or
next
;
my
$singleton
=
$spec
->{singleton} || 0;
my
@disallows
=
ref
$spec
->{disallows} eq
'ARRAY'
? @{
$spec
->{disallows} } : ( );
my
@depends
=
ref
$spec
->{depends} eq
'ARRAY'
? @{
$spec
->{depends} } : ( );
my
@range
=
ref
$spec
->{range} eq
'ARRAY'
? @{
$spec
->{range} } : ( );
my
%range
=
map
{
$_
=> 1 }
@range
;
$self
->__rule_error(
$tag
,
"'$opt' cannot be combined with any other option"
)
if
$singleton
and
keys
%$rules
!= 1;
$rules
->{
$_
} &&
$self
->__rule_error(
$tag
,
"'$opt' cannot be combined with '$_'"
)
foreach
@disallows
;
!
$rules
->{
$_
} &&
$self
->__rule_error(
$tag
,
"'$opt' must be combined with '$_'"
)
foreach
@depends
;
$self
->__rule_error(
$tag
,
"Unknown '$opt' value '$rules->{$opt}'. '$opt' must be one of "
,
join
(
', '
,
map
{
"'$_'"
}
@range
) )
if
@range
and !
exists
$range
{
$rules
->{
$opt
}};
}
}
}
sub
__rule_error {
my
(
$self
,
$tag
,
@msg
) =
@_
;
my
$dialect
=
ref
$self
;
croak
@msg
,
" in tag '$tag', dialect '$dialect'.\n"
;
}
sub
get_elem_contents {
my
(
$self
,
$node
) =
@_
;
my
$str
=
join
''
,
map
{
$self
->__wikify(
$_
) }
$node
->content_list;
return
defined
$str
?
$str
:
''
;
}
sub
get_wiki_page {
my
(
$self
,
$uri
) =
@_
;
my
@wiki_uris
=
ref
$self
->wiki_uri eq
'ARRAY'
? @{
$self
->wiki_uri} :
$self
->wiki_uri;
foreach
my
$wiki_uri
(
@wiki_uris
) {
my
$page
=
$self
->__extract_wiki_page(
$uri
,
$wiki_uri
);
return
$page
if
$page
;
}
return
undef
;
}
sub
__extract_wiki_page {
my
(
$self
,
$uri
,
$wiki_uri
) =
@_
;
return
undef
unless
$wiki_uri
;
if
(
ref
$wiki_uri
eq
'Regexp'
) {
return
$uri
=~
$wiki_uri
? $1 :
undef
;
}
elsif
(
ref
$wiki_uri
eq
'CODE'
) {
return
$wiki_uri
->(
$self
, URI->new(
$uri
) );
}
else
{
$wiki_uri
= URI->new_abs(
$wiki_uri
,
$self
->base_uri )->as_string;
return
undef
unless
index
(
$uri
,
$wiki_uri
) == 0;
return
undef
unless
length
$uri
>
length
$wiki_uri
;
return
substr
(
$uri
,
length
$wiki_uri
);
}
}
my
$UPPER
=
'\p{UppercaseLetter}'
;
my
$LOWER
=
'\p{LowercaseLetter}'
;
my
$WIKIWORD
=
"$UPPER$LOWER\\p{Number}\\p{ConnectorPunctuation}"
;
sub
is_camel_case {
return
$_
[1] =~ /(?:[
$UPPER
](?=[
$WIKIWORD
]*[
$UPPER
])(?=[
$WIKIWORD
]*[
$LOWER
])[
$WIKIWORD
]+)/ }
sub
get_attr_str {
my
(
$self
,
$node
,
@attrs
) =
@_
;
my
%attrs
=
map
{
$_
=>
$node
->attr(
$_
) }
@attrs
;
my
$str
=
join
' '
,
map
{
$_
.
'="'
.encode_entities(
$attrs
{
$_
}).
'"'
}
grep
{
$attrs
{
$_
} }
@attrs
;
$str
=~ s/[\n\r]/ /g
if
$str
;
return
defined
$str
?
$str
:
''
;
}
sub
given_html {
shift
->_attr( {
internal
=> 1 },
__given_html
=>
@_
) }
sub
parsed_html {
shift
->_attr( {
internal
=> 1 },
__parsed_html
=>
@_
) }
sub
available_dialects {
my
@dialects
;
my
%seen
;
for
my
$inc
(
@INC
) {
my
$dir
= File::Spec->catfile(
$inc
,
'HTML'
,
'WikiConverter'
);
my
$dh
= DirHandle->new(
$dir
) or
next
;
while
(
my
$f
=
$dh
->
read
) {
next
unless
$f
=~ /^(\w+)\.pm$/;
my
$dialect
= $1;
next
if
$seen
{
$dialect
}++;
next
if
$dialect
eq
'Normalizer'
or
$dialect
eq
'WebApp'
;
push
@dialects
,
$dialect
;
}
}
return
wantarray
?
sort
@dialects
:
@dialects
;
}
sub
rules_for_tag {
my
(
$self
,
$tag
) =
@_
;
my
$rules
=
$self
->__rules_for_tag(
$tag
);
return
$rules
->{alias} ?
$self
->__rules_for_tag(
$rules
->{alias} ) :
$rules
;
}
sub
__rules_for_tag {
my
(
$self
,
$tag
) =
@_
;
return
$self
->__rules->{
$tag
}
if
$self
->__rules->{
$tag
};
return
$self
->__rules->{UNKNOWN}
if
$self
->__rules->{UNKNOWN} and !
$isKnownTag
{
$tag
};
return
{ };
}
sub
__default_attribute_specs { {
base_uri
=> {
type
=> SCALAR,
default
=>
''
},
dialect
=> {
type
=> SCALAR,
optional
=> 0 },
encoding
=> {
type
=> SCALAR,
default
=>
'utf-8'
},
escape_entities
=> {
type
=> BOOLEAN,
default
=> 1 },
normalize
=> {
type
=> BOOLEAN,
default
=> 1 },
p_strict
=> {
type
=> BOOLEAN,
default
=> 1 },
preprocess
=> {
type
=> CODEREF | UNDEF,
default
=>
undef
},
strip_empty_tags
=> {
type
=> BOOLEAN,
default
=> 0 },
slurp
=> {
type
=> BOOLEAN,
default
=> 0 },
strip_tags
=> {
type
=> ARRAYREF,
default
=> [
qw/ ~comment head script style /
] },
passthrough_naked_tags
=> {
type
=> ARRAYREF | BOOLEAN,
default
=> 0 },
user_agent
=> {
type
=> OBJECT | UNDEF,
default
=>
undef
},
wiki_uri
=> {
type
=> SCALAR | ARRAYREF,
default
=>
''
},
wrap_in_html
=> {
type
=> BOOLEAN,
default
=> 1 },
} }
1;