<commit_before>package ui.issuepanel.expanded;

import java.awt.Rectangle;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import javafx.concurrent.Task;

import org.apache.commons.io.IOUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

import service.ServiceManager;
import ui.UI;
import util.GitHubURL;
import util.IOUtilities;

/**
 * An abstraction for the functions of the Selenium web driver.
 * It depends minimally on UI for width adjustments.
 */
public class BrowserComponent {
	
	private static final boolean USE_MOBILE_USER_AGENT = false;

	private static String HIDE_ELEMENTS_SCRIPT_PATH = USE_MOBILE_USER_AGENT
			? "ui/issuepanel/expanded/mobileHideUI.js"
			: "ui/issuepanel/expanded/hideUI.js";

	// Chrome, Android 4.2.2, Samsung Galaxy S4
	private static final String MOBILE_USER_AGENT = "Mozilla/5.0 (Linux; Android 4.2.2; GT-I9505 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36";
	
	static {
		setupChromeDriverExecutable();
	}
	
	private final UI ui;
	private ChromeDriver driver = null;
	
	// We want browser commands to be run on a separate thread, but not to
	// interfere with each other. This executor is limited to a single instance,
	// so it ensures that browser commands are queued and executed in sequence.

	// The alternatives would be to:
	// - allow race conditions
	// - interrupt the blocking WebDriver::get method
	
	// The first is not desirable and the second does not seem to be possible
	// at the moment.
	private Executor executor;
	
	public BrowserComponent(UI ui) {
		this.ui = ui;
		this.executor = Executors.newSingleThreadExecutor();
	}

	/**
	 * Called on application startup. Blocks until the driver is created.
	 * Guaranteed to only happen once.
	 */
	public void initialise() {
		assert driver == null;
		System.out.println("initialise");
		driver = setupChromeDriver();
	}

	/**
	 * Called when application quits. Guaranteed to only happen once.
	 */
	public void quit() {
		assert driver != null;
		try {
			driver.close();
		} catch (WebDriverException e) {
			// Chrome was closed; do nothing
		}
	}
	
	/**
	 * Creates, initialises, and returns a ChromeDriver.
	 * @return
	 */
	private ChromeDriver setupChromeDriver() {
		ChromeOptions options = new ChromeOptions();
		if (USE_MOBILE_USER_AGENT) {
			options.addArguments(String.format("user-agent=\"%s\"", MOBILE_USER_AGENT));
		}
		ChromeDriver driver = new ChromeDriver(options);
		driver.manage().window().setPosition(new Point((int) ui.getCollapsedX(), 0));
		Rectangle availableDimensions = ui.getAvailableDimensions();
		driver.manage().window().setSize(new Dimension(
				(int) availableDimensions.getWidth(),
				(int) availableDimensions.getHeight()));
		return driver;
	}

	/**
	 * Executes Javascript in the currently-active driver window.
	 * Run on the UI thread (will block until execution is complete,
	 * i.e. change implementation if long-running scripts must be run).
	 * @param script
	 */
	private void executeJavaScript(String script) {
		if (driver instanceof JavascriptExecutor) {
			((JavascriptExecutor) driver).executeScript(script);
		} else {
			assert false : "Driver cannot execute JS";
		}
	}
	
	/**
	 * Runs a script in the currently-active driver window to hide GitHub UI elements.
	 */
	private void hidePageElements() {
		Optional<String> file = IOUtilities.readResource(HIDE_ELEMENTS_SCRIPT_PATH);
		if (file.isPresent()) {
			executeJavaScript(file.get());
		} else {
			System.out.println("Failed to read script for hiding elements; did not execute");
		}
	}

	/**
	 * Navigates to the New Label page on GitHub.
	 * Run on a separate thread.
	 */
	public void newLabel() {
		runBrowserOperation(() -> {
			if (!driver.getCurrentUrl().equals(GitHubURL.getPathForNewLabel())) {
				driver.get(GitHubURL.getPathForNewLabel());
				hidePageElements();
			}
		});
	}

	/**
	 * Navigates to the New Milestone page on GitHub.
	 * Run on a separate thread.
	 */
	public void newMilestone() {
		runBrowserOperation(() -> {
			if (!driver.getCurrentUrl().equals(GitHubURL.getPathForNewMilestone())) {
				driver.get(GitHubURL.getPathForNewMilestone());
				hidePageElements();
			}
		});
	}

	/**
	 * Navigates to the New Issue page on GitHub.
	 * Run on a separate thread.
	 */
	public void newIssue() {
		runBrowserOperation(() -> {
			if (!driver.getCurrentUrl().equals(GitHubURL.getPathForNewIssue())) {
				driver.get(GitHubURL.getPathForNewIssue());
				hidePageElements();
			}
		});
	}
	
	/**
	 * Navigates to the GitHub page for the given issue in the currently-active
	 * driver window.
	 * Run on a separate thread.
	 */
	public void showIssue(int id) {
		runBrowserOperation(() -> {
			if (!driver.getCurrentUrl().equals(GitHubURL.getPathForIssue(id))) {
				driver.get(GitHubURL.getPathForIssue(id));
				hidePageElements();
//				driver.findElement(By.id("#issue_title")).click();
			}
		});
	}
	
	/**
	 * A helper function for running browser operations.
	 * Takes care of running it on a separate thread, and normalises error-handling across<commit_msg>Disabled hiding of page elements<commit_after>package ui.issuepanel.expanded;

import java.awt.Rectangle;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Optional;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import javafx.concurrent.Task;

import org.apache.commons.io.IOUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.Point;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;

import service.ServiceManager;
import ui.UI;
import util.GitHubURL;
import util.IOUtilities;

/**
 * An abstraction for the functions of the Selenium web driver.
 * It depends minimally on UI for width adjustments.
 */
public class BrowserComponent {
	
	private static final boolean USE_MOBILE_USER_AGENT = false;

	private static String HIDE_ELEMENTS_SCRIPT_PATH = USE_MOBILE_USER_AGENT
			? "ui/issuepanel/expanded/mobileHideUI.js"
			: "ui/issuepanel/expanded/hideUI.js";

	// Chrome, Android 4.2.2, Samsung Galaxy S4
	private static final String MOBILE_USER_AGENT = "Mozilla/5.0 (Linux; Android 4.2.2; GT-I9505 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36";
	
	static {
		setupChromeDriverExecutable();
	}
	
	private final UI ui;
	private ChromeDriver driver = null;
	
	// We want browser commands to be run on a separate thread, but not to
	// interfere with each other. This executor is limited to a single instance,
	// so it ensures that browser commands are queued and executed in sequence.

	// The alternatives would be to:
	// - allow race conditions
	// - interrupt the blocking WebDriver::get method
	
	// The first is not desirable and the second does not seem to be possible
	// at the moment.
	private Executor executor;
	
	public BrowserComponent(UI ui) {
		this.ui = ui;
		this.executor = Executors.newSingleThreadExecutor();
	}

	/**
	 * Called on application startup. Blocks until the driver is created.
	 * Guaranteed to only happen once.
	 */
	public void initialise() {
		assert driver == null;
		driver = setupChromeDriver();
	}

	/**
	 * Called when application quits. Guaranteed to only happen once.
	 */
	public void quit() {
		assert driver != null;
		try {
			driver.close();
		} catch (WebDriverException e) {
			// Chrome was closed; do nothing
		}
	}
	
	/**
	 * Creates, initialises, and returns a ChromeDriver.
	 * @return
	 */
	private ChromeDriver setupChromeDriver() {
		ChromeOptions options = new ChromeOptions();
		if (USE_MOBILE_USER_AGENT) {
			options.addArguments(String.format("user-agent=\"%s\"", MOBILE_USER_AGENT));
		}
		ChromeDriver driver = new ChromeDriver(options);
		driver.manage().window().setPosition(new Point((int) ui.getCollapsedX(), 0));
		Rectangle availableDimensions = ui.getAvailableDimensions();
		driver.manage().window().setSize(new Dimension(
				(int) availableDimensions.getWidth(),
				(int) availableDimensions.getHeight()));
		return driver;
	}

	/**
	 * Executes Javascript in the currently-active driver window.
	 * Run on the UI thread (will block until execution is complete,
	 * i.e. change implementation if long-running scripts must be run).
	 * @param script
	 */
	private void executeJavaScript(String script) {
		if (driver instanceof JavascriptExecutor) {
			((JavascriptExecutor) driver).executeScript(script);
		} else {
			assert false : "Driver cannot execute JS";
		}
	}
	
	/**
	 * Runs a script in the currently-active driver window to hide GitHub UI elements.
	 */
	private void hidePageElements() {
		Optional<String> file = IOUtilities.readResource(HIDE_ELEMENTS_SCRIPT_PATH);
		if (file.isPresent()) {
			executeJavaScript(file.get());
		} else {
			System.out.println("Failed to read script for hiding elements; did not execute");
		}
	}

	/**
	 * Navigates to the New Label page on GitHub.
	 * Run on a separate thread.
	 */
	public void newLabel() {
		runBrowserOperation(() -> {
			if (!driver.getCurrentUrl().equals(GitHubURL.getPathForNewLabel())) {
				driver.get(GitHubURL.getPathForNewLabel());
			}
		});
	}

	/**
	 * Navigates to the New Milestone page on GitHub.
	 * Run on a separate thread.
	 */
	public void newMilestone() {
		runBrowserOperation(() -> {
			if (!driver.getCurrentUrl().equals(GitHubURL.getPathForNewMilestone())) {
				driver.get(GitHubURL.getPathForNewMilestone());
			}
		});
	}

	/**
	 * Navigates to the New Issue page on GitHub.
	 * Run on a separate thread.
	 */
	public void newIssue() {
		runBrowserOperation(() -> {
			if (!driver.getCurrentUrl().equals(GitHubURL.getPathForNewIssue())) {
				driver.get(GitHubURL.getPathForNewIssue());
			}
		});
	}
	
	/**
	 * Navigates to the GitHub page for the given issue in the currently-active
	 * driver window.
	 * Run on a separate thread.
	 */
	public void showIssue(int id) {
		runBrowserOperation(() -> {
			if (!driver.getCurrentUrl().equals(GitHubURL.getPathForIssue(id))) {
				driver.get(GitHubURL.getPathForIssue(id));
			}
		});
	}
	
	/**
	 * A helper function for running browser operations.
	 * Takes care of running it on a separate thread, and normalises error-handling across
{'fraction_non_alphanumeric': 0.11222080987833666, 'fraction_numerical': 0.006355547485019067, 'mean_word_length': 3.984162895927602, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3947832', 'n_tokens_mistral': 3733, 'n_tokens_neox': 3297, 'n_words': 945}