├── _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 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 2 | # Howzatt!! 3 | 4 | ![Howzatt icon](https://raw.githubusercontent.com/varunoberoi/Howzatt/master/assets/icon128.png) 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 | ![Howzatt screenshot](https://raw.githubusercontent.com/varunoberoi/Howzatt/master/assets/screenshot.jpg) 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 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 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 | 34 | 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 | --------------------------------------------------------------------------------