├── _config.yml
├── assets
├── icon128.png
└── screenshot.jpg
├── Howzatt
├── Images.xcassets
│ └── AppIcon.appiconset
│ │ ├── icon128.png
│ │ ├── icon16.png
│ │ ├── icon256.png
│ │ ├── icon32.png
│ │ ├── icon64.png
│ │ ├── icon256-1.png
│ │ ├── icon32-1.png
│ │ └── Contents.json
├── MenuViewController.swift
├── Match.swift
├── StatusItemView.swift
├── Info.plist
├── String+MinimumEditDistance.swift
├── Base.lproj
│ └── MainMenu.xib
├── NSRegularExpression+match.swift
├── Menu.xib
├── PopoverMenu.xib
├── AppDelegate.swift
├── AppDelegate.swift.orig
├── String+Regex.swift
└── ScoreAPI.swift
├── Howzatt.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── README.md
├── HowzattTests
├── Info.plist
└── HowzattTests.swift
├── LICENSE.md
└── .gitignore
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-minimal
--------------------------------------------------------------------------------
/assets/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varunoberoi/Howzatt/HEAD/assets/icon128.png
--------------------------------------------------------------------------------
/assets/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varunoberoi/Howzatt/HEAD/assets/screenshot.jpg
--------------------------------------------------------------------------------
/Howzatt/Images.xcassets/AppIcon.appiconset/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varunoberoi/Howzatt/HEAD/Howzatt/Images.xcassets/AppIcon.appiconset/icon128.png
--------------------------------------------------------------------------------
/Howzatt/Images.xcassets/AppIcon.appiconset/icon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varunoberoi/Howzatt/HEAD/Howzatt/Images.xcassets/AppIcon.appiconset/icon16.png
--------------------------------------------------------------------------------
/Howzatt/Images.xcassets/AppIcon.appiconset/icon256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varunoberoi/Howzatt/HEAD/Howzatt/Images.xcassets/AppIcon.appiconset/icon256.png
--------------------------------------------------------------------------------
/Howzatt/Images.xcassets/AppIcon.appiconset/icon32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varunoberoi/Howzatt/HEAD/Howzatt/Images.xcassets/AppIcon.appiconset/icon32.png
--------------------------------------------------------------------------------
/Howzatt/Images.xcassets/AppIcon.appiconset/icon64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varunoberoi/Howzatt/HEAD/Howzatt/Images.xcassets/AppIcon.appiconset/icon64.png
--------------------------------------------------------------------------------
/Howzatt/Images.xcassets/AppIcon.appiconset/icon256-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varunoberoi/Howzatt/HEAD/Howzatt/Images.xcassets/AppIcon.appiconset/icon256-1.png
--------------------------------------------------------------------------------
/Howzatt/Images.xcassets/AppIcon.appiconset/icon32-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/varunoberoi/Howzatt/HEAD/Howzatt/Images.xcassets/AppIcon.appiconset/icon32-1.png
--------------------------------------------------------------------------------
/Howzatt.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Howzatt.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Howzatt/MenuViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuItemViewController.swift
3 | // Cricket Scoreboard
4 | //
5 | // Created by Varun Oberoi on 14/01/15.
6 | // Copyright (c) 2015 Varun Oberoi. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Cocoa
11 |
12 | class MenuViewController: NSViewController {
13 |
14 | override func viewDidAppear() {
15 | super.viewDidAppear()
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/Howzatt/Match.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Match.swift
3 | // Swifting
4 | //
5 | // Created by Varun Oberoi on 1/5/20.
6 | // Copyright © 2020 Varun Oberoi. All rights reserved.
7 | //
8 |
9 | struct Match: Hashable, Codable {
10 | var title: String
11 | var link: String
12 | var status: String
13 | var summary: String
14 | var teams: [Team]
15 | var shortScore: String
16 | var fromPage: Bool
17 | var matchStarted: Bool
18 | }
19 |
20 | struct Team: Hashable, Codable {
21 | var name: String
22 | var scores: [Score]
23 | var batting: Bool
24 | }
25 |
26 | struct Score: Hashable, Codable {
27 | var score: String
28 | var overs: String
29 | }
30 |
--------------------------------------------------------------------------------
/Howzatt/StatusItemView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatusItemView.swift
3 | // Cricket Scoreboard
4 | //
5 | // Created by Varun Oberoi on 22/01/15.
6 | // Copyright (c) 2015 Varun Oberoi. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Cocoa
11 |
12 | class StatusItemView: NSView {
13 |
14 | override init(frame frameRect: NSRect) {
15 | super.init(frame: frameRect);
16 | }
17 |
18 | required init?(coder: NSCoder) {
19 | fatalError("init(coder:) has not been implemented")
20 | }
21 |
22 | override func drawRect(dirtyRect: NSRect) {
23 | println("Draw Rect is called");
24 | println(dirtyRect)
25 | setMen
26 | }
27 |
28 | override func mouseDown(theEvent: NSEvent) {
29 | println("StatusItemView is clicked(mouseDown)");
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://opensource.org/licenses/MIT)
2 | # Howzatt!!
3 |
4 | 
5 |
6 | ## Overview
7 |
8 | **Howzatt**, is an elegant app that displays *live cricket score* on your mac's menu bar.
9 |
10 | ### See it live
11 |
12 | 
13 |
14 | ### Contribution
15 |
16 | #### Pull requests are welcome!!
17 |
18 | Feel free to contribute to Howzatt!!
19 |
20 | If you've fixed a bug or have a feature you've added, just create a pull request.
21 |
22 | If you have any feedback to share please use [this](http://goo.gl/forms/EQaJdMG9GL) link to do that.
23 |
24 | ### License
25 |
26 | See the LICENSE file for license rights and limitations (MIT).
27 |
--------------------------------------------------------------------------------
/HowzattTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.1
19 | CFBundleSignature
20 | ????
21 | LSApplicationCategoryType
22 |
23 | CFBundleDisplayName
24 |
25 | NSAppTransportSecurity
26 |
27 | NSAllowsArbitraryLoads
28 |
29 |
30 | CFBundleVersion
31 | 2
32 |
33 |
34 |
--------------------------------------------------------------------------------
/HowzattTests/HowzattTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Cricket_ScoreboardTests.swift
3 | // Cricket ScoreboardTests
4 | //
5 | // Created by Varun Oberoi on 10/01/15.
6 | // Copyright (c) 2015 Varun Oberoi. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import XCTest
11 |
12 | class HowzattTests: XCTestCase {
13 |
14 | override func setUp() {
15 | super.setUp()
16 | // Put setup code here. This method is called before the invocation of each test method in the class.
17 | }
18 |
19 | override func tearDown() {
20 | // Put teardown code here. This method is called after the invocation of each test method in the class.
21 | super.tearDown()
22 | }
23 |
24 | func testExample() {
25 | // This is an example of a functional test case.
26 | XCTAssert(true, "Pass")
27 | }
28 |
29 | func testPerformanceExample() {
30 | // This is an example of a performance test case.
31 | self.measure() {
32 | // Put the code you want to measure the time of here.
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Varun Oberoi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Howzatt/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "icon16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "icon32.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "icon32-1.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "icon64.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "icon128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "icon256.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "icon256-1.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "idiom" : "mac",
47 | "size" : "256x256",
48 | "scale" : "2x"
49 | },
50 | {
51 | "idiom" : "mac",
52 | "size" : "512x512",
53 | "scale" : "1x"
54 | },
55 | {
56 | "idiom" : "mac",
57 | "size" : "512x512",
58 | "scale" : "2x"
59 | }
60 | ],
61 | "info" : {
62 | "version" : 1,
63 | "author" : "xcode"
64 | }
65 | }
--------------------------------------------------------------------------------
/Howzatt/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSApplicationCategoryType
26 | public.app-category.sports
27 | LSMinimumSystemVersion
28 | $(MACOSX_DEPLOYMENT_TARGET)
29 | LSUIElement
30 |
31 | NSAppTransportSecurity
32 |
33 | NSAllowsArbitraryLoads
34 |
35 |
36 | NSHumanReadableCopyright
37 | Copyright © 2023 Varun Oberoi. All rights reserved.
38 | NSMainNibFile
39 | MainMenu
40 | NSPrincipalClass
41 | NSApplication
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Howzatt/String+MinimumEditDistance.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+MinimumEditDistance.swift
3 | // Swifting
4 | //
5 | // Created by Varun Oberoi on 1/31/20.
6 | // Copyright © 2020 Varun Oberoi. All rights reserved.
7 | //
8 |
9 | // Minimum Edit Distance
10 |
11 | extension String {
12 |
13 | public func minimumEditDistance(other: String) -> Int {
14 | let m = self.count
15 | let n = other.count
16 | var matrix = [[Int]](repeating: [Int](repeating: 0, count: n + 1), count: m + 1)
17 |
18 | // initialize matrix
19 | for index in 1...m {
20 | // the distance of any first string to an empty second string
21 | matrix[index][0] = index
22 | }
23 |
24 | for index in 1...n {
25 | // the distance of any second string to an empty first string
26 | matrix[0][index] = index
27 | }
28 |
29 | // compute Levenshtein distance
30 | for (i, selfChar) in self.enumerated() {
31 | for (j, otherChar) in other.enumerated() {
32 | if otherChar == selfChar {
33 | // substitution of equal symbols with cost 0
34 | matrix[i + 1][j + 1] = matrix[i][j]
35 | } else {
36 | // minimum of the cost of insertion, deletion, or substitution
37 | // added to the already computed costs in the corresponding cells
38 | matrix[i + 1][j + 1] = Swift.min(matrix[i][j] + 1, matrix[i + 1][j] + 1, matrix[i][j + 1] + 1)
39 | }
40 | }
41 | }
42 | return matrix[m][n]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ########### Xcode ###########
2 | # Xcode temporary files that should never be committed
3 |
4 | ## Build generated
5 | build/
6 | DerivedData
7 |
8 | # NB: NIB/XIB files still exist even on Storyboard projects, so we want this
9 | *~.nib
10 | *.swp
11 |
12 | ## Various settings
13 | *.pbxuser
14 | !default.pbxuser
15 | *.mode1v3
16 | !default.mode1v3
17 | *.mode2v3
18 | !default.mode2v3
19 | *.perspectivev3
20 | !default.perspectivev3
21 | xcuserdata
22 |
23 | ## Other
24 | *.xccheckout
25 | *.moved-aside
26 | *.xcuserstate
27 | *.xcscmblueprint
28 | *.xcscheme
29 |
30 | ########### OSX ###########
31 | # OS X temporary files that should never be committed
32 |
33 | .DS_Store
34 | .AppleDouble
35 | .LSOverride
36 |
37 | # Icon must end with two \r
38 | Icon
39 |
40 |
41 | # Thumbnails
42 | ._*
43 |
44 | # Files that might appear in the root of a volume
45 | .DocumentRevisions-V100
46 | .fseventsd
47 | .Spotlight-V100
48 | .TemporaryItems
49 | .Trashes
50 | .VolumeIcon.icns
51 |
52 | # Directories potentially created on remote AFP share
53 | .AppleDB
54 | .AppleDesktop
55 | Network Trash Folder
56 | Temporary Items
57 | .apdisk
58 |
59 | ########## Obj-C/Swift specific ##########
60 | *.hmap
61 | *.ipa
62 |
63 | # CocoaPods
64 | #
65 | # We recommend against adding the Pods directory to your .gitignore. However
66 | # you should judge for yourself, the pros and cons are mentioned at:
67 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
68 | #
69 | # Pods/
70 |
71 | # Carthage
72 | #
73 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
74 | # Carthage/Checkouts
75 |
76 | Carthage/Build
77 |
78 | # fastlane
79 | #
80 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
81 |
82 | fastlane/report.xml
83 | fastlane/screenshots
84 |
85 |
--------------------------------------------------------------------------------
/Howzatt/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/Howzatt/NSRegularExpression+match.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSRegularExpression+match.swift
3 | // Swifting
4 | //
5 | // Created by Varun Oberoi on 2/1/20.
6 | // Copyright © 2020 Varun Oberoi. All rights reserved.
7 | //
8 | import Foundation
9 |
10 | extension NSRegularExpression {
11 | convenience init(substrings: [String], options: NSRegularExpression.Options) throws {
12 | let escapedSubstrings: [String] = substrings.map(NSRegularExpression.escapedTemplate)
13 | let pattern: String = escapedSubstrings.joined(separator: "|")
14 | try self.init(pattern: pattern, options: options)
15 | }
16 |
17 | convenience init?(with pattern: String, options: NSRegularExpression.Options = []) {
18 | do {
19 | try self.init(pattern: pattern, options: options)
20 | } catch {
21 | return nil
22 | }
23 | }
24 |
25 | func match(in input: String) -> Bool {
26 | return numberOfMatches(in: input, options: [], range: input.range) > 0
27 | }
28 |
29 | func split(_ str: String) -> [String] {
30 | let range = NSRange(location: 0, length: str.count)
31 |
32 | //get locations of matches
33 | var matchingRanges: [NSRange] = []
34 | let matches: [NSTextCheckingResult] = self.matches(in: str, options: [], range: range)
35 | for match: NSTextCheckingResult in matches {
36 | matchingRanges.append(match.range)
37 | }
38 |
39 | //invert ranges - get ranges of non-matched pieces
40 | var pieceRanges: [NSRange] = []
41 |
42 | //add first range
43 | pieceRanges.append(NSRange(location: 0, length: (matchingRanges.count == 0 ? str.count : matchingRanges[0].location)))
44 |
45 | //add between splits ranges and last range
46 | for i in 0..
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Howzatt/PopoverMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/Howzatt/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Cricket Scoreboard
4 | //
5 | // Created by Varun Oberoi on 10/01/15.
6 | // Copyright (c) 2021 Varun Oberoi. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate, XMLParserDelegate {
13 |
14 | @IBOutlet weak var statusMenu: NSMenu!
15 | @IBOutlet weak var searchMenuItem: NSMenuItem!
16 |
17 | private let statusItem = NSStatusBar.system.statusItem(withLength: -1)
18 | private let scoreAPI = ScoreAPI()
19 | private var currentMatch: Match! = nil
20 | private var currentMatchIndex: Int = 0
21 | private let matchListUpdateInterval = 25.0
22 |
23 | func applicationDidFinishLaunching(_ notification: Notification) {
24 | statusItem.button?.title = "Loading Matches"
25 | statusItem.menu = statusMenu
26 |
27 | Timer.scheduledTimer(
28 | timeInterval: matchListUpdateInterval,
29 | target: self,
30 | selector: #selector(getAndUpdateScores),
31 | userInfo: nil,
32 | repeats: true
33 | ).fire()
34 | }
35 |
36 | @objc func getAndUpdateScores() {
37 | scoreAPI.fetchScore(currentMatch: currentMatch) { result in
38 | DispatchQueue.main.async { [weak self] in
39 | guard let strongSelf = self else { return }
40 | if !(result.matches.count == 0) {
41 | strongSelf.currentMatch = result.currentMatch
42 | strongSelf.updateCurrentScore()
43 | strongSelf.insertMatchesIntoMenu(matchList: result.matches)
44 | }
45 | }
46 | return
47 | }
48 | }
49 |
50 | func updateCurrentScore() {
51 | statusItem.button?.title = currentMatch.shortScore
52 | statusItem.button?.toolTip = currentMatch.summary
53 | statusItem.menu = statusMenu
54 | }
55 |
56 | func insertMatchesIntoMenu(matchList: [Match]) {
57 | statusMenu.removeAllItems()
58 | var firstItem: NSMenuItem!
59 | var foundMatch: Bool = false
60 | for (index, match) in matchList.enumerated() {
61 | let item = NSMenuItem(title: match.title, action: #selector(selectMatch), keyEquivalent: "")
62 | if index == 0 {
63 | firstItem = item
64 | }
65 | item.state = .off
66 | if currentMatch != nil && currentMatch.link == match.link {
67 | item.state = .on
68 | foundMatch = true
69 | currentMatchIndex = index
70 | }
71 | item.tag = index
72 | item.representedObject = match
73 | statusMenu.insertItem(item, at: index)
74 | }
75 | if !foundMatch && firstItem != nil {
76 | firstItem.state = .on
77 | currentMatchIndex = 0
78 | }
79 |
80 | statusMenu.addItem(.separator())
81 |
82 | statusMenu.insertItem(
83 | withTitle: "Quit",
84 | action: #selector(quit),
85 | keyEquivalent: "q",
86 | at: matchList.count + 1
87 | )
88 | }
89 |
90 | @IBAction func selectMatch(sender: NSMenuItem) {
91 | let match = sender.representedObject as? Match
92 | if NSEvent.modifierFlags == .command {
93 | if match != nil {
94 | guard let matchLinkUrlString = match?.link
95 | .trimmingCharacters(in: .whitespacesAndNewlines)
96 | .addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
97 | let matchLinkURL = URL(string: matchLinkUrlString) else {
98 | return
99 | }
100 | NSWorkspace.shared.open(matchLinkURL)
101 | }
102 | }
103 |
104 | statusMenu.item(withTag: currentMatchIndex)?.state = .off
105 | sender.state = .on
106 | currentMatch = match
107 | updateCurrentScore()
108 | getAndUpdateScores()
109 | }
110 |
111 | @IBAction func quit(sender: NSMenuItem) {
112 | NSApplication.shared.terminate(self)
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/Howzatt/AppDelegate.swift.orig:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Cricket Scoreboard
4 | //
5 | // Created by Varun Oberoi on 10/01/15.
6 | // Copyright (c) 2015 Varun Oberoi. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate, NSXMLParserDelegate {
13 |
14 | @IBOutlet weak var window: NSWindow!
15 |
16 | @IBOutlet weak var statusMenu: NSMenu!
17 |
18 | var score: Score!
19 |
20 | let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-1)
21 |
22 | //var tick: NSImage = NSImage(named: "icon")!
23 |
24 | func applicationDidFinishLaunching(aNotification: NSNotification) {
25 | statusItem.title = "Loading Matches";
26 | statusItem.menu = statusMenu
27 |
28 | // Passing an event handler to Score Class
29 | score = Score(onUpdateListener: displayScore)
30 | score.updateScore()
31 | }
32 |
33 | func applicationWillTerminate(aNotification: NSNotification) {
34 |
35 | }
36 |
37 | func appBundleName() -> String {
38 | return NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String
39 | }
40 |
41 | // Called Every 10 Seconds
42 | func displayScore(score: String, matchList: NSMutableArray) -> Void {
43 | statusItem.title = shrinkScore(score)
44 | statusItem.menu = statusMenu
45 | insertMatchesIntoMenu(matchList)
46 | }
47 |
48 | <<<<<<< HEAD
49 | func shrinkScore(score: String) -> String {
50 | var teamStr = "IND" as String
51 | var scoreStr = "123/1" as String
52 |
53 | var parts = score.componentsSeparatedByString(" v ")
54 |
55 | var range1: NSRange = (parts[0] as NSString).rangeOfString("*");
56 | var range2: NSRange = (parts[1] as NSString).rangeOfString("*");
57 |
58 | if range1.length == 0 && range2.length == 0 {
59 | return score;
60 | } else if range2.length != 0 {
61 | return parts[1]
62 | } else {
63 | return parts[0];
64 | }
65 | }
66 |
67 | func insertMatchesIntoMenu(matchList: NSMutableArray){
68 | =======
69 | func insertMatchesIntoMenu(matchList: NSMutableArray) {
70 | >>>>>>> d22abc49a208f48bec1229dde01ba04010f3b02c
71 | // Clearing previous menuItems
72 | statusMenu.removeAllItems()
73 |
74 | // Adding new matches to the menu
75 | for (index, match) in enumerate(matchList) {
76 | var item = NSMenuItem(title: match["title"] as! String, action: "selectMatch:", keyEquivalent: "")
77 | var matchLink = match["link"] as! String
78 | matchLink = matchLink.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
79 | var matchLinkURL = NSURL(string: matchLink)
80 | item.representedObject = matchLinkURL
81 |
82 | //item.on
83 | statusMenu.insertItem(item, atIndex: index)
84 | item.state = NSOffState
85 | item.tag = index
86 | if score.selectedMatch == index {
87 | item.state = NSOnState
88 | }
89 | }
90 |
91 | // Other menuItems
92 | var seperator = NSMenuItem.separatorItem()
93 | statusMenu.addItem(seperator)
94 | statusMenu.insertItemWithTitle("Quit " + self.appBundleName(), action: "quit:", keyEquivalent: "q", atIndex: matchList.count + 1)
95 | }
96 |
97 | // On Click Event Handler for menuItems
98 | @IBAction func selectMatch(sender: NSMenuItem) {
99 | if NSEvent.modifierFlags() == NSEventModifierFlags.CommandKeyMask {
100 | NSWorkspace.sharedWorkspace().openURL(sender.representedObject as! NSURL);
101 | }
102 | //Uncheck previous match
103 | statusMenu.itemWithTag(score.selectedMatch)?.state = NSOffState
104 |
105 | score.selectedMatch = sender.tag
106 |
107 | // Ticking click match
108 | sender.state = NSOnState
109 |
110 | score.updateScore()
111 | }
112 |
113 | // Event Handler for quit menuItem
114 | @IBAction func quit(sender: NSMenuItem) {
115 | NSApplication.sharedApplication().terminate(self)
116 | }
117 |
118 | }
119 |
120 |
--------------------------------------------------------------------------------
/Howzatt/String+Regex.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+Regex.swift
3 | // Swifting
4 | //
5 | // Created by Varun Oberoi on 1/31/20.
6 | // Copyright © 2020 Varun Oberoi. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension String {
12 | public func replaceAll(of pattern:String,
13 | with replacement:String,
14 | options: NSRegularExpression.Options = []) -> String{
15 | do{
16 | let regex = try NSRegularExpression(pattern: pattern, options: options)
17 | let range = NSRange(0.. String {
30 | return trimmingCharacters(in: .whitespaces)
31 | }
32 |
33 | /// Strips (or removes) given character from string
34 | ///
35 | /// - Parameters:
36 | /// - character: String of one character to be removed
37 | ///
38 | /// - Returns: self with character removed from string
39 | ///
40 | func strip(_ character: String) -> String {
41 | return replacingOccurrences(of: character, with: "")
42 | }
43 |
44 | /// Strips (or removes) several characters from string
45 | ///
46 | /// - Parameters:
47 | /// - characters: Characters you want to be removed (one character per string!)
48 | ///
49 | /// - Returns: self with characters removed from string
50 | ///
51 | func strip(_ characters: [String]) -> String {
52 | var output = self
53 | for character in characters {
54 | output = replacingOccurrences(of: character, with: "")
55 | }
56 | return output
57 | }
58 |
59 | /// Splits a string into an array of strings with specifed components
60 | /// Starts at the beginning of the string and keeps splitting until end of string OR end of components.
61 | ///
62 | /// - Parameters:
63 | /// - components: length of each desired substring
64 | ///
65 | /// - Returns: array of strings split into each given component lenght
66 | ///
67 | /// ```
68 | /// "12345".split([1, 2, 3]) == ["1", "23", "45"]
69 | /// ```
70 | ///
71 | // func split(_ components: [Int]) -> [String?] {
72 | // let maxIndex = index(startIndex, offsetBy: count)
73 | // return components.enumerated().map { idx, length in
74 | // let start = components[0.. Bool {
90 | do {
91 | return try NSRegularExpression(pattern: pattern, options: options).match(in: self)
92 | } catch {
93 | return false
94 | }
95 | }
96 |
97 | func matches(regex: NSRegularExpression) -> Bool {
98 | return regex.match(in: self)
99 | }
100 |
101 | // func isMatch(regex: String, options: NSRegularExpression.Options) -> Bool {
102 | // do {
103 | // let exp = try NSRegularExpression(pattern: regex, options: options)
104 | // let matchCount = exp.numberOfMatches(in: self, range: NSMakeRange(0, self.count))
105 | // return matchCount > 0
106 | // } catch {
107 | // return false
108 | // }
109 | // }
110 | //
111 | // func getMatches(regex: String, options: NSRegularExpression.Options) -> [NSTextCheckingResult]
112 | // {
113 | // do {
114 | // let exp = try NSRegularExpression(pattern: regex, options: options)
115 | // let matches = exp.matches(in: self, range: NSMakeRange(0, self.count))
116 | // return matches as [NSTextCheckingResult]
117 | // } catch {
118 | // return []
119 | // }
120 | // }
121 | }
122 |
123 | //import Foundation
124 | //
125 | //struct Regex {
126 | // var pattern: String {
127 | // didSet {
128 | // updateRegex()
129 | // }
130 | // }
131 | // var expressionOptions: NSRegularExpression.Options {
132 | // didSet {
133 | // updateRegex()
134 | // }
135 | // }
136 | // var matchingOptions: NSRegularExpression.MatchingOptions
137 | //
138 | // var regex: NSRegularExpression?
139 | //
140 | // init(pattern: String, expressionOptions: NSRegularExpression.MatchingOptions, matchingOptions: NSRegularExpression.MatchingOptions) {
141 | // self.pattern = pattern
142 | // self.expressionOptions = expressionOptions
143 | // self.matchingOptions = matchingOptions
144 | // updateRegex()
145 | // }
146 | //
147 | // init(pattern: String) {
148 | // self.pattern = pattern
149 | // expressionOptions = NSRegularExpression.MatchingFlags.
150 | // matchingOptions = NSMatchingOptions(0)
151 | // updateRegex()
152 | // }
153 | //
154 | // mutating func updateRegex() {
155 | // do {
156 | // regex = try NSRegularExpression(pattern: pattern, options: expressionOptions)
157 | // } catch {}
158 | // }
159 | //}
160 | //
161 | //
162 | //extension String {
163 | // func matchRegex(pattern: Regex) -> Bool {
164 | // let range: NSRange = NSMakeRange(0, countElements(self))
165 | // if pattern.regex != nil {
166 | // let matches: [AnyObject] = pattern.regex!.matchesInString(self, options: pattern.matchingOptions, range: range)
167 | // return matches.count > 0
168 | // }
169 | // return false
170 | // }
171 | //
172 | // func match(patternString: String) -> Bool {
173 | // return self.matchRegex(Regex(pattern: patternString))
174 | // }
175 | //
176 | // func replaceRegex(pattern: Regex, template: String) -> String {
177 | // if self.matchRegex(pattern) {
178 | // let range: NSRange = NSMakeRange(0, countElements(self))
179 | // if pattern.regex != nil {
180 | // return pattern.regex!.stringByReplacingMatchesInString(self, options: pattern.matchingOptions, range: range, withTemplate: template)
181 | // }
182 | // }
183 | // return self
184 | // }
185 | //
186 | // func replace(pattern: String, template: String) -> String {
187 | // return self.replaceRegex(Regex(pattern: pattern), template: template)
188 | // }
189 | //}
190 |
--------------------------------------------------------------------------------
/Howzatt/ScoreAPI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Score.swift
3 | // Swifting
4 | //
5 | // Created by Varun Oberoi on 6/17/21.
6 | // Copyright © 2021 Varun Oberoi. All rights reserved.
7 | //
8 |
9 |
10 | import Foundation
11 |
12 | private extension Array {
13 | subscript (safe index: Int) -> Element? {
14 | return indices ~= index ? self[index] : nil
15 | }
16 | }
17 |
18 | private enum ScoreElement: String {
19 | case title, guid
20 | }
21 |
22 | struct ScoreResult {
23 | let currentMatch: Match!
24 | let matches: [Match]
25 | }
26 |
27 | class ScoreAPI: NSObject {
28 |
29 | enum ParsingError: Error {
30 | case RuntimeError(String)
31 | }
32 |
33 | private let liveScoresUrl =
34 | URL(string: "http://static.espncricinfo.com/rss/livescores.xml")!
35 |
36 | private var allMatchScores = [Match]()
37 | private var currentScoreElement: ScoreElement?
38 | private var selectedMatch: Match?
39 | private var currentTitle = ""
40 | private var currentLink = ""
41 |
42 | func fetchScore(currentMatch: Match!, completion: @escaping ((ScoreResult) -> Void)) {
43 | let result = ScoreResult(currentMatch: nil, matches: [])
44 | guard let xmlParser = XMLParser(contentsOf: liveScoresUrl)
45 | else {
46 | return completion(result)
47 | }
48 |
49 | print("Loading matches...")
50 | allMatchScores.removeAll();
51 |
52 | xmlParser.delegate = self
53 | xmlParser.parse()
54 |
55 | selectedMatch = allMatchScores.first{(match: Match) -> Bool in
56 | if currentMatch == nil {
57 | return true
58 | } else {
59 | return match.link == currentMatch.link
60 | }
61 | }
62 | if selectedMatch == nil && allMatchScores.count > 0 {
63 | selectedMatch = allMatchScores[0]
64 | }
65 | if allMatchScores.count > 0 {
66 | guard let matchUrl = URL(string: selectedMatch!.link) else { return completion(result) }
67 | URLSession.shared.dataTask(with: URLRequest(url: matchUrl))
68 | { data, response, error in
69 | if error != nil {
70 | print("Error while fetching html for match page")
71 | return
72 | }
73 | guard let data = data,
74 | let html = String(data: data, encoding: .utf8)
75 | else { return }
76 |
77 | guard let parsedMatch = ScoreAPI.parseScoreFromPage(
78 | page: html as String, title: self.selectedMatch!.title, link: self.selectedMatch!.link)
79 | else {
80 | print("An error occurred while parsing score")
81 | return
82 | }
83 | completion(ScoreResult(currentMatch: parsedMatch, matches: self.allMatchScores))
84 | }.resume()
85 | } else {
86 | completion(result)
87 | }
88 | }
89 |
90 | class func parseScoreFromPage(page: String, title: String, link: String) -> Match? {
91 | let strFrom = ""
92 | let strTo = ""
93 |
94 | guard let pageTitle = page.components(separatedBy: strFrom)[safe: 1]?
95 | .components(separatedBy: strTo)[safe: 0] else { return nil }
96 |
97 | var formattedScore = ""
98 | if pageTitle.contains("|") {
99 | formattedScore = pageTitle.components(separatedBy: "|")[0]
100 | } else {
101 | formattedScore = pageTitle
102 | }
103 | var overs = ""
104 | let summary = formattedScore
105 | var status = ""
106 | var matchStarted = true
107 | let liveScore = formattedScore.components(separatedBy: "(")[safe: 0]?.trim()
108 | let summaryPart = formattedScore.components(separatedBy: "(")[safe: 1]?.components(separatedBy: ")")[safe: 0]
109 | if summaryPart?.contains("ov") ?? false {
110 | overs = summaryPart?.components(separatedBy: "ov")[0].trim() ?? ""
111 | // summary = summaryPart?.components(separatedBy: "ov,")[1].trim() ?? ""
112 | let parsedStatus = formattedScore.components(separatedBy: ") -")[safe: 1]?.components(separatedBy: "-")[safe: 0]
113 | status = parsedStatus?.trim().uppercased() ?? ""
114 | }
115 | let teams = title.replaceAll(of: #"(\d)|(\/)|(&)"#, with: "").components(separatedBy: " v ").map { $0.trim() }
116 | let scores = title.components(separatedBy: " v ").map { $0.replaceAll(of: #"((?!\d|\/|&).)*"#, with: "").trim() }
117 | if (scores.filter { !$0.isEmpty }).count == 0 {
118 | matchStarted = false
119 | }
120 | var teamArray = [Team]()
121 | for (index, score) in scores.enumerated() {
122 | let battingTeam = teams[index].contains("*")
123 | let teamScores = score.components(separatedBy: "&")
124 | var scoreArray = [Score]()
125 | for (scoreIndex, teamScore) in teamScores.enumerated() {
126 | // Overs info is only available for currently batting team
127 | if (battingTeam && scoreIndex == teamScores.count - 1 && !overs.isEmpty) {
128 | scoreArray.append(Score(score: teamScore, overs: "(" + overs + " ov)"))
129 | } else {
130 | scoreArray.append(Score(score: teamScore, overs: ""))
131 | }
132 | }
133 | teamArray.append(Team(name: teams[index].replacingOccurrences(of: "*", with: "").trim(), scores: scoreArray, batting: battingTeam))
134 | }
135 |
136 | if !overs.isEmpty {
137 | formattedScore = (liveScore ?? "") + " (\(overs) ov)"
138 | } else {
139 | formattedScore = title.trimmingCharacters(
140 | in: .whitespacesAndNewlines)
141 | }
142 |
143 | // Add space around &
144 | formattedScore = formattedScore.replacingOccurrences(
145 | of: "&", with: " & ")
146 | return Match(title: title, link: link, status: status, summary: summary, teams: teamArray, shortScore: formattedScore, fromPage: true, matchStarted: matchStarted)
147 | }
148 |
149 | private func isScoreItem(_ elementName: String) -> Bool {
150 | return elementName == "item"
151 | }
152 | }
153 |
154 | extension ScoreAPI: XMLParserDelegate {
155 |
156 | func parser(_ parser: XMLParser, didStartElement elementName: String,
157 | namespaceURI: String?, qualifiedName qName: String?,
158 | attributes attributeDict: [String : String]) {
159 | currentScoreElement = ScoreElement(rawValue: elementName)
160 |
161 | if isScoreItem(elementName) {
162 | currentTitle = ""
163 | currentLink = ""
164 | }
165 | }
166 |
167 | func parser(_ parser: XMLParser, foundCharacters string: String) {
168 | if let element = currentScoreElement {
169 | switch element {
170 | case .title:
171 | currentTitle.append(
172 | string.trimmingCharacters(in: .whitespacesAndNewlines))
173 | case .guid:
174 | currentLink.append(
175 | string.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: "cricinfo", with: "espncricinfo"))
176 | }
177 | }
178 | }
179 |
180 | func parser(_ parser: XMLParser, didEndElement elementName: String,
181 | namespaceURI: String?, qualifiedName qName: String?) {
182 | if isScoreItem(elementName) {
183 | if currentTitle.isEmpty || currentLink.isEmpty { return }
184 | allMatchScores.append(
185 | Match(title: currentTitle, link: currentLink, status: "", summary: "", teams: [], shortScore: currentTitle, fromPage: false, matchStarted: true))
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/Howzatt.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 075AA50E1A61A2360015B318 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075AA50D1A61A2360015B318 /* AppDelegate.swift */; };
11 | 075AA5101A61A2360015B318 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 075AA50F1A61A2360015B318 /* Images.xcassets */; };
12 | 075AA5131A61A2360015B318 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 075AA5111A61A2360015B318 /* MainMenu.xib */; };
13 | 075AA51F1A61A2360015B318 /* HowzattTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 075AA51E1A61A2360015B318 /* HowzattTests.swift */; };
14 | 07A8CC1E267BA19300011225 /* ScoreAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07A8CC1D267BA19300011225 /* ScoreAPI.swift */; };
15 | 07A8CC20267BA1C300011225 /* Match.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07A8CC1F267BA1C300011225 /* Match.swift */; };
16 | 07A8CC24267BA1F700011225 /* String+MinimumEditDistance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07A8CC21267BA1F700011225 /* String+MinimumEditDistance.swift */; };
17 | 07A8CC25267BA1F700011225 /* String+Regex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07A8CC22267BA1F700011225 /* String+Regex.swift */; };
18 | 07A8CC26267BA1F700011225 /* NSRegularExpression+match.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07A8CC23267BA1F700011225 /* NSRegularExpression+match.swift */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXContainerItemProxy section */
22 | 075AA5191A61A2360015B318 /* PBXContainerItemProxy */ = {
23 | isa = PBXContainerItemProxy;
24 | containerPortal = 075AA5001A61A2360015B318 /* Project object */;
25 | proxyType = 1;
26 | remoteGlobalIDString = 075AA5071A61A2360015B318;
27 | remoteInfo = "Cricket Scoreboard";
28 | };
29 | /* End PBXContainerItemProxy section */
30 |
31 | /* Begin PBXFileReference section */
32 | 075AA5081A61A2360015B318 /* Howzatt.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Howzatt.app; sourceTree = BUILT_PRODUCTS_DIR; };
33 | 075AA50C1A61A2360015B318 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
34 | 075AA50D1A61A2360015B318 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
35 | 075AA50F1A61A2360015B318 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
36 | 075AA5121A61A2360015B318 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
37 | 075AA5181A61A2360015B318 /* HowzattTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HowzattTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
38 | 075AA51D1A61A2360015B318 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
39 | 075AA51E1A61A2360015B318 /* HowzattTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HowzattTests.swift; path = HowzattTests/HowzattTests.swift; sourceTree = SOURCE_ROOT; };
40 | 07A8CC1D267BA19300011225 /* ScoreAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScoreAPI.swift; sourceTree = ""; };
41 | 07A8CC1F267BA1C300011225 /* Match.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Match.swift; sourceTree = ""; };
42 | 07A8CC21267BA1F700011225 /* String+MinimumEditDistance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+MinimumEditDistance.swift"; sourceTree = ""; };
43 | 07A8CC22267BA1F700011225 /* String+Regex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Regex.swift"; sourceTree = ""; };
44 | 07A8CC23267BA1F700011225 /* NSRegularExpression+match.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSRegularExpression+match.swift"; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 075AA5051A61A2360015B318 /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | 075AA5151A61A2360015B318 /* Frameworks */ = {
56 | isa = PBXFrameworksBuildPhase;
57 | buildActionMask = 2147483647;
58 | files = (
59 | );
60 | runOnlyForDeploymentPostprocessing = 0;
61 | };
62 | /* End PBXFrameworksBuildPhase section */
63 |
64 | /* Begin PBXGroup section */
65 | 075AA4FF1A61A2360015B318 = {
66 | isa = PBXGroup;
67 | children = (
68 | 075AA50A1A61A2360015B318 /* Howzatt */,
69 | 075AA51B1A61A2360015B318 /* HowzattTests */,
70 | 075AA5091A61A2360015B318 /* Products */,
71 | );
72 | sourceTree = "";
73 | };
74 | 075AA5091A61A2360015B318 /* Products */ = {
75 | isa = PBXGroup;
76 | children = (
77 | 075AA5081A61A2360015B318 /* Howzatt.app */,
78 | 075AA5181A61A2360015B318 /* HowzattTests.xctest */,
79 | );
80 | name = Products;
81 | sourceTree = "";
82 | };
83 | 075AA50A1A61A2360015B318 /* Howzatt */ = {
84 | isa = PBXGroup;
85 | children = (
86 | 07A8CC23267BA1F700011225 /* NSRegularExpression+match.swift */,
87 | 07A8CC21267BA1F700011225 /* String+MinimumEditDistance.swift */,
88 | 07A8CC22267BA1F700011225 /* String+Regex.swift */,
89 | 20B60F6F2679BF35006A33F0 /* Model */,
90 | 075AA50D1A61A2360015B318 /* AppDelegate.swift */,
91 | 075AA50F1A61A2360015B318 /* Images.xcassets */,
92 | 075AA5111A61A2360015B318 /* MainMenu.xib */,
93 | 075AA50B1A61A2360015B318 /* Supporting Files */,
94 | 07A8CC1D267BA19300011225 /* ScoreAPI.swift */,
95 | );
96 | path = Howzatt;
97 | sourceTree = "";
98 | };
99 | 075AA50B1A61A2360015B318 /* Supporting Files */ = {
100 | isa = PBXGroup;
101 | children = (
102 | 075AA50C1A61A2360015B318 /* Info.plist */,
103 | );
104 | name = "Supporting Files";
105 | sourceTree = "";
106 | };
107 | 075AA51B1A61A2360015B318 /* HowzattTests */ = {
108 | isa = PBXGroup;
109 | children = (
110 | 075AA51E1A61A2360015B318 /* HowzattTests.swift */,
111 | 075AA51C1A61A2360015B318 /* Supporting Files */,
112 | );
113 | path = HowzattTests;
114 | sourceTree = SOURCE_ROOT;
115 | };
116 | 075AA51C1A61A2360015B318 /* Supporting Files */ = {
117 | isa = PBXGroup;
118 | children = (
119 | 075AA51D1A61A2360015B318 /* Info.plist */,
120 | );
121 | name = "Supporting Files";
122 | sourceTree = "";
123 | };
124 | 20B60F6F2679BF35006A33F0 /* Model */ = {
125 | isa = PBXGroup;
126 | children = (
127 | 07A8CC1F267BA1C300011225 /* Match.swift */,
128 | );
129 | name = Model;
130 | sourceTree = "";
131 | };
132 | /* End PBXGroup section */
133 |
134 | /* Begin PBXNativeTarget section */
135 | 075AA5071A61A2360015B318 /* Howzatt */ = {
136 | isa = PBXNativeTarget;
137 | buildConfigurationList = 075AA5221A61A2360015B318 /* Build configuration list for PBXNativeTarget "Howzatt" */;
138 | buildPhases = (
139 | 075AA5041A61A2360015B318 /* Sources */,
140 | 075AA5051A61A2360015B318 /* Frameworks */,
141 | 075AA5061A61A2360015B318 /* Resources */,
142 | );
143 | buildRules = (
144 | );
145 | dependencies = (
146 | );
147 | name = Howzatt;
148 | productName = "Cricket Scoreboard";
149 | productReference = 075AA5081A61A2360015B318 /* Howzatt.app */;
150 | productType = "com.apple.product-type.application";
151 | };
152 | 075AA5171A61A2360015B318 /* HowzattTests */ = {
153 | isa = PBXNativeTarget;
154 | buildConfigurationList = 075AA5251A61A2360015B318 /* Build configuration list for PBXNativeTarget "HowzattTests" */;
155 | buildPhases = (
156 | 075AA5141A61A2360015B318 /* Sources */,
157 | 075AA5151A61A2360015B318 /* Frameworks */,
158 | 075AA5161A61A2360015B318 /* Resources */,
159 | );
160 | buildRules = (
161 | );
162 | dependencies = (
163 | 075AA51A1A61A2360015B318 /* PBXTargetDependency */,
164 | );
165 | name = HowzattTests;
166 | productName = "Cricket ScoreboardTests";
167 | productReference = 075AA5181A61A2360015B318 /* HowzattTests.xctest */;
168 | productType = "com.apple.product-type.bundle.unit-test";
169 | };
170 | /* End PBXNativeTarget section */
171 |
172 | /* Begin PBXProject section */
173 | 075AA5001A61A2360015B318 /* Project object */ = {
174 | isa = PBXProject;
175 | attributes = {
176 | LastSwiftMigration = 0710;
177 | LastSwiftUpdateCheck = 0710;
178 | LastUpgradeCheck = 1330;
179 | ORGANIZATIONNAME = "Varun Oberoi";
180 | TargetAttributes = {
181 | 075AA5071A61A2360015B318 = {
182 | CreatedOnToolsVersion = 6.1.1;
183 | DevelopmentTeam = 89X9RLE56U;
184 | ProvisioningStyle = Automatic;
185 | };
186 | 075AA5171A61A2360015B318 = {
187 | CreatedOnToolsVersion = 6.1.1;
188 | TestTargetID = 075AA5071A61A2360015B318;
189 | };
190 | };
191 | };
192 | buildConfigurationList = 075AA5031A61A2360015B318 /* Build configuration list for PBXProject "Howzatt" */;
193 | compatibilityVersion = "Xcode 3.2";
194 | developmentRegion = en;
195 | hasScannedForEncodings = 0;
196 | knownRegions = (
197 | en,
198 | Base,
199 | );
200 | mainGroup = 075AA4FF1A61A2360015B318;
201 | productRefGroup = 075AA5091A61A2360015B318 /* Products */;
202 | projectDirPath = "";
203 | projectRoot = "";
204 | targets = (
205 | 075AA5071A61A2360015B318 /* Howzatt */,
206 | 075AA5171A61A2360015B318 /* HowzattTests */,
207 | );
208 | };
209 | /* End PBXProject section */
210 |
211 | /* Begin PBXResourcesBuildPhase section */
212 | 075AA5061A61A2360015B318 /* Resources */ = {
213 | isa = PBXResourcesBuildPhase;
214 | buildActionMask = 2147483647;
215 | files = (
216 | 075AA5101A61A2360015B318 /* Images.xcassets in Resources */,
217 | 075AA5131A61A2360015B318 /* MainMenu.xib in Resources */,
218 | );
219 | runOnlyForDeploymentPostprocessing = 0;
220 | };
221 | 075AA5161A61A2360015B318 /* Resources */ = {
222 | isa = PBXResourcesBuildPhase;
223 | buildActionMask = 2147483647;
224 | files = (
225 | );
226 | runOnlyForDeploymentPostprocessing = 0;
227 | };
228 | /* End PBXResourcesBuildPhase section */
229 |
230 | /* Begin PBXSourcesBuildPhase section */
231 | 075AA5041A61A2360015B318 /* Sources */ = {
232 | isa = PBXSourcesBuildPhase;
233 | buildActionMask = 2147483647;
234 | files = (
235 | 07A8CC1E267BA19300011225 /* ScoreAPI.swift in Sources */,
236 | 075AA50E1A61A2360015B318 /* AppDelegate.swift in Sources */,
237 | 07A8CC26267BA1F700011225 /* NSRegularExpression+match.swift in Sources */,
238 | 07A8CC20267BA1C300011225 /* Match.swift in Sources */,
239 | 07A8CC25267BA1F700011225 /* String+Regex.swift in Sources */,
240 | 07A8CC24267BA1F700011225 /* String+MinimumEditDistance.swift in Sources */,
241 | );
242 | runOnlyForDeploymentPostprocessing = 0;
243 | };
244 | 075AA5141A61A2360015B318 /* Sources */ = {
245 | isa = PBXSourcesBuildPhase;
246 | buildActionMask = 2147483647;
247 | files = (
248 | 075AA51F1A61A2360015B318 /* HowzattTests.swift in Sources */,
249 | );
250 | runOnlyForDeploymentPostprocessing = 0;
251 | };
252 | /* End PBXSourcesBuildPhase section */
253 |
254 | /* Begin PBXTargetDependency section */
255 | 075AA51A1A61A2360015B318 /* PBXTargetDependency */ = {
256 | isa = PBXTargetDependency;
257 | target = 075AA5071A61A2360015B318 /* Howzatt */;
258 | targetProxy = 075AA5191A61A2360015B318 /* PBXContainerItemProxy */;
259 | };
260 | /* End PBXTargetDependency section */
261 |
262 | /* Begin PBXVariantGroup section */
263 | 075AA5111A61A2360015B318 /* MainMenu.xib */ = {
264 | isa = PBXVariantGroup;
265 | children = (
266 | 075AA5121A61A2360015B318 /* Base */,
267 | );
268 | name = MainMenu.xib;
269 | sourceTree = "";
270 | };
271 | /* End PBXVariantGroup section */
272 |
273 | /* Begin XCBuildConfiguration section */
274 | 075AA5201A61A2360015B318 /* Debug */ = {
275 | isa = XCBuildConfiguration;
276 | buildSettings = {
277 | ALWAYS_SEARCH_USER_PATHS = NO;
278 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
279 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
280 | CLANG_CXX_LIBRARY = "libc++";
281 | CLANG_ENABLE_MODULES = YES;
282 | CLANG_ENABLE_OBJC_ARC = YES;
283 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
284 | CLANG_WARN_BOOL_CONVERSION = YES;
285 | CLANG_WARN_COMMA = YES;
286 | CLANG_WARN_CONSTANT_CONVERSION = YES;
287 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
288 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
289 | CLANG_WARN_EMPTY_BODY = YES;
290 | CLANG_WARN_ENUM_CONVERSION = YES;
291 | CLANG_WARN_INFINITE_RECURSION = YES;
292 | CLANG_WARN_INT_CONVERSION = YES;
293 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
294 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
295 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
296 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
297 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
298 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
299 | CLANG_WARN_STRICT_PROTOTYPES = YES;
300 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
301 | CLANG_WARN_UNREACHABLE_CODE = YES;
302 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
303 | CODE_SIGN_IDENTITY = "-";
304 | COPY_PHASE_STRIP = NO;
305 | ENABLE_HARDENED_RUNTIME = YES;
306 | ENABLE_STRICT_OBJC_MSGSEND = YES;
307 | ENABLE_TESTABILITY = YES;
308 | GCC_C_LANGUAGE_STANDARD = gnu99;
309 | GCC_DYNAMIC_NO_PIC = NO;
310 | GCC_NO_COMMON_BLOCKS = YES;
311 | GCC_OPTIMIZATION_LEVEL = 0;
312 | GCC_PREPROCESSOR_DEFINITIONS = (
313 | "DEBUG=1",
314 | "$(inherited)",
315 | );
316 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
317 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
318 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
319 | GCC_WARN_UNDECLARED_SELECTOR = YES;
320 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
321 | GCC_WARN_UNUSED_FUNCTION = YES;
322 | GCC_WARN_UNUSED_VARIABLE = YES;
323 | MACOSX_DEPLOYMENT_TARGET = 10.15;
324 | MTL_ENABLE_DEBUG_INFO = YES;
325 | ONLY_ACTIVE_ARCH = YES;
326 | SDKROOT = macosx;
327 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
328 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
329 | SWIFT_VERSION = 5.0;
330 | };
331 | name = Debug;
332 | };
333 | 075AA5211A61A2360015B318 /* Release */ = {
334 | isa = XCBuildConfiguration;
335 | buildSettings = {
336 | ALWAYS_SEARCH_USER_PATHS = NO;
337 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
338 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
339 | CLANG_CXX_LIBRARY = "libc++";
340 | CLANG_ENABLE_MODULES = YES;
341 | CLANG_ENABLE_OBJC_ARC = YES;
342 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
343 | CLANG_WARN_BOOL_CONVERSION = YES;
344 | CLANG_WARN_COMMA = YES;
345 | CLANG_WARN_CONSTANT_CONVERSION = YES;
346 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
347 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
348 | CLANG_WARN_EMPTY_BODY = YES;
349 | CLANG_WARN_ENUM_CONVERSION = YES;
350 | CLANG_WARN_INFINITE_RECURSION = YES;
351 | CLANG_WARN_INT_CONVERSION = YES;
352 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
353 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
354 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
355 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
356 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
357 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
358 | CLANG_WARN_STRICT_PROTOTYPES = YES;
359 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
360 | CLANG_WARN_UNREACHABLE_CODE = YES;
361 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
362 | CODE_SIGN_IDENTITY = "-";
363 | COPY_PHASE_STRIP = YES;
364 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
365 | ENABLE_HARDENED_RUNTIME = YES;
366 | ENABLE_NS_ASSERTIONS = NO;
367 | ENABLE_STRICT_OBJC_MSGSEND = YES;
368 | GCC_C_LANGUAGE_STANDARD = gnu99;
369 | GCC_NO_COMMON_BLOCKS = YES;
370 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
371 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
372 | GCC_WARN_UNDECLARED_SELECTOR = YES;
373 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
374 | GCC_WARN_UNUSED_FUNCTION = YES;
375 | GCC_WARN_UNUSED_VARIABLE = YES;
376 | MACOSX_DEPLOYMENT_TARGET = 10.15;
377 | MTL_ENABLE_DEBUG_INFO = NO;
378 | ONLY_ACTIVE_ARCH = NO;
379 | SDKROOT = macosx;
380 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
381 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
382 | SWIFT_VERSION = 5.0;
383 | };
384 | name = Release;
385 | };
386 | 075AA5231A61A2360015B318 /* Debug */ = {
387 | isa = XCBuildConfiguration;
388 | buildSettings = {
389 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
390 | CODE_SIGN_IDENTITY = "-";
391 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
392 | CODE_SIGN_STYLE = Automatic;
393 | COMBINE_HIDPI_IMAGES = YES;
394 | CURRENT_PROJECT_VERSION = 7;
395 | DEVELOPMENT_TEAM = 89X9RLE56U;
396 | INFOPLIST_FILE = Howzatt/Info.plist;
397 | INFOPLIST_KEY_CFBundleDisplayName = Howzatt;
398 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports";
399 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
400 | MACOSX_DEPLOYMENT_TARGET = 10.15;
401 | MARKETING_VERSION = 2.1;
402 | PRODUCT_BUNDLE_IDENTIFIER = com.rubird.howzatt;
403 | PRODUCT_NAME = Howzatt;
404 | PROVISIONING_PROFILE_SPECIFIER = "";
405 | };
406 | name = Debug;
407 | };
408 | 075AA5241A61A2360015B318 /* Release */ = {
409 | isa = XCBuildConfiguration;
410 | buildSettings = {
411 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
412 | CODE_SIGN_IDENTITY = "-";
413 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
414 | CODE_SIGN_STYLE = Automatic;
415 | COMBINE_HIDPI_IMAGES = YES;
416 | CURRENT_PROJECT_VERSION = 7;
417 | DEVELOPMENT_TEAM = 89X9RLE56U;
418 | INFOPLIST_FILE = Howzatt/Info.plist;
419 | INFOPLIST_KEY_CFBundleDisplayName = Howzatt;
420 | INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.sports";
421 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
422 | MACOSX_DEPLOYMENT_TARGET = 10.15;
423 | MARKETING_VERSION = 2.1;
424 | PRODUCT_BUNDLE_IDENTIFIER = com.rubird.howzatt;
425 | PRODUCT_NAME = Howzatt;
426 | PROVISIONING_PROFILE_SPECIFIER = "";
427 | };
428 | name = Release;
429 | };
430 | 075AA5261A61A2360015B318 /* Debug */ = {
431 | isa = XCBuildConfiguration;
432 | buildSettings = {
433 | BUNDLE_LOADER = "$(TEST_HOST)";
434 | COMBINE_HIDPI_IMAGES = YES;
435 | FRAMEWORK_SEARCH_PATHS = (
436 | "$(DEVELOPER_FRAMEWORKS_DIR)",
437 | "$(inherited)",
438 | );
439 | GCC_PREPROCESSOR_DEFINITIONS = (
440 | "DEBUG=1",
441 | "$(inherited)",
442 | );
443 | INFOPLIST_FILE = HowzattTests/Info.plist;
444 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
445 | PRODUCT_BUNDLE_IDENTIFIER = "com.rubird.$(PRODUCT_NAME:rfc1034identifier)";
446 | PRODUCT_NAME = HowzattTests;
447 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Howzatt.app/Contents/MacOS/Howzatt";
448 | };
449 | name = Debug;
450 | };
451 | 075AA5271A61A2360015B318 /* Release */ = {
452 | isa = XCBuildConfiguration;
453 | buildSettings = {
454 | BUNDLE_LOADER = "$(TEST_HOST)";
455 | COMBINE_HIDPI_IMAGES = YES;
456 | FRAMEWORK_SEARCH_PATHS = (
457 | "$(DEVELOPER_FRAMEWORKS_DIR)",
458 | "$(inherited)",
459 | );
460 | INFOPLIST_FILE = HowzattTests/Info.plist;
461 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
462 | PRODUCT_BUNDLE_IDENTIFIER = "com.rubird.$(PRODUCT_NAME:rfc1034identifier)";
463 | PRODUCT_NAME = HowzattTests;
464 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Howzatt.app/Contents/MacOS/Howzatt";
465 | };
466 | name = Release;
467 | };
468 | /* End XCBuildConfiguration section */
469 |
470 | /* Begin XCConfigurationList section */
471 | 075AA5031A61A2360015B318 /* Build configuration list for PBXProject "Howzatt" */ = {
472 | isa = XCConfigurationList;
473 | buildConfigurations = (
474 | 075AA5201A61A2360015B318 /* Debug */,
475 | 075AA5211A61A2360015B318 /* Release */,
476 | );
477 | defaultConfigurationIsVisible = 0;
478 | defaultConfigurationName = Release;
479 | };
480 | 075AA5221A61A2360015B318 /* Build configuration list for PBXNativeTarget "Howzatt" */ = {
481 | isa = XCConfigurationList;
482 | buildConfigurations = (
483 | 075AA5231A61A2360015B318 /* Debug */,
484 | 075AA5241A61A2360015B318 /* Release */,
485 | );
486 | defaultConfigurationIsVisible = 0;
487 | defaultConfigurationName = Release;
488 | };
489 | 075AA5251A61A2360015B318 /* Build configuration list for PBXNativeTarget "HowzattTests" */ = {
490 | isa = XCConfigurationList;
491 | buildConfigurations = (
492 | 075AA5261A61A2360015B318 /* Debug */,
493 | 075AA5271A61A2360015B318 /* Release */,
494 | );
495 | defaultConfigurationIsVisible = 0;
496 | defaultConfigurationName = Release;
497 | };
498 | /* End XCConfigurationList section */
499 | };
500 | rootObject = 075AA5001A61A2360015B318 /* Project object */;
501 | }
502 |
--------------------------------------------------------------------------------