Code Sketch
yoiiiiii
Category: Programming
import java.awt.BorderLayout
import java.awt.Color
import java.awt.Dimension
import java.awt.FlowLayout
import java.awt.Font
import java.awt.Graphics
import java.awt.Graphics2D
import java.awt.GridLayout
import java.awt.Insets
import java.awt.RenderingHints
import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import java.awt.event.WindowAdapter
import java.awt.event.WindowEvent
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date
import java.util.regex.Pattern
import javax.swing.BorderFactory
import javax.swing.Box
import javax.swing.BoxLayout
import javax.swing.JButton
import javax.swing.DefaultListModel
import javax.swing.JFrame
import javax.swing.JLabel
import javax.swing.JList
import javax.swing.JOptionPane
import javax.swing.JPanel
import javax.swing.JPasswordField
import javax.swing.JScrollPane
import javax.swing.JSplitPane
import javax.swing.JTabbedPane
import javax.swing.JTable
import javax.swing.JTextArea
import javax.swing.JTextField
import javax.swing.ListSelectionModel
import javax.swing.SwingConstants
import javax.swing.SwingUtilities
import javax.swing.Timer
import javax.swing.WindowConstants
import javax.swing.table.DefaultTableModel
/* =========================================================
CONFIG
========================================================= */
object CricketConfig {
val APP_TITLE =
"CRICKET LIVE CENTER"
/*
SHA-256 for the password.
The password itself is NOT displayed in the app.
*/
val PASSWORD_SHA256 =
"07a3ccbb2c0983f152db1bc75b540d945c82372fe1771d71f3c025e89e4bd79b"
/*
Direct public web connection.
No API key.
*/
val HOST =
"https://" + "www.cricbuzz.com"
val LIVE_SCORE_PATH =
"/cricket-match/live-scores"
/*
Fallback for India vs West Indies.
*/
val DEFAULT_MATCH_PATH =
"/live-cricket-scores/151532/ind-vs-wi-1st-odi-west-indies-tour-of-india-2026"
/*
Auto refresh.
*/
val REFRESH_MS =
20000
val CONNECT_TIMEOUT =
10000
val READ_TIMEOUT =
12000
/* COLORS */
val BG =
new Color(
7,
11,
20
)
val PANEL =
new Color(
17,
23,
36
)
val PANEL2 =
new Color(
25,
32,
49
)
val CARD =
new Color(
23,
30,
46
)
val WHITE =
Color.WHITE
val MUTED =
new Color(
176,
187,
203
)
val GREEN =
new Color(
53,
215,
143
)
val GOLD =
new Color(
255,
196,
62
)
val BLUE =
new Color(
80,
150,
255
)
val RED =
new Color(
255,
85,
96
)
val BORDER =
new Color(
55,
66,
85
)
}
/* =========================================================
DATA MODELS
========================================================= */
case class CricketMatch(
name: String,
url: String,
category: String,
status: String,
preview: String
)
case class CricketBatter(
name: String,
runs: Int,
balls: Int,
fours: Int,
sixes: Int,
strikeRate: String,
state: String
)
case class CricketBowler(
name: String,
overs: String,
maidens: Int,
runs: Int,
wickets: Int,
economy: String,
state: String
)
case class HistoryPoint(
overs: Double,
runs: Int
)
/*
EXACTLY 21 FIELDS.
*/
case class LiveScoreData(
matchName: String,
state: String,
score: String,
team: String,
overs: String,
crr: String,
req: String,
target: String,
venue: String,
toss: String,
partnership: String,
striker: String,
nonStriker: String,
currentBowler: String,
lastBall: String,
recentOver: String,
updated: String,
crowd: String,
batters:
scala.collection.mutable.ArrayBuffer[CricketBatter],
bowlers:
scala.collection.mutable.ArrayBuffer[CricketBowler],
commentary:
scala.collection.mutable.ArrayBuffer[String]
)
/* =========================================================
PASSWORD SECURITY
========================================================= */
object PasswordSecurity {
def sha256(
text: String
): String = {
val digest =
MessageDigest.getInstance(
"SHA-256"
)
val bytes =
digest.digest(
text.getBytes(
"UTF-8"
)
)
val result =
new StringBuilder
var i =
0
while (
i < bytes.length
) {
val value =
bytes(i) & 255
val hex =
Integer.toHexString(
value
)
if (
hex.length == 1
) {
result.append(
"0"
)
}
result.append(
hex
)
i +=
1
}
result.toString
}
def verify(
entered: String
): Boolean = {
sha256(
entered
) ==
CricketConfig.PASSWORD_SHA256
}
}
/* =========================================================
WEB ENGINE
========================================================= */
object CricketWeb {
def absoluteUrl(
value: String
): String = {
val text =
value.trim
if (
text.startsWith(
"http://"
)
) {
text
} else if (
text.startsWith(
"https://"
)
) {
text
} else if (
text.startsWith(
"/"
)
) {
CricketConfig.HOST +
text
} else {
CricketConfig.HOST +
"/" +
text
}
}
def read(
pageUrl: String
): String = {
val stamp =
System.currentTimeMillis().toString
val separator =
if (
pageUrl.indexOf(
"?"
) >= 0
) {
"&"
} else {
"?"
}
val requestUrl =
pageUrl +
separator +
"refresh=" +
stamp
val url =
new URL(
requestUrl
)
val connection =
url
.openConnection()
.asInstanceOf[
HttpURLConnection
]
connection.setRequestMethod(
"GET"
)
connection.setConnectTimeout(
CricketConfig.CONNECT_TIMEOUT
)
connection.setReadTimeout(
CricketConfig.READ_TIMEOUT
)
connection.setUseCaches(
false
)
connection.setRequestProperty(
"User-Agent",
"Mozilla/5.0"
)
connection.setRequestProperty(
"Accept",
"text/html,application/xhtml+xml"
)
connection.setRequestProperty(
"Cache-Control",
"no-cache"
)
try {
val code =
connection.getResponseCode
val input =
if (
code >= 200 &&
code < 400
) {
connection.getInputStream
} else {
connection.getErrorStream
}
if (
input == null
) {
return ""
}
val reader =
new BufferedReader(
new InputStreamReader(
input,
"UTF-8"
)
)
val builder =
new StringBuilder
var line: String =
null
do {
line =
reader.readLine()
if (
line != null
) {
builder.append(
line
)
builder.append(
"\n"
)
}
} while (
line != null
)
reader.close()
builder.toString
} finally {
connection.disconnect()
}
}
def htmlToText(
html: String
): String = {
if (
html == null ||
html.length == 0
) {
return ""
}
var text =
html
text =
text.replaceAll(
"(?is)<!--.*?-->",
" "
)
text =
text.replaceAll(
"(?is)<script.*?</script>",
" "
)
text =
text.replaceAll(
"(?is)<style.*?</style>",
" "
)
text =
text.replaceAll(
"(?is)<noscript.*?</noscript>",
" "
)
text =
text.replaceAll(
"(?i)<br\\s*/?>",
"\n"
)
text =
text.replaceAll(
"(?i)</div>",
"\n"
)
text =
text.replaceAll(
"(?i)</p>",
"\n"
)
text =
text.replaceAll(
"(?i)</li>",
"\n"
)
text =
text.replaceAll(
"(?i)</tr>",
"\n"
)
text =
text.replaceAll(
"(?i)</td>",
"\n"
)
text =
text.replaceAll(
"(?i)</th>",
"\n"
)
text =
text.replaceAll(
"(?i)</h1>",
"\n"
)
text =
text.replaceAll(
"(?i)</h2>",
"\n"
)
text =
text.replaceAll(
"(?i)</h3>",
"\n"
)
text =
text.replaceAll(
"<[^>]*>",
" "
)
text =
text.replace(
" ",
" "
)
text =
text.replace(
"&",
"&"
)
text =
text.replace(
""",
"\""
)
text =
text.replace(
"'",
"'"
)
text =
text.replace(
"'",
"'"
)
text =
text.replace(
"<",
"<"
)
text =
text.replace(
">",
">"
)
text =
text.replaceAll(
"&#[0-9]+;",
" "
)
text =
text.replaceAll(
"[ \\t]+",
" "
)
text =
text.replaceAll(
"[ \\t]+\\n",
"\n"
)
text =
text.replaceAll(
"\\n[ \\t]+",
"\n"
)
text =
text.replaceAll(
"\\n{3,}",
"\n\n"
)
text.trim
}
}
/* =========================================================
MATCH LIST PARSER
========================================================= */
object MatchListParser {
private def clean(
value: String
): String = {
CricketWeb
.htmlToText(
value
)
.replaceAll(
"\\s+",
" "
)
.trim
}
private def getCategory(
text: String
): String = {
val lower =
text.toLowerCase
if (
lower.indexOf(
"women"
) >= 0
) {
"WOMEN"
} else if (
lower.indexOf(
"ipl"
) >= 0
) {
"IPL"
} else if (
lower.indexOf(
"county"
) >= 0
) {
"DOMESTIC"
} else if (
lower.indexOf(
"league"
) >= 0 ||
lower.indexOf(
"t20"
) >= 0
) {
"LEAGUE"
} else {
"INTERNATIONAL"
}
}
private def getStatus(
text: String
): String = {
val lower =
text.toLowerCase
val live =
lower.indexOf(
"live"
) >= 0 ||
lower.indexOf(
"need "
) >= 0 ||
lower.indexOf(
"innings break"
) >= 0 ||
lower.indexOf(
"opt to"
) >= 0
val recent =
lower.indexOf(
"won by"
) >= 0 ||
lower.indexOf(
"match drawn"
) >= 0 ||
lower.indexOf(
"completed"
) >= 0 ||
lower.indexOf(
"stumps"
) >= 0
if (
live
) {
"LIVE"
} else if (
recent
) {
"RECENT"
} else {
"UPCOMING"
}
}
private def buildName(
text: String
): String = {
val pattern =
Pattern.compile(
"(?i)([A-Za-z .'-]+\\s+vs\\s+[A-Za-z .'-]+)"
)
val matcher =
pattern.matcher(
text
)
if (
matcher.find()
) {
matcher.group(
1
).trim
} else {
if (
text.length > 55
) {
text.substring(
0,
55
) +
"..."
} else {
text
}
}
}
private def alreadyExists(
list:
scala.collection.mutable.ArrayBuffer[CricketMatch],
pageUrl: String
): Boolean = {
var found =
false
var i =
0
while (
i < list.length
) {
if (
list(i).url ==
pageUrl
) {
found =
true
}
i +=
1
}
found
}
def parse(
html: String
):
scala.collection.mutable.ArrayBuffer[CricketMatch] = {
val result =
scala.collection.mutable.ArrayBuffer[CricketMatch]()
val pattern =
Pattern.compile(
"(?is)<a[^>]+href=[\"']([^\"']*(?:live-cricket-scores|live-cricket-scorecard)[^\"']*)[\"'][^>]*>(.*?)</a>"
)
val matcher =
pattern.matcher(
html
)
while (
matcher.find()
) {
val pageUrl =
CricketWeb.absoluteUrl(
matcher.group(
1
)
)
val text =
clean(
matcher.group(
2
)
)
val lower =
text.toLowerCase
val looksLikeMatch =
lower.indexOf(
" vs "
) >= 0 ||
lower.indexOf(
"need "
) >= 0 ||
lower.indexOf(
"won by"
) >= 0 ||
lower.indexOf(
"innings break"
) >= 0 ||
lower.indexOf(
"opt to"
) >= 0
if (
looksLikeMatch &&
text.length > 8 &&
!alreadyExists(
result,
pageUrl
)
) {
result +=
CricketMatch(
buildName(
text
),
pageUrl,
getCategory(
text
),
getStatus(
text
),
text
)
}
}
result
}
}
/* =========================================================
LIVE DETAIL PARSER
========================================================= */
object LiveParser {
private def getLines(
text: String
):
scala.collection.mutable.ArrayBuffer[String] = {
val result =
scala.collection.mutable.ArrayBuffer[String]()
val raw =
text.split(
"\n"
)
var i =
0
while (
i < raw.length
) {
val line =
raw(i).trim
if (
line.length > 0
) {
result +=
line
}
i +=
1
}
result
}
private def toInt(
value: String
): Int = {
try {
value.trim.toInt
} catch {
case _: Throwable =>
0
}
}
private def first(
regex: String,
text: String
): String = {
val pattern =
Pattern.compile(
regex
)
val matcher =
pattern.matcher(
text
)
if (
matcher.find()
) {
matcher.group(
1
).trim
} else {
""
}
}
/* =======================================================
SCORE
======================================================= */
private def findScore(
text: String
):
(String, Int, Int, String) = {
val pattern =
Pattern.compile(
"(?i)(India|West Indies)\\s+(\\d+)-(\\d+)\\s*\\((\\d+(?:\\.\\d+)?)\\)"
)
val matcher =
pattern.matcher(
text
)
var team =
"Unknown"
var runs =
0
var wickets =
0
var overs =
"0.0"
while (
matcher.find()
) {
team =
matcher.group(
1
)
runs =
toInt(
matcher.group(
2
)
)
wickets =
toInt(
matcher.group(
3
)
)
overs =
matcher.group(
4
)
}
(
team,
runs,
wickets,
overs
)
}
/* =======================================================
CRR
======================================================= */
private def getCRR(
text: String,
runs: Int,
overs: String
): String = {
val value =
first(
"(?i)CRR\\s*:?\\s*([0-9]+\\.[0-9]+)",
text
)
if (
value.length > 0
) {
value
} else {
val overNumber =
try {
overs.toDouble
} catch {
case _: Throwable =>
0.0
}
if (
overNumber > 0.0
) {
String.format(
"%.2f",
Double.box(
runs.toDouble /
overNumber
)
)
} else {
"0.00"
}
}
}
/* =======================================================
REQ
======================================================= */
private def getREQ(
text: String
): String = {
val value =
first(
"(?i)REQ\\s*:?\\s*([0-9]+\\.[0-9]+)",
text
)
if (
value.length > 0
) {
value
} else {
"-"
}
}
/* =======================================================
TARGET
======================================================= */
private def getTarget(
text: String
): String = {
val pattern =
Pattern.compile(
"(?i)(India|West Indies)\\s+need\\s+(\\d+)\\s+runs"
)
val matcher =
pattern.matcher(
text
)
if (
matcher.find()
) {
matcher.group(
1
) +
" need " +
matcher.group(
2
) +
" runs"
} else {
"Target unavailable"
}
}
/* =======================================================
VENUE
======================================================= */
private def getVenue(
text: String
): String = {
val value =
first(
"(?i)Venue:\\s*([^\\n]+)",
text
)
if (
value.length > 0
) {
value
} else {
"Venue unavailable"
}
}
/* =======================================================
TOSS
======================================================= */
private def getToss(
text: String
): String = {
val value =
first(
"(?i)Toss:\\s*([^\\n]+)",
text
)
if (
value.length > 0
) {
value
} else {
"Toss unavailable"
}
}
/* =======================================================
PARTNERSHIP
======================================================= */
private def getPartnership(
text: String
): String = {
val value =
first(
"(?i)P'SHIP\\s+([0-9]+\\([0-9]+\\))",
text
)
if (
value.length > 0
) {
value
} else {
"-"
}
}
/* =======================================================
BATTER PARSER
======================================================= */
private def parseBatters(
lineData:
scala.collection.mutable.ArrayBuffer[String]
):
scala.collection.mutable.ArrayBuffer[CricketBatter] = {
val result =
scala.collection.mutable.ArrayBuffer[CricketBatter]()
var header =
-1
var i =
0
while (
i < lineData.length &&
header < 0
) {
if (
lineData(i).equalsIgnoreCase(
"Batter"
)
) {
header =
i
}
i +=
1
}
if (
header < 0
) {
return result
}
var index =
header + 1
while (
index + 5 < lineData.length &&
result.length < 10
) {
val nameLine =
lineData(index)
if (
nameLine.equalsIgnoreCase(
"Bowler"
) ||
nameLine.equalsIgnoreCase(
"Bowlers"
) ||
nameLine.equalsIgnoreCase(
"Commentary"
)
) {
index =
lineData.length
} else {
val runLine =
lineData(index + 1)
val ballLine =
lineData(index + 2)
val fourLine =
lineData(index + 3)
val sixLine =
lineData(index + 4)
val srLine =
lineData(index + 5)
val valid =
runLine.matches(
"[0-9]+"
) &&
ballLine.matches(
"[0-9]+"
) &&
fourLine.matches(
"[0-9]+"
) &&
sixLine.matches(
"[0-9]+"
) &&
srLine.matches(
"[0-9]+\\.[0-9]+"
)
if (
valid
) {
val cleanName =
nameLine
.replace(
"*",
""
)
.trim
val state =
if (
nameLine.indexOf(
"*"
) >= 0
) {
"STRIKER"
} else {
"BATTING"
}
result +=
CricketBatter(
cleanName,
toInt(runLine),
toInt(ballLine),
toInt(fourLine),
toInt(sixLine),
srLine,
state
)
index +=
6
} else {
index +=
1
}
}
}
result
}
/* =======================================================
BOWLER PARSER
======================================================= */
private def parseBowlers(
lineData:
scala.collection.mutable.ArrayBuffer[String]
):
scala.collection.mutable.ArrayBuffer[CricketBowler] = {
val result =
scala.collection.mutable.ArrayBuffer[CricketBowler]()
var header =
-1
var i =
0
while (
i < lineData.length &&
header < 0
) {
if (
lineData(i).equalsIgnoreCase(
"Bowler"
)
) {
header =
i
}
i +=
1
}
if (
header < 0
) {
return result
}
var index =
header + 1
while (
index < lineData.length &&
!lineData(index).equalsIgnoreCase(
"ECO"
)
) {
index +=
1
}
if (
index >= lineData.length
) {
return result
}
index +=
1
while (
index + 5 < lineData.length &&
result.length < 10
) {
val nameLine =
lineData(index)
val oversLine =
lineData(index + 1)
val maidenLine =
lineData(index + 2)
val runLine =
lineData(index + 3)
val wicketLine =
lineData(index + 4)
val economyLine =
lineData(index + 5)
val valid =
oversLine.matches(
"[0-9]+(?:\\.[0-9]+)?"
) &&
maidenLine.matches(
"[0-9]+"
) &&
runLine.matches(
"[0-9]+"
) &&
wicketLine.matches(
"[0-9]+"
) &&
economyLine.matches(
"[0-9]+\\.[0-9]+"
)
if (
valid &&
nameLine.length > 0
) {
val cleanName =
nameLine
.replace(
"*",
""
)
.trim
val state =
if (
nameLine.indexOf(
"*"
) >= 0
) {
"CURRENT"
} else {
"BOWLING"
}
result +=
CricketBowler(
cleanName,
oversLine,
toInt(
maidenLine
),
toInt(
runLine
),
toInt(
wicketLine
),
economyLine,
state
)
index +=
6
} else {
index +=
1
}
}
result
}
/* =======================================================
COMMENTARY
======================================================= */
private def parseCommentary(
text: String
):
scala.collection.mutable.ArrayBuffer[String] = {
val result =
scala.collection.mutable.ArrayBuffer[String]()
val pattern =
Pattern.compile(
"(?i)([0-9]+\\.[0-9]+)\\s+([A-Za-z .'-]+)\\s+to\\s+([A-Za-z .'-]+),\\s+([^\\n]+)"
)
val matcher =
pattern.matcher(
text
)
while (
matcher.find()
) {
result +=
matcher.group(
1
) +
" " +
matcher.group(
2
).trim +
" to " +
matcher.group(
3
).trim +
", " +
matcher.group(
4
).trim
}
if (
result.length > 20
) {
result.remove(
0,
result.length - 20
)
}
result
}
/* =======================================================
RECENT OVER
======================================================= */
private def parseRecentOver(
text: String
): String = {
val pattern =
Pattern.compile(
"(?is)Over\\s+([0-9]+)\\s+[^\\n]*\\n\\s*([^\\n]+)"
)
val matcher =
pattern.matcher(
text
)
var result =
""
while (
matcher.find()
) {
result =
"Over " +
matcher.group(
1
) +
": " +
matcher.group(
2
).trim
}
if (
result.length == 0
) {
"Recent over unavailable"
} else {
result
}
}
/* =======================================================
MAIN PARSER
======================================================= */
def parse(
html: String,
fallbackTitle: String
): LiveScoreData = {
val text =
CricketWeb.htmlToText(
html
)
val lineData =
getLines(
text
)
val scoreData =
findScore(
text
)
val team =
scoreData._1
val runs =
scoreData._2
val wickets =
scoreData._3
val overs =
scoreData._4
val batterData =
parseBatters(
lineData
)
val bowlerData =
parseBowlers(
lineData
)
val commentaryData =
parseCommentary(
text
)
var striker =
"-"
var nonStriker =
"-"
var i =
0
while (
i < batterData.length
) {
val player =
batterData(i)
if (
player.state ==
"STRIKER"
) {
striker =
player.name +
" " +
player.runs +
"(" +
player.balls +
")"
}
i +=
1
}
i =
0
while (
i < batterData.length
) {
val player =
batterData(i)
if (
player.state !=
"STRIKER" &&
nonStriker ==
"-"
) {
nonStriker =
player.name +
" " +
player.runs +
"(" +
player.balls +
")"
}
i +=
1
}
var currentBowler =
"-"
var bowlerStats =
""
i =
0
while (
i < bowlerData.length
) {
val player =
bowlerData(i)
if (
player.state ==
"CURRENT"
) {
currentBowler =
player.name
bowlerStats =
player.overs +
"-" +
player.maidens +
"-" +
player.runs +
"-" +
player.wickets
}
i +=
1
}
if (
currentBowler ==
"-" &&
bowlerData.length > 0
) {
currentBowler =
bowlerData(0).name
bowlerStats =
bowlerData(0).overs +
"-" +
bowlerData(0).maidens +
"-" +
bowlerData(0).runs +
"-" +
bowlerData(0).wickets
}
val state =
if (
runs > 0 ||
wickets > 0
) {
"LIVE"
} else {
"LIVE PAGE"
}
/*
IMPORTANT:
Exactly 21 arguments.
The old extra currentTime() argument has been removed.
*/
LiveScoreData(
fallbackTitle,
state,
team +
" " +
runs +
"-" +
wickets +
" (" +
overs +
")",
team,
overs,
getCRR(
text,
runs,
overs
),
getREQ(
text
),
getTarget(
text
),
getVenue(
text
),
getToss(
text
),
getPartnership(
text
),
striker,
nonStriker,
currentBowler +
" " +
bowlerStats,
if (
commentaryData.length > 0
) {
commentaryData(
commentaryData.length - 1
)
} else {
"Waiting for latest ball..."
},
parseRecentOver(
text
),
new SimpleDateFormat(
"dd MMM yyyy HH:mm:ss"
).format(
new Date()
),
"Crowd has really picked up now. Lots of noise, good atmosphere.",
batterData,
bowlerData,
commentaryData
)
}
}
/* =========================================================
TV STYLE BOTTOM STRIP
========================================================= */
class TVScoreStrip
extends JPanel {
private val strikerLabel =
new JLabel(
"STRIKER -"
)
private val nonStrikerLabel =
new JLabel(
"NON-STRIKER -"
)
private val bowlerLabel =
new JLabel(
"CURRENT BOWLER -"
)
private val lastBallLabel =
new JLabel(
"LAST BALL -"
)
private val recentOverLabel =
new JLabel(
"RECENT OVER -"
)
setLayout(
new BorderLayout(
4,
4
)
)
setBackground(
CricketConfig.PANEL2
)
setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(
CricketConfig.GREEN
),
BorderFactory.createEmptyBorder(
7,
10,
7,
10
)
)
)
val firstRow =
new JPanel(
new GridLayout(
1,
3,
7,
0
)
)
firstRow.setBackground(
CricketConfig.PANEL2
)
styleLabel(
strikerLabel,
CricketConfig.GOLD
)
styleLabel(
nonStrikerLabel,
CricketConfig.WHITE
)
styleLabel(
bowlerLabel,
CricketConfig.BLUE
)
firstRow.add(
strikerLabel
)
firstRow.add(
nonStrikerLabel
)
firstRow.add(
bowlerLabel
)
val secondRow =
new JPanel(
new GridLayout(
1,
2,
7,
0
)
)
secondRow.setBackground(
CricketConfig.PANEL2
)
styleLabel(
lastBallLabel,
CricketConfig.WHITE
)
styleLabel(
recentOverLabel,
CricketConfig.GREEN
)
secondRow.add(
lastBallLabel
)
secondRow.add(
recentOverLabel
)
add(
firstRow,
BorderLayout.NORTH
)
add(
secondRow,
BorderLayout.SOUTH
)
private def styleLabel(
label: JLabel,
color: Color
): Unit = {
label.setForeground(
color
)
label.setFont(
new Font(
"Arial",
Font.BOLD,
12
)
)
label.setBorder(
BorderFactory.createEmptyBorder(
4,
4,
4,
4
)
)
}
def update(
data: LiveScoreData
): Unit = {
strikerLabel.setText(
"STRIKER " +
data.striker
)
nonStrikerLabel.setText(
"NON-STRIKER " +
data.nonStriker
)
bowlerLabel.setText(
"CURRENT BOWLER " +
data.currentBowler
)
lastBallLabel.setText(
"LAST BALL " +
data.lastBall
)
recentOverLabel.setText(
"RECENT OVER " +
data.recentOver
)
}
}
/* =========================================================
RUN GRAPH
========================================================= */
class RunGraph
extends JPanel {
private var points =
scala.collection.mutable.ArrayBuffer[HistoryPoint]()
setBackground(
CricketConfig.PANEL
)
setPreferredSize(
new Dimension(
800,
270
)
)
def updateData(
values:
scala.collection.mutable.ArrayBuffer[HistoryPoint]
): Unit = {
points.clear()
values.foreach {
item =>
points +=
item
}
repaint()
}
override def paintComponent(
graphics: Graphics
): Unit = {
super.paintComponent(
graphics
)
val g =
graphics.asInstanceOf[
Graphics2D
]
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
)
g.setColor(
CricketConfig.WHITE
)
g.setFont(
new Font(
"Arial",
Font.BOLD,
17
)
)
g.drawString(
"LIVE RUN GRAPH",
20,
27
)
val left =
55
val right =
getWidth - 25
val top =
55
val bottom =
getHeight - 40
g.setColor(
CricketConfig.BORDER
)
g.drawLine(
left,
bottom,
right,
bottom
)
g.drawLine(
left,
top,
left,
bottom
)
if (
points.length < 2
) {
g.setColor(
CricketConfig.MUTED
)
g.drawString(
"Waiting for more live score points...",
left + 15,
top + 45
)
return
}
var maxRuns =
1
var i =
0
while (
i < points.length
) {
if (
points(i).runs > maxRuns
) {
maxRuns =
points(i).runs
}
i +=
1
}
val maxOvers =
if (
points(
points.length - 1
).overs > 1.0
) {
points(
points.length - 1
).overs
} else {
1.0
}
g.setColor(
CricketConfig.GREEN
)
var p =
1
while (
p < points.length
) {
val a =
points(
p - 1
)
val b =
points(
p
)
val x1 =
left +
(
a.overs /
maxOvers *
(
right -
left
)
).toInt
val y1 =
bottom -
(
a.runs.toDouble /
maxRuns.toDouble *
(
bottom -
top
)
).toInt
val x2 =
left +
(
b.overs /
maxOvers *
(
right -
left
)
).toInt
val y2 =
bottom -
(
b.runs.toDouble /
maxRuns.toDouble *
(
bottom -
top
)
).toInt
g.drawLine(
x1,
y1,
x2,
y2
)
g.fillOval(
x2 - 4,
y2 - 4,
8,
8
)
p +=
1
}
g.setColor(
CricketConfig.MUTED
)
g.drawString(
"OVERS",
right - 55,
bottom + 25
)
g.drawString(
"RUNS",
8,
top
)
}
}
/* =========================================================
LOGIN
========================================================= */
class LoginWindow(
success: () => Unit
)
extends JFrame {
setTitle(
CricketConfig.APP_TITLE
)
setSize(
430,
310
)
setLocationRelativeTo(
null
)
setDefaultCloseOperation(
WindowConstants.DISPOSE_ON_CLOSE
)
val root =
new JPanel()
root.setBackground(
CricketConfig.BG
)
root.setLayout(
new BoxLayout(
root,
BoxLayout.Y_AXIS
)
)
root.setBorder(
BorderFactory.createEmptyBorder(
30,
35,
30,
35
)
)
val heading =
new JLabel(
CricketConfig.APP_TITLE
)
heading.setForeground(
CricketConfig.GREEN
)
heading.setFont(
new Font(
"Arial",
Font.BOLD,
27
)
)
heading.setAlignmentX(
SwingConstants.CENTER
)
root.add(
heading
)
root.add(
Box.createVerticalStrut(
28
)
)
val prompt =
new JLabel(
"ENTER PASSWORD"
)
prompt.setForeground(
CricketConfig.WHITE
)
prompt.setAlignmentX(
SwingConstants.CENTER
)
root.add(
prompt
)
root.add(
Box.createVerticalStrut(
10
)
)
val passwordField =
new JPasswordField()
passwordField.setMaximumSize(
new Dimension(
320,
40
)
)
root.add(
passwordField
)
root.add(
Box.createVerticalStrut(
18
)
)
val loginButton =
new JButton(
"LOGIN"
)
loginButton.setAlignmentX(
SwingConstants.CENTER
)
root.add(
loginButton
)
/*
Password is intentionally NOT shown anywhere.
*/
loginButton.addActionListener(
new ActionListener {
override def actionPerformed(
event: ActionEvent
): Unit = {
val entered =
new String(
passwordField.getPassword
)
if (
PasswordSecurity.verify(
entered
)
) {
dispose()
success()
} else {
passwordField.setText(
""
)
JOptionPane.showMessageDialog(
LoginWindow.this,
"Incorrect password.",
"LOGIN",
JOptionPane.ERROR_MESSAGE
)
}
}
}
)
add(
root
)
setVisible(
true
)
}
/* =========================================================
MAIN APPLICATION
========================================================= */
class CricketLiveCenter
extends JFrame {
private var matches =
scala.collection.mutable.ArrayBuffer[CricketMatch]()
private var visibleMatches =
scala.collection.mutable.ArrayBuffer[CricketMatch]()
private var selectedMatch:
CricketMatch =
null
private var history =
scala.collection.mutable.ArrayBuffer[HistoryPoint]()
private var autoRefresh =
true
/* =======================================================
HEADER
======================================================= */
private val titleLabel =
new JLabel(
CricketConfig.APP_TITLE
)
private val statusLabel =
new JLabel(
"CONNECTING..."
)
private val scoreLabel =
new JLabel(
"NO DATA"
)
private val oversLabel =
new JLabel(
"0.0 overs"
)
private val crrLabel =
new JLabel(
"CRR 0.00"
)
/* =======================================================
MATCH INFO
======================================================= */
private val battingLabel =
new JLabel(
"Batting: -"
)
private val targetLabel =
new JLabel(
"Target: -"
)
private val reqLabel =
new JLabel(
"REQ: -"
)
private val venueLabel =
new JLabel(
"Venue: -"
)
private val tossLabel =
new JLabel(
"Toss: -"
)
private val partnershipLabel =
new JLabel(
"Partnership: -"
)
private val updatedLabel =
new JLabel(
"Updated: -"
)
private val searchField =
new JTextField()
/* =======================================================
SIDEBAR
======================================================= */
private val listModel =
new DefaultListModel[String]()
private val matchList =
new JList[String](
listModel
)
private val matchCount =
new JLabel(
"0 MATCHES"
)
/* =======================================================
TABLES
======================================================= */
private val batterModel =
new DefaultTableModel(
Array[AnyRef](
"BATTER",
"R",
"B",
"4s",
"6s",
"SR",
"STATE"
),
0
) {
override def isCellEditable(
row: Int,
column: Int
): Boolean = {
false
}
}
private val batterTable =
new JTable(
batterModel
)
private val bowlerModel =
new DefaultTableModel(
Array[AnyRef](
"BOWLER",
"O",
"M",
"R",
"W",
"ECON",
"STATE"
),
0
) {
override def isCellEditable(
row: Int,
column: Int
): Boolean = {
false
}
}
private val bowlerTable =
new JTable(
bowlerModel
)
private val commentaryArea =
new JTextArea()
private val runGraph =
new RunGraph()
private val tvStrip =
new TVScoreStrip()
/* =======================================================
TIMER
======================================================= */
private val refreshTimer =
new Timer(
CricketConfig.REFRESH_MS,
new ActionListener {
override def actionPerformed(
event: ActionEvent
): Unit = {
if (
autoRefresh
) {
refreshAll()
}
}
}
)
/* =======================================================
FRAME
======================================================= */
setTitle(
CricketConfig.APP_TITLE
)
setSize(
1480,
930
)
setMinimumSize(
new Dimension(
1080,
700
)
)
setLocationRelativeTo(
null
)
setDefaultCloseOperation(
WindowConstants.EXIT_ON_CLOSE
)
/* =======================================================
HEADER
======================================================= */
val header =
new JPanel(
new BorderLayout()
)
header.setBackground(
CricketConfig.PANEL
)
header.setBorder(
BorderFactory.createEmptyBorder(
12,
15,
12,
15
)
)
val headingBox =
new JPanel(
new GridLayout(
2,
1
)
)
headingBox.setBackground(
CricketConfig.PANEL
)
titleLabel.setForeground(
CricketConfig.WHITE
)
titleLabel.setFont(
new Font(
"Arial",
Font.BOLD,
23
)
)
statusLabel.setForeground(
CricketConfig.GREEN
)
statusLabel.setFont(
new Font(
"Arial",
Font.BOLD,
14
)
)
headingBox.add(
titleLabel
)
headingBox.add(
statusLabel
)
val scoreBox =
new JPanel(
new FlowLayout(
FlowLayout.RIGHT,
14,
0
)
)
scoreBox.setBackground(
CricketConfig.PANEL
)
scoreLabel.setForeground(
CricketConfig.GOLD
)
scoreLabel.setFont(
new Font(
"Arial",
Font.BOLD,
35
)
)
oversLabel.setForeground(
CricketConfig.WHITE
)
oversLabel.setFont(
new Font(
"Arial",
Font.BOLD,
16
)
)
crrLabel.setForeground(
CricketConfig.MUTED
)
crrLabel.setFont(
new Font(
"Arial",
Font.BOLD,
14
)
)
scoreBox.add(
scoreLabel
)
scoreBox.add(
oversLabel
)
scoreBox.add(
crrLabel
)
header.add(
headingBox,
BorderLayout.WEST
)
header.add(
scoreBox,
BorderLayout.EAST
)
/* =======================================================
CONTROLS
======================================================= */
val controls =
new JPanel(
new FlowLayout(
FlowLayout.LEFT,
8,
8
)
)
controls.setBackground(
CricketConfig.PANEL2
)
val refreshButton =
new JButton(
"REFRESH NOW"
)
val autoButton =
new JButton(
"AUTO: ON"
)
val sourceButton =
new JButton(
"SOURCE"
)
val directLabel =
new JLabel(
"DIRECT WEB ? NO API"
)
directLabel.setForeground(
CricketConfig.GREEN
)
controls.add(
refreshButton
)
controls.add(
autoButton
)
controls.add(
sourceButton
)
controls.add(
directLabel
)
refreshButton.addActionListener(
new ActionListener {
override def actionPerformed(
event: ActionEvent
): Unit = {
refreshAll()
}
}
)
autoButton.addActionListener(
new ActionListener {
override def actionPerformed(
event: ActionEvent
): Unit = {
autoRefresh =
!autoRefresh
if (
autoRefresh
) {
autoButton.setText(
"AUTO: ON"
)
refreshTimer.start()
} else {
autoButton.setText(
"AUTO: OFF"
)
refreshTimer.stop()
}
}
}
)
sourceButton.addActionListener(
new ActionListener {
override def actionPerformed(
event: ActionEvent
): Unit = {
val source =
if (
selectedMatch == null
) {
CricketConfig.HOST +
CricketConfig.DEFAULT_MATCH_PATH
} else {
selectedMatch.url
}
JOptionPane.showMessageDialog(
CricketLiveCenter.this,
source,
"LIVE SOURCE",
JOptionPane.INFORMATION_MESSAGE
)
}
}
)
/* =======================================================
SIDEBAR
======================================================= */
val sidebar =
new JPanel(
new BorderLayout()
)
sidebar.setBackground(
CricketConfig.PANEL
)
sidebar.setPreferredSize(
new Dimension(
380,
700
)
)
val sidebarTitle =
new JLabel(
"LIVE MATCH CENTER"
)
sidebarTitle.setForeground(
CricketConfig.GREEN
)
sidebarTitle.setFont(
new Font(
"Arial",
Font.BOLD,
21
)
)
sidebarTitle.setBorder(
BorderFactory.createEmptyBorder(
12,
12,
8,
12
)
)
val sidebarTools =
new JPanel(
new BorderLayout()
)
sidebarTools.setBackground(
CricketConfig.PANEL
)
sidebarTools.setBorder(
BorderFactory.createEmptyBorder(
0,
10,
8,
10
)
)
searchField.setPreferredSize(
new Dimension(
250,
31
)
)
sidebarTools.add(
searchField,
BorderLayout.NORTH
)
matchCount.setForeground(
CricketConfig.MUTED
)
matchCount.setHorizontalAlignment(
SwingConstants.CENTER
)
sidebarTools.add(
matchCount,
BorderLayout.SOUTH
)
val sidebarTop =
new JPanel(
new BorderLayout()
)
sidebarTop.setBackground(
CricketConfig.PANEL
)
sidebarTop.add(
sidebarTitle,
BorderLayout.NORTH
)
sidebarTop.add(
sidebarTools,
BorderLayout.CENTER
)
sidebar.add(
sidebarTop,
BorderLayout.NORTH
)
matchList.setBackground(
CricketConfig.BG
)
matchList.setForeground(
CricketConfig.WHITE
)
matchList.setFont(
new Font(
"Arial",
Font.PLAIN,
13
)
)
matchList.setSelectionMode(
ListSelectionModel.SINGLE_SELECTION
)
matchList.setFixedCellHeight(
78
)
sidebar.add(
new JScrollPane(
matchList
),
BorderLayout.CENTER
)
/* =======================================================
INFO GRID
======================================================= */
val infoGrid =
new JPanel(
new GridLayout(
2,
4,
6,
6
)
)
infoGrid.setBackground(
CricketConfig.BG
)
infoGrid.setBorder(
BorderFactory.createEmptyBorder(
5,
6,
5,
6
)
)
styleInfo(
battingLabel
)
styleInfo(
targetLabel
)
styleInfo(
reqLabel
)
styleInfo(
venueLabel
)
styleInfo(
tossLabel
)
styleInfo(
partnershipLabel
)
styleInfo(
updatedLabel
)
val modeLabel =
new JLabel(
"DIRECT WEB"
)
styleInfo(
modeLabel
)
infoGrid.add(
battingLabel
)
infoGrid.add(
targetLabel
)
infoGrid.add(
reqLabel
)
infoGrid.add(
venueLabel
)
infoGrid.add(
tossLabel
)
infoGrid.add(
partnershipLabel
)
infoGrid.add(
updatedLabel
)
infoGrid.add(
modeLabel
)
/* =======================================================
RIGHT SIDE
======================================================= */
val rightTop =
new JPanel(
new BorderLayout()
)
rightTop.setBackground(
CricketConfig.BG
)
rightTop.add(
controls,
BorderLayout.NORTH
)
rightTop.add(
infoGrid,
BorderLayout.CENTER
)
val right =
new JPanel(
new BorderLayout(
0,
5
)
)
right.setBackground(
CricketConfig.BG
)
right.add(
rightTop,
BorderLayout.NORTH
)
/* =======================================================
TABS
======================================================= */
val tabs =
new JTabbedPane()
tabs.addTab(
"LIVE",
createLiveTab()
)
tabs.addTab(
"SCORECARD",
createScorecardTab()
)
tabs.addTab(
"COMMENTARY",
createCommentaryTab()
)
tabs.addTab(
"ANALYTICS",
createAnalyticsTab()
)
right.add(
tabs,
BorderLayout.CENTER
)
right.add(
tvStrip,
BorderLayout.SOUTH
)
/* =======================================================
SPLIT
======================================================= */
val split =
new JSplitPane(
JSplitPane.HORIZONTAL_SPLIT,
sidebar,
right
)
split.setDividerLocation(
380
)
split.setResizeWeight(
0.0
)
val root =
new JPanel(
new BorderLayout()
)
root.setBackground(
CricketConfig.BG
)
root.add(
header,
BorderLayout.NORTH
)
root.add(
split,
BorderLayout.CENTER
)
add(
root
)
/* =======================================================
MATCH CLICK
======================================================= */
matchList.addListSelectionListener(
new javax.swing.event.ListSelectionListener {
override def valueChanged(
event:
javax.swing.event.ListSelectionEvent
): Unit = {
if (
!event.getValueIsAdjusting
) {
val index =
matchList.getSelectedIndex
if (
index >= 0 &&
index < visibleMatches.length
) {
selectMatch(
visibleMatches(index)
)
}
}
}
}
)
/* =======================================================
SEARCH
======================================================= */
searchField.addActionListener(
new ActionListener {
override def actionPerformed(
event: ActionEvent
): Unit = {
filterMatches()
}
}
)
/* =======================================================
CLOSE
======================================================= */
addWindowListener(
new WindowAdapter {
override def windowClosing(
event: WindowEvent
): Unit = {
refreshTimer.stop()
}
}
)
/* =======================================================
START
======================================================= */
setVisible(
true
)
refreshAll()
refreshTimer.start()
/* =======================================================
INFO STYLE
======================================================= */
private def styleInfo(
label: JLabel
): Unit = {
label.setForeground(
CricketConfig.WHITE
)
label.setBackground(
CricketConfig.PANEL
)
label.setOpaque(
true
)
label.setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(
CricketConfig.BORDER
),
BorderFactory.createEmptyBorder(
7,
8,
7,
8
)
)
)
}
/* =======================================================
LIVE TAB
======================================================= */
private def createLiveTab():
JPanel = {
val panel =
new JPanel(
new BorderLayout()
)
panel.setBackground(
CricketConfig.BG
)
val liveText =
new JTextArea()
liveText.setEditable(
false
)
liveText.setBackground(
CricketConfig.PANEL
)
liveText.setForeground(
CricketConfig.WHITE
)
liveText.setFont(
new Font(
"Monospaced",
Font.PLAIN,
17
)
)
liveText.setLineWrap(
true
)
liveText.setWrapStyleWord(
true
)
liveText.setMargin(
new Insets(
20,
20,
20,
20
)
)
liveText.setText(
"LIVE CRICKET CENTER\n\n" +
"Select a match from the left side.\n\n" +
"LIVE TV INFORMATION\n\n" +
"? Current score\n" +
"? Overs\n" +
"? CRR\n" +
"? Required rate\n" +
"? Target\n" +
"? Striker\n" +
"? Non-striker\n" +
"? Current bowler\n" +
"? Last ball\n" +
"? Recent over\n" +
"? Partnership\n" +
"? Venue\n" +
"? Toss\n\n" +
"Automatic direct-web refresh is enabled."
)
panel.add(
new JScrollPane(
liveText
),
BorderLayout.CENTER
)
panel
}
/* =======================================================
SCORECARD TAB
======================================================= */
private def createScorecardTab():
JPanel = {
val panel =
new JPanel(
new GridLayout(
1,
2,
7,
7
)
)
panel.setBackground(
CricketConfig.BG
)
setupTable(
batterTable
)
setupTable(
bowlerTable
)
val batterPanel =
new JPanel(
new BorderLayout()
)
batterPanel.setBackground(
CricketConfig.PANEL
)
val batterTitle =
new JLabel(
"BATTERS"
)
batterTitle.setForeground(
CricketConfig.GOLD
)
batterTitle.setFont(
new Font(
"Arial",
Font.BOLD,
17
)
)
batterTitle.setBorder(
BorderFactory.createEmptyBorder(
10,
10,
10,
10
)
)
batterPanel.add(
batterTitle,
BorderLayout.NORTH
)
batterPanel.add(
new JScrollPane(
batterTable
),
BorderLayout.CENTER
)
val bowlerPanel =
new JPanel(
new BorderLayout()
)
bowlerPanel.setBackground(
CricketConfig.PANEL
)
val bowlerTitle =
new JLabel(
"BOWLERS"
)
bowlerTitle.setForeground(
CricketConfig.BLUE
)
bowlerTitle.setFont(
new Font(
"Arial",
Font.BOLD,
17
)
)
bowlerTitle.setBorder(
BorderFactory.createEmptyBorder(
10,
10,
10,
10
)
)
bowlerPanel.add(
bowlerTitle,
BorderLayout.NORTH
)
bowlerPanel.add(
new JScrollPane(
bowlerTable
),
BorderLayout.CENTER
)
panel.add(
batterPanel
)
panel.add(
bowlerPanel
)
panel
}
/* =======================================================
COMMENTARY
======================================================= */
private def createCommentaryTab():
JPanel = {
val panel =
new JPanel(
new BorderLayout()
)
panel.setBackground(
CricketConfig.BG
)
commentaryArea.setEditable(
false
)
commentaryArea.setBackground(
CricketConfig.PANEL
)
commentaryArea.setForeground(
CricketConfig.WHITE
)
commentaryArea.setLineWrap(
true
)
commentaryArea.setWrapStyleWord(
true
)
commentaryArea.setFont(
new Font(
"Monospaced",
Font.PLAIN,
14
)
)
commentaryArea.setMargin(
new Insets(
15,
15,
15,
15
)
)
panel.add(
new JScrollPane(
commentaryArea
),
BorderLayout.CENTER
)
panel
}
/* =======================================================
ANALYTICS
======================================================= */
private def createAnalyticsTab():
JPanel = {
val panel =
new JPanel(
new BorderLayout(
7,
7
)
)
panel.setBackground(
CricketConfig.BG
)
panel.add(
runGraph,
BorderLayout.NORTH
)
val analytics =
new JTextArea()
analytics.setEditable(
false
)
analytics.setBackground(
CricketConfig.PANEL
)
analytics.setForeground(
CricketConfig.WHITE
)
analytics.setFont(
new Font(
"Monospaced",
Font.PLAIN,
14
)
)
analytics.setMargin(
new Insets(
15,
15,
15,
15
)
)
analytics.setText(
"LIVE ANALYTICS\n\n" +
"The run graph is updated after successful score refreshes.\n\n" +
"PLAYER TRACKING\n" +
"? Striker\n" +
"? Non-striker\n" +
"? Current bowler\n" +
"? Batter runs\n" +
"? Batter balls\n" +
"? 4s / 6s\n" +
"? Strike rate\n" +
"? Bowler overs\n" +
"? Bowler runs\n" +
"? Bowler wickets\n" +
"? Economy\n\n" +
"The app never invents a score when live data is unavailable."
)
panel.add(
new JScrollPane(
analytics
),
BorderLayout.CENTER
)
panel
}
/* =======================================================
TABLE STYLE
======================================================= */
private def setupTable(
table: JTable
): Unit = {
table.setBackground(
CricketConfig.CARD
)
table.setForeground(
CricketConfig.WHITE
)
table.setGridColor(
CricketConfig.BORDER
)
table.setRowHeight(
28
)
table.getTableHeader.setBackground(
CricketConfig.PANEL2
)
table.getTableHeader.setForeground(
CricketConfig.WHITE
)
table.getTableHeader.setFont(
new Font(
"Arial",
Font.BOLD,
12
)
)
}
/* =======================================================
REFRESH
======================================================= */
private def refreshAll():
Unit = {
statusLabel.setText(
"UPDATING LIVE MATCHES..."
)
statusLabel.setForeground(
CricketConfig.GOLD
)
val worker =
new Thread(
new Runnable {
override def run():
Unit = {
loadMatches()
}
}
)
worker.start()
}
/* =======================================================
LOAD MATCHES
======================================================= */
private def loadMatches():
Unit = {
try {
val html =
CricketWeb.read(
CricketConfig.HOST +
CricketConfig.LIVE_SCORE_PATH
)
val found =
MatchListParser.parse(
html
)
SwingUtilities.invokeLater(
new Runnable {
override def run():
Unit = {
matches.clear()
found.foreach {
item =>
matches +=
item
}
if (
matches.length == 0
) {
val fallback =
CricketMatch(
"India vs West Indies - 1st ODI",
CricketConfig.HOST +
CricketConfig.DEFAULT_MATCH_PATH,
"INTERNATIONAL",
"LIVE",
"Direct live score source"
)
matches +=
fallback
}
filterMatches()
if (
selectedMatch == null
) {
chooseInitialMatch()
} else {
loadSelected(
selectedMatch
)
}
}
}
)
} catch {
case _: Throwable =>
SwingUtilities.invokeLater(
new Runnable {
override def run():
Unit = {
statusLabel.setText(
"DIRECT WEB ERROR"
)
statusLabel.setForeground(
CricketConfig.RED
)
if (
matches.length == 0
) {
val fallback =
CricketMatch(
"India vs West Indies - 1st ODI",
CricketConfig.HOST +
CricketConfig.DEFAULT_MATCH_PATH,
"INTERNATIONAL",
"LIVE",
"Direct live source"
)
matches +=
fallback
filterMatches()
chooseInitialMatch()
}
}
}
)
}
}
/* =======================================================
INITIAL MATCH
======================================================= */
private def chooseInitialMatch():
Unit = {
var index =
0
var i =
0
while (
i < matches.length
) {
val lower =
matches(i).name.toLowerCase
if (
lower.indexOf(
"india"
) >= 0 &&
lower.indexOf(
"west indies"
) >= 0
) {
index =
i
}
i +=
1
}
if (
matches.length > 0
) {
selectMatch(
matches(index)
)
}
}
/* =======================================================
FILTER
======================================================= */
private def filterMatches():
Unit = {
visibleMatches.clear()
listModel.clear()
val search =
searchField
.getText
.trim
.toLowerCase
var i =
0
while (
i < matches.length
) {
val item =
matches(i)
val combined =
(
item.name +
" " +
item.status +
" " +
item.category +
" " +
item.preview
).toLowerCase
val visible =
search.length == 0 ||
combined.indexOf(
search
) >= 0
if (
visible
) {
visibleMatches +=
item
val preview =
if (
item.preview.length > 85
) {
item.preview.substring(
0,
85
) +
"..."
} else {
item.preview
}
listModel.addElement(
item.name +
"\n[" +
item.status +
"] " +
item.category +
"\n" +
preview
)
}
i +=
1
}
matchCount.setText(
visibleMatches.length.toString +
" MATCHES"
)
}
/* =======================================================
SELECT MATCH
======================================================= */
private def selectMatch(
selected: CricketMatch
): Unit = {
selectedMatch =
selected
history.clear()
titleLabel.setText(
selected.name
)
statusLabel.setText(
"LOADING LIVE SCORE..."
)
statusLabel.setForeground(
CricketConfig.GOLD
)
loadSelected(
selected
)
}
/* =======================================================
LOAD SELECTED
======================================================= */
private def loadSelected(
selected: CricketMatch
): Unit = {
val worker =
new Thread(
new Runnable {
override def run():
Unit = {
try {
val html =
CricketWeb.read(
selected.url
)
val data =
LiveParser.parse(
html,
selected.name
)
SwingUtilities.invokeLater(
new Runnable {
override def run():
Unit = {
applyData(
data
)
}
}
)
} catch {
case _: Throwable =>
SwingUtilities.invokeLater(
new Runnable {
override def run():
Unit = {
statusLabel.setText(
"LIVE SOURCE ERROR"
)
statusLabel.setForeground(
CricketConfig.RED
)
}
}
)
}
}
}
)
worker.start()
}
/* =======================================================
APPLY DATA
======================================================= */
private def applyData(
data: LiveScoreData
): Unit = {
titleLabel.setText(
data.matchName
)
statusLabel.setText(
data.state
)
statusLabel.setForeground(
CricketConfig.GREEN
)
scoreLabel.setText(
data.score
)
oversLabel.setText(
data.overs +
" overs"
)
crrLabel.setText(
"CRR " +
data.crr
)
battingLabel.setText(
"Batting: " +
data.team
)
targetLabel.setText(
"Target: " +
data.target
)
reqLabel.setText(
"REQ: " +
data.req
)
venueLabel.setText(
"Venue: " +
data.venue
)
tossLabel.setText(
"Toss: " +
data.toss
)
partnershipLabel.setText(
"P'SHIP: " +
data.partnership
)
updatedLabel.setText(
"Updated: " +
data.updated
)
tvStrip.update(
data
)
updateTables(
data
)
updateCommentary(
data
)
updateHistory(
data
)
}
/* =======================================================
TABLES
======================================================= */
private def updateTables(
data: LiveScoreData
): Unit = {
batterModel.setRowCount(
0
)
var i =
0
while (
i < data.batters.length
) {
val p =
data.batters(i)
batterModel.addRow(
Array[AnyRef](
p.name,
Int.box(
p.runs
),
Int.box(
p.balls
),
Int.box(
p.fours
),
Int.box(
p.sixes
),
p.strikeRate,
p.state
)
)
i +=
1
}
bowlerModel.setRowCount(
0
)
i =
0
while (
i < data.bowlers.length
) {
val p =
data.bowlers(i)
bowlerModel.addRow(
Array[AnyRef](
p.name,
p.overs,
Int.box(
p.maidens
),
Int.box(
p.runs
),
Int.box(
p.wickets
),
p.economy,
p.state
)
)
i +=
1
}
}
/* =======================================================
COMMENTARY
======================================================= */
private def updateCommentary(
data: LiveScoreData
): Unit = {
val builder =
new StringBuilder
builder.append(
"LIVE BALL-BY-BALL COMMENTARY\n"
)
builder.append(
"===========================================\n\n"
)
if (
data.commentary.length == 0
) {
builder.append(
"Waiting for commentary..."
)
} else {
var i =
data.commentary.length - 1
while (
i >= 0
) {
builder.append(
data.commentary(i)
)
builder.append(
"\n\n"
)
i -=
1
}
}
commentaryArea.setText(
builder.toString
)
commentaryArea.setCaretPosition(
0
)
}
/* =======================================================
HISTORY
======================================================= */
private def updateHistory(
data: LiveScoreData
): Unit = {
val oversNumber =
try {
data.overs.toDouble
} catch {
case _: Throwable =>
0.0
}
val runsNumber =
extractRuns(
data.score
)
if (
runsNumber >= 0
) {
if (
history.length == 0
) {
history +=
HistoryPoint(
oversNumber,
runsNumber
)
} else {
val last =
history(
history.length - 1
)
val scoreChanged =
last.runs !=
runsNumber
val overChanged =
last.overs !=
oversNumber
if (
scoreChanged ||
overChanged
) {
history +=
HistoryPoint(
oversNumber,
runsNumber
)
}
}
}
if (
history.length > 60
) {
history.remove(
0,
history.length - 60
)
}
runGraph.updateData(
history
)
}
private def extractRuns(
score: String
): Int = {
val pattern =
Pattern.compile(
"[A-Za-z]+\\s+(\\d+)-\\d+"
)
val matcher =
pattern.matcher(
score
)
if (
matcher.find()
) {
try {
matcher.group(
1
).toInt
} catch {
case _: Throwable =>
-1
}
} else {
-1
}
}
}
/* =========================================================
START APPLICATION
========================================================= */
SwingUtilities.invokeLater(
new Runnable {
override def run():
Unit = {
new LoginWindow(
() => {
new CricketLiveCenter()
}
)
}
}
)