Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
jinli gu
Litemall
Commits
854bacf6
Commit
854bacf6
authored
Aug 25, 2018
by
Menethil
Browse files
添加Renard小程序
parent
a50998e6
Changes
222
Hide whitespace changes
Inline
Side-by-side
renard-wx/images/tabbar/user-o.png
0 → 100644
View file @
854bacf6
6.92 KB
renard-wx/lib/wxParse/html2json.js
0 → 100644
View file @
854bacf6
/**
* author: Di (微信小程序开发工程师)
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
* 垂直微信小程序开发交流社区
*
* github地址: https://github.com/icindy/wxParse
*
* for: 微信小程序富文本解析
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
*/
var
__placeImgeUrlHttps
=
"
https
"
;
var
__emojisReg
=
''
;
var
__emojisBaseSrc
=
''
;
var
__emojis
=
{};
var
wxDiscode
=
require
(
'
wxDiscode.js
'
);
var
HTMLParser
=
require
(
'
htmlparser.js
'
);
// Empty Elements - HTML 5
var
empty
=
makeMap
(
"
area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr
"
);
// Block Elements - HTML 5
var
block
=
makeMap
(
"
br,a,code,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video
"
);
// Inline Elements - HTML 5
var
inline
=
makeMap
(
"
abbr,acronym,applet,b,basefont,bdo,big,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
"
);
// Elements that you can, intentionally, leave open
// (and which close themselves)
var
closeSelf
=
makeMap
(
"
colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr
"
);
// Attributes that have their values filled in disabled="disabled"
var
fillAttrs
=
makeMap
(
"
checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected
"
);
// Special Elements (can contain anything)
var
special
=
makeMap
(
"
wxxxcode-style,script,style,view,scroll-view,block
"
);
function
makeMap
(
str
)
{
var
obj
=
{},
items
=
str
.
split
(
"
,
"
);
for
(
var
i
=
0
;
i
<
items
.
length
;
i
++
)
obj
[
items
[
i
]]
=
true
;
return
obj
;
}
function
q
(
v
)
{
return
'
"
'
+
v
+
'
"
'
;
}
function
removeDOCTYPE
(
html
)
{
return
html
.
replace
(
/<
\?
xml.*
\?
>
\n
/
,
''
)
.
replace
(
/<!doctype.*
\>\n
/
,
''
)
.
replace
(
/<!DOCTYPE.*
\>\n
/
,
''
);
}
function
html2json
(
html
,
bindName
)
{
//处理字符串
html
=
removeDOCTYPE
(
html
);
html
=
wxDiscode
.
strDiscode
(
html
);
//生成node节点
var
bufArray
=
[];
var
results
=
{
node
:
bindName
,
nodes
:
[],
images
:[],
imageUrls
:[]
};
HTMLParser
(
html
,
{
start
:
function
(
tag
,
attrs
,
unary
)
{
//debug(tag, attrs, unary);
// node for this element
var
node
=
{
node
:
'
element
'
,
tag
:
tag
,
};
if
(
block
[
tag
])
{
node
.
tagType
=
"
block
"
;
}
else
if
(
inline
[
tag
])
{
node
.
tagType
=
"
inline
"
;
}
else
if
(
closeSelf
[
tag
])
{
node
.
tagType
=
"
closeSelf
"
;
}
if
(
attrs
.
length
!==
0
)
{
node
.
attr
=
attrs
.
reduce
(
function
(
pre
,
attr
)
{
var
name
=
attr
.
name
;
var
value
=
attr
.
value
;
if
(
name
==
'
class
'
)
{
// console.dir(value);
// value = value.join("")
node
.
classStr
=
value
;
}
// has multi attibutes
// make it array of attribute
if
(
name
==
'
style
'
)
{
// console.dir(value);
// value = value.join("")
node
.
styleStr
=
value
;
}
if
(
value
.
match
(
/ /
))
{
value
=
value
.
split
(
'
'
);
}
// if attr already exists
// merge it
if
(
pre
[
name
])
{
if
(
Array
.
isArray
(
pre
[
name
]))
{
// already array, push to last
pre
[
name
].
push
(
value
);
}
else
{
// single value, make it array
pre
[
name
]
=
[
pre
[
name
],
value
];
}
}
else
{
// not exist, put it
pre
[
name
]
=
value
;
}
return
pre
;
},
{});
}
//对img添加额外数据
if
(
node
.
tag
===
'
img
'
)
{
node
.
imgIndex
=
results
.
images
.
length
;
var
imgUrl
=
node
.
attr
.
src
;
imgUrl
=
wxDiscode
.
urlToHttpUrl
(
imgUrl
,
__placeImgeUrlHttps
);
node
.
attr
.
src
=
imgUrl
;
node
.
from
=
bindName
;
results
.
images
.
push
(
node
);
results
.
imageUrls
.
push
(
imgUrl
);
}
if
(
unary
)
{
// if this tag dosen't have end tag
// like <img src="hoge.png"/>
// add to parents
var
parent
=
bufArray
[
0
]
||
results
;
if
(
parent
.
nodes
===
undefined
)
{
parent
.
nodes
=
[];
}
parent
.
nodes
.
push
(
node
);
}
else
{
bufArray
.
unshift
(
node
);
}
},
end
:
function
(
tag
)
{
//debug(tag);
// merge into parent tag
var
node
=
bufArray
.
shift
();
if
(
node
.
tag
!==
tag
)
console
.
error
(
'
invalid state: mismatch end tag
'
);
if
(
bufArray
.
length
===
0
)
{
results
.
nodes
.
push
(
node
);
}
else
{
var
parent
=
bufArray
[
0
];
if
(
parent
.
nodes
===
undefined
)
{
parent
.
nodes
=
[];
}
parent
.
nodes
.
push
(
node
);
}
},
chars
:
function
(
text
)
{
//debug(text);
var
node
=
{
node
:
'
text
'
,
text
:
text
,
textArray
:
transEmojiStr
(
text
)
};
if
(
bufArray
.
length
===
0
)
{
results
.
nodes
.
push
(
node
);
}
else
{
var
parent
=
bufArray
[
0
];
if
(
parent
.
nodes
===
undefined
)
{
parent
.
nodes
=
[];
}
parent
.
nodes
.
push
(
node
);
}
},
comment
:
function
(
text
)
{
//debug(text);
var
node
=
{
node
:
'
comment
'
,
text
:
text
,
};
var
parent
=
bufArray
[
0
];
if
(
parent
.
nodes
===
undefined
)
{
parent
.
nodes
=
[];
}
parent
.
nodes
.
push
(
node
);
},
});
return
results
;
};
function
transEmojiStr
(
str
){
// var eReg = new RegExp("["+__reg+' '+"]");
// str = str.replace(/\[([^\[\]]+)\]/g,':$1:')
var
emojiObjs
=
[];
//如果正则表达式为空
if
(
__emojisReg
.
length
==
0
||
!
__emojis
){
var
emojiObj
=
{}
emojiObj
.
node
=
"
text
"
;
emojiObj
.
text
=
str
;
array
=
[
emojiObj
];
return
array
;
}
//这个地方需要调整
str
=
str
.
replace
(
/
\[([^\[\]]
+
)\]
/g
,
'
:$1:
'
)
var
eReg
=
new
RegExp
(
"
[:]
"
);
var
array
=
str
.
split
(
eReg
);
for
(
var
i
=
0
;
i
<
array
.
length
;
i
++
){
var
ele
=
array
[
i
];
var
emojiObj
=
{};
if
(
__emojis
[
ele
]){
emojiObj
.
node
=
"
element
"
;
emojiObj
.
tag
=
"
emoji
"
;
emojiObj
.
text
=
__emojis
[
ele
];
emojiObj
.
baseSrc
=
__emojisBaseSrc
;
}
else
{
emojiObj
.
node
=
"
text
"
;
emojiObj
.
text
=
ele
;
}
emojiObjs
.
push
(
emojiObj
);
}
return
emojiObjs
;
}
function
emojisInit
(
reg
=
''
,
baseSrc
=
"
/wxParse/emojis/
"
,
emojis
){
__emojisReg
=
reg
;
__emojisBaseSrc
=
baseSrc
;
__emojis
=
emojis
;
}
module
.
exports
=
{
html2json
:
html2json
,
emojisInit
:
emojisInit
};
renard-wx/lib/wxParse/htmlparser.js
0 → 100644
View file @
854bacf6
/**
* author: Di (微信小程序开发工程师)
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
* 垂直微信小程序开发交流社区
*
* github地址: https://github.com/icindy/wxParse
*
* for: 微信小程序富文本解析
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
*/
// Regular Expressions for parsing tags and attributes
var
startTag
=
/^<
([
-A-Za-z0-9_
]
+
)((?:\s
+
[
a-zA-Z_:
][
-a-zA-Z0-9_:.
]
*
(?:\s
*=
\s
*
(?:(?:
"
[^
"
]
*"
)
|
(?:
'
[^
'
]
*'
)
|
[^
>
\s]
+
))?)
*
)\s
*
(\/?)
>/
,
endTag
=
/^<
\/([
-A-Za-z0-9_
]
+
)[^
>
]
*>/
,
attr
=
/
([
a-zA-Z_:
][
-a-zA-Z0-9_:.
]
*
)(?:\s
*=
\s
*
(?:(?:
"
((?:\\
.|
[^
"
])
*
)
"
)
|
(?:
'
((?:\\
.|
[^
'
])
*
)
'
)
|
([^
>
\s]
+
)))?
/g
;
// Empty Elements - HTML 5
var
empty
=
makeMap
(
"
area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr
"
);
// Block Elements - HTML 5
var
block
=
makeMap
(
"
a,address,code,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video
"
);
// Inline Elements - HTML 5
var
inline
=
makeMap
(
"
abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var
"
);
// Elements that you can, intentionally, leave open
// (and which close themselves)
var
closeSelf
=
makeMap
(
"
colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr
"
);
// Attributes that have their values filled in disabled="disabled"
var
fillAttrs
=
makeMap
(
"
checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected
"
);
// Special Elements (can contain anything)
var
special
=
makeMap
(
"
wxxxcode-style,script,style,view,scroll-view,block
"
);
function
HTMLParser
(
html
,
handler
)
{
var
index
,
chars
,
match
,
stack
=
[],
last
=
html
;
stack
.
last
=
function
()
{
return
this
[
this
.
length
-
1
];
};
while
(
html
)
{
chars
=
true
;
// Make sure we're not in a script or style element
if
(
!
stack
.
last
()
||
!
special
[
stack
.
last
()])
{
// Comment
if
(
html
.
indexOf
(
"
<!--
"
)
==
0
)
{
index
=
html
.
indexOf
(
"
-->
"
);
if
(
index
>=
0
)
{
if
(
handler
.
comment
)
handler
.
comment
(
html
.
substring
(
4
,
index
));
html
=
html
.
substring
(
index
+
3
);
chars
=
false
;
}
// end tag
}
else
if
(
html
.
indexOf
(
"
</
"
)
==
0
)
{
match
=
html
.
match
(
endTag
);
if
(
match
)
{
html
=
html
.
substring
(
match
[
0
].
length
);
match
[
0
].
replace
(
endTag
,
parseEndTag
);
chars
=
false
;
}
// start tag
}
else
if
(
html
.
indexOf
(
"
<
"
)
==
0
)
{
match
=
html
.
match
(
startTag
);
if
(
match
)
{
html
=
html
.
substring
(
match
[
0
].
length
);
match
[
0
].
replace
(
startTag
,
parseStartTag
);
chars
=
false
;
}
}
if
(
chars
)
{
index
=
html
.
indexOf
(
"
<
"
);
var
text
=
index
<
0
?
html
:
html
.
substring
(
0
,
index
);
html
=
index
<
0
?
""
:
html
.
substring
(
index
);
if
(
handler
.
chars
)
handler
.
chars
(
text
);
}
}
else
{
html
=
html
.
replace
(
new
RegExp
(
"
([
\\
s
\\
S]*?)<
\
/
"
+
stack
.
last
()
+
"
[^>]*>
"
),
function
(
all
,
text
)
{
text
=
text
.
replace
(
/<!--
([\s\S]
*
?)
-->|<!
\[
CDATA
\[([\s\S]
*
?)
]]>/g
,
"
$1$2
"
);
if
(
handler
.
chars
)
handler
.
chars
(
text
);
return
""
;
});
parseEndTag
(
""
,
stack
.
last
());
}
if
(
html
==
last
)
throw
"
Parse Error:
"
+
html
;
last
=
html
;
}
// Clean up any remaining tags
parseEndTag
();
function
parseStartTag
(
tag
,
tagName
,
rest
,
unary
)
{
tagName
=
tagName
.
toLowerCase
();
if
(
block
[
tagName
])
{
while
(
stack
.
last
()
&&
inline
[
stack
.
last
()])
{
parseEndTag
(
""
,
stack
.
last
());
}
}
if
(
closeSelf
[
tagName
]
&&
stack
.
last
()
==
tagName
)
{
parseEndTag
(
""
,
tagName
);
}
unary
=
empty
[
tagName
]
||
!!
unary
;
if
(
!
unary
)
stack
.
push
(
tagName
);
if
(
handler
.
start
)
{
var
attrs
=
[];
rest
.
replace
(
attr
,
function
(
match
,
name
)
{
var
value
=
arguments
[
2
]
?
arguments
[
2
]
:
arguments
[
3
]
?
arguments
[
3
]
:
arguments
[
4
]
?
arguments
[
4
]
:
fillAttrs
[
name
]
?
name
:
""
;
attrs
.
push
({
name
:
name
,
value
:
value
,
escaped
:
value
.
replace
(
/
(
^|
[^\\])
"/g
,
'
$1
\\\
"
'
)
//"
});
});
if
(
handler
.
start
)
{
handler
.
start
(
tagName
,
attrs
,
unary
);
}
}
}
function
parseEndTag
(
tag
,
tagName
)
{
// If no tag name is provided, clean shop
if
(
!
tagName
)
var
pos
=
0
;
// Find the closest opened tag of the same type
else
for
(
var
pos
=
stack
.
length
-
1
;
pos
>=
0
;
pos
--
)
if
(
stack
[
pos
]
==
tagName
)
break
;
if
(
pos
>=
0
)
{
// Close all the open elements, up the stack
for
(
var
i
=
stack
.
length
-
1
;
i
>=
pos
;
i
--
)
if
(
handler
.
end
)
handler
.
end
(
stack
[
i
]);
// Remove the open elements from the stack
stack
.
length
=
pos
;
}
}
};
function
makeMap
(
str
)
{
var
obj
=
{},
items
=
str
.
split
(
"
,
"
);
for
(
var
i
=
0
;
i
<
items
.
length
;
i
++
)
obj
[
items
[
i
]]
=
true
;
return
obj
;
}
module
.
exports
=
HTMLParser
;
renard-wx/lib/wxParse/showdown.js
0 → 100644
View file @
854bacf6
/**
* author: Di (微信小程序开发工程师)
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
* 垂直微信小程序开发交流社区
*
* github地址: https://github.com/icindy/wxParse
*
* for: 微信小程序富文本解析
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
*/
function
getDefaultOpts
(
simple
)
{
'
use strict
'
;
var
defaultOptions
=
{
omitExtraWLInCodeBlocks
:
{
defaultValue
:
false
,
describe
:
'
Omit the default extra whiteline added to code blocks
'
,
type
:
'
boolean
'
},
noHeaderId
:
{
defaultValue
:
false
,
describe
:
'
Turn on/off generated header id
'
,
type
:
'
boolean
'
},
prefixHeaderId
:
{
defaultValue
:
false
,
describe
:
'
Specify a prefix to generated header ids
'
,
type
:
'
string
'
},
headerLevelStart
:
{
defaultValue
:
false
,
describe
:
'
The header blocks level start
'
,
type
:
'
integer
'
},
parseImgDimensions
:
{
defaultValue
:
false
,
describe
:
'
Turn on/off image dimension parsing
'
,
type
:
'
boolean
'
},
simplifiedAutoLink
:
{
defaultValue
:
false
,
describe
:
'
Turn on/off GFM autolink style
'
,
type
:
'
boolean
'
},
literalMidWordUnderscores
:
{
defaultValue
:
false
,
describe
:
'
Parse midword underscores as literal underscores
'
,
type
:
'
boolean
'
},
strikethrough
:
{
defaultValue
:
false
,
describe
:
'
Turn on/off strikethrough support
'
,
type
:
'
boolean
'
},
tables
:
{
defaultValue
:
false
,
describe
:
'
Turn on/off tables support
'
,
type
:
'
boolean
'
},
tablesHeaderId
:
{
defaultValue
:
false
,
describe
:
'
Add an id to table headers
'
,
type
:
'
boolean
'
},
ghCodeBlocks
:
{
defaultValue
:
true
,
describe
:
'
Turn on/off GFM fenced code blocks support
'
,
type
:
'
boolean
'
},
tasklists
:
{
defaultValue
:
false
,
describe
:
'
Turn on/off GFM tasklist support
'
,
type
:
'
boolean
'
},
smoothLivePreview
:
{
defaultValue
:
false
,
describe
:
'
Prevents weird effects in live previews due to incomplete input
'
,
type
:
'
boolean
'
},
smartIndentationFix
:
{
defaultValue
:
false
,
description
:
'
Tries to smartly fix identation in es6 strings
'
,
type
:
'
boolean
'
}
};
if
(
simple
===
false
)
{
return
JSON
.
parse
(
JSON
.
stringify
(
defaultOptions
));
}
var
ret
=
{};
for
(
var
opt
in
defaultOptions
)
{
if
(
defaultOptions
.
hasOwnProperty
(
opt
))
{
ret
[
opt
]
=
defaultOptions
[
opt
].
defaultValue
;
}
}
return
ret
;
}
/**
* Created by Tivie on 06-01-2015.
*/
// Private properties
var
showdown
=
{},
parsers
=
{},
extensions
=
{},
globalOptions
=
getDefaultOpts
(
true
),
flavor
=
{
github
:
{
omitExtraWLInCodeBlocks
:
true
,
prefixHeaderId
:
'
user-content-
'
,
simplifiedAutoLink
:
true
,
literalMidWordUnderscores
:
true
,
strikethrough
:
true
,
tables
:
true
,
tablesHeaderId
:
true
,
ghCodeBlocks
:
true
,
tasklists
:
true
},
vanilla
:
getDefaultOpts
(
true
)
};
/**
* helper namespace
* @type {{}}
*/
showdown
.
helper
=
{};
/**
* TODO LEGACY SUPPORT CODE
* @type {{}}
*/
showdown
.
extensions
=
{};
/**
* Set a global option
* @static
* @param {string} key
* @param {*} value
* @returns {showdown}
*/
showdown
.
setOption
=
function
(
key
,
value
)
{
'
use strict
'
;
globalOptions
[
key
]
=
value
;
return
this
;
};
/**
* Get a global option
* @static
* @param {string} key
* @returns {*}
*/
showdown
.
getOption
=
function
(
key
)
{
'
use strict
'
;
return
globalOptions
[
key
];
};
/**
* Get the global options
* @static
* @returns {{}}
*/
showdown
.
getOptions
=
function
()
{
'
use strict
'
;
return
globalOptions
;
};
/**
* Reset global options to the default values
* @static
*/
showdown
.
resetOptions
=
function
()
{
'
use strict
'
;
globalOptions
=
getDefaultOpts
(
true
);
};
/**
* Set the flavor showdown should use as default
* @param {string} name
*/
showdown
.
setFlavor
=
function
(
name
)
{
'
use strict
'
;
if
(
flavor
.
hasOwnProperty
(
name
))
{
var
preset
=
flavor
[
name
];
for
(
var
option
in
preset
)
{
if
(
preset
.
hasOwnProperty
(
option
))
{
globalOptions
[
option
]
=
preset
[
option
];
}
}
}
};
/**
* Get the default options
* @static
* @param {boolean} [simple=true]
* @returns {{}}
*/
showdown
.
getDefaultOptions
=
function
(
simple
)
{
'
use strict
'
;
return
getDefaultOpts
(
simple
);
};
/**
* Get or set a subParser
*
* subParser(name) - Get a registered subParser
* subParser(name, func) - Register a subParser
* @static
* @param {string} name
* @param {function} [func]
* @returns {*}
*/
showdown
.
subParser
=
function
(
name
,
func
)
{
'
use strict
'
;
if
(
showdown
.
helper
.
isString
(
name
))
{
if
(
typeof
func
!==
'
undefined
'
)
{
parsers
[
name
]
=
func
;
}
else
{
if
(
parsers
.
hasOwnProperty
(
name
))
{
return
parsers
[
name
];
}
else
{
throw
Error
(
'
SubParser named
'
+
name
+
'
not registered!
'
);
}
}
}
};
/**
* Gets or registers an extension
* @static
* @param {string} name
* @param {object|function=} ext
* @returns {*}
*/
showdown
.
extension
=
function
(
name
,
ext
)
{
'
use strict
'
;
if
(
!
showdown
.
helper
.
isString
(
name
))
{
throw
Error
(
'
Extension
\'
name
\'
must be a string
'
);
}
name
=
showdown
.
helper
.
stdExtName
(
name
);
// Getter
if
(
showdown
.
helper
.
isUndefined
(
ext
))
{
if
(
!
extensions
.
hasOwnProperty
(
name
))
{
throw
Error
(
'
Extension named
'
+
name
+
'
is not registered!
'
);
}
return
extensions
[
name
];
// Setter
}
else
{
// Expand extension if it's wrapped in a function
if
(
typeof
ext
===
'
function
'
)
{
ext
=
ext
();
}
// Ensure extension is an array
if
(
!
showdown
.
helper
.
isArray
(
ext
))
{
ext
=
[
ext
];
}
var
validExtension
=
validate
(
ext
,
name
);
if
(
validExtension
.
valid
)
{
extensions
[
name
]
=
ext
;
}
else
{
throw
Error
(
validExtension
.
error
);
}
}
};
/**
* Gets all extensions registered
* @returns {{}}
*/
showdown
.
getAllExtensions
=
function
()
{
'
use strict
'
;
return
extensions
;
};
/**
* Remove an extension
* @param {string} name
*/
showdown
.
removeExtension
=
function
(
name
)
{
'
use strict
'
;
delete
extensions
[
name
];
};
/**
* Removes all extensions
*/
showdown
.
resetExtensions
=
function
()
{
'
use strict
'
;
extensions
=
{};
};
/**
* Validate extension
* @param {array} extension
* @param {string} name
* @returns {{valid: boolean, error: string}}
*/
function
validate
(
extension
,
name
)
{
'
use strict
'
;
var
errMsg
=
(
name
)
?
'
Error in
'
+
name
+
'
extension->
'
:
'
Error in unnamed extension
'
,
ret
=
{
valid
:
true
,
error
:
''
};
if
(
!
showdown
.
helper
.
isArray
(
extension
))
{
extension
=
[
extension
];
}
for
(
var
i
=
0
;
i
<
extension
.
length
;
++
i
)
{
var
baseMsg
=
errMsg
+
'
sub-extension
'
+
i
+
'
:
'
,
ext
=
extension
[
i
];
if
(
typeof
ext
!==
'
object
'
)
{
ret
.
valid
=
false
;
ret
.
error
=
baseMsg
+
'
must be an object, but
'
+
typeof
ext
+
'
given
'
;
return
ret
;
}
if
(
!
showdown
.
helper
.
isString
(
ext
.
type
))
{
ret
.
valid
=
false
;
ret
.
error
=
baseMsg
+
'
property "type" must be a string, but
'
+
typeof
ext
.
type
+
'
given
'
;
return
ret
;
}
var
type
=
ext
.
type
=
ext
.
type
.
toLowerCase
();
// normalize extension type
if
(
type
===
'
language
'
)
{
type
=
ext
.
type
=
'
lang
'
;
}
if
(
type
===
'
html
'
)
{
type
=
ext
.
type
=
'
output
'
;
}
if
(
type
!==
'
lang
'
&&
type
!==
'
output
'
&&
type
!==
'
listener
'
)
{
ret
.
valid
=
false
;
ret
.
error
=
baseMsg
+
'
type
'
+
type
+
'
is not recognized. Valid values: "lang/language", "output/html" or "listener"
'
;
return
ret
;
}
if
(
type
===
'
listener
'
)
{
if
(
showdown
.
helper
.
isUndefined
(
ext
.
listeners
))
{
ret
.
valid
=
false
;
ret
.
error
=
baseMsg
+
'
. Extensions of type "listener" must have a property called "listeners"
'
;
return
ret
;
}
}
else
{
if
(
showdown
.
helper
.
isUndefined
(
ext
.
filter
)
&&
showdown
.
helper
.
isUndefined
(
ext
.
regex
))
{
ret
.
valid
=
false
;
ret
.
error
=
baseMsg
+
type
+
'
extensions must define either a "regex" property or a "filter" method
'
;
return
ret
;
}
}
if
(
ext
.
listeners
)
{
if
(
typeof
ext
.
listeners
!==
'
object
'
)
{
ret
.
valid
=
false
;
ret
.
error
=
baseMsg
+
'
"listeners" property must be an object but
'
+
typeof
ext
.
listeners
+
'
given
'
;
return
ret
;
}
for
(
var
ln
in
ext
.
listeners
)
{
if
(
ext
.
listeners
.
hasOwnProperty
(
ln
))
{
if
(
typeof
ext
.
listeners
[
ln
]
!==
'
function
'
)
{
ret
.
valid
=
false
;
ret
.
error
=
baseMsg
+
'
"listeners" property must be an hash of [event name]: [callback]. listeners.
'
+
ln
+
'
must be a function but
'
+
typeof
ext
.
listeners
[
ln
]
+
'
given
'
;
return
ret
;
}
}
}
}
if
(
ext
.
filter
)
{
if
(
typeof
ext
.
filter
!==
'
function
'
)
{
ret
.
valid
=
false
;
ret
.
error
=
baseMsg
+
'
"filter" must be a function, but
'
+
typeof
ext
.
filter
+
'
given
'
;
return
ret
;
}
}
else
if
(
ext
.
regex
)
{
if
(
showdown
.
helper
.
isString
(
ext
.
regex
))
{
ext
.
regex
=
new
RegExp
(
ext
.
regex
,
'
g
'
);
}
if
(
!
ext
.
regex
instanceof
RegExp
)
{
ret
.
valid
=
false
;
ret
.
error
=
baseMsg
+
'
"regex" property must either be a string or a RegExp object, but
'
+
typeof
ext
.
regex
+
'
given
'
;
return
ret
;
}
if
(
showdown
.
helper
.
isUndefined
(
ext
.
replace
))
{
ret
.
valid
=
false
;
ret
.
error
=
baseMsg
+
'
"regex" extensions must implement a replace string or function
'
;
return
ret
;
}
}
}
return
ret
;
}
/**
* Validate extension
* @param {object} ext
* @returns {boolean}
*/
showdown
.
validateExtension
=
function
(
ext
)
{
'
use strict
'
;
var
validateExtension
=
validate
(
ext
,
null
);
if
(
!
validateExtension
.
valid
)
{
console
.
warn
(
validateExtension
.
error
);
return
false
;
}
return
true
;
};
/**
* showdownjs helper functions
*/
if
(
!
showdown
.
hasOwnProperty
(
'
helper
'
))
{
showdown
.
helper
=
{};
}
/**
* Check if var is string
* @static
* @param {string} a
* @returns {boolean}
*/
showdown
.
helper
.
isString
=
function
isString
(
a
)
{
'
use strict
'
;
return
(
typeof
a
===
'
string
'
||
a
instanceof
String
);
};
/**
* Check if var is a function
* @static
* @param {string} a
* @returns {boolean}
*/
showdown
.
helper
.
isFunction
=
function
isFunction
(
a
)
{
'
use strict
'
;
var
getType
=
{};
return
a
&&
getType
.
toString
.
call
(
a
)
===
'
[object Function]
'
;
};
/**
* ForEach helper function
* @static
* @param {*} obj
* @param {function} callback
*/
showdown
.
helper
.
forEach
=
function
forEach
(
obj
,
callback
)
{
'
use strict
'
;
if
(
typeof
obj
.
forEach
===
'
function
'
)
{
obj
.
forEach
(
callback
);
}
else
{
for
(
var
i
=
0
;
i
<
obj
.
length
;
i
++
)
{
callback
(
obj
[
i
],
i
,
obj
);
}
}
};
/**
* isArray helper function
* @static
* @param {*} a
* @returns {boolean}
*/
showdown
.
helper
.
isArray
=
function
isArray
(
a
)
{
'
use strict
'
;
return
a
.
constructor
===
Array
;
};
/**
* Check if value is undefined
* @static
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
*/
showdown
.
helper
.
isUndefined
=
function
isUndefined
(
value
)
{
'
use strict
'
;
return
typeof
value
===
'
undefined
'
;
};
/**
* Standardidize extension name
* @static
* @param {string} s extension name
* @returns {string}
*/
showdown
.
helper
.
stdExtName
=
function
(
s
)
{
'
use strict
'
;
return
s
.
replace
(
/
[
_-
]
||
\s
/g
,
''
).
toLowerCase
();
};
function
escapeCharactersCallback
(
wholeMatch
,
m1
)
{
'
use strict
'
;
var
charCodeToEscape
=
m1
.
charCodeAt
(
0
);
return
'
~E
'
+
charCodeToEscape
+
'
E
'
;
}
/**
* Callback used to escape characters when passing through String.replace
* @static
* @param {string} wholeMatch
* @param {string} m1
* @returns {string}
*/
showdown
.
helper
.
escapeCharactersCallback
=
escapeCharactersCallback
;
/**
* Escape characters in a string
* @static
* @param {string} text
* @param {string} charsToEscape
* @param {boolean} afterBackslash
* @returns {XML|string|void|*}
*/
showdown
.
helper
.
escapeCharacters
=
function
escapeCharacters
(
text
,
charsToEscape
,
afterBackslash
)
{
'
use strict
'
;
// First we have to escape the escape characters so that
// we can build a character class out of them
var
regexString
=
'
([
'
+
charsToEscape
.
replace
(
/
([\[\]\\])
/g
,
'
\\
$1
'
)
+
'
])
'
;
if
(
afterBackslash
)
{
regexString
=
'
\\\\
'
+
regexString
;
}
var
regex
=
new
RegExp
(
regexString
,
'
g
'
);
text
=
text
.
replace
(
regex
,
escapeCharactersCallback
);
return
text
;
};
var
rgxFindMatchPos
=
function
(
str
,
left
,
right
,
flags
)
{
'
use strict
'
;
var
f
=
flags
||
''
,
g
=
f
.
indexOf
(
'
g
'
)
>
-
1
,
x
=
new
RegExp
(
left
+
'
|
'
+
right
,
'
g
'
+
f
.
replace
(
/g/g
,
''
)),
l
=
new
RegExp
(
left
,
f
.
replace
(
/g/g
,
''
)),
pos
=
[],
t
,
s
,
m
,
start
,
end
;
do
{
t
=
0
;
while
((
m
=
x
.
exec
(
str
)))
{
if
(
l
.
test
(
m
[
0
]))
{
if
(
!
(
t
++
))
{
s
=
x
.
lastIndex
;
start
=
s
-
m
[
0
].
length
;
}
}
else
if
(
t
)
{
if
(
!--
t
)
{
end
=
m
.
index
+
m
[
0
].
length
;
var
obj
=
{
left
:
{
start
:
start
,
end
:
s
},
match
:
{
start
:
s
,
end
:
m
.
index
},
right
:
{
start
:
m
.
index
,
end
:
end
},
wholeMatch
:
{
start
:
start
,
end
:
end
}
};
pos
.
push
(
obj
);
if
(
!
g
)
{
return
pos
;
}
}
}
}
}
while
(
t
&&
(
x
.
lastIndex
=
s
));
return
pos
;
};
/**
* matchRecursiveRegExp
*
* (c) 2007 Steven Levithan <stevenlevithan.com>
* MIT License
*
* Accepts a string to search, a left and right format delimiter
* as regex patterns, and optional regex flags. Returns an array
* of matches, allowing nested instances of left/right delimiters.
* Use the "g" flag to return all matches, otherwise only the
* first is returned. Be careful to ensure that the left and
* right format delimiters produce mutually exclusive matches.
* Backreferences are not supported within the right delimiter
* due to how it is internally combined with the left delimiter.
* When matching strings whose format delimiters are unbalanced
* to the left or right, the output is intentionally as a
* conventional regex library with recursion support would
* produce, e.g. "<<x>" and "<x>>" both produce ["x"] when using
* "<" and ">" as the delimiters (both strings contain a single,
* balanced instance of "<x>").
*
* examples:
* matchRecursiveRegExp("test", "\\(", "\\)")
* returns: []
* matchRecursiveRegExp("<t<<e>><s>>t<>", "<", ">", "g")
* returns: ["t<<e>><s>", ""]
* matchRecursiveRegExp("<div id=\"x\">test</div>", "<div\\b[^>]*>", "</div>", "gi")
* returns: ["test"]
*/
showdown
.
helper
.
matchRecursiveRegExp
=
function
(
str
,
left
,
right
,
flags
)
{
'
use strict
'
;
var
matchPos
=
rgxFindMatchPos
(
str
,
left
,
right
,
flags
),
results
=
[];
for
(
var
i
=
0
;
i
<
matchPos
.
length
;
++
i
)
{
results
.
push
([
str
.
slice
(
matchPos
[
i
].
wholeMatch
.
start
,
matchPos
[
i
].
wholeMatch
.
end
),
str
.
slice
(
matchPos
[
i
].
match
.
start
,
matchPos
[
i
].
match
.
end
),
str
.
slice
(
matchPos
[
i
].
left
.
start
,
matchPos
[
i
].
left
.
end
),
str
.
slice
(
matchPos
[
i
].
right
.
start
,
matchPos
[
i
].
right
.
end
)
]);
}
return
results
;
};
/**
*
* @param {string} str
* @param {string|function} replacement
* @param {string} left
* @param {string} right
* @param {string} flags
* @returns {string}
*/
showdown
.
helper
.
replaceRecursiveRegExp
=
function
(
str
,
replacement
,
left
,
right
,
flags
)
{
'
use strict
'
;
if
(
!
showdown
.
helper
.
isFunction
(
replacement
))
{
var
repStr
=
replacement
;
replacement
=
function
()
{
return
repStr
;
};
}
var
matchPos
=
rgxFindMatchPos
(
str
,
left
,
right
,
flags
),
finalStr
=
str
,
lng
=
matchPos
.
length
;
if
(
lng
>
0
)
{
var
bits
=
[];
if
(
matchPos
[
0
].
wholeMatch
.
start
!==
0
)
{
bits
.
push
(
str
.
slice
(
0
,
matchPos
[
0
].
wholeMatch
.
start
));
}
for
(
var
i
=
0
;
i
<
lng
;
++
i
)
{
bits
.
push
(
replacement
(
str
.
slice
(
matchPos
[
i
].
wholeMatch
.
start
,
matchPos
[
i
].
wholeMatch
.
end
),
str
.
slice
(
matchPos
[
i
].
match
.
start
,
matchPos
[
i
].
match
.
end
),
str
.
slice
(
matchPos
[
i
].
left
.
start
,
matchPos
[
i
].
left
.
end
),
str
.
slice
(
matchPos
[
i
].
right
.
start
,
matchPos
[
i
].
right
.
end
)
)
);
if
(
i
<
lng
-
1
)
{
bits
.
push
(
str
.
slice
(
matchPos
[
i
].
wholeMatch
.
end
,
matchPos
[
i
+
1
].
wholeMatch
.
start
));
}
}
if
(
matchPos
[
lng
-
1
].
wholeMatch
.
end
<
str
.
length
)
{
bits
.
push
(
str
.
slice
(
matchPos
[
lng
-
1
].
wholeMatch
.
end
));
}
finalStr
=
bits
.
join
(
''
);
}
return
finalStr
;
};
/**
* POLYFILLS
*/
if
(
showdown
.
helper
.
isUndefined
(
console
))
{
console
=
{
warn
:
function
(
msg
)
{
'
use strict
'
;
alert
(
msg
);
},
log
:
function
(
msg
)
{
'
use strict
'
;
alert
(
msg
);
},
error
:
function
(
msg
)
{
'
use strict
'
;
throw
msg
;
}
};
}
/**
* Created by Estevao on 31-05-2015.
*/
/**
* Showdown Converter class
* @class
* @param {object} [converterOptions]
* @returns {Converter}
*/
showdown
.
Converter
=
function
(
converterOptions
)
{
'
use strict
'
;
var
/**
* Options used by this converter
* @private
* @type {{}}
*/
options
=
{},
/**
* Language extensions used by this converter
* @private
* @type {Array}
*/
langExtensions
=
[],
/**
* Output modifiers extensions used by this converter
* @private
* @type {Array}
*/
outputModifiers
=
[],
/**
* Event listeners
* @private
* @type {{}}
*/
listeners
=
{};
_constructor
();
/**
* Converter constructor
* @private
*/
function
_constructor
()
{
converterOptions
=
converterOptions
||
{};
for
(
var
gOpt
in
globalOptions
)
{
if
(
globalOptions
.
hasOwnProperty
(
gOpt
))
{
options
[
gOpt
]
=
globalOptions
[
gOpt
];
}
}
// Merge options
if
(
typeof
converterOptions
===
'
object
'
)
{
for
(
var
opt
in
converterOptions
)
{
if
(
converterOptions
.
hasOwnProperty
(
opt
))
{
options
[
opt
]
=
converterOptions
[
opt
];
}
}
}
else
{
throw
Error
(
'
Converter expects the passed parameter to be an object, but
'
+
typeof
converterOptions
+
'
was passed instead.
'
);
}
if
(
options
.
extensions
)
{
showdown
.
helper
.
forEach
(
options
.
extensions
,
_parseExtension
);
}
}
/**
* Parse extension
* @param {*} ext
* @param {string} [name='']
* @private
*/
function
_parseExtension
(
ext
,
name
)
{
name
=
name
||
null
;
// If it's a string, the extension was previously loaded
if
(
showdown
.
helper
.
isString
(
ext
))
{
ext
=
showdown
.
helper
.
stdExtName
(
ext
);
name
=
ext
;
// LEGACY_SUPPORT CODE
if
(
showdown
.
extensions
[
ext
])
{
console
.
warn
(
'
DEPRECATION WARNING:
'
+
ext
+
'
is an old extension that uses a deprecated loading method.
'
+
'
Please inform the developer that the extension should be updated!
'
);
legacyExtensionLoading
(
showdown
.
extensions
[
ext
],
ext
);
return
;
// END LEGACY SUPPORT CODE
}
else
if
(
!
showdown
.
helper
.
isUndefined
(
extensions
[
ext
]))
{
ext
=
extensions
[
ext
];
}
else
{
throw
Error
(
'
Extension "
'
+
ext
+
'
" could not be loaded. It was either not found or is not a valid extension.
'
);
}
}
if
(
typeof
ext
===
'
function
'
)
{
ext
=
ext
();
}
if
(
!
showdown
.
helper
.
isArray
(
ext
))
{
ext
=
[
ext
];
}
var
validExt
=
validate
(
ext
,
name
);
if
(
!
validExt
.
valid
)
{
throw
Error
(
validExt
.
error
);
}
for
(
var
i
=
0
;
i
<
ext
.
length
;
++
i
)
{
switch
(
ext
[
i
].
type
)
{
case
'
lang
'
:
langExtensions
.
push
(
ext
[
i
]);
break
;
case
'
output
'
:
outputModifiers
.
push
(
ext
[
i
]);
break
;
}
if
(
ext
[
i
].
hasOwnProperty
(
listeners
))
{
for
(
var
ln
in
ext
[
i
].
listeners
)
{
if
(
ext
[
i
].
listeners
.
hasOwnProperty
(
ln
))
{
listen
(
ln
,
ext
[
i
].
listeners
[
ln
]);
}
}
}
}
}
/**
* LEGACY_SUPPORT
* @param {*} ext
* @param {string} name
*/
function
legacyExtensionLoading
(
ext
,
name
)
{
if
(
typeof
ext
===
'
function
'
)
{
ext
=
ext
(
new
showdown
.
Converter
());
}
if
(
!
showdown
.
helper
.
isArray
(
ext
))
{
ext
=
[
ext
];
}
var
valid
=
validate
(
ext
,
name
);
if
(
!
valid
.
valid
)
{
throw
Error
(
valid
.
error
);
}
for
(
var
i
=
0
;
i
<
ext
.
length
;
++
i
)
{
switch
(
ext
[
i
].
type
)
{
case
'
lang
'
:
langExtensions
.
push
(
ext
[
i
]);
break
;
case
'
output
'
:
outputModifiers
.
push
(
ext
[
i
]);
break
;
default
:
// should never reach here
throw
Error
(
'
Extension loader error: Type unrecognized!!!
'
);
}
}
}
/**
* Listen to an event
* @param {string} name
* @param {function} callback
*/
function
listen
(
name
,
callback
)
{
if
(
!
showdown
.
helper
.
isString
(
name
))
{
throw
Error
(
'
Invalid argument in converter.listen() method: name must be a string, but
'
+
typeof
name
+
'
given
'
);
}
if
(
typeof
callback
!==
'
function
'
)
{
throw
Error
(
'
Invalid argument in converter.listen() method: callback must be a function, but
'
+
typeof
callback
+
'
given
'
);
}
if
(
!
listeners
.
hasOwnProperty
(
name
))
{
listeners
[
name
]
=
[];
}
listeners
[
name
].
push
(
callback
);
}
function
rTrimInputText
(
text
)
{
var
rsp
=
text
.
match
(
/^
\s
*/
)[
0
].
length
,
rgx
=
new
RegExp
(
'
^
\\
s{0,
'
+
rsp
+
'
}
'
,
'
gm
'
);
return
text
.
replace
(
rgx
,
''
);
}
/**
* Dispatch an event
* @private
* @param {string} evtName Event name
* @param {string} text Text
* @param {{}} options Converter Options
* @param {{}} globals
* @returns {string}
*/
this
.
_dispatch
=
function
dispatch
(
evtName
,
text
,
options
,
globals
)
{
if
(
listeners
.
hasOwnProperty
(
evtName
))
{
for
(
var
ei
=
0
;
ei
<
listeners
[
evtName
].
length
;
++
ei
)
{
var
nText
=
listeners
[
evtName
][
ei
](
evtName
,
text
,
this
,
options
,
globals
);
if
(
nText
&&
typeof
nText
!==
'
undefined
'
)
{
text
=
nText
;
}
}
}
return
text
;
};
/**
* Listen to an event
* @param {string} name
* @param {function} callback
* @returns {showdown.Converter}
*/
this
.
listen
=
function
(
name
,
callback
)
{
listen
(
name
,
callback
);
return
this
;
};
/**
* Converts a markdown string into HTML
* @param {string} text
* @returns {*}
*/
this
.
makeHtml
=
function
(
text
)
{
//check if text is not falsy
if
(
!
text
)
{
return
text
;
}
var
globals
=
{
gHtmlBlocks
:
[],
gHtmlMdBlocks
:
[],
gHtmlSpans
:
[],
gUrls
:
{},
gTitles
:
{},
gDimensions
:
{},
gListLevel
:
0
,
hashLinkCounts
:
{},
langExtensions
:
langExtensions
,
outputModifiers
:
outputModifiers
,
converter
:
this
,
ghCodeBlocks
:
[]
};
// attacklab: Replace ~ with ~T
// This lets us use tilde as an escape char to avoid md5 hashes
// The choice of character is arbitrary; anything that isn't
// magic in Markdown will work.
text
=
text
.
replace
(
/~/g
,
'
~T
'
);
// attacklab: Replace $ with ~D
// RegExp interprets $ as a special character
// when it's in a replacement string
text
=
text
.
replace
(
/
\$
/g
,
'
~D
'
);
// Standardize line endings
text
=
text
.
replace
(
/
\r\n
/g
,
'
\n
'
);
// DOS to Unix
text
=
text
.
replace
(
/
\r
/g
,
'
\n
'
);
// Mac to Unix
if
(
options
.
smartIndentationFix
)
{
text
=
rTrimInputText
(
text
);
}
// Make sure text begins and ends with a couple of newlines:
//text = '\n\n' + text + '\n\n';
text
=
text
;
// detab
text
=
showdown
.
subParser
(
'
detab
'
)(
text
,
options
,
globals
);
// stripBlankLines
text
=
showdown
.
subParser
(
'
stripBlankLines
'
)(
text
,
options
,
globals
);
//run languageExtensions
showdown
.
helper
.
forEach
(
langExtensions
,
function
(
ext
)
{
text
=
showdown
.
subParser
(
'
runExtension
'
)(
ext
,
text
,
options
,
globals
);
});
// run the sub parsers
text
=
showdown
.
subParser
(
'
hashPreCodeTags
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
githubCodeBlocks
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
hashHTMLBlocks
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
hashHTMLSpans
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
stripLinkDefinitions
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
blockGamut
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
unhashHTMLSpans
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
unescapeSpecialChars
'
)(
text
,
options
,
globals
);
// attacklab: Restore dollar signs
text
=
text
.
replace
(
/~D/g
,
'
$$
'
);
// attacklab: Restore tildes
text
=
text
.
replace
(
/~T/g
,
'
~
'
);
// Run output modifiers
showdown
.
helper
.
forEach
(
outputModifiers
,
function
(
ext
)
{
text
=
showdown
.
subParser
(
'
runExtension
'
)(
ext
,
text
,
options
,
globals
);
});
return
text
;
};
/**
* Set an option of this Converter instance
* @param {string} key
* @param {*} value
*/
this
.
setOption
=
function
(
key
,
value
)
{
options
[
key
]
=
value
;
};
/**
* Get the option of this Converter instance
* @param {string} key
* @returns {*}
*/
this
.
getOption
=
function
(
key
)
{
return
options
[
key
];
};
/**
* Get the options of this Converter instance
* @returns {{}}
*/
this
.
getOptions
=
function
()
{
return
options
;
};
/**
* Add extension to THIS converter
* @param {{}} extension
* @param {string} [name=null]
*/
this
.
addExtension
=
function
(
extension
,
name
)
{
name
=
name
||
null
;
_parseExtension
(
extension
,
name
);
};
/**
* Use a global registered extension with THIS converter
* @param {string} extensionName Name of the previously registered extension
*/
this
.
useExtension
=
function
(
extensionName
)
{
_parseExtension
(
extensionName
);
};
/**
* Set the flavor THIS converter should use
* @param {string} name
*/
this
.
setFlavor
=
function
(
name
)
{
if
(
flavor
.
hasOwnProperty
(
name
))
{
var
preset
=
flavor
[
name
];
for
(
var
option
in
preset
)
{
if
(
preset
.
hasOwnProperty
(
option
))
{
options
[
option
]
=
preset
[
option
];
}
}
}
};
/**
* Remove an extension from THIS converter.
* Note: This is a costly operation. It's better to initialize a new converter
* and specify the extensions you wish to use
* @param {Array} extension
*/
this
.
removeExtension
=
function
(
extension
)
{
if
(
!
showdown
.
helper
.
isArray
(
extension
))
{
extension
=
[
extension
];
}
for
(
var
a
=
0
;
a
<
extension
.
length
;
++
a
)
{
var
ext
=
extension
[
a
];
for
(
var
i
=
0
;
i
<
langExtensions
.
length
;
++
i
)
{
if
(
langExtensions
[
i
]
===
ext
)
{
langExtensions
[
i
].
splice
(
i
,
1
);
}
}
for
(
var
ii
=
0
;
ii
<
outputModifiers
.
length
;
++
i
)
{
if
(
outputModifiers
[
ii
]
===
ext
)
{
outputModifiers
[
ii
].
splice
(
i
,
1
);
}
}
}
};
/**
* Get all extension of THIS converter
* @returns {{language: Array, output: Array}}
*/
this
.
getAllExtensions
=
function
()
{
return
{
language
:
langExtensions
,
output
:
outputModifiers
};
};
};
/**
* Turn Markdown link shortcuts into XHTML <a> tags.
*/
showdown
.
subParser
(
'
anchors
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
anchors.before
'
,
text
,
options
,
globals
);
var
writeAnchorTag
=
function
(
wholeMatch
,
m1
,
m2
,
m3
,
m4
,
m5
,
m6
,
m7
)
{
if
(
showdown
.
helper
.
isUndefined
(
m7
))
{
m7
=
''
;
}
wholeMatch
=
m1
;
var
linkText
=
m2
,
linkId
=
m3
.
toLowerCase
(),
url
=
m4
,
title
=
m7
;
if
(
!
url
)
{
if
(
!
linkId
)
{
// lower-case and turn embedded newlines into spaces
linkId
=
linkText
.
toLowerCase
().
replace
(
/
?\n
/g
,
'
'
);
}
url
=
'
#
'
+
linkId
;
if
(
!
showdown
.
helper
.
isUndefined
(
globals
.
gUrls
[
linkId
]))
{
url
=
globals
.
gUrls
[
linkId
];
if
(
!
showdown
.
helper
.
isUndefined
(
globals
.
gTitles
[
linkId
]))
{
title
=
globals
.
gTitles
[
linkId
];
}
}
else
{
if
(
wholeMatch
.
search
(
/
\(\s
*
\)
$/m
)
>
-
1
)
{
// Special case for explicit empty url
url
=
''
;
}
else
{
return
wholeMatch
;
}
}
}
url
=
showdown
.
helper
.
escapeCharacters
(
url
,
'
*_
'
,
false
);
var
result
=
'
<a href="
'
+
url
+
'
"
'
;
if
(
title
!==
''
&&
title
!==
null
)
{
title
=
title
.
replace
(
/"/g
,
'
"
'
);
title
=
showdown
.
helper
.
escapeCharacters
(
title
,
'
*_
'
,
false
);
result
+=
'
title="
'
+
title
+
'
"
'
;
}
result
+=
'
>
'
+
linkText
+
'
</a>
'
;
return
result
;
};
// First, handle reference-style links: [link text] [id]
/*
text = text.replace(/
( // wrap whole match in $1
\[
(
(?:
\[[^\]]*\] // allow brackets nested one level
|
[^\[] // or anything else
)*
)
\]
[ ]? // one optional space
(?:\n[ ]*)? // one optional newline followed by spaces
\[
(.*?) // id = $3
\]
)()()()() // pad remaining backreferences
/g,_DoAnchors_callback);
*/
text
=
text
.
replace
(
/
(\[((?:\[[^\]]
*]|
[^\[\]])
*
)
]
[
]?(?:\n[
]
*
)?\[(
.*
?)
]
)()()()()
/g
,
writeAnchorTag
);
//
// Next, inline-style links: [link text](url "optional title")
//
/*
text = text.replace(/
( // wrap whole match in $1
\[
(
(?:
\[[^\]]*\] // allow brackets nested one level
|
[^\[\]] // or anything else
)
)
\]
\( // literal paren
[ \t]*
() // no id, so leave $3 empty
<?(.*?)>? // href = $4
[ \t]*
( // $5
(['"]) // quote char = $6
(.*?) // Title = $7
\6 // matching quote
[ \t]* // ignore any spaces/tabs between closing quote and )
)? // title is optional
\)
)
/g,writeAnchorTag);
*/
text
=
text
.
replace
(
/
(\[((?:\[[^\]]
*]|
[^\[\]])
*
)
]
\([
\t]
*
()
<
?(
.*
?(?:\(
.*
?\)
.*
?)?)
>
?[
\t]
*
(([
'"
])(
.*
?)\6[
\t]
*
)?\))
/g
,
writeAnchorTag
);
//
// Last, handle reference-style shortcuts: [link text]
// These must come last in case you've also got [link test][1]
// or [link test](/foo)
//
/*
text = text.replace(/
( // wrap whole match in $1
\[
([^\[\]]+) // link text = $2; can't contain '[' or ']'
\]
)()()()()() // pad rest of backreferences
/g, writeAnchorTag);
*/
text
=
text
.
replace
(
/
(\[([^\[\]]
+
)
]
)()()()()()
/g
,
writeAnchorTag
);
text
=
globals
.
converter
.
_dispatch
(
'
anchors.after
'
,
text
,
options
,
globals
);
return
text
;
});
showdown
.
subParser
(
'
autoLinks
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
autoLinks.before
'
,
text
,
options
,
globals
);
var
simpleURLRegex
=
/
\b(((
https
?
|ftp|dict
)
:
\/\/
|www
\.)[^
'">
\s]
+
\.[^
'">
\s]
+
)(?=\s
|$
)(?![
"<>
])
/gi
,
delimUrlRegex
=
/<
(((
https
?
|ftp|dict
)
:
\/\/
|www
\.)[^
'">
\s]
+
)
>/gi
,
simpleMailRegex
=
/
(?:
^|
[
\n\t])([
A-Za-z0-9!#$%&'*+-
/
=?^_`
\{
|}~
\.]
+@
[
-a-z0-9
]
+
(\.[
-a-z0-9
]
+
)
*
\.[
a-z
]
+
)(?:
$|
[
\n\t])
/gi
,
delimMailRegex
=
/<
(?:
mailto:
)?([
-.
\w]
+@
[
-a-z0-9
]
+
(\.[
-a-z0-9
]
+
)
*
\.[
a-z
]
+
)
>/gi
;
text
=
text
.
replace
(
delimUrlRegex
,
replaceLink
);
text
=
text
.
replace
(
delimMailRegex
,
replaceMail
);
// simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi,
// Email addresses: <address@domain.foo>
if
(
options
.
simplifiedAutoLink
)
{
text
=
text
.
replace
(
simpleURLRegex
,
replaceLink
);
text
=
text
.
replace
(
simpleMailRegex
,
replaceMail
);
}
function
replaceLink
(
wm
,
link
)
{
var
lnkTxt
=
link
;
if
(
/^www
\.
/i
.
test
(
link
))
{
link
=
link
.
replace
(
/^www
\.
/i
,
'
http://www.
'
);
}
return
'
<a href="
'
+
link
+
'
">
'
+
lnkTxt
+
'
</a>
'
;
}
function
replaceMail
(
wholeMatch
,
m1
)
{
var
unescapedStr
=
showdown
.
subParser
(
'
unescapeSpecialChars
'
)(
m1
);
return
showdown
.
subParser
(
'
encodeEmailAddress
'
)(
unescapedStr
);
}
text
=
globals
.
converter
.
_dispatch
(
'
autoLinks.after
'
,
text
,
options
,
globals
);
return
text
;
});
/**
* These are all the transformations that form block-level
* tags like paragraphs, headers, and list items.
*/
showdown
.
subParser
(
'
blockGamut
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
blockGamut.before
'
,
text
,
options
,
globals
);
// we parse blockquotes first so that we can have headings and hrs
// inside blockquotes
text
=
showdown
.
subParser
(
'
blockQuotes
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
headers
'
)(
text
,
options
,
globals
);
// Do Horizontal Rules:
var
key
=
showdown
.
subParser
(
'
hashBlock
'
)(
'
<hr />
'
,
options
,
globals
);
text
=
text
.
replace
(
/^
[
]{0,2}([
]?\*[
]?){3,}[
\t]
*$/gm
,
key
);
text
=
text
.
replace
(
/^
[
]{0,2}([
]?\-[
]?){3,}[
\t]
*$/gm
,
key
);
text
=
text
.
replace
(
/^
[
]{0,2}([
]?
_
[
]?){3,}[
\t]
*$/gm
,
key
);
text
=
showdown
.
subParser
(
'
lists
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
codeBlocks
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
tables
'
)(
text
,
options
,
globals
);
// We already ran _HashHTMLBlocks() before, in Markdown(), but that
// was to escape raw HTML in the original Markdown source. This time,
// we're escaping the markup we've just created, so that we don't wrap
// <p> tags around block-level tags.
text
=
showdown
.
subParser
(
'
hashHTMLBlocks
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
paragraphs
'
)(
text
,
options
,
globals
);
text
=
globals
.
converter
.
_dispatch
(
'
blockGamut.after
'
,
text
,
options
,
globals
);
return
text
;
});
showdown
.
subParser
(
'
blockQuotes
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
blockQuotes.before
'
,
text
,
options
,
globals
);
/*
text = text.replace(/
( // Wrap whole match in $1
(
^[ \t]*>[ \t]? // '>' at the start of a line
.+\n // rest of the first line
(.+\n)* // subsequent consecutive lines
\n* // blanks
)+
)
/gm, function(){...});
*/
text
=
text
.
replace
(
/
((
^
[
\t]{0,3}
>
[
\t]?
.+
\n(
.+
\n)
*
\n
*
)
+
)
/gm
,
function
(
wholeMatch
,
m1
)
{
var
bq
=
m1
;
// attacklab: hack around Konqueror 3.5.4 bug:
// "----------bug".replace(/^-/g,"") == "bug"
bq
=
bq
.
replace
(
/^
[
\t]
*>
[
\t]?
/gm
,
'
~0
'
);
// trim one level of quoting
// attacklab: clean up hack
bq
=
bq
.
replace
(
/~0/g
,
''
);
bq
=
bq
.
replace
(
/^
[
\t]
+$/gm
,
''
);
// trim whitespace-only lines
bq
=
showdown
.
subParser
(
'
githubCodeBlocks
'
)(
bq
,
options
,
globals
);
bq
=
showdown
.
subParser
(
'
blockGamut
'
)(
bq
,
options
,
globals
);
// recurse
bq
=
bq
.
replace
(
/
(
^|
\n)
/g
,
'
$1
'
);
// These leading spaces screw with <pre> content, so we need to fix that:
bq
=
bq
.
replace
(
/
(\s
*<pre>
[^\r]
+
?
<
\/
pre>
)
/gm
,
function
(
wholeMatch
,
m1
)
{
var
pre
=
m1
;
// attacklab: hack around Konqueror 3.5.4 bug:
pre
=
pre
.
replace
(
/^ /mg
,
'
~0
'
);
pre
=
pre
.
replace
(
/~0/g
,
''
);
return
pre
;
});
return
showdown
.
subParser
(
'
hashBlock
'
)(
'
<blockquote>
\n
'
+
bq
+
'
\n
</blockquote>
'
,
options
,
globals
);
});
text
=
globals
.
converter
.
_dispatch
(
'
blockQuotes.after
'
,
text
,
options
,
globals
);
return
text
;
});
/**
* Process Markdown `<pre><code>` blocks.
*/
showdown
.
subParser
(
'
codeBlocks
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
codeBlocks.before
'
,
text
,
options
,
globals
);
/*
text = text.replace(text,
/(?:\n\n|^)
( // $1 = the code block -- one or more lines, starting with a space/tab
(?:
(?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
.*\n+
)+
)
(\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
/g,function(){...});
*/
// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
text
+=
'
~0
'
;
var
pattern
=
/
(?:\n\n
|^
)((?:(?:[
]{4}
|
\t)
.*
\n
+
)
+
)(\n
*
[
]{0,3}[^
\t\n]
|
(?=
~0
))
/g
;
text
=
text
.
replace
(
pattern
,
function
(
wholeMatch
,
m1
,
m2
)
{
var
codeblock
=
m1
,
nextChar
=
m2
,
end
=
'
\n
'
;
codeblock
=
showdown
.
subParser
(
'
outdent
'
)(
codeblock
);
codeblock
=
showdown
.
subParser
(
'
encodeCode
'
)(
codeblock
);
codeblock
=
showdown
.
subParser
(
'
detab
'
)(
codeblock
);
codeblock
=
codeblock
.
replace
(
/^
\n
+/g
,
''
);
// trim leading newlines
codeblock
=
codeblock
.
replace
(
/
\n
+$/g
,
''
);
// trim trailing newlines
if
(
options
.
omitExtraWLInCodeBlocks
)
{
end
=
''
;
}
codeblock
=
'
<pre><code>
'
+
codeblock
+
end
+
'
</code></pre>
'
;
return
showdown
.
subParser
(
'
hashBlock
'
)(
codeblock
,
options
,
globals
)
+
nextChar
;
});
// attacklab: strip sentinel
text
=
text
.
replace
(
/~0/
,
''
);
text
=
globals
.
converter
.
_dispatch
(
'
codeBlocks.after
'
,
text
,
options
,
globals
);
return
text
;
});
/**
*
* * Backtick quotes are used for <code></code> spans.
*
* * You can use multiple backticks as the delimiters if you want to
* include literal backticks in the code span. So, this input:
*
* Just type ``foo `bar` baz`` at the prompt.
*
* Will translate to:
*
* <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
*
* There's no arbitrary limit to the number of backticks you
* can use as delimters. If you need three consecutive backticks
* in your code, use four for delimiters, etc.
*
* * You can use spaces to get literal backticks at the edges:
*
* ... type `` `bar` `` ...
*
* Turns to:
*
* ... type <code>`bar`</code> ...
*/
showdown
.
subParser
(
'
codeSpans
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
codeSpans.before
'
,
text
,
options
,
globals
);
/*
text = text.replace(/
(^|[^\\]) // Character before opening ` can't be a backslash
(`+) // $2 = Opening run of `
( // $3 = The code block
[^\r]*?
[^`] // attacklab: work around lack of lookbehind
)
\2 // Matching closer
(?!`)
/gm, function(){...});
*/
if
(
typeof
(
text
)
===
'
undefined
'
)
{
text
=
''
;
}
text
=
text
.
replace
(
/
(
^|
[^\\])(
`+
)([^\r]
*
?[^
`
])\2(?!
`
)
/gm
,
function
(
wholeMatch
,
m1
,
m2
,
m3
)
{
var
c
=
m3
;
c
=
c
.
replace
(
/^
([
\t]
*
)
/g
,
''
);
// leading whitespace
c
=
c
.
replace
(
/
[
\t]
*$/g
,
''
);
// trailing whitespace
c
=
showdown
.
subParser
(
'
encodeCode
'
)(
c
);
return
m1
+
'
<code>
'
+
c
+
'
</code>
'
;
}
);
text
=
globals
.
converter
.
_dispatch
(
'
codeSpans.after
'
,
text
,
options
,
globals
);
return
text
;
});
/**
* Convert all tabs to spaces
*/
showdown
.
subParser
(
'
detab
'
,
function
(
text
)
{
'
use strict
'
;
// expand first n-1 tabs
text
=
text
.
replace
(
/
\t(?=\t)
/g
,
'
'
);
// g_tab_width
// replace the nth with two sentinels
text
=
text
.
replace
(
/
\t
/g
,
'
~A~B
'
);
// use the sentinel to anchor our regex so it doesn't explode
text
=
text
.
replace
(
/~B
(
.+
?)
~A/g
,
function
(
wholeMatch
,
m1
)
{
var
leadingText
=
m1
,
numSpaces
=
4
-
leadingText
.
length
%
4
;
// g_tab_width
// there *must* be a better way to do this:
for
(
var
i
=
0
;
i
<
numSpaces
;
i
++
)
{
leadingText
+=
'
'
;
}
return
leadingText
;
});
// clean up sentinels
text
=
text
.
replace
(
/~A/g
,
'
'
);
// g_tab_width
text
=
text
.
replace
(
/~B/g
,
''
);
return
text
;
});
/**
* Smart processing for ampersands and angle brackets that need to be encoded.
*/
showdown
.
subParser
(
'
encodeAmpsAndAngles
'
,
function
(
text
)
{
'
use strict
'
;
// Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
// http://bumppo.net/projects/amputator/
text
=
text
.
replace
(
/&
(?!
#
?[
xX
]?(?:[
0-9a-fA-F
]
+|
\w
+
)
;
)
/g
,
'
&
'
);
// Encode naked <'s
text
=
text
.
replace
(
/<
(?![
a-z
\/
?
\$
!
])
/gi
,
'
<
'
);
return
text
;
});
/**
* Returns the string, with after processing the following backslash escape sequences.
*
* attacklab: The polite way to do this is with the new escapeCharacters() function:
*
* text = escapeCharacters(text,"\\",true);
* text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
*
* ...but we're sidestepping its use of the (slow) RegExp constructor
* as an optimization for Firefox. This function gets called a LOT.
*/
showdown
.
subParser
(
'
encodeBackslashEscapes
'
,
function
(
text
)
{
'
use strict
'
;
text
=
text
.
replace
(
/
\\(\\)
/g
,
showdown
.
helper
.
escapeCharactersCallback
);
text
=
text
.
replace
(
/
\\([
`*_{}
\[\]
()>#+-.!
])
/g
,
showdown
.
helper
.
escapeCharactersCallback
);
return
text
;
});
/**
* Encode/escape certain characters inside Markdown code runs.
* The point is that in code, these characters are literals,
* and lose their special Markdown meanings.
*/
showdown
.
subParser
(
'
encodeCode
'
,
function
(
text
)
{
'
use strict
'
;
// Encode all ampersands; HTML entities are not
// entities within a Markdown code span.
text
=
text
.
replace
(
/&/g
,
'
&
'
);
// Do the angle bracket song and dance:
text
=
text
.
replace
(
/</g
,
'
<
'
);
text
=
text
.
replace
(
/>/g
,
'
>
'
);
// Now, escape characters that are magic in Markdown:
text
=
showdown
.
helper
.
escapeCharacters
(
text
,
'
*_{}[]
\\
'
,
false
);
// jj the line above breaks this:
//---
//* Item
// 1. Subitem
// special char: *
// ---
return
text
;
});
/**
* Input: an email address, e.g. "foo@example.com"
*
* Output: the email address as a mailto link, with each character
* of the address encoded as either a decimal or hex entity, in
* the hopes of foiling most address harvesting spam bots. E.g.:
*
* <a href="mailto:foo@e
* xample.com">foo
* @example.com</a>
*
* Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
* mailing list: <http://tinyurl.com/yu7ue>
*
*/
showdown
.
subParser
(
'
encodeEmailAddress
'
,
function
(
addr
)
{
'
use strict
'
;
var
encode
=
[
function
(
ch
)
{
return
'
&#
'
+
ch
.
charCodeAt
(
0
)
+
'
;
'
;
},
function
(
ch
)
{
return
'
&#x
'
+
ch
.
charCodeAt
(
0
).
toString
(
16
)
+
'
;
'
;
},
function
(
ch
)
{
return
ch
;
}
];
addr
=
'
mailto:
'
+
addr
;
addr
=
addr
.
replace
(
/./g
,
function
(
ch
)
{
if
(
ch
===
'
@
'
)
{
// this *must* be encoded. I insist.
ch
=
encode
[
Math
.
floor
(
Math
.
random
()
*
2
)](
ch
);
}
else
if
(
ch
!==
'
:
'
)
{
// leave ':' alone (to spot mailto: later)
var
r
=
Math
.
random
();
// roughly 10% raw, 45% hex, 45% dec
ch
=
(
r
>
0.9
?
encode
[
2
](
ch
)
:
r
>
0.45
?
encode
[
1
](
ch
)
:
encode
[
0
](
ch
)
);
}
return
ch
;
});
addr
=
'
<a href="
'
+
addr
+
'
">
'
+
addr
+
'
</a>
'
;
addr
=
addr
.
replace
(
/">.+:/g
,
'
">
'
);
// strip the mailto: from the visible part
return
addr
;
});
/**
* Within tags -- meaning between < and > -- encode [\ ` * _] so they
* don't conflict with their use in Markdown for code, italics and strong.
*/
showdown
.
subParser
(
'
escapeSpecialCharsWithinTagAttributes
'
,
function
(
text
)
{
'
use strict
'
;
// Build a regex to find HTML tags and comments. See Friedl's
// "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
var
regex
=
/
(
<
[
a-z
\/
!$
](
"
[^
"
]
*"|'
[^
'
]
*'|
[^
'">
])
*>|<!
(
--.*
?
--
\s
*
)
+>
)
/gi
;
text
=
text
.
replace
(
regex
,
function
(
wholeMatch
)
{
var
tag
=
wholeMatch
.
replace
(
/
(
.
)
<
\/?
code>
(?=
.
)
/g
,
'
$1`
'
);
tag
=
showdown
.
helper
.
escapeCharacters
(
tag
,
'
\\
`*_
'
,
false
);
return
tag
;
});
return
text
;
});
/**
* Handle github codeblocks prior to running HashHTML so that
* HTML contained within the codeblock gets escaped properly
* Example:
* ```ruby
* def hello_world(x)
* puts "Hello, #{x}"
* end
* ```
*/
showdown
.
subParser
(
'
githubCodeBlocks
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
// early exit if option is not enabled
if
(
!
options
.
ghCodeBlocks
)
{
return
text
;
}
text
=
globals
.
converter
.
_dispatch
(
'
githubCodeBlocks.before
'
,
text
,
options
,
globals
);
text
+=
'
~0
'
;
text
=
text
.
replace
(
/
(?:
^|
\n)
```
(
.*
)\n([\s\S]
*
?)\n
```/g
,
function
(
wholeMatch
,
language
,
codeblock
)
{
var
end
=
(
options
.
omitExtraWLInCodeBlocks
)
?
''
:
'
\n
'
;
// First parse the github code block
codeblock
=
showdown
.
subParser
(
'
encodeCode
'
)(
codeblock
);
codeblock
=
showdown
.
subParser
(
'
detab
'
)(
codeblock
);
codeblock
=
codeblock
.
replace
(
/^
\n
+/g
,
''
);
// trim leading newlines
codeblock
=
codeblock
.
replace
(
/
\n
+$/g
,
''
);
// trim trailing whitespace
codeblock
=
'
<pre><code
'
+
(
language
?
'
class="
'
+
language
+
'
language-
'
+
language
+
'
"
'
:
''
)
+
'
>
'
+
codeblock
+
end
+
'
</code></pre>
'
;
codeblock
=
showdown
.
subParser
(
'
hashBlock
'
)(
codeblock
,
options
,
globals
);
// Since GHCodeblocks can be false positives, we need to
// store the primitive text and the parsed text in a global var,
// and then return a token
return
'
\n\n
~G
'
+
(
globals
.
ghCodeBlocks
.
push
({
text
:
wholeMatch
,
codeblock
:
codeblock
})
-
1
)
+
'
G
\n\n
'
;
});
// attacklab: strip sentinel
text
=
text
.
replace
(
/~0/
,
''
);
return
globals
.
converter
.
_dispatch
(
'
githubCodeBlocks.after
'
,
text
,
options
,
globals
);
});
showdown
.
subParser
(
'
hashBlock
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
text
.
replace
(
/
(
^
\n
+|
\n
+$
)
/g
,
''
);
return
'
\n\n
~K
'
+
(
globals
.
gHtmlBlocks
.
push
(
text
)
-
1
)
+
'
K
\n\n
'
;
});
showdown
.
subParser
(
'
hashElement
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
return
function
(
wholeMatch
,
m1
)
{
var
blockText
=
m1
;
// Undo double lines
blockText
=
blockText
.
replace
(
/
\n\n
/g
,
'
\n
'
);
blockText
=
blockText
.
replace
(
/^
\n
/
,
''
);
// strip trailing blank lines
blockText
=
blockText
.
replace
(
/
\n
+$/g
,
''
);
// Replace the element text with a marker ("~KxK" where x is its key)
blockText
=
'
\n\n
~K
'
+
(
globals
.
gHtmlBlocks
.
push
(
blockText
)
-
1
)
+
'
K
\n\n
'
;
return
blockText
;
};
});
showdown
.
subParser
(
'
hashHTMLBlocks
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
var
blockTags
=
[
'
pre
'
,
'
div
'
,
'
h1
'
,
'
h2
'
,
'
h3
'
,
'
h4
'
,
'
h5
'
,
'
h6
'
,
'
blockquote
'
,
'
table
'
,
'
dl
'
,
'
ol
'
,
'
ul
'
,
'
script
'
,
'
noscript
'
,
'
form
'
,
'
fieldset
'
,
'
iframe
'
,
'
math
'
,
'
style
'
,
'
section
'
,
'
header
'
,
'
footer
'
,
'
nav
'
,
'
article
'
,
'
aside
'
,
'
address
'
,
'
audio
'
,
'
canvas
'
,
'
figure
'
,
'
hgroup
'
,
'
output
'
,
'
video
'
,
'
p
'
],
repFunc
=
function
(
wholeMatch
,
match
,
left
,
right
)
{
var
txt
=
wholeMatch
;
// check if this html element is marked as markdown
// if so, it's contents should be parsed as markdown
if
(
left
.
search
(
/
\b
markdown
\b
/
)
!==
-
1
)
{
txt
=
left
+
globals
.
converter
.
makeHtml
(
match
)
+
right
;
}
return
'
\n\n
~K
'
+
(
globals
.
gHtmlBlocks
.
push
(
txt
)
-
1
)
+
'
K
\n\n
'
;
};
for
(
var
i
=
0
;
i
<
blockTags
.
length
;
++
i
)
{
text
=
showdown
.
helper
.
replaceRecursiveRegExp
(
text
,
repFunc
,
'
^(?: |
\\
t){0,3}<
'
+
blockTags
[
i
]
+
'
\\
b[^>]*>
'
,
'
</
'
+
blockTags
[
i
]
+
'
>
'
,
'
gim
'
);
}
// HR SPECIAL CASE
text
=
text
.
replace
(
/
(\n[
]{0,3}(
<
(
hr
)\b([^
<>
])
*
?\/?
>
)[
\t]
*
(?=\n{2,}))
/g
,
showdown
.
subParser
(
'
hashElement
'
)(
text
,
options
,
globals
));
// Special case for standalone HTML comments:
text
=
text
.
replace
(
/
(
<!--
[\s\S]
*
?
-->
)
/g
,
showdown
.
subParser
(
'
hashElement
'
)(
text
,
options
,
globals
));
// PHP and ASP-style processor instructions (<?...?> and <%...%>)
text
=
text
.
replace
(
/
(?:\n\n)([
]{0,3}(?:
<
([
?%
])[^\r]
*
?\2
>
)[
\t]
*
(?=\n{2,}))
/g
,
showdown
.
subParser
(
'
hashElement
'
)(
text
,
options
,
globals
));
return
text
;
});
/**
* Hash span elements that should not be parsed as markdown
*/
showdown
.
subParser
(
'
hashHTMLSpans
'
,
function
(
text
,
config
,
globals
)
{
'
use strict
'
;
var
matches
=
showdown
.
helper
.
matchRecursiveRegExp
(
text
,
'
<code
\\
b[^>]*>
'
,
'
</code>
'
,
'
gi
'
);
for
(
var
i
=
0
;
i
<
matches
.
length
;
++
i
)
{
text
=
text
.
replace
(
matches
[
i
][
0
],
'
~L
'
+
(
globals
.
gHtmlSpans
.
push
(
matches
[
i
][
0
])
-
1
)
+
'
L
'
);
}
return
text
;
});
/**
* Unhash HTML spans
*/
showdown
.
subParser
(
'
unhashHTMLSpans
'
,
function
(
text
,
config
,
globals
)
{
'
use strict
'
;
for
(
var
i
=
0
;
i
<
globals
.
gHtmlSpans
.
length
;
++
i
)
{
text
=
text
.
replace
(
'
~L
'
+
i
+
'
L
'
,
globals
.
gHtmlSpans
[
i
]);
}
return
text
;
});
/**
* Hash span elements that should not be parsed as markdown
*/
showdown
.
subParser
(
'
hashPreCodeTags
'
,
function
(
text
,
config
,
globals
)
{
'
use strict
'
;
var
repFunc
=
function
(
wholeMatch
,
match
,
left
,
right
)
{
// encode html entities
var
codeblock
=
left
+
showdown
.
subParser
(
'
encodeCode
'
)(
match
)
+
right
;
return
'
\n\n
~G
'
+
(
globals
.
ghCodeBlocks
.
push
({
text
:
wholeMatch
,
codeblock
:
codeblock
})
-
1
)
+
'
G
\n\n
'
;
};
text
=
showdown
.
helper
.
replaceRecursiveRegExp
(
text
,
repFunc
,
'
^(?: |
\\
t){0,3}<pre
\\
b[^>]*>
\\
s*<code
\\
b[^>]*>
'
,
'
^(?: |
\\
t){0,3}</code>
\\
s*</pre>
'
,
'
gim
'
);
return
text
;
});
showdown
.
subParser
(
'
headers
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
headers.before
'
,
text
,
options
,
globals
);
var
prefixHeader
=
options
.
prefixHeaderId
,
headerLevelStart
=
(
isNaN
(
parseInt
(
options
.
headerLevelStart
)))
?
1
:
parseInt
(
options
.
headerLevelStart
),
// Set text-style headers:
// Header 1
// ========
//
// Header 2
// --------
//
setextRegexH1
=
(
options
.
smoothLivePreview
)
?
/^
(
.+
)[
\t]
*
\n
=
{2,}[
\t]
*
\n
+/gm
:
/^
(
.+
)[
\t]
*
\n
=+
[
\t]
*
\n
+/gm
,
setextRegexH2
=
(
options
.
smoothLivePreview
)
?
/^
(
.+
)[
\t]
*
\n
-
{2,}[
\t]
*
\n
+/gm
:
/^
(
.+
)[
\t]
*
\n
-+
[
\t]
*
\n
+/gm
;
text
=
text
.
replace
(
setextRegexH1
,
function
(
wholeMatch
,
m1
)
{
var
spanGamut
=
showdown
.
subParser
(
'
spanGamut
'
)(
m1
,
options
,
globals
),
hID
=
(
options
.
noHeaderId
)
?
''
:
'
id="
'
+
headerId
(
m1
)
+
'
"
'
,
hLevel
=
headerLevelStart
,
hashBlock
=
'
<h
'
+
hLevel
+
hID
+
'
>
'
+
spanGamut
+
'
</h
'
+
hLevel
+
'
>
'
;
return
showdown
.
subParser
(
'
hashBlock
'
)(
hashBlock
,
options
,
globals
);
});
text
=
text
.
replace
(
setextRegexH2
,
function
(
matchFound
,
m1
)
{
var
spanGamut
=
showdown
.
subParser
(
'
spanGamut
'
)(
m1
,
options
,
globals
),
hID
=
(
options
.
noHeaderId
)
?
''
:
'
id="
'
+
headerId
(
m1
)
+
'
"
'
,
hLevel
=
headerLevelStart
+
1
,
hashBlock
=
'
<h
'
+
hLevel
+
hID
+
'
>
'
+
spanGamut
+
'
</h
'
+
hLevel
+
'
>
'
;
return
showdown
.
subParser
(
'
hashBlock
'
)(
hashBlock
,
options
,
globals
);
});
// atx-style headers:
// # Header 1
// ## Header 2
// ## Header 2 with closing hashes ##
// ...
// ###### Header 6
//
text
=
text
.
replace
(
/^
(
#
{1,6})[
\t]
*
(
.+
?)[
\t]
*#*
\n
+/gm
,
function
(
wholeMatch
,
m1
,
m2
)
{
var
span
=
showdown
.
subParser
(
'
spanGamut
'
)(
m2
,
options
,
globals
),
hID
=
(
options
.
noHeaderId
)
?
''
:
'
id="
'
+
headerId
(
m2
)
+
'
"
'
,
hLevel
=
headerLevelStart
-
1
+
m1
.
length
,
header
=
'
<h
'
+
hLevel
+
hID
+
'
>
'
+
span
+
'
</h
'
+
hLevel
+
'
>
'
;
return
showdown
.
subParser
(
'
hashBlock
'
)(
header
,
options
,
globals
);
});
function
headerId
(
m
)
{
var
title
,
escapedId
=
m
.
replace
(
/
[^\w]
/g
,
''
).
toLowerCase
();
if
(
globals
.
hashLinkCounts
[
escapedId
])
{
title
=
escapedId
+
'
-
'
+
(
globals
.
hashLinkCounts
[
escapedId
]
++
);
}
else
{
title
=
escapedId
;
globals
.
hashLinkCounts
[
escapedId
]
=
1
;
}
// Prefix id to prevent causing inadvertent pre-existing style matches.
if
(
prefixHeader
===
true
)
{
prefixHeader
=
'
section
'
;
}
if
(
showdown
.
helper
.
isString
(
prefixHeader
))
{
return
prefixHeader
+
title
;
}
return
title
;
}
text
=
globals
.
converter
.
_dispatch
(
'
headers.after
'
,
text
,
options
,
globals
);
return
text
;
});
/**
* Turn Markdown image shortcuts into <img> tags.
*/
showdown
.
subParser
(
'
images
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
images.before
'
,
text
,
options
,
globals
);
var
inlineRegExp
=
/!
\[(
.*
?)
]
\s?\([
\t]
*
()
<
?(\S
+
?)
>
?(?:
=
([
*
\d]
+
[
A-Za-z%
]{0,4})
x
([
*
\d]
+
[
A-Za-z%
]{0,4}))?[
\t]
*
(?:([
'"
])(
.*
?)\6[
\t]
*
)?\)
/g
,
referenceRegExp
=
/!
\[([^\]]
*
?)
]
?(?:\n
*
)?\[(
.*
?)
]
()()()()()
/g
;
function
writeImageTag
(
wholeMatch
,
altText
,
linkId
,
url
,
width
,
height
,
m5
,
title
)
{
var
gUrls
=
globals
.
gUrls
,
gTitles
=
globals
.
gTitles
,
gDims
=
globals
.
gDimensions
;
linkId
=
linkId
.
toLowerCase
();
if
(
!
title
)
{
title
=
''
;
}
if
(
url
===
''
||
url
===
null
)
{
if
(
linkId
===
''
||
linkId
===
null
)
{
// lower-case and turn embedded newlines into spaces
linkId
=
altText
.
toLowerCase
().
replace
(
/
?\n
/g
,
'
'
);
}
url
=
'
#
'
+
linkId
;
if
(
!
showdown
.
helper
.
isUndefined
(
gUrls
[
linkId
]))
{
url
=
gUrls
[
linkId
];
if
(
!
showdown
.
helper
.
isUndefined
(
gTitles
[
linkId
]))
{
title
=
gTitles
[
linkId
];
}
if
(
!
showdown
.
helper
.
isUndefined
(
gDims
[
linkId
]))
{
width
=
gDims
[
linkId
].
width
;
height
=
gDims
[
linkId
].
height
;
}
}
else
{
return
wholeMatch
;
}
}
altText
=
altText
.
replace
(
/"/g
,
'
"
'
);
altText
=
showdown
.
helper
.
escapeCharacters
(
altText
,
'
*_
'
,
false
);
url
=
showdown
.
helper
.
escapeCharacters
(
url
,
'
*_
'
,
false
);
var
result
=
'
<img src="
'
+
url
+
'
" alt="
'
+
altText
+
'
"
'
;
if
(
title
)
{
title
=
title
.
replace
(
/"/g
,
'
"
'
);
title
=
showdown
.
helper
.
escapeCharacters
(
title
,
'
*_
'
,
false
);
result
+=
'
title="
'
+
title
+
'
"
'
;
}
if
(
width
&&
height
)
{
width
=
(
width
===
'
*
'
)
?
'
auto
'
:
width
;
height
=
(
height
===
'
*
'
)
?
'
auto
'
:
height
;
result
+=
'
width="
'
+
width
+
'
"
'
;
result
+=
'
height="
'
+
height
+
'
"
'
;
}
result
+=
'
/>
'
;
return
result
;
}
// First, handle reference-style labeled images: ![alt text][id]
text
=
text
.
replace
(
referenceRegExp
,
writeImageTag
);
// Next, handle inline images: 
text
=
text
.
replace
(
inlineRegExp
,
writeImageTag
);
text
=
globals
.
converter
.
_dispatch
(
'
images.after
'
,
text
,
options
,
globals
);
return
text
;
});
showdown
.
subParser
(
'
italicsAndBold
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
italicsAndBold.before
'
,
text
,
options
,
globals
);
if
(
options
.
literalMidWordUnderscores
)
{
//underscores
// Since we are consuming a \s character, we need to add it
text
=
text
.
replace
(
/
(
^|
\s
|>|
\b)
__
(?=\S)([\s\S]
+
?)
__
(?=\b
|<|
\s
|$
)
/gm
,
'
$1<strong>$2</strong>
'
);
text
=
text
.
replace
(
/
(
^|
\s
|>|
\b)
_
(?=\S)([\s\S]
+
?)
_
(?=\b
|<|
\s
|$
)
/gm
,
'
$1<em>$2</em>
'
);
//asterisks
text
=
text
.
replace
(
/
(\*\*)(?=\S)([^\r]
*
?\S[
*
]
*
)\1
/g
,
'
<strong>$2</strong>
'
);
text
=
text
.
replace
(
/
(\*)(?=\S)([^\r]
*
?\S)\1
/g
,
'
<em>$2</em>
'
);
}
else
{
// <strong> must go first:
text
=
text
.
replace
(
/
(\*\*
|__
)(?=\S)([^\r]
*
?\S[
*_
]
*
)\1
/g
,
'
<strong>$2</strong>
'
);
text
=
text
.
replace
(
/
(\*
|_
)(?=\S)([^\r]
*
?\S)\1
/g
,
'
<em>$2</em>
'
);
}
text
=
globals
.
converter
.
_dispatch
(
'
italicsAndBold.after
'
,
text
,
options
,
globals
);
return
text
;
});
/**
* Form HTML ordered (numbered) and unordered (bulleted) lists.
*/
showdown
.
subParser
(
'
lists
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
lists.before
'
,
text
,
options
,
globals
);
/**
* Process the contents of a single ordered or unordered list, splitting it
* into individual list items.
* @param {string} listStr
* @param {boolean} trimTrailing
* @returns {string}
*/
function
processListItems
(
listStr
,
trimTrailing
)
{
// The $g_list_level global keeps track of when we're inside a list.
// Each time we enter a list, we increment it; when we leave a list,
// we decrement. If it's zero, we're not in a list anymore.
//
// We do this because when we're not inside a list, we want to treat
// something like this:
//
// I recommend upgrading to version
// 8. Oops, now this line is treated
// as a sub-list.
//
// As a single paragraph, despite the fact that the second line starts
// with a digit-period-space sequence.
//
// Whereas when we're inside a list (or sub-list), that line will be
// treated as the start of a sub-list. What a kludge, huh? This is
// an aspect of Markdown's syntax that's hard to parse perfectly
// without resorting to mind-reading. Perhaps the solution is to
// change the syntax rules such that sub-lists must start with a
// starting cardinal number; e.g. "1." or "a.".
globals
.
gListLevel
++
;
// trim trailing blank lines:
listStr
=
listStr
.
replace
(
/
\n{2,}
$/
,
'
\n
'
);
// attacklab: add sentinel to emulate \z
listStr
+=
'
~0
'
;
var
rgx
=
/
(\n)?(
^
[
\t]
*
)([
*+-
]
|
\d
+
[
.
])[
\t]
+
((\[(
x|X|
)?
]
)?[
\t]
*
[^\r]
+
?(\n{1,2}))(?=\n
*
(
~0|
\2([
*+-
]
|
\d
+
[
.
])[
\t]
+
))
/gm
,
isParagraphed
=
(
/
\n[
\t]
*
\n(?!
~0
)
/
.
test
(
listStr
));
listStr
=
listStr
.
replace
(
rgx
,
function
(
wholeMatch
,
m1
,
m2
,
m3
,
m4
,
taskbtn
,
checked
)
{
checked
=
(
checked
&&
checked
.
trim
()
!==
''
);
var
item
=
showdown
.
subParser
(
'
outdent
'
)(
m4
,
options
,
globals
),
bulletStyle
=
''
;
// Support for github tasklists
if
(
taskbtn
&&
options
.
tasklists
)
{
bulletStyle
=
'
class="task-list-item" style="list-style-type: none;"
'
;
item
=
item
.
replace
(
/^
[
\t]
*
\[(
x|X|
)?
]/m
,
function
()
{
var
otp
=
'
<input type="checkbox" disabled style="margin: 0px 0.35em 0.25em -1.6em; vertical-align: middle;"
'
;
if
(
checked
)
{
otp
+=
'
checked
'
;
}
otp
+=
'
>
'
;
return
otp
;
});
}
// m1 - Leading line or
// Has a double return (multi paragraph) or
// Has sublist
if
(
m1
||
(
item
.
search
(
/
\n{2,}
/
)
>
-
1
))
{
item
=
showdown
.
subParser
(
'
githubCodeBlocks
'
)(
item
,
options
,
globals
);
item
=
showdown
.
subParser
(
'
blockGamut
'
)(
item
,
options
,
globals
);
}
else
{
// Recursion for sub-lists:
item
=
showdown
.
subParser
(
'
lists
'
)(
item
,
options
,
globals
);
item
=
item
.
replace
(
/
\n
$/
,
''
);
// chomp(item)
if
(
isParagraphed
)
{
item
=
showdown
.
subParser
(
'
paragraphs
'
)(
item
,
options
,
globals
);
}
else
{
item
=
showdown
.
subParser
(
'
spanGamut
'
)(
item
,
options
,
globals
);
}
}
item
=
'
\n
<li
'
+
bulletStyle
+
'
>
'
+
item
+
'
</li>
\n
'
;
return
item
;
});
// attacklab: strip sentinel
listStr
=
listStr
.
replace
(
/~0/g
,
''
);
globals
.
gListLevel
--
;
if
(
trimTrailing
)
{
listStr
=
listStr
.
replace
(
/
\s
+$/
,
''
);
}
return
listStr
;
}
/**
* Check and parse consecutive lists (better fix for issue #142)
* @param {string} list
* @param {string} listType
* @param {boolean} trimTrailing
* @returns {string}
*/
function
parseConsecutiveLists
(
list
,
listType
,
trimTrailing
)
{
// check if we caught 2 or more consecutive lists by mistake
// we use the counterRgx, meaning if listType is UL we look for UL and vice versa
var
counterRxg
=
(
listType
===
'
ul
'
)
?
/^
{0,2}\d
+
\.[
\t]
/gm
:
/^
{0,2}[
*+-
][
\t]
/gm
,
subLists
=
[],
result
=
''
;
if
(
list
.
search
(
counterRxg
)
!==
-
1
)
{
(
function
parseCL
(
txt
)
{
var
pos
=
txt
.
search
(
counterRxg
);
if
(
pos
!==
-
1
)
{
// slice
result
+=
'
\n\n
<
'
+
listType
+
'
>
'
+
processListItems
(
txt
.
slice
(
0
,
pos
),
!!
trimTrailing
)
+
'
</
'
+
listType
+
'
>
\n\n
'
;
// invert counterType and listType
listType
=
(
listType
===
'
ul
'
)
?
'
ol
'
:
'
ul
'
;
counterRxg
=
(
listType
===
'
ul
'
)
?
/^
{0,2}\d
+
\.[
\t]
/gm
:
/^
{0,2}[
*+-
][
\t]
/gm
;
//recurse
parseCL
(
txt
.
slice
(
pos
));
}
else
{
result
+=
'
\n\n
<
'
+
listType
+
'
>
'
+
processListItems
(
txt
,
!!
trimTrailing
)
+
'
</
'
+
listType
+
'
>
\n\n
'
;
}
})(
list
);
for
(
var
i
=
0
;
i
<
subLists
.
length
;
++
i
)
{
}
}
else
{
result
=
'
\n\n
<
'
+
listType
+
'
>
'
+
processListItems
(
list
,
!!
trimTrailing
)
+
'
</
'
+
listType
+
'
>
\n\n
'
;
}
return
result
;
}
// attacklab: add sentinel to hack around khtml/safari bug:
// http://bugs.webkit.org/show_bug.cgi?id=11231
text
+=
'
~0
'
;
// Re-usable pattern to match any entire ul or ol list:
var
wholeList
=
/^
(([
]{0,3}([
*+-
]
|
\d
+
[
.
])[
\t]
+
)[^\r]
+
?(
~0|
\n{2,}(?=\S)(?![
\t]
*
(?:[
*+-
]
|
\d
+
[
.
])[
\t]
+
)))
/gm
;
if
(
globals
.
gListLevel
)
{
text
=
text
.
replace
(
wholeList
,
function
(
wholeMatch
,
list
,
m2
)
{
var
listType
=
(
m2
.
search
(
/
[
*+-
]
/g
)
>
-
1
)
?
'
ul
'
:
'
ol
'
;
return
parseConsecutiveLists
(
list
,
listType
,
true
);
});
}
else
{
wholeList
=
/
(\n\n
|^
\n?)(([
]{0,3}([
*+-
]
|
\d
+
[
.
])[
\t]
+
)[^\r]
+
?(
~0|
\n{2,}(?=\S)(?![
\t]
*
(?:[
*+-
]
|
\d
+
[
.
])[
\t]
+
)))
/gm
;
//wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g;
text
=
text
.
replace
(
wholeList
,
function
(
wholeMatch
,
m1
,
list
,
m3
)
{
var
listType
=
(
m3
.
search
(
/
[
*+-
]
/g
)
>
-
1
)
?
'
ul
'
:
'
ol
'
;
return
parseConsecutiveLists
(
list
,
listType
);
});
}
// attacklab: strip sentinel
text
=
text
.
replace
(
/~0/
,
''
);
text
=
globals
.
converter
.
_dispatch
(
'
lists.after
'
,
text
,
options
,
globals
);
return
text
;
});
/**
* Remove one level of line-leading tabs or spaces
*/
showdown
.
subParser
(
'
outdent
'
,
function
(
text
)
{
'
use strict
'
;
// attacklab: hack around Konqueror 3.5.4 bug:
// "----------bug".replace(/^-/g,"") == "bug"
text
=
text
.
replace
(
/^
(\t
|
[
]{1,4})
/gm
,
'
~0
'
);
// attacklab: g_tab_width
// attacklab: clean up hack
text
=
text
.
replace
(
/~0/g
,
''
);
return
text
;
});
/**
*
*/
showdown
.
subParser
(
'
paragraphs
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
paragraphs.before
'
,
text
,
options
,
globals
);
// Strip leading and trailing lines:
text
=
text
.
replace
(
/^
\n
+/g
,
''
);
text
=
text
.
replace
(
/
\n
+$/g
,
''
);
var
grafs
=
text
.
split
(
/
\n{2,}
/g
),
grafsOut
=
[],
end
=
grafs
.
length
;
// Wrap <p> tags
for
(
var
i
=
0
;
i
<
end
;
i
++
)
{
var
str
=
grafs
[
i
];
// if this is an HTML marker, copy it
if
(
str
.
search
(
/~
(
K|G
)(\d
+
)\1
/g
)
>=
0
)
{
grafsOut
.
push
(
str
);
}
else
{
str
=
showdown
.
subParser
(
'
spanGamut
'
)(
str
,
options
,
globals
);
str
=
str
.
replace
(
/^
([
\t]
*
)
/g
,
'
<p>
'
);
str
+=
'
</p>
'
;
grafsOut
.
push
(
str
);
}
}
/** Unhashify HTML blocks */
end
=
grafsOut
.
length
;
for
(
i
=
0
;
i
<
end
;
i
++
)
{
var
blockText
=
''
,
grafsOutIt
=
grafsOut
[
i
],
codeFlag
=
false
;
// if this is a marker for an html block...
while
(
grafsOutIt
.
search
(
/~
(
K|G
)(\d
+
)\1
/
)
>=
0
)
{
var
delim
=
RegExp
.
$1
,
num
=
RegExp
.
$2
;
if
(
delim
===
'
K
'
)
{
blockText
=
globals
.
gHtmlBlocks
[
num
];
}
else
{
// we need to check if ghBlock is a false positive
if
(
codeFlag
)
{
// use encoded version of all text
blockText
=
showdown
.
subParser
(
'
encodeCode
'
)(
globals
.
ghCodeBlocks
[
num
].
text
);
}
else
{
blockText
=
globals
.
ghCodeBlocks
[
num
].
codeblock
;
}
}
blockText
=
blockText
.
replace
(
/
\$
/g
,
'
$$$$
'
);
// Escape any dollar signs
grafsOutIt
=
grafsOutIt
.
replace
(
/
(\n\n)?
~
(
K|G
)\d
+
\2(\n\n)?
/
,
blockText
);
// Check if grafsOutIt is a pre->code
if
(
/^<pre
\b[^
>
]
*>
\s
*<code
\b[^
>
]
*>/
.
test
(
grafsOutIt
))
{
codeFlag
=
true
;
}
}
grafsOut
[
i
]
=
grafsOutIt
;
}
text
=
grafsOut
.
join
(
'
\n\n
'
);
// Strip leading and trailing lines:
text
=
text
.
replace
(
/^
\n
+/g
,
''
);
text
=
text
.
replace
(
/
\n
+$/g
,
''
);
return
globals
.
converter
.
_dispatch
(
'
paragraphs.after
'
,
text
,
options
,
globals
);
});
/**
* Run extension
*/
showdown
.
subParser
(
'
runExtension
'
,
function
(
ext
,
text
,
options
,
globals
)
{
'
use strict
'
;
if
(
ext
.
filter
)
{
text
=
ext
.
filter
(
text
,
globals
.
converter
,
options
);
}
else
if
(
ext
.
regex
)
{
// TODO remove this when old extension loading mechanism is deprecated
var
re
=
ext
.
regex
;
if
(
!
re
instanceof
RegExp
)
{
re
=
new
RegExp
(
re
,
'
g
'
);
}
text
=
text
.
replace
(
re
,
ext
.
replace
);
}
return
text
;
});
/**
* These are all the transformations that occur *within* block-level
* tags like paragraphs, headers, and list items.
*/
showdown
.
subParser
(
'
spanGamut
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
text
=
globals
.
converter
.
_dispatch
(
'
spanGamut.before
'
,
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
codeSpans
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
escapeSpecialCharsWithinTagAttributes
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
encodeBackslashEscapes
'
)(
text
,
options
,
globals
);
// Process anchor and image tags. Images must come first,
// because ![foo][f] looks like an anchor.
text
=
showdown
.
subParser
(
'
images
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
anchors
'
)(
text
,
options
,
globals
);
// Make links out of things like `<http://example.com/>`
// Must come after _DoAnchors(), because you can use < and >
// delimiters in inline links like [this](<url>).
text
=
showdown
.
subParser
(
'
autoLinks
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
encodeAmpsAndAngles
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
italicsAndBold
'
)(
text
,
options
,
globals
);
text
=
showdown
.
subParser
(
'
strikethrough
'
)(
text
,
options
,
globals
);
// Do hard breaks:
text
=
text
.
replace
(
/ +
\n
/g
,
'
<br />
\n
'
);
text
=
globals
.
converter
.
_dispatch
(
'
spanGamut.after
'
,
text
,
options
,
globals
);
return
text
;
});
showdown
.
subParser
(
'
strikethrough
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
if
(
options
.
strikethrough
)
{
text
=
globals
.
converter
.
_dispatch
(
'
strikethrough.before
'
,
text
,
options
,
globals
);
text
=
text
.
replace
(
/
(?:
~T
){2}([\s\S]
+
?)(?:
~T
){2}
/g
,
'
<del>$1</del>
'
);
text
=
globals
.
converter
.
_dispatch
(
'
strikethrough.after
'
,
text
,
options
,
globals
);
}
return
text
;
});
/**
* Strip any lines consisting only of spaces and tabs.
* This makes subsequent regexs easier to write, because we can
* match consecutive blank lines with /\n+/ instead of something
* contorted like /[ \t]*\n+/
*/
showdown
.
subParser
(
'
stripBlankLines
'
,
function
(
text
)
{
'
use strict
'
;
return
text
.
replace
(
/^
[
\t]
+$/mg
,
''
);
});
/**
* Strips link definitions from text, stores the URLs and titles in
* hash references.
* Link defs are in the form: ^[id]: url "optional title"
*
* ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
* [ \t]*
* \n? // maybe *one* newline
* [ \t]*
* <?(\S+?)>? // url = $2
* [ \t]*
* \n? // maybe one newline
* [ \t]*
* (?:
* (\n*) // any lines skipped = $3 attacklab: lookbehind removed
* ["(]
* (.+?) // title = $4
* [")]
* [ \t]*
* )? // title is optional
* (?:\n+|$)
* /gm,
* function(){...});
*
*/
showdown
.
subParser
(
'
stripLinkDefinitions
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
var
regex
=
/^
{0,3}\[(
.+
)
]:
[
\t]
*
\n?[
\t]
*<
?(\S
+
?)
>
?(?:
=
([
*
\d]
+
[
A-Za-z%
]{0,4})
x
([
*
\d]
+
[
A-Za-z%
]{0,4}))?[
\t]
*
\n?[
\t]
*
(?:(\n
*
)[
"|'(
](
.+
?)[
"|')
][
\t]
*
)?(?:\n
+|
(?=
~0
))
/gm
;
// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
text
+=
'
~0
'
;
text
=
text
.
replace
(
regex
,
function
(
wholeMatch
,
linkId
,
url
,
width
,
height
,
blankLines
,
title
)
{
linkId
=
linkId
.
toLowerCase
();
globals
.
gUrls
[
linkId
]
=
showdown
.
subParser
(
'
encodeAmpsAndAngles
'
)(
url
);
// Link IDs are case-insensitive
if
(
blankLines
)
{
// Oops, found blank lines, so it's not a title.
// Put back the parenthetical statement we stole.
return
blankLines
+
title
;
}
else
{
if
(
title
)
{
globals
.
gTitles
[
linkId
]
=
title
.
replace
(
/"|'/g
,
'
"
'
);
}
if
(
options
.
parseImgDimensions
&&
width
&&
height
)
{
globals
.
gDimensions
[
linkId
]
=
{
width
:
width
,
height
:
height
};
}
}
// Completely remove the definition from the text
return
''
;
});
// attacklab: strip sentinel
text
=
text
.
replace
(
/~0/
,
''
);
return
text
;
});
showdown
.
subParser
(
'
tables
'
,
function
(
text
,
options
,
globals
)
{
'
use strict
'
;
if
(
!
options
.
tables
)
{
return
text
;
}
var
tableRgx
=
/^
[
\t]{0,3}\|?
.+
\|
.+
\n[
\t]{0,3}\|?[
\t]
*:
?[
\t]
*
(?:
-|=
){2,}[
\t]
*:
?[
\t]
*
\|[
\t]
*:
?[
\t]
*
(?:
-|=
){2,}[\s\S]
+
?(?:\n\n
|~0
)
/gm
;
function
parseStyles
(
sLine
)
{
if
(
/^:
[
\t]
*--*$/
.
test
(
sLine
))
{
return
'
style="text-align:left;"
'
;
}
else
if
(
/^--*
[
\t]
*:
[
\t]
*$/
.
test
(
sLine
))
{
return
'
style="text-align:right;"
'
;
}
else
if
(
/^:
[
\t]
*--*
[
\t]
*:$/
.
test
(
sLine
))
{
return
'
style="text-align:center;"
'
;
}
else
{
return
''
;
}
}
function
parseHeaders
(
header
,
style
)
{
var
id
=
''
;
header
=
header
.
trim
();
if
(
options
.
tableHeaderId
)
{
id
=
'
id="
'
+
header
.
replace
(
/ /g
,
'
_
'
).
toLowerCase
()
+
'
"
'
;
}
header
=
showdown
.
subParser
(
'
spanGamut
'
)(
header
,
options
,
globals
);
return
'
<th
'
+
id
+
style
+
'
>
'
+
header
+
'
</th>
\n
'
;
}
function
parseCells
(
cell
,
style
)
{
var
subText
=
showdown
.
subParser
(
'
spanGamut
'
)(
cell
,
options
,
globals
);
return
'
<td
'
+
style
+
'
>
'
+
subText
+
'
</td>
\n
'
;
}
function
buildTable
(
headers
,
cells
)
{
var
tb
=
'
<table>
\n
<thead>
\n
<tr>
\n
'
,
tblLgn
=
headers
.
length
;
for
(
var
i
=
0
;
i
<
tblLgn
;
++
i
)
{
tb
+=
headers
[
i
];
}
tb
+=
'
</tr>
\n
</thead>
\n
<tbody>
\n
'
;
for
(
i
=
0
;
i
<
cells
.
length
;
++
i
)
{
tb
+=
'
<tr>
\n
'
;
for
(
var
ii
=
0
;
ii
<
tblLgn
;
++
ii
)
{
tb
+=
cells
[
i
][
ii
];
}
tb
+=
'
</tr>
\n
'
;
}
tb
+=
'
</tbody>
\n
</table>
\n
'
;
return
tb
;
}
text
=
globals
.
converter
.
_dispatch
(
'
tables.before
'
,
text
,
options
,
globals
);
text
=
text
.
replace
(
tableRgx
,
function
(
rawTable
)
{
var
i
,
tableLines
=
rawTable
.
split
(
'
\n
'
);
// strip wrong first and last column if wrapped tables are used
for
(
i
=
0
;
i
<
tableLines
.
length
;
++
i
)
{
if
(
/^
[
\t]{0,3}\|
/
.
test
(
tableLines
[
i
]))
{
tableLines
[
i
]
=
tableLines
[
i
].
replace
(
/^
[
\t]{0,3}\|
/
,
''
);
}
if
(
/
\|[
\t]
*$/
.
test
(
tableLines
[
i
]))
{
tableLines
[
i
]
=
tableLines
[
i
].
replace
(
/
\|[
\t]
*$/
,
''
);
}
}
var
rawHeaders
=
tableLines
[
0
].
split
(
'
|
'
).
map
(
function
(
s
)
{
return
s
.
trim
();}),
rawStyles
=
tableLines
[
1
].
split
(
'
|
'
).
map
(
function
(
s
)
{
return
s
.
trim
();}),
rawCells
=
[],
headers
=
[],
styles
=
[],
cells
=
[];
tableLines
.
shift
();
tableLines
.
shift
();
for
(
i
=
0
;
i
<
tableLines
.
length
;
++
i
)
{
if
(
tableLines
[
i
].
trim
()
===
''
)
{
continue
;
}
rawCells
.
push
(
tableLines
[
i
]
.
split
(
'
|
'
)
.
map
(
function
(
s
)
{
return
s
.
trim
();
})
);
}
if
(
rawHeaders
.
length
<
rawStyles
.
length
)
{
return
rawTable
;
}
for
(
i
=
0
;
i
<
rawStyles
.
length
;
++
i
)
{
styles
.
push
(
parseStyles
(
rawStyles
[
i
]));
}
for
(
i
=
0
;
i
<
rawHeaders
.
length
;
++
i
)
{
if
(
showdown
.
helper
.
isUndefined
(
styles
[
i
]))
{
styles
[
i
]
=
''
;
}
headers
.
push
(
parseHeaders
(
rawHeaders
[
i
],
styles
[
i
]));
}
for
(
i
=
0
;
i
<
rawCells
.
length
;
++
i
)
{
var
row
=
[];
for
(
var
ii
=
0
;
ii
<
headers
.
length
;
++
ii
)
{
if
(
showdown
.
helper
.
isUndefined
(
rawCells
[
i
][
ii
]))
{
}
row
.
push
(
parseCells
(
rawCells
[
i
][
ii
],
styles
[
ii
]));
}
cells
.
push
(
row
);
}
return
buildTable
(
headers
,
cells
);
});
text
=
globals
.
converter
.
_dispatch
(
'
tables.after
'
,
text
,
options
,
globals
);
return
text
;
});
/**
* Swap back in all the special characters we've hidden.
*/
showdown
.
subParser
(
'
unescapeSpecialChars
'
,
function
(
text
)
{
'
use strict
'
;
text
=
text
.
replace
(
/~E
(\d
+
)
E/g
,
function
(
wholeMatch
,
m1
)
{
var
charCodeToReplace
=
parseInt
(
m1
);
return
String
.
fromCharCode
(
charCodeToReplace
);
});
return
text
;
});
module
.
exports
=
showdown
;
renard-wx/lib/wxParse/wxDiscode.js
0 → 100644
View file @
854bacf6
// HTML 支持的数学符号
function
strNumDiscode
(
str
){
str
=
str
.
replace
(
/∀/g
,
'
∀
'
);
str
=
str
.
replace
(
/∂/g
,
'
∂
'
);
str
=
str
.
replace
(
/&exists;/g
,
'
∃
'
);
str
=
str
.
replace
(
/∅/g
,
'
∅
'
);
str
=
str
.
replace
(
/∇/g
,
'
∇
'
);
str
=
str
.
replace
(
/∈/g
,
'
∈
'
);
str
=
str
.
replace
(
/∉/g
,
'
∉
'
);
str
=
str
.
replace
(
/∋/g
,
'
∋
'
);
str
=
str
.
replace
(
/∏/g
,
'
∏
'
);
str
=
str
.
replace
(
/∑/g
,
'
∑
'
);
str
=
str
.
replace
(
/−/g
,
'
−
'
);
str
=
str
.
replace
(
/∗/g
,
'
∗
'
);
str
=
str
.
replace
(
/√/g
,
'
√
'
);
str
=
str
.
replace
(
/∝/g
,
'
∝
'
);
str
=
str
.
replace
(
/∞/g
,
'
∞
'
);
str
=
str
.
replace
(
/∠/g
,
'
∠
'
);
str
=
str
.
replace
(
/∧/g
,
'
∧
'
);
str
=
str
.
replace
(
/∨/g
,
'
∨
'
);
str
=
str
.
replace
(
/∩/g
,
'
∩
'
);
str
=
str
.
replace
(
/∩/g
,
'
∪
'
);
str
=
str
.
replace
(
/∫/g
,
'
∫
'
);
str
=
str
.
replace
(
/∴/g
,
'
∴
'
);
str
=
str
.
replace
(
/∼/g
,
'
∼
'
);
str
=
str
.
replace
(
/≅/g
,
'
≅
'
);
str
=
str
.
replace
(
/≈/g
,
'
≈
'
);
str
=
str
.
replace
(
/≠/g
,
'
≠
'
);
str
=
str
.
replace
(
/≤/g
,
'
≤
'
);
str
=
str
.
replace
(
/≥/g
,
'
≥
'
);
str
=
str
.
replace
(
/⊂/g
,
'
⊂
'
);
str
=
str
.
replace
(
/⊃/g
,
'
⊃
'
);
str
=
str
.
replace
(
/⊄/g
,
'
⊄
'
);
str
=
str
.
replace
(
/⊆/g
,
'
⊆
'
);
str
=
str
.
replace
(
/⊇/g
,
'
⊇
'
);
str
=
str
.
replace
(
/⊕/g
,
'
⊕
'
);
str
=
str
.
replace
(
/⊗/g
,
'
⊗
'
);
str
=
str
.
replace
(
/⊥/g
,
'
⊥
'
);
str
=
str
.
replace
(
/⋅/g
,
'
⋅
'
);
return
str
;
}
//HTML 支持的希腊字母
function
strGreeceDiscode
(
str
){
str
=
str
.
replace
(
/Α/g
,
'
Α
'
);
str
=
str
.
replace
(
/Β/g
,
'
Β
'
);
str
=
str
.
replace
(
/Γ/g
,
'
Γ
'
);
str
=
str
.
replace
(
/Δ/g
,
'
Δ
'
);
str
=
str
.
replace
(
/Ε/g
,
'
Ε
'
);
str
=
str
.
replace
(
/Ζ/g
,
'
Ζ
'
);
str
=
str
.
replace
(
/Η/g
,
'
Η
'
);
str
=
str
.
replace
(
/Θ/g
,
'
Θ
'
);
str
=
str
.
replace
(
/Ι/g
,
'
Ι
'
);
str
=
str
.
replace
(
/Κ/g
,
'
Κ
'
);
str
=
str
.
replace
(
/Λ/g
,
'
Λ
'
);
str
=
str
.
replace
(
/Μ/g
,
'
Μ
'
);
str
=
str
.
replace
(
/Ν/g
,
'
Ν
'
);
str
=
str
.
replace
(
/Ξ/g
,
'
Ν
'
);
str
=
str
.
replace
(
/Ο/g
,
'
Ο
'
);
str
=
str
.
replace
(
/Π/g
,
'
Π
'
);
str
=
str
.
replace
(
/Ρ/g
,
'
Ρ
'
);
str
=
str
.
replace
(
/Σ/g
,
'
Σ
'
);
str
=
str
.
replace
(
/Τ/g
,
'
Τ
'
);
str
=
str
.
replace
(
/Υ/g
,
'
Υ
'
);
str
=
str
.
replace
(
/Φ/g
,
'
Φ
'
);
str
=
str
.
replace
(
/Χ/g
,
'
Χ
'
);
str
=
str
.
replace
(
/Ψ/g
,
'
Ψ
'
);
str
=
str
.
replace
(
/Ω/g
,
'
Ω
'
);
str
=
str
.
replace
(
/α/g
,
'
α
'
);
str
=
str
.
replace
(
/β/g
,
'
β
'
);
str
=
str
.
replace
(
/γ/g
,
'
γ
'
);
str
=
str
.
replace
(
/δ/g
,
'
δ
'
);
str
=
str
.
replace
(
/ε/g
,
'
ε
'
);
str
=
str
.
replace
(
/ζ/g
,
'
ζ
'
);
str
=
str
.
replace
(
/η/g
,
'
η
'
);
str
=
str
.
replace
(
/θ/g
,
'
θ
'
);
str
=
str
.
replace
(
/ι/g
,
'
ι
'
);
str
=
str
.
replace
(
/κ/g
,
'
κ
'
);
str
=
str
.
replace
(
/λ/g
,
'
λ
'
);
str
=
str
.
replace
(
/μ/g
,
'
μ
'
);
str
=
str
.
replace
(
/ν/g
,
'
ν
'
);
str
=
str
.
replace
(
/ξ/g
,
'
ξ
'
);
str
=
str
.
replace
(
/ο/g
,
'
ο
'
);
str
=
str
.
replace
(
/π/g
,
'
π
'
);
str
=
str
.
replace
(
/ρ/g
,
'
ρ
'
);
str
=
str
.
replace
(
/ς/g
,
'
ς
'
);
str
=
str
.
replace
(
/σ/g
,
'
σ
'
);
str
=
str
.
replace
(
/τ/g
,
'
τ
'
);
str
=
str
.
replace
(
/υ/g
,
'
υ
'
);
str
=
str
.
replace
(
/φ/g
,
'
φ
'
);
str
=
str
.
replace
(
/χ/g
,
'
χ
'
);
str
=
str
.
replace
(
/ψ/g
,
'
ψ
'
);
str
=
str
.
replace
(
/ω/g
,
'
ω
'
);
str
=
str
.
replace
(
/ϑ/g
,
'
ϑ
'
);
str
=
str
.
replace
(
/ϒ/g
,
'
ϒ
'
);
str
=
str
.
replace
(
/ϖ/g
,
'
ϖ
'
);
str
=
str
.
replace
(
/·/g
,
'
·
'
);
return
str
;
}
//
function
strcharacterDiscode
(
str
){
// 加入常用解析
str
=
str
.
replace
(
/ /g
,
'
'
);
str
=
str
.
replace
(
/"/g
,
'
"
'
);
str
=
str
.
replace
(
/&/g
,
'
&
'
);
// str = str.replace(/</g, '‹');
// str = str.replace(/>/g, '›');
str
=
str
.
replace
(
/</g
,
'
<
'
);
str
=
str
.
replace
(
/>/g
,
'
>
'
);
return
str
;
}
// HTML 支持的其他实体
function
strOtherDiscode
(
str
){
str
=
str
.
replace
(
/Œ/g
,
'
Œ
'
);
str
=
str
.
replace
(
/œ/g
,
'
œ
'
);
str
=
str
.
replace
(
/Š/g
,
'
Š
'
);
str
=
str
.
replace
(
/š/g
,
'
š
'
);
str
=
str
.
replace
(
/Ÿ/g
,
'
Ÿ
'
);
str
=
str
.
replace
(
/ƒ/g
,
'
ƒ
'
);
str
=
str
.
replace
(
/ˆ/g
,
'
ˆ
'
);
str
=
str
.
replace
(
/˜/g
,
'
˜
'
);
str
=
str
.
replace
(
/ /g
,
''
);
str
=
str
.
replace
(
/ /g
,
''
);
str
=
str
.
replace
(
/ /g
,
''
);
str
=
str
.
replace
(
/‌/g
,
''
);
str
=
str
.
replace
(
/‍/g
,
''
);
str
=
str
.
replace
(
/‎/g
,
''
);
str
=
str
.
replace
(
/‏/g
,
''
);
str
=
str
.
replace
(
/–/g
,
'
–
'
);
str
=
str
.
replace
(
/—/g
,
'
—
'
);
str
=
str
.
replace
(
/‘/g
,
'
‘
'
);
str
=
str
.
replace
(
/’/g
,
'
’
'
);
str
=
str
.
replace
(
/‚/g
,
'
‚
'
);
str
=
str
.
replace
(
/“/g
,
'
“
'
);
str
=
str
.
replace
(
/”/g
,
'
”
'
);
str
=
str
.
replace
(
/„/g
,
'
„
'
);
str
=
str
.
replace
(
/†/g
,
'
†
'
);
str
=
str
.
replace
(
/‡/g
,
'
‡
'
);
str
=
str
.
replace
(
/•/g
,
'
•
'
);
str
=
str
.
replace
(
/…/g
,
'
…
'
);
str
=
str
.
replace
(
/‰/g
,
'
‰
'
);
str
=
str
.
replace
(
/′/g
,
'
′
'
);
str
=
str
.
replace
(
/″/g
,
'
″
'
);
str
=
str
.
replace
(
/‹/g
,
'
‹
'
);
str
=
str
.
replace
(
/›/g
,
'
›
'
);
str
=
str
.
replace
(
/‾/g
,
'
‾
'
);
str
=
str
.
replace
(
/€/g
,
'
€
'
);
str
=
str
.
replace
(
/™/g
,
'
™
'
);
str
=
str
.
replace
(
/←/g
,
'
←
'
);
str
=
str
.
replace
(
/↑/g
,
'
↑
'
);
str
=
str
.
replace
(
/→/g
,
'
→
'
);
str
=
str
.
replace
(
/↓/g
,
'
↓
'
);
str
=
str
.
replace
(
/↔/g
,
'
↔
'
);
str
=
str
.
replace
(
/↵/g
,
'
↵
'
);
str
=
str
.
replace
(
/⌈/g
,
'
⌈
'
);
str
=
str
.
replace
(
/⌉/g
,
'
⌉
'
);
str
=
str
.
replace
(
/⌊/g
,
'
⌊
'
);
str
=
str
.
replace
(
/⌋/g
,
'
⌋
'
);
str
=
str
.
replace
(
/◊/g
,
'
◊
'
);
str
=
str
.
replace
(
/♠/g
,
'
♠
'
);
str
=
str
.
replace
(
/♣/g
,
'
♣
'
);
str
=
str
.
replace
(
/♥/g
,
'
♥
'
);
str
=
str
.
replace
(
/♦/g
,
'
♦
'
);
return
str
;
}
function
strMoreDiscode
(
str
){
str
=
str
.
replace
(
/
\r\n
/g
,
""
);
str
=
str
.
replace
(
/
\n
/g
,
""
);
str
=
str
.
replace
(
/code/g
,
"
wxxxcode-style
"
);
return
str
;
}
function
strDiscode
(
str
){
str
=
strNumDiscode
(
str
);
str
=
strGreeceDiscode
(
str
);
str
=
strcharacterDiscode
(
str
);
str
=
strOtherDiscode
(
str
);
str
=
strMoreDiscode
(
str
);
return
str
;
}
function
urlToHttpUrl
(
url
,
rep
){
var
patt1
=
new
RegExp
(
"
^//
"
);
var
result
=
patt1
.
test
(
url
);
if
(
result
){
url
=
rep
+
"
:
"
+
url
;
}
return
url
;
}
module
.
exports
=
{
strDiscode
:
strDiscode
,
urlToHttpUrl
:
urlToHttpUrl
}
\ No newline at end of file
renard-wx/lib/wxParse/wxParse.js
0 → 100644
View file @
854bacf6
/**
* author: Di (微信小程序开发工程师)
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
* 垂直微信小程序开发交流社区
*
* github地址: https://github.com/icindy/wxParse
*
* for: 微信小程序富文本解析
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
*/
/**
* utils函数引入
**/
import
showdown
from
'
showdown.js
'
;
import
HtmlToJson
from
'
html2json.js
'
;
/**
* 配置及公有属性
**/
/**
* 主函数入口区
**/
function
wxParse
(
bindName
=
'
wxParseData
'
,
type
=
'
html
'
,
data
=
'
<div class="color:red;">数据不能为空</div>
'
,
target
,
imagePadding
)
{
var
that
=
target
;
var
transData
=
{};
//存放转化后的数据
if
(
type
==
'
html
'
)
{
transData
=
HtmlToJson
.
html2json
(
data
,
bindName
);
// console.log(JSON.stringify(transData, ' ', ' '));
}
else
if
(
type
==
'
md
'
||
type
==
'
markdown
'
)
{
var
converter
=
new
showdown
.
Converter
();
var
html
=
converter
.
makeHtml
(
data
);
transData
=
HtmlToJson
.
html2json
(
html
,
bindName
);
// console.log(JSON.stringify(transData, ' ', ' '));
}
transData
.
view
=
{};
transData
.
view
.
imagePadding
=
0
;
if
(
typeof
(
imagePadding
)
!=
'
undefined
'
){
transData
.
view
.
imagePadding
=
imagePadding
}
var
bindData
=
{};
bindData
[
bindName
]
=
transData
;
that
.
setData
(
bindData
)
that
.
wxParseImgLoad
=
wxParseImgLoad
;
that
.
wxParseImgTap
=
wxParseImgTap
;
}
// 图片点击事件
function
wxParseImgTap
(
e
)
{
var
that
=
this
;
var
nowImgUrl
=
e
.
target
.
dataset
.
src
;
var
tagFrom
=
e
.
target
.
dataset
.
from
;
if
(
typeof
(
tagFrom
)
!=
'
undefined
'
&&
tagFrom
.
length
>
0
)
{
wx
.
previewImage
({
current
:
nowImgUrl
,
// 当前显示图片的http链接
urls
:
that
.
data
[
tagFrom
].
imageUrls
// 需要预览的图片http链接列表
})
}
}
/**
* 图片视觉宽高计算函数区
**/
function
wxParseImgLoad
(
e
)
{
var
that
=
this
;
var
tagFrom
=
e
.
target
.
dataset
.
from
;
var
idx
=
e
.
target
.
dataset
.
idx
;
if
(
typeof
(
tagFrom
)
!=
'
undefined
'
&&
tagFrom
.
length
>
0
)
{
calMoreImageInfo
(
e
,
idx
,
that
,
tagFrom
)
}
}
// 假循环获取计算图片视觉最佳宽高
function
calMoreImageInfo
(
e
,
idx
,
that
,
bindName
)
{
var
temData
=
that
.
data
[
bindName
];
if
(
temData
.
images
.
length
==
0
)
{
return
;
}
var
temImages
=
temData
.
images
;
//因为无法获取view宽度 需要自定义padding进行计算,稍后处理
var
recal
=
wxAutoImageCal
(
e
.
detail
.
width
,
e
.
detail
.
height
,
that
,
bindName
);
temImages
[
idx
].
width
=
recal
.
imageWidth
;
temImages
[
idx
].
height
=
recal
.
imageheight
;
temData
.
images
=
temImages
;
var
bindData
=
{};
bindData
[
bindName
]
=
temData
;
that
.
setData
(
bindData
);
}
// 计算视觉优先的图片宽高
function
wxAutoImageCal
(
originalWidth
,
originalHeight
,
that
,
bindName
)
{
//获取图片的原始长宽
var
windowWidth
=
0
,
windowHeight
=
0
;
var
autoWidth
=
0
,
autoHeight
=
0
;
var
results
=
{};
wx
.
getSystemInfo
({
success
:
function
(
res
)
{
var
padding
=
that
.
data
[
bindName
].
view
.
imagePadding
;
windowWidth
=
res
.
windowWidth
-
2
*
padding
;
windowHeight
=
res
.
windowHeight
;
//判断按照那种方式进行缩放
// console.log("windowWidth" + windowWidth);
if
(
originalWidth
>
windowWidth
)
{
//在图片width大于手机屏幕width时候
autoWidth
=
windowWidth
;
// console.log("autoWidth" + autoWidth);
autoHeight
=
(
autoWidth
*
originalHeight
)
/
originalWidth
;
// console.log("autoHeight" + autoHeight);
results
.
imageWidth
=
autoWidth
;
results
.
imageheight
=
autoHeight
;
}
else
{
//否则展示原来的数据
results
.
imageWidth
=
originalWidth
;
results
.
imageheight
=
originalHeight
;
}
}
})
return
results
;
}
function
wxParseTemArray
(
temArrayName
,
bindNameReg
,
total
,
that
){
var
array
=
[];
var
temData
=
that
.
data
;
var
obj
=
null
;
for
(
var
i
=
0
;
i
<
total
;
i
++
){
var
simArr
=
temData
[
bindNameReg
+
i
].
nodes
;
array
.
push
(
simArr
);
}
temArrayName
=
temArrayName
||
'
wxParseTemArray
'
;
obj
=
JSON
.
parse
(
'
{"
'
+
temArrayName
+
'
":""}
'
);
obj
[
temArrayName
]
=
array
;
that
.
setData
(
obj
);
}
/**
* 配置emojis
*
*/
function
emojisInit
(
reg
=
''
,
baseSrc
=
"
/wxParse/emojis/
"
,
emojis
){
HtmlToJson
.
emojisInit
(
reg
,
baseSrc
,
emojis
);
}
module
.
exports
=
{
wxParse
:
wxParse
,
wxParseTemArray
:
wxParseTemArray
,
emojisInit
:
emojisInit
}
renard-wx/lib/wxParse/wxParse.wxml
0 → 100644
View file @
854bacf6
<!--**
* author: Di (微信小程序开发工程师)
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
* 垂直微信小程序开发交流社区
*
* github地址: https://github.com/icindy/wxParse
*
* for: 微信小程序富文本解析
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
*/-->
<!--基础元素-->
<template name="wxParseVideo">
<!--增加video标签支持,并循环添加-->
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<video class="{{item.classStr}} wxParse-{{item.tag}}-video" src="{{item.attr.src}}"></video>
</view>
</template>
<template name="wxParseImg">
<image class="{{item.classStr}} wxParse-{{item.tag}}" data-from="{{item.from}}" data-src="{{item.attr.src}}" data-idx="{{item.imgIndex}}" src="{{item.attr.src}}" mode="aspectFit" bindload="wxParseImgLoad" bindtap="wxParseImgTap" style="width:{{item.width}}px;height:{{item.height}}px;{{item.attr.style}}" />
</template>
<template name="WxEmojiView">
<view class="WxEmojiView wxParse-inline" style="{{item.styleStr}}">
<block wx:for="{{item.textArray}}" wx:key="">
<block class="{{item.text == '\\n' ? 'wxParse-hide':''}}" wx:if="{{item.node == 'text'}}">{{item.text}}</block>
<block wx:elif="{{item.node == 'element'}}">
<image class="wxEmoji" src="{{item.baseSrc}}{{item.text}}" />
</block>
</block>
</view>
</template>
<!--入口模版-->
<template name="wxParse">
<block wx:for="{{wxParseData}}" wx:key="">
<template is="wxParse0" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse0">
<!--<template is="wxParse1" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse1" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse1" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-c="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse1" data="{{item}}"/>
</block>
</view>
</block>
<block wx:elif="{{item.tag == 'table'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse1" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse1" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse1" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse1">
<!--<template is="wxParse2" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse2" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse2" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse2" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse2" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse2" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse2">
<!--<template is="wxParse3" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse3" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse3" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse3" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse3" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse3" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse3">
<!--<template is="wxParse4" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse4" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse4" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse4" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse4" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse4" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse4">
<!--<template is="wxParse5" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse5" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse5" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse5" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse5" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse5" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse5">
<!--<template is="wxParse6" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse6" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse6" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse6" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse6" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse6" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse6">
<!--<template is="wxParse7" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse7" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse7" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse7" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse7" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse7" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse7">
<!--<template is="wxParse8" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse8" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse8" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse8" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse8" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse8" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse8">
<!--<template is="wxParse9" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse9" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse9" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse9" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse9" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse9" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse9">
<!--<template is="wxParse10" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse10" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse10" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse10" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse10" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse10" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse10">
<!--<template is="wxParse11" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse11" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse11" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse11" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse11" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse11" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
<!--循环模版-->
<template name="wxParse11">
<!--<template is="wxParse12" data="{{item}}" />-->
<!--判断是否是标签节点-->
<block wx:if="{{item.node == 'element'}}">
<block wx:if="{{item.tag == 'button'}}">
<button type="default" size="mini" >
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse12" data="{{item}}"/>
</block>
</button>
</block>
<!--li类型-->
<block wx:elif="{{item.tag == 'li'}}">
<view class="{{item.classStr}} wxParse-li">
<view class="{{item.classStr}} wxParse-li-inner">
<view class="{{item.classStr}} wxParse-li-text">
<view class="{{item.classStr}} wxParse-li-circle"></view>
</view>
<view class="{{item.classStr}} wxParse-li-text">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse12" data="{{item}}"/>
</block>
</view>
</view>
</view>
</block>
<!--video类型-->
<block wx:elif="{{item.tag == 'video'}}">
<template is="wxParseVideo" data="{{item}}"/>
</block>
<!--img类型-->
<block wx:elif="{{item.tag == 'img'}}">
<template is="wxParseImg" data="{{item}}"/>
</block>
<!--a类型-->
<block wx:elif="{{item.tag == 'a'}}">
<view bindtap="wxParseTagATap" class="wxParse-inline {{item.classStr}} wxParse-{{item.tag}}" data-src="{{item.attr.href}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse12" data="{{item}}"/>
</block>
</view>
</block>
<!--其他块级标签-->
<block wx:elif="{{item.tagType == 'block'}}">
<view class="{{item.classStr}} wxParse-{{item.tag}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse12" data="{{item}}"/>
</block>
</view>
</block>
<!--内联标签-->
<view wx:else class="{{item.classStr}} wxParse-{{item.tag}} wxParse-{{item.tagType}}" style="{{item.styleStr}}">
<block wx:for="{{item.nodes}}" wx:for-item="item" wx:key="">
<template is="wxParse12" data="{{item}}"/>
</block>
</view>
</block>
<!--判断是否是文本节点-->
<block wx:elif="{{item.node == 'text'}}">
<!--如果是,直接进行-->
<template is="WxEmojiView" data="{{item}}"/>
</block>
</template>
\ No newline at end of file
renard-wx/lib/wxParse/wxParse.wxss
0 → 100644
View file @
854bacf6
/**
* author: Di (微信小程序开发工程师)
* organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com)
* 垂直微信小程序开发交流社区
*
* github地址: https://github.com/icindy/wxParse
*
* for: 微信小程序富文本解析
* detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184
*/
.wxParse{
margin: 0 5px;
font-family: Helvetica,sans-serif;
font-size: 28rpx;
color: #666;
line-height: 1.8;
}
view{
word-break:break-all; overflow:auto;
}
.wxParse-inline{
display: inline;
margin: 0;
padding: 0;
}
/*//标题 */
.wxParse-div{margin: 0;padding: 0;}
.wxParse-h1{ font-size:2em; margin: .67em 0 }
.wxParse-h2{ font-size:1.5em; margin: .75em 0 }
.wxParse-h3{ font-size:1.17em; margin: .83em 0 }
.wxParse-h4{ margin: 1.12em 0}
.wxParse-h5 { font-size:.83em; margin: 1.5em 0 }
.wxParse-h6{ font-size:.75em; margin: 1.67em 0 }
.wxParse-h1 {
font-size: 18px;
font-weight: 400;
margin-bottom: .9em;
}
.wxParse-h2 {
font-size: 16px;
font-weight: 400;
margin-bottom: .34em;
}
.wxParse-h3 {
font-weight: 400;
font-size: 15px;
margin-bottom: .34em;
}
.wxParse-h4 {
font-weight: 400;
font-size: 14px;
margin-bottom: .24em;
}
.wxParse-h5 {
font-weight: 400;
font-size: 13px;
margin-bottom: .14em;
}
.wxParse-h6 {
font-weight: 400;
font-size: 12px;
margin-bottom: .04em;
}
.wxParse-h1, .wxParse-h2, .wxParse-h3, .wxParse-h4, .wxParse-h5, .wxParse-h6, .wxParse-b, .wxParse-strong { font-weight: bolder }
.wxParse-i,.wxParse-cite,.wxParse-em,.wxParse-var,.wxParse-address{font-style:italic}
.wxParse-pre,.wxParse-tt,.wxParse-code,.wxParse-kbd,.wxParse-samp{font-family:monospace}
.wxParse-pre{white-space:pre}
.wxParse-big{font-size:1.17em}
.wxParse-small,.wxParse-sub,.wxParse-sup{font-size:.83em}
.wxParse-sub{vertical-align:sub}
.wxParse-sup{vertical-align:super}
.wxParse-s,.wxParse-strike,.wxParse-del{text-decoration:line-through}
/*wxparse-自定义个性化的css样式*/
/*增加video的css样式*/
.wxParse-strong,wxParse-s{display: inline}
.wxParse-a{
color: deepskyblue;
word-break:break-all;
overflow:auto;
}
.wxParse-video{
text-align: center;
margin: 10px 0;
}
.wxParse-video-video{
width:100%;
}
.wxParse-img{
background-color: #efefef;
overflow: hidden;
width:40px;
height: 40px;
}
.wxParse-blockquote {
margin: 0;
padding:10px 0 10px 5px;
font-family:Courier, Calibri,"宋体";
background:#f5f5f5;
border-left: 3px solid #dbdbdb;
}
.wxParse-code,.wxParse-wxxxcode-style{
display: inline;
background:#f5f5f5;
}
.wxParse-ul{
margin: 20rpx 10rpx;
}
.wxParse-li,.wxParse-li-inner{
display: flex;
align-items: baseline;
margin: 10rpx 0;
}
.wxParse-li-text{
align-items: center;
line-height: 20px;
}
.wxParse-li-circle{
display: inline-flex;
width: 5px;
height: 5px;
background-color: #333;
margin-right: 5px;
}
.wxParse-li-square{
display: inline-flex;
width: 10rpx;
height: 10rpx;
background-color: #333;
margin-right: 5px;
}
.wxParse-li-ring{
display: inline-flex;
width: 10rpx;
height: 10rpx;
border: 2rpx solid #333;
border-radius: 50%;
background-color: #fff;
margin-right: 5px;
}
/*.wxParse-table{
width: 100%;
height: 400px;
}
.wxParse-thead,.wxParse-tfoot,.wxParse-tr{
display: flex;
flex-direction: row;
}
.wxParse-th,.wxParse-td{
display: flex;
width: 580px;
overflow: auto;
}*/
.wxParse-u {
text-decoration: underline;
}
.wxParse-hide{
display: none;
}
.WxEmojiView{
align-items: center;
}
.wxEmoji{
width: 16px;
height:16px;
}
.wxParse-tr{
display: flex;
border-right:1px solid #e0e0e0;
border-bottom:1px solid #e0e0e0;
}
.wxParse-th,
.wxParse-td{
flex:1;
padding:5px;
font-size:28rpx;
border-left:1px solid #e0e0e0;
word-break: break-all;
}
.wxParse-td:last{
border-top:1px solid #e0e0e0;
}
.wxParse-th{
background:#f0f0f0;
border-top:1px solid #e0e0e0;
}
renard-wx/pages/auth/login/login.js
0 → 100644
View file @
854bacf6
var
api
=
require
(
'
../../../config/api.js
'
);
var
util
=
require
(
'
../../../utils/util.js
'
);
var
user
=
require
(
'
../../../utils/user.js
'
);
var
app
=
getApp
();
Page
({
data
:
{
username
:
''
,
password
:
''
,
code
:
''
,
loginErrorCount
:
0
},
onLoad
:
function
(
options
)
{
// 页面初始化 options为页面跳转所带来的参数
// 页面渲染完成
},
onReady
:
function
()
{
},
onShow
:
function
()
{
// 页面显示
},
onHide
:
function
()
{
// 页面隐藏
},
onUnload
:
function
()
{
// 页面关闭
},
wxLogin
:
function
(
e
)
{
if
(
e
.
detail
.
userInfo
==
undefined
){
app
.
globalData
.
hasLogin
=
false
;
util
.
showErrorToast
(
'
微信登录失败
'
);
return
;
}
user
.
checkLogin
().
catch
(()
=>
{
user
.
loginByWeixin
(
e
.
detail
.
userInfo
).
then
(
res
=>
{
app
.
globalData
.
hasLogin
=
true
;
wx
.
navigateBack
({
delta
:
1
})
}).
catch
((
err
)
=>
{
app
.
globalData
.
hasLogin
=
false
;
util
.
showErrorToast
(
'
微信登录失败
'
);
});
});
},
accountLogin
:
function
()
{
var
that
=
this
;
if
(
this
.
data
.
password
.
length
<
1
||
this
.
data
.
username
.
length
<
1
)
{
wx
.
showModal
({
title
:
'
错误信息
'
,
content
:
'
请输入用户名和密码
'
,
showCancel
:
false
});
return
false
;
}
wx
.
request
({
url
:
api
.
AuthLoginByAccount
,
data
:
{
username
:
that
.
data
.
username
,
password
:
that
.
data
.
password
},
method
:
'
POST
'
,
header
:
{
'
content-type
'
:
'
application/json
'
},
success
:
function
(
res
)
{
if
(
res
.
data
.
errno
==
0
){
that
.
setData
({
loginErrorCount
:
0
});
app
.
globalData
.
hasLogin
=
true
;
wx
.
setStorageSync
(
'
userInfo
'
,
res
.
data
.
data
.
userInfo
);
wx
.
setStorage
({
key
:
"
token
"
,
data
:
res
.
data
.
data
.
token
,
success
:
function
(){
wx
.
switchTab
({
url
:
'
/pages/ucenter/index/index
'
});
}
});
}
else
{
that
.
setData
({
loginErrorCount
:
that
.
data
.
loginErrorCount
+
1
});
app
.
globalData
.
hasLogin
=
false
;
util
.
showErrorToast
(
'
账户登录失败
'
);
}
}
});
},
bindUsernameInput
:
function
(
e
)
{
this
.
setData
({
username
:
e
.
detail
.
value
});
},
bindPasswordInput
:
function
(
e
)
{
this
.
setData
({
password
:
e
.
detail
.
value
});
},
bindCodeInput
:
function
(
e
)
{
this
.
setData
({
code
:
e
.
detail
.
value
});
},
clearInput
:
function
(
e
)
{
switch
(
e
.
currentTarget
.
id
)
{
case
'
clear-username
'
:
this
.
setData
({
username
:
''
});
break
;
case
'
clear-password
'
:
this
.
setData
({
password
:
''
});
break
;
case
'
clear-code
'
:
this
.
setData
({
code
:
''
});
break
;
}
}
})
\ No newline at end of file
renard-wx/pages/auth/login/login.json
0 → 100644
View file @
854bacf6
{
"navigationBarTitleText"
:
"登录"
}
\ No newline at end of file
renard-wx/pages/auth/login/login.wxml
0 → 100644
View file @
854bacf6
<view class="container">
<view class="form-box">
<!-- <view class="form-item">
<input class="username" value="{{username}}" bindinput="bindUsernameInput" placeholder="账号"/>
<image wx:if="{{ username.length > 0 }}" id="clear-username" class="clear" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<view class="form-item">
<input class="password" value="{{password}}" password bindinput="bindPasswordInput" placeholder="密码"/>
<image class="clear" id="clear-password" wx:if="{{ password.length > 0 }}" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<view class="form-item-code" wx-if="{{loginErrorCount >= 3}}">
<view class="form-item code-item">
<input class="code" value="{{code}}" bindinput="bindCodeInput" placeholder="验证码"/>
<image class="clear" id="clear-code" wx:if="{{ code.length > 0 }}" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<image class="code-img" src="https://dl.reg.163.com/cp?pd=yanxuan_web&pkid=SkeBZeG&random=1489903563234"></image>
</view>
<button type="default" class="login-btn" bindtap="accountLogin">账号登录</button>
<view class="form-item-text">
<navigator url="/pages/auth/register/register" class="register">注册账号</navigator>
<navigator url="/pages/auth/reset/reset" class="reset">忘记密码</navigator>
</view> -->
<button type="primary" open-type="getUserInfo" class="login-btn" bindgetuserinfo="wxLogin">微信直接登录</button>
</view>
</view>
\ No newline at end of file
renard-wx/pages/auth/login/login.wxss
0 → 100644
View file @
854bacf6
.form-box{
width: 100%;
height: auto;
overflow: hidden;
padding: 0 40rpx;
margin-top: 96rpx;
background: #fff;
}
.form-item{
position: relative;
background: #fff;
height: 96rpx;
border-bottom: 1px solid #a78845;
}
.form-item .username, .form-item .password, .form-item .code{
position: absolute;
top: 26rpx;
left: 0;
display: block;
width: 100%;
height: 44rpx;
background: #fff;
color: #a78845;
font-size: 30rpx;
}
.form-item-code{
margin-top:32rpx;
height: auto;
overflow: hidden;
width: 100%;
}
.form-item-code .form-item{
float: left;
width: 350rpx;
}
.form-item-code .code-img{
float: right;
margin-top: 4rpx;
height: 88rpx;
width: 236rpx;
}
.form-item .clear{
position: absolute;
top: 26rpx;
right: 18rpx;
z-index: 2;
display: block;
background: #fff;
height: 44rpx;
width: 44rpx;
}
.login-btn{
margin: 60rpx 0 40rpx 0;
height: 96rpx;
line-height: 96rpx;
color: #a78845;
font-size: 30rpx;
width: 100%;
background: #b4282d;
border-radius: 6rpx;
}
.form-item-text{
height: 35rpx;
width: 100%;
color: #a78845;
}
.form-item-text .register{
display: block;
height: 34rpx;
float: left;
font-size: 28rpx;
color: #a78845;
}
.form-item-text .reset{
display: block;
height: 34rpx;
float: right;
font-size: 28rpx;
color: #a78845;
}
\ No newline at end of file
renard-wx/pages/auth/register/register.js
0 → 100644
View file @
854bacf6
var
api
=
require
(
'
../../../config/api.js
'
);
var
check
=
require
(
'
../../../utils/check.js
'
);
var
app
=
getApp
();
Page
({
data
:
{
username
:
''
,
password
:
''
,
confirmPassword
:
''
,
mobile
:
''
,
code
:
''
},
onLoad
:
function
(
options
)
{
// 页面初始化 options为页面跳转所带来的参数
// 页面渲染完成
},
onReady
:
function
()
{
},
onShow
:
function
()
{
// 页面显示
},
onHide
:
function
()
{
// 页面隐藏
},
onUnload
:
function
()
{
// 页面关闭
},
sendCode
:
function
()
{
wx
.
showModal
({
title
:
'
注意
'
,
content
:
'
由于目前不支持手机短信发送,因此验证码任意值都可以
'
,
showCancel
:
false
});
},
startRegister
:
function
()
{
var
that
=
this
;
if
(
this
.
data
.
password
.
length
<
3
||
this
.
data
.
username
.
length
<
3
)
{
wx
.
showModal
({
title
:
'
错误信息
'
,
content
:
'
用户名和密码不得少于3位
'
,
showCancel
:
false
});
return
false
;
}
if
(
this
.
data
.
password
!=
this
.
data
.
confirmPassword
)
{
wx
.
showModal
({
title
:
'
错误信息
'
,
content
:
'
确认密码不一致
'
,
showCancel
:
false
});
return
false
;
}
if
(
this
.
data
.
mobile
.
length
==
0
||
this
.
data
.
code
.
length
==
0
)
{
wx
.
showModal
({
title
:
'
错误信息
'
,
content
:
'
手机号和验证码不能为空
'
,
showCancel
:
false
});
return
false
;
}
if
(
!
check
.
isValidPhone
(
this
.
data
.
mobile
))
{
wx
.
showModal
({
title
:
'
错误信息
'
,
content
:
'
手机号输入不正确
'
,
showCancel
:
false
});
return
false
;
}
wx
.
request
({
url
:
api
.
AuthRegister
,
data
:
{
username
:
that
.
data
.
username
,
password
:
that
.
data
.
password
,
mobile
:
that
.
data
.
mobile
,
code
:
that
.
data
.
code
},
method
:
'
POST
'
,
header
:
{
'
content-type
'
:
'
application/json
'
},
success
:
function
(
res
)
{
if
(
res
.
data
.
errno
==
0
)
{
app
.
globalData
.
hasLogin
=
true
;
wx
.
setStorageSync
(
'
userInfo
'
,
res
.
data
.
data
.
userInfo
);
wx
.
setStorage
({
key
:
"
token
"
,
data
:
res
.
data
.
data
.
token
,
success
:
function
()
{
wx
.
switchTab
({
url
:
'
/pages/ucenter/index/index
'
});
}
});
}
else
{
wx
.
showModal
({
title
:
'
错误信息
'
,
content
:
res
.
data
.
errmsg
,
showCancel
:
false
});
}
}
});
},
bindUsernameInput
:
function
(
e
)
{
this
.
setData
({
username
:
e
.
detail
.
value
});
},
bindPasswordInput
:
function
(
e
)
{
this
.
setData
({
password
:
e
.
detail
.
value
});
},
bindConfirmPasswordInput
:
function
(
e
)
{
this
.
setData
({
confirmPassword
:
e
.
detail
.
value
});
},
bindMobileInput
:
function
(
e
)
{
this
.
setData
({
mobile
:
e
.
detail
.
value
});
},
bindCodeInput
:
function
(
e
)
{
this
.
setData
({
code
:
e
.
detail
.
value
});
},
clearInput
:
function
(
e
)
{
switch
(
e
.
currentTarget
.
id
)
{
case
'
clear-username
'
:
this
.
setData
({
username
:
''
});
break
;
case
'
clear-password
'
:
this
.
setData
({
password
:
''
});
break
;
case
'
clear-confirm-password
'
:
this
.
setData
({
confirmPassword
:
''
});
break
;
case
'
clear-mobile
'
:
this
.
setData
({
mobile
:
''
});
break
;
case
'
clear-code
'
:
this
.
setData
({
code
:
''
});
break
;
}
}
})
\ No newline at end of file
renard-wx/pages/auth/register/register.json
0 → 100644
View file @
854bacf6
{
"navigationBarTitleText"
:
"注册"
}
\ No newline at end of file
renard-wx/pages/auth/register/register.wxml
0 → 100644
View file @
854bacf6
<view class="container">
<view class="form-box">
<view class="form-item">
<input class="username" value="{{username}}" bindinput="bindUsernameInput" placeholder="用户名" auto-focus/>
<image wx:if="{{ username.length > 0 }}" id="clear-username" class="clear" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<view class="form-item">
<input class="password" value="{{password}}" password bindinput="bindPasswordInput" placeholder="密码"/>
<image class="clear" id="clear-password" wx:if="{{ password.length > 0 }}" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<view class="form-item">
<input class="password" value="{{confirmPassword}}" password bindinput="bindConfirmPasswordInput" placeholder="确认密码"/>
<image class="clear" id="clear-confirm-password" wx:if="{{ confirmPassword.length > 0 }}" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<view class="form-item">
<input class="mobile" value="{{mobile}}" bindinput="bindMobileInput" placeholder="手机号" />
<image wx:if="{{ mobile.length > 0 }}" id="clear-mobile" class="clear" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<view class="form-item-code" >
<view class="form-item code-item">
<input class="code" value="{{code}}" bindinput="bindCodeInput" placeholder="验证码"/>
<image class="clear" id="clear-code" wx:if="{{ code.length > 0 }}" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<view class="code-btn" bindtap="sendCode">获取验证码</view>
</view>
<button type="default" class="register-btn" bindtap="startRegister">注册</button>
</view>
</view>
\ No newline at end of file
renard-wx/pages/auth/register/register.wxss
0 → 100644
View file @
854bacf6
.form-box{
width: 100%;
height: auto;
overflow: hidden;
padding: 0 40rpx;
margin-top: 96rpx;
background: #fff;
}
.form-item{
position: relative;
background: #fff;
height: 96rpx;
border-bottom: 1px solid #a78845;
}
.form-item .username, .form-item .password, .form-item .mobile, .form-item .code{
position: absolute;
top: 26rpx;
left: 0;
display: block;
width: 100%;
height: 44rpx;
background: #fff;
color: #a78845;
font-size: 30rpx;
}
.form-item-code{
margin-top:32rpx;
height: auto;
overflow: hidden;
width: 100%;
}
.form-item-code .form-item{
float: left;
width: 350rpx;
}
.form-item-code .code-btn{
float: right;
padding: 20rpx 40rpx;
border: 1px solid #d9d9d9;
color: #a78845;
border-radius: 10rpx;
}
.form-item .clear{
position: absolute;
top: 26rpx;
right: 18rpx;
z-index: 2;
display: block;
background: #fff;
height: 44rpx;
width: 44rpx;
}
.register-btn{
margin: 60rpx 0 40rpx 0;
height: 96rpx;
line-height: 96rpx;
color: #a78845;
font-size: 30rpx;
width: 100%;
background: #b4282d;
border-radius: 6rpx;
}
\ No newline at end of file
renard-wx/pages/auth/reset/reset.js
0 → 100644
View file @
854bacf6
var
api
=
require
(
'
../../../config/api.js
'
);
var
check
=
require
(
'
../../../utils/check.js
'
);
var
app
=
getApp
();
Page
({
data
:
{
mobile
:
''
,
code
:
''
,
password
:
''
,
confirmPassword
:
''
},
onLoad
:
function
(
options
)
{
// 页面初始化 options为页面跳转所带来的参数
// 页面渲染完成
},
onReady
:
function
()
{
},
onShow
:
function
()
{
// 页面显示
},
onHide
:
function
()
{
// 页面隐藏
},
onUnload
:
function
()
{
// 页面关闭
},
sendCode
:
function
()
{
wx
.
showModal
({
title
:
'
注意
'
,
content
:
'
由于目前不支持手机短信发送,因此验证码任意值都可以
'
,
showCancel
:
false
});
},
startReset
:
function
(){
var
that
=
this
;
if
(
this
.
data
.
mobile
.
length
==
0
||
this
.
data
.
code
.
length
==
0
)
{
wx
.
showModal
({
title
:
'
错误信息
'
,
content
:
'
手机号和验证码不能为空
'
,
showCancel
:
false
});
return
false
;
}
if
(
!
check
.
isValidPhone
(
this
.
data
.
mobile
))
{
wx
.
showModal
({
title
:
'
错误信息
'
,
content
:
'
手机号输入不正确
'
,
showCancel
:
false
});
return
false
;
}
if
(
this
.
data
.
password
.
length
<
3
)
{
wx
.
showModal
({
title
:
'
错误信息
'
,
content
:
'
用户名和密码不得少于3位
'
,
showCancel
:
false
});
return
false
;
}
if
(
this
.
data
.
password
!=
this
.
data
.
confirmPassword
)
{
wx
.
showModal
({
title
:
'
错误信息
'
,
content
:
'
确认密码不一致
'
,
showCancel
:
false
});
return
false
;
}
wx
.
request
({
url
:
api
.
AuthReset
,
data
:
{
mobile
:
that
.
data
.
mobile
,
code
:
that
.
data
.
code
,
password
:
that
.
data
.
password
},
method
:
'
POST
'
,
header
:
{
'
content-type
'
:
'
application/json
'
},
success
:
function
(
res
)
{
if
(
res
.
data
.
errno
==
0
)
{
wx
.
navigateBack
();
}
else
{
wx
.
showModal
({
title
:
'
密码重置失败
'
,
content
:
res
.
data
.
errmsg
,
showCancel
:
false
});
}
}
});
},
bindPasswordInput
:
function
(
e
)
{
this
.
setData
({
password
:
e
.
detail
.
value
});
},
bindConfirmPasswordInput
:
function
(
e
)
{
this
.
setData
({
confirmPassword
:
e
.
detail
.
value
});
},
bindMobileInput
:
function
(
e
)
{
this
.
setData
({
mobile
:
e
.
detail
.
value
});
},
bindCodeInput
:
function
(
e
){
this
.
setData
({
code
:
e
.
detail
.
value
});
},
clearInput
:
function
(
e
){
switch
(
e
.
currentTarget
.
id
){
case
'
clear-password
'
:
this
.
setData
({
password
:
''
});
break
;
case
'
clear-confirm-password
'
:
this
.
setData
({
confirmPassword
:
''
});
break
;
case
'
clear-mobile
'
:
this
.
setData
({
mobile
:
''
});
break
;
case
'
clear-code
'
:
this
.
setData
({
code
:
''
});
break
;
}
}
})
\ No newline at end of file
renard-wx/pages/auth/reset/reset.json
0 → 100644
View file @
854bacf6
{
"navigationBarTitleText"
:
"密码重置"
}
\ No newline at end of file
renard-wx/pages/auth/reset/reset.wxml
0 → 100644
View file @
854bacf6
<view class="container">
<view class="form-box">
<view class="form-item">
<input class="mobile" value="{{mobile}}" bindinput="bindMobileInput" placeholder="手机号" />
<image wx:if="{{ mobile.length > 0 }}" id="clear-mobile" class="clear" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<view class="form-item-code">
<view class="form-item code-item">
<input class="code" value="{{code}}" bindinput="bindCodeInput" placeholder="验证码" />
<image class="clear" id="clear-code" wx:if="{{ code.length > 0 }}" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<view class="code-btn" bindtap="sendCode">获取验证码</view>
</view>
<view class="form-item">
<input class="password" value="{{password}}" password bindinput="bindPasswordInput" placeholder="密码" />
<image class="clear" id="clear-password" wx:if="{{ password.length > 0 }}" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<view class="form-item">
<input class="password" value="{{confirmPassword}}" password bindinput="bindConfirmPasswordInput" placeholder="确认密码" />
<image class="clear" id="clear-confirm-password" wx:if="{{ confirmPassword.length > 0 }}" src="/static/images/clear_input.png" catchtap="clearInput"></image>
</view>
<button type="default" class="reset-btn" bindtap="startReset">密码重置</button>
</view>
</view>
\ No newline at end of file
renard-wx/pages/auth/reset/reset.wxss
0 → 100644
View file @
854bacf6
.form-box{
width: 100%;
height: auto;
overflow: hidden;
padding: 0 40rpx;
margin-top: 96rpx;
background: #fff;
}
.form-item{
position: relative;
background: #fff;
height: 96rpx;
border-bottom: 1px solid #d9d9d9;
}
.form-item .mobile, .form-item .password, .form-item .code{
position: absolute;
top: 26rpx;
left: 0;
display: block;
width: 100%;
height: 44rpx;
background: #fff;
color: #333;
font-size: 30rpx;
}
.form-item-code{
margin-top:32rpx;
height: auto;
overflow: hidden;
width: 100%;
}
.form-item-code .form-item{
float: left;
width: 350rpx;
}
.form-item-code .code-btn{
float: right;
padding: 20rpx 40rpx;
border: 1px solid #d9d9d9;
border-radius: 10rpx;
}
.form-item .clear{
position: absolute;
top: 26rpx;
right: 18rpx;
z-index: 2;
display: block;
background: #fff;
height: 44rpx;
width: 44rpx;
}
.reset-btn{
margin: 60rpx 0 40rpx 0;
height: 96rpx;
line-height: 96rpx;
color: #fff;
font-size: 30rpx;
width: 100%;
background: #b4282d;
border-radius: 6rpx;
}
\ No newline at end of file
Prev
1
2
3
4
5
6
7
8
…
12
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment