line-chart
Advanced tools
Comparing version 1.0.6 to 2.0.0
{ | ||
"name": "line-chart", | ||
"version": "1.0.6", | ||
"version": "2.0.0", | ||
"description": "Line Chart", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -13,6 +13,8 @@ ## 雷達圖 Radar Chart | ||
var data = [ | ||
{title: "寫作能力", tTitle: "精熟平均", bTitle: "基礎平均", rightRate: 0.2, basicRate:0.5, skilledRate: 0.7}, | ||
{title: "形音義", tTitle: "精熟平均", bTitle: "基礎平均", rightRate: 0.8, basicRate:0.6, skilledRate: 0.9}, | ||
{title: "詞句成語", tTitle: "精熟平均", bTitle: "基礎平均", rightRate: 0.7, basicRate:0.5, skilledRate: 0.8}, | ||
{title: "詞句成語2", tTitle: "精熟平均", bTitle: "基礎平均", rightRate: 1, basicRate:0.5, skilledRate: 0.8} | ||
{title: "寫作能力", tTitle: "精熟平均", bTitle: "基礎平均", rightRate: 0.2, basicRate:0.5, skilledRate: 0.7}, | ||
{title: "形音義", tTitle: "精熟平均", bTitle: "基礎平均", rightRate: 0.8, basicRate:0.6, skilledRate: 0.9}, | ||
{title: "詞句成語", tTitle: "精熟平均", bTitle: "基礎平均", rightRate: 0.7, basicRate:0.5, skilledRate: 0.8}, | ||
{title: "java", tTitle: "精熟平均", bTitle: "基礎平均", rightRate: 0.01, basicRate:0.5, skilledRate: 0.8}, | ||
{title: "java", tTitle: "精熟平均", bTitle: "基礎平均", rightRate: 0.02, basicRate:0.5, skilledRate: 0.5}, | ||
{title: "java", tTitle: "精熟平均", bTitle: "基礎平均", rightRate: 1, basicRate:0.5, skilledRate: 0.8} | ||
]; | ||
@@ -19,0 +21,0 @@ |
(function(){ | ||
var LineChart = { | ||
options : { | ||
viewBox : '0,0,1280,610', | ||
viewBox : '0,0,1100,120', | ||
preserveAspectRatio : "none", | ||
chart : {width:'100%', height:'100%'}, | ||
axisZoom : 0.8, //縮放 (縱軸padding功用) | ||
maxValue : 100, //軸的最大值 | ||
axisZoom : 1, //縮放 | ||
maxValue : 100, //軸最大值 | ||
minValue : 0, //軸的最小值 | ||
scale : 10, //軸刻度數 | ||
scaleNeedleH : 15, //刻度軸刻度垂直線高度 | ||
scaleNeedleHColor : "grey", //刻度軸刻度垂直線顏色 | ||
scaleTextGap : 0, | ||
scaleTextOffsetH : 5, | ||
axisHeight : 20, //line bar height | ||
radius: {rx:0, ry:0}, //圓角 | ||
arc : 2 * Math.PI, //圓弧 | ||
top : 50, // | ||
left: 10, // | ||
barTop : 35, | ||
gap : 90, //lineBar , text 通用垂直gap | ||
hGap : 10, //lineBar 與 text 之間水平 gap | ||
barHeight : 35, | ||
barBackOpacity : 0.4, //lineBar 背景透明度 | ||
barBackColor : 'grey', //lineBar 背景色 | ||
scoreOpacity : 0.9, //lineBar 使用者成績透明 | ||
scoreBackColor : '#ff9800', // | ||
surmiseFontGap: 3, //假設的font gap | ||
color : d3.scale.category10(), | ||
//標記 | ||
tagRadius : 6, //標記大小 | ||
tagOpacity : 0.9, //標記透明 | ||
topTagColor : '#259b24', // | ||
bottomTagColor : '#29b6f6', | ||
tagOffset : 15, | ||
//指針 | ||
topNeedleColor : '#259b24', | ||
topNeedleOpacity : 0.8, | ||
bottomNeedleColor : '#29b6f6', | ||
bottomNeedOpacity : 0.8, | ||
//標記標題 | ||
topTitleGap :15, | ||
bottomTitleGap : 15, | ||
//user score | ||
scoreOffsetH : 5, //使用者分數位置左水平間隔用 | ||
scoreOffsetV : 0, //使用者分數位置垂直校正用 | ||
scoreColor : 'black', | ||
isDebug : false | ||
zoomW : 0.85, //水平縮放係數 | ||
zoomH : 1, //垂直縮放係數 | ||
//刻度 | ||
scale : { | ||
count : 10, | ||
Height : 15, //刻度軸刻度垂直線高度 | ||
color : "grey", //刻度顏色 | ||
textGap : 0 | ||
}, | ||
bar :{ | ||
//繪製起點 | ||
top:0, | ||
left:20 , | ||
height : 35, //條的高度 | ||
rx : 15 , //圓角x | ||
ry : 15 , //圓角y | ||
}, | ||
background : { | ||
color : '#eeeeee', | ||
opacity : 0.7, //透明 | ||
textColor : '#ff9800' | ||
}, | ||
//bar 主要資訊 | ||
mainInfo : { | ||
color : '#ffeb3b', | ||
opacity : 0.9, | ||
textColor : '#212121', | ||
fontSize : 16, | ||
textGapX : 5, | ||
textGapY : 5 | ||
}, | ||
gradien : [ | ||
{ offset : '0%' , color : "#ffeb3b" , opacity : "1"}, | ||
{ offset : '50%' , color : "#fdd835" , opacity : "1"}, | ||
{ offset : '100%' , color : "#f5f5f5" , opacity : "1"}, | ||
], | ||
//上標記 | ||
tMark : { | ||
radius : 8, | ||
opacity : 0.9, | ||
color : '#673ab7', | ||
gap : 15, //與bar的gap | ||
lineColor : '#673ab7', | ||
lineOpacity : 0.5, | ||
isDash : true, | ||
offsetY : -15, | ||
textGapX : 15, | ||
fontSize : 16, | ||
text : '精熟平均' | ||
}, | ||
//下標記 | ||
bMark : { | ||
radius : 8, | ||
opacity : 0.9, | ||
color : '#5677fc', | ||
gap : 15, | ||
lineColor : '#29b6f6', | ||
lineOpacity : 0.7, | ||
isDash : true, | ||
offsetY : 15, | ||
textGapX : 15, | ||
fontSize : 16, | ||
text : '基礎平均' | ||
} | ||
}, | ||
@@ -63,175 +88,192 @@ mixOptions : function(options){ | ||
}, | ||
//利用三角函式計算坐標點 | ||
getPoint : function(radius, radians, offsetX, offsetY){ | ||
return { | ||
x : radius * Math.sin(radians) + offsetX, | ||
y : radius * Math.cos(radians) + offsetY | ||
}; | ||
para : null, | ||
isFloat : function(n){ | ||
return n % 1 !== 0; | ||
}, | ||
titleWidth : 0, | ||
//繪製軸上的標題 | ||
drawAxisText : function(svg, title, group, p, width, opt, self){ | ||
var text = group.append('text').text(title).attr('x', p.x).attr('y', p.y); | ||
var fontSize = text.style("font-size").replace('pt','').replace('px','') | 0; | ||
var gapTotal = opt.surmiseFontGap * (title.length - 1); | ||
var offsetW = (fontSize * title.length + gapTotal) ; // | ||
if(offsetW > self.titleWidth) | ||
self.titleWidth = offsetW; | ||
formatStr : function(val){ | ||
return (val * 100 | 0) / 100; | ||
}, | ||
//繪製bar | ||
drawLineBar : function(svg, group, p, minLength, color, opacity, opt, self){ | ||
group.append('rect') | ||
.attr('x', p.x) | ||
.attr('y', p.y) | ||
.attr('width', minLength) | ||
.attr('height', opt.barHeight) | ||
.attr('fill', color) | ||
.attr('fill-opacity', opacity); | ||
randerMarkText : function(svg, g, x, y, text, fontSize, color){ | ||
g.append('text') | ||
.attr('x', x) | ||
.attr('y', y) | ||
.attr('fill', color) | ||
.attr('font-size', fontSize+'px') | ||
.text(text); | ||
}, | ||
drawTag : function(svg, group, cx, cy, r, color, opacity, opt, self){ | ||
group.append('circle') | ||
randerMarkLine : function(svg, g, p1, p2, color, isDash, opacity){ | ||
var line = g.append('line'); | ||
line.attr('stroke',color) | ||
.attr('stroke-opacity', opacity) | ||
.attr('x1', p1.x) | ||
.attr('y1', p1.y) | ||
.attr('x2', p2.x) | ||
.attr('y2', p2.y); | ||
if(isDash){ | ||
line.attr('stroke-dasharray', '5 5'); | ||
} | ||
}, | ||
randerTopMark : function(svg, g, parameter, opt, self){ | ||
var cy = (parameter.chartHeight - opt.bar.height) / 2 + opt.bar.top + opt.tMark.offsetY; | ||
var basic = parameter.chartWidth / (opt.maxValue - opt.minValue); | ||
var val = Number(svg.datum().skilledRate ) * 100; | ||
var w = basic * val; | ||
var cx = w + opt.bar.left; | ||
//指針 | ||
var p1 = { x : cx, y : cy}; | ||
var p2 = { x : cx, y : (parameter.chartHeight - opt.bar.height) / 2 + opt.bar.top + opt.bar.height}; | ||
svg.call(self.randerMarkLine,g, p1, p2, opt.tMark.lineColor, opt.tMark.isDash, opt.tMark.lineOpacity); | ||
//文字 | ||
var ty = p1.y + (opt.bMark.fontSize - opt.bMark.radius) /2; | ||
var tx = p1.x + opt.tMark.textGapX; | ||
var text = opt.tMark.text + ' : '; | ||
if(val > 0){ | ||
if( self.isFloat(val) ){ | ||
text += self.formatStr(val); | ||
} | ||
else{ | ||
text += val; | ||
} | ||
} | ||
svg.call(self.randerMarkText,g, tx , ty, text , opt.tMark.fontSize , opt.tMark.color); | ||
//標記 | ||
g.append('circle') | ||
.attr('cx', cx) | ||
.attr('cy', cy) | ||
.attr('r', r) | ||
.attr('fill', color) | ||
.attr('fill-opacity', opacity); | ||
.attr('r', opt.tMark.radius * opt.zoomW) | ||
.attr('fill', opt.tMark.color) | ||
.attr('fill-opacity', opt.tMark.opacity); | ||
}, | ||
drawNeedle: function(svg, group , p1, p2, color, opacity){ | ||
group.append('line') | ||
.attr('stroke', color) | ||
.attr('stroke-opacity', opacity) | ||
.attr('x1', p1.x) | ||
.attr('y1', p1.y) | ||
.attr('x2', p2.x) | ||
.attr('y2', p2.y); | ||
randerBottomMark : function(svg, g, parameter, opt, self){ | ||
var cy = (parameter.chartHeight - opt.bar.height) / 2 + opt.bar.top + opt.bar.height + + opt.bMark.offsetY; | ||
var basic = parameter.chartWidth / (opt.maxValue - opt.minValue); | ||
var val = Number(svg.datum().basicRate ) * 100; | ||
var w = basic * val; | ||
var cx = w + opt.bar.left; | ||
//指針 | ||
var p1 = { x : cx, y : cy}; | ||
var p2 = { x : cx, y : (parameter.chartHeight - opt.bar.height) / 2 + opt.bar.top}; | ||
svg.call(self.randerMarkLine,g, p1, p2, opt.bMark.lineColor, opt.bMark.isDash, opt.bMark.lineOpacity); | ||
//文字 | ||
var ty = p1.y + (opt.bMark.fontSize - opt.bMark.radius) /2; | ||
var tx = p1.x + opt.bMark.textGapX; | ||
var text = opt.bMark.text + ' : '; | ||
if(val > 0){ | ||
if( self.isFloat(val) ){ | ||
text += self.formatStr(val); | ||
} | ||
else{ | ||
text += val; | ||
} | ||
} | ||
svg.call(self.randerMarkText,g, tx , ty, text , opt.bMark.fontSize , opt.bMark.color); | ||
//標記 | ||
g.append('circle') | ||
.attr('cx', cx) | ||
.attr('cy', cy) | ||
.attr('r', opt.bMark.radius * opt.zoomW) | ||
.attr('fill', opt.bMark.color) | ||
.attr('fill-opacity', opt.bMark.opacity); | ||
}, | ||
drawTagTitle:function(svg, group, p, color, title, value, opt){ | ||
var textTitle = title + " : " + value; | ||
var text = group.append('text'); | ||
text.attr('y', p.y) | ||
.attr('x', p.x) | ||
.attr('fill', color) | ||
.text(textTitle); | ||
}, | ||
drawUserScoreTitle : function(svg, group, p, color, value, opt){ | ||
var textTitle = value + ''; | ||
var text = group.append('text'); | ||
text.attr('fill', color) | ||
.text(textTitle); | ||
randerMainSourceText:function(svg, g, parameter, opt, self){ | ||
var val = self.formatStr( Number(svg.datum().rightRate) * 100 ); | ||
var y = (parameter.chartHeight / 2) + opt.bar.top + (opt.mainInfo.fontSize / 2); | ||
var basic = parameter.chartWidth / (opt.maxValue - opt.minValue); | ||
var x = basic * val + opt.bar.left; | ||
var text = '0'; | ||
var offsetX = 0; | ||
var fontSize = text.style("font-size").replace('pt','').replace('px','') | 0; | ||
var gapTotal = opt.surmiseFontGap * (textTitle.length - 1); | ||
var offsetW = (fontSize * textTitle.length + gapTotal) / 2; | ||
var offsetH = (opt.axisHeight ) / 2 + fontSize; | ||
p.x -= (offsetW + opt.scoreOffsetH); | ||
p.y += offsetH + opt.scoreOffsetV; | ||
text.attr('x', p.x); | ||
text.attr('y', p.y); | ||
if(val > 0){ | ||
if( self.isFloat(val) ){ | ||
text = val + ''; | ||
if(val > 1) | ||
offsetX = (text.length-1) * opt.mainInfo.fontSize; | ||
} | ||
else{ | ||
text = val+''; | ||
if(val > 1){ | ||
offsetX = (text.length) * opt.mainInfo.fontSize; | ||
}else{ | ||
x = opt.bar.left; | ||
} | ||
} | ||
} | ||
g.append('text') | ||
.attr('font-size', opt.mainInfo.fontSize + 'px') | ||
.text(text) | ||
.attr('x', x - offsetX ) | ||
.attr('y', y) | ||
.attr('fill', opt.mainInfo.textColor); | ||
}, | ||
drawScalaText : function(svg, group, p, color, value, opt){ | ||
var textTitle = value + ''; | ||
var text = group.append('text'); | ||
text.attr('fill', color) | ||
.text(textTitle); | ||
var fontSize = text.style("font-size").replace('pt','').replace('px','') | 0; | ||
var gapTotal = opt.surmiseFontGap * (textTitle.length - 1); | ||
var offsetW = (fontSize * textTitle.length + gapTotal) / 2; | ||
var offsetH = (opt.axisHeight ) / 2 + fontSize; | ||
p.x -= offsetW; | ||
p.y += offsetH; | ||
text.attr('x', p.x); | ||
text.attr('y', p.y); | ||
randerMainSourceBar : function(svg, g, parameter, opt, self){ | ||
var d = svg.datum(); | ||
var y = (parameter.chartHeight - opt.bar.height) / 2 + opt.bar.top; | ||
var basic = parameter.chartWidth / (opt.maxValue - opt.minValue); | ||
var w = basic * Number(d.rightRate) * 100; | ||
g.append('rect') | ||
.attr('x', opt.bar.left) | ||
.attr('y', y) | ||
.attr('rx', opt.bar.rx) | ||
.attr('ry', opt.bar.ry) | ||
.attr('width' , w) | ||
.attr('height', opt.bar.height) | ||
.attr('fill', opt.mainInfo.color) | ||
.attr('fill-opacity', opt.mainInfo.opacity) | ||
.attr('style', 'fill:url(#barGradient)'); | ||
}, | ||
drawAxis : function(svg, minLength, p1_radians, p2_radians, opt, self){ | ||
var scaleGroup = svg.append('g').attr('class', 'scaleGroup'); | ||
var d = svg.datum(); | ||
var groupList = []; | ||
for(var i=0, count=d.length ; i < count ; i++){ | ||
var p = self.getPoint(opt.gap * i, p1_radians, 0, opt.top); | ||
p.x += opt.left; | ||
var g = svg.append('g'); | ||
groupList.push(g); | ||
svg.call(self.drawAxisText, d[i].title, groupList[i], p, opt.titleWidth, opt, self); | ||
randerBackgroune : function(svg, parameter, opt, self){ | ||
var y = (parameter.chartHeight - opt.bar.height) / 2 + opt.bar.top; | ||
var g = svg.append('g'); | ||
g.append('rect') | ||
.attr('x', opt.bar.left) | ||
.attr('y', y) | ||
.attr('rx', opt.bar.rx) | ||
.attr('ry', opt.bar.ry) | ||
.attr('width' , parameter.chartWidth) | ||
.attr('height', opt.bar.height) | ||
.attr('fill', opt.background.color) | ||
.attr('fill-opacity', opt.background.opacity); | ||
return g; | ||
}, | ||
randerGradien : function(svg, d, opt ,self){ | ||
var defs = svg.append('g').append('defs'); | ||
var gradient = defs.append('linearGradient'); | ||
gradient.attr('id', 'barGradient') | ||
.attr('x1','0%') | ||
.attr('y1','0%') | ||
.attr('x2','0%') | ||
.attr('y2','100%') | ||
.attr('spreadMethod', 'pad'); | ||
for(var i=0, l=d.length ; i < l ; i++){ | ||
gradient.append('stop') | ||
.attr('offset', d[i].offset) | ||
.attr('stop-color', d[i].color) | ||
.attr('stop-opacity', d[i].opacity); | ||
} | ||
for(var j=0, count=d.length ; j < count ; j++){ | ||
var p = self.getPoint(opt.gap * j, p1_radians, opt.hGap, opt.barTop); | ||
p.x += opt.left + self.titleWidth; | ||
//bar background | ||
svg.call(self.drawLineBar, groupList[j], p, minLength, opt.barBackColor, opt.barBackOpacity, opt, self); | ||
//bar user score | ||
var userScoreW = minLength * d[j].rightRate; | ||
svg.call(self.drawLineBar, groupList[j], p, userScoreW, opt.scoreBackColor , opt.scoreOpacity , opt, self); | ||
//basicRate tag | ||
var basicOffset = minLength * d[j].basicRate; | ||
var basicX = p.x + basicOffset; | ||
var basicY = p.y + opt.barHeight + opt.tagOffset; | ||
svg.call(self.drawTag, groupList[j], basicX, basicY , opt.tagRadius, opt.bottomTagColor, opt.tagOpacity, opt, self); | ||
//skilledRate tag | ||
var skilledOffset = minLength * d[j].skilledRate; | ||
var skilledX = p.x + skilledOffset; | ||
var skilledY = p.y - opt.tagOffset; | ||
svg.call(self.drawTag, groupList[j], skilledX, skilledY , opt.tagRadius, opt.topTagColor, opt.tagOpacity, opt, self); | ||
//basic 指針 | ||
var bNP1 = {x : basicX , y: basicY}; | ||
var bNP2 = {x : basicX , y: p.y}; | ||
svg.call(self.drawNeedle, groupList[j], bNP1, bNP2, opt.bottomNeedleColor, opt.bottomNeedOpacity); | ||
//skill 指針 | ||
var SNP1 = {x : skilledX , y: skilledY}; | ||
var SNP2 = {x : skilledX , y: skilledY + opt.barHeight + opt.tagOffset}; | ||
svg.call(self.drawNeedle, groupList[j], SNP1, SNP2, opt.topNeedleColor, opt.topNeedleOpacity); | ||
//basic title | ||
var BTP = {x: basicX + opt.bottomTitleGap, y: basicY + opt.tagRadius}; | ||
var bval = Math.floor(d[j].basicRate * 100); | ||
svg.call(self.drawTagTitle, groupList[j], BTP ,opt.bottomTagColor, d[j].bTitle, bval, opt); | ||
//skill title | ||
var TTP = {x: skilledX + opt.topTitleGap, y: skilledY + opt.tagRadius}; | ||
var tval = Math.floor(d[j].skilledRate * 100); | ||
svg.call(self.drawTagTitle, groupList[j], TTP ,opt.topTagColor, d[j].tTitle, tval, opt); | ||
//學生分數 title | ||
var STP = {x : p.x + userScoreW , y : p.y }; | ||
var sval = Math.floor(d[j].rightRate * 100); | ||
svg.call(self.drawUserScoreTitle, groupList[j], STP ,opt.scoreColor, sval, opt); | ||
} | ||
//刻度 | ||
//start | ||
var sp1 = self.getPoint(opt.gap * j+1, p1_radians, opt.hGap, opt.barTop); | ||
sp1.x += opt.left + self.titleWidth; | ||
//var ep2 = {x: sp1.x + minLength , y : sp1.y}; | ||
var sectionVal = minLength / opt.scale; | ||
var sectionV = (opt.maxValue - opt.minValue) / opt.scale; | ||
for(var i=0 , count=opt.scale ; i <= count ; i++){ | ||
var scaleP1 = {x : sp1.x + i * sectionVal, y: sp1.y}; | ||
var scaleP2 = {x : scaleP1.x , y: sp1.y + opt.scaleNeedleH}; | ||
//刻度直線 | ||
svg.call(self.drawNeedle, scaleGroup, scaleP1, scaleP2, opt.scaleNeedleHColor, opt.topNeedleOpacity); | ||
//刻度 text | ||
var valP = {x : scaleP1.x + opt.scaleTextOffsetH , y : scaleP2.y + opt.scaleTextGap}; | ||
svg.call(self.drawScalaText, scaleGroup, valP, 'black', sectionV * i, opt ); | ||
} | ||
//刻度橫線 | ||
var sph1 = {x: sp1.x , y : sp1.y + opt.scaleNeedleH/2 }; | ||
var sph2 = {x: sp1.x + minLength , y : sph1.y}; | ||
svg.call(self.drawNeedle, scaleGroup, sph1, sph2, opt.scaleNeedleHColor, opt.topNeedleOpacity); | ||
// | ||
}, | ||
renderRadar : function(svg, opt, self){ | ||
var p1_radians = 0; | ||
var p2_radians = opt.arc / 8 * 2; | ||
var parameter = {}; | ||
var viewBoxList = opt.viewBox.split(','); | ||
var w = viewBoxList[2]; | ||
var h = viewBoxList[3]; | ||
var minLength = w * opt.axisZoom; | ||
//render | ||
svg.call(self.drawAxis, minLength, p1_radians, p2_radians, opt, self); | ||
parameter.w = viewBoxList[2]; | ||
parameter.h = viewBoxList[3]; | ||
//圖長寬 | ||
parameter.chartWidth = parameter.w * opt.axisZoom * opt.zoomW; | ||
parameter.chartHeight = parameter.h * opt.axisZoom * opt.zoomH; | ||
// | ||
parameter.halfW = parameter.chartWidth / 2; | ||
parameter.halfH = parameter.chartHeight / 2; | ||
self.para = parameter; | ||
svg.call(self.randerGradien, opt.gradien, opt, self); | ||
var g = svg.call(self.randerBackgroune, parameter, opt, self); | ||
svg.call(self.randerMainSourceBar, g, parameter, opt, self); | ||
svg.call(self.randerMainSourceText, g, parameter, opt, self); | ||
svg.call(self.randerTopMark, g, parameter, opt, self); | ||
svg.call(self.randerBottomMark, g, parameter, opt, self); | ||
}, | ||
draw : function(id, data, options){ | ||
this.reset(id); | ||
var opt = (options) ? this.mixOptions(options) : this.mixOptions(null) ; | ||
@@ -244,4 +286,4 @@ var svg = d3.select(id).append("svg"); | ||
svg.attr("preserveAspectRatio", opt.preserveAspectRatio); | ||
svg.datum(data); //塞入數據 | ||
svg.call(this.renderRadar, opt, this); //要呼叫的函數 (會先執行) | ||
svg.datum(data); | ||
svg.call(this.renderRadar, opt, this); //要呼叫的函數 | ||
} | ||
@@ -248,0 +290,0 @@ }; |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14971
288
70
1