<file_sep>import Cocoa

class AppDelegate: NSObject, NSApplicationDelegate {

    private var context: Context!
    private var statusBarHandler: StatusBarHandler!

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        self.context = Context()
        self.statusBarHandler = StatusBarHandler(context: context)
    }
}
<file_sep>import Cocoa

final class StatusBarHandler {
    private static let updateInterval: TimeInterval = 60
    
    private let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
    private let popover = NSPopover()
    
    private let context: Context
    private var temperatureTimer: Timer!
    
    init(context: Context) {
        self.context = context
        
        setupStatusBarButton()
        setupPopover()
        scheduleUpdate()
    }
}

// MARK: - Setup

private extension StatusBarHandler {
    func setupStatusBarButton() {
        if let button = statusItem.button {
            button.target = self
            button.action = #selector(togglePopover(sender:))
        }
    }
    
    func setupPopover() {
        popover.contentViewController = WeatherViewController.create()
        popover.behavior = .transient
    }
    
    func scheduleUpdate() {
        temperatureTimer = Timer.scheduledTimer(
            withTimeInterval: StatusBarHandler.updateInterval, repeats: true
        ) { [weak self] _ in
            self?.context.weatherService.getTemperature { result in
                DispatchQueue.main.async {
                    switch result {
                    case .success(let temperature):
                        self?.statusItem.button?.title = temperature
                    case .failure(let error):
                        self?.statusItem.button?.title = "-"
                        print(error.localizedDescription)
                    }
                }
            }
        }
        
        temperatureTimer.fire()
    }
}

// MARK: - Popover actions

private extension StatusBarHandler {
    @objc func togglePopover(sender: Any?) {
        if popover.isShown {
            closePopover(sender: sender)
        } else {
            showPopover()
        }
    }
    
    func showPopover() {
        if let button = statusItem.button {
            popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
        }
    }
    
    func closePopover(sender: Any?) {
        popover.performClose(sender)
    }
}
<file_sep>import Foundation

enum WeatherServiceError: Error {
    case emptyResponse
    case incorrectResponse
}

// MARK: - LocalizedError implementation

extension WeatherServiceError: LocalizedError {
    var errorDescription: String? {
        switch self {
        case .emptyResponse:
            return "Server returned empty response"
        case .incorrectResponse:
            return "Response couldn't be parsed"
        }
    }
}
<file_sep>import Cocoa
import WebKit

final class WeatherViewController: NSViewController {
    @IBOutlet private var webView: WKWebView!
    
    private lazy var pageTransformer = WeatherViewController.loadTransformationCode()
}

// MARK: - Lifecycle

extension WeatherViewController {
    override func viewWillAppear() {
        super.viewWillAppear()
        loadPage()
    }
}

// MARK: - Fabric method

extension WeatherViewController {
    class func create() -> WeatherViewController {
        let storyboard = NSStoryboard(name: "WeatherViewController", bundle: nil)
        let identifier = "WeatherViewController"
        
        guard let weatherVC = storyboard.instantiateController(withIdentifier: identifier) as? WeatherViewController else {
            fatalError()
        }
        
        NSLayoutConstraint.activate([
            weatherVC.view.heightAnchor.constraint(equalToConstant: 550),
            weatherVC.view.widthAnchor.constraint(equalToConstant: 550)
        ])
        
        return weatherVC
    }
}

// MARK: - WKNavigationDelegate implementation

extension WeatherViewController: WKNavigationDelegate {
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        transformPage()
    }
}

// MARK: - Javascript helpers

private extension WeatherViewController {
    func loadPage() {
        let url = URL(string: "http://weather.nsu.ru")!
        webView.load(URLRequest(url: url))
    }
    
    func transformPage() {
        webView.evaluateJavaScript(pageTransformer) { (_, error) in
            error.flatMap { print($0) }
        }
    }
}

// MARK: - Load transforming code

private extension WeatherViewController {
    class func loadTransformationCode() -> String {
        guard let path = Bundle.main.url(forResource: "WeatherTransform", withExtension: "js") else {
            fatalError("Couldn't find WeatherTransform.js")
        }
        
        guard let jsCode = try? String(contentsOf: path) else {
            fatalError("Couldn't read WeatherTransform.js")
        }
        
        return jsCode
    }
}

<file_sep>import Foundation

final class WeatherService {
    private static let url = URL(string: "http://weather.nsu.ru/loadata.php")!
    private let urlSession = URLSession.shared
}

// MARK: - getTemperature method

extension WeatherService {
    func getTemperature(completion: @escaping (Result<String, Error>) -> ()) {
        let task = urlSession.dataTask(with: WeatherService.url) { data, response, error in
            if let error = error {
                completion(.failure(error))
            }
            
            guard let data = data, let string = String(data: data, encoding: .utf8) else {
                completion(.failure(WeatherServiceError.emptyResponse))
                return
            }
            
            guard let jsRange = string.range(of: "cnv.innerHTML = '") else {
                completion(.failure(WeatherServiceError.incorrectResponse))
                return
            }
            
            let beginTemperature = jsRange.upperBound
            guard let endTemperature = string.nextOccurance(of: "'", afterIndex: beginTemperature) else {
                completion(.failure(WeatherServiceError.incorrectResponse))
                return
            }
            
            let result = string[beginTemperature..<endTemperature]            
            completion(.success(String(result).withReplacedDegreeSymbol))
        }
        
        task.resume()
    }
}

// MARK: - String helpers

private extension String {
    func nextOccurance(of character: Character, afterIndex: String.Index) -> String.Index? {
        return suffix(from: afterIndex).firstIndex(of: character)
    }
    
    var withReplacedDegreeSymbol: String {
        return replacingOccurrences(of: "&deg;", with: "°")
    }
}
<file_sep>import Foundation

final class Context {
    let weatherService: WeatherService
    
    init() {
        self.weatherService = WeatherService()
    }
}
<file_sep>document.getElementsByTagName("small")[0].innerHTML = ""
document.getElementsByTagName("p")[0].innerHTML = ""

document.getElementsByTagName("h1")[0].setAttribute("style", "margin-top: -25px;")
document.getElementsByTagName("body")[0].setAttribute("style", "transform: scale(0.9, 0.9);")
