package com.codeborne.selenide.impl;

import org.apache.commons.io.IOUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.By.ByCssSelector;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;

import java.io.IOException;
import java.util.List;

import static com.codeborne.selenide.Configuration.SelectorMode.CSS;
import static com.codeborne.selenide.Configuration.selectorMode;
import static com.codeborne.selenide.Selenide.executeJavaScript;
import static java.lang.Thread.currentThread;

/**
 * Thanks to http://selenium.polteq.com/en/injecting-the-sizzle-css-selector-library/
 */
public class WebElementSelector {
  public static WebElementSelector instance = new WebElementSelector();
  
  protected String sizzleSource;

  public WebElement findElement(SearchContext context, By selector) {
    if (selectorMode == CSS || !(selector instanceof ByCssSelector)) {
      return context.findElement(selector);
    }
    
    List<WebElement> webElements = evaluateSizzleSelector(context, (ByCssSelector) selector);
    return webElements.isEmpty() ? null : webElements.get(0);
  }

  public List<WebElement> findElements(SearchContext context, By selector) {
    if (selectorMode == CSS || !(selector instanceof ByCssSelector)) {
      return context.findElements(selector);
    }
    
    return evaluateSizzleSelector(context, (ByCssSelector) selector);
  }

  protected List<WebElement> evaluateSizzleSelector(SearchContext context, ByCssSelector sizzleCssSelector) {
    injectSizzleIfNeeded();

    String sizzleSelector = sizzleCssSelector.toString()
        .replace("By.selector: ", "")
        .replace("By.cssSelector: ", "");
    
    if (context instanceof WebElement)
      return executeJavaScript("return Sizzle(arguments[0], arguments[1])", sizzleSelector, context);
    else
      return executeJavaScript("return Sizzle(arguments[0])", sizzleSelector);
  }

  protected void injectSizzleIfNeeded() {
    if (!sizzleLoaded()) {
      injectSizzle();
    }
  }

  protected Boolean sizzleLoaded() {
    try {
      return executeJavaScript("return Sizzle() != null");
    } catch (WebDriverException e) {
      return false;
    }
  }
  
  protected synchronized void injectSizzle() {
    if (sizzleSource == null) {
      try {
        sizzleSource = IOUtils.toString(currentThread().getContextClassLoader().getResource("sizzle.js"));
      } catch (IOException e) {
        throw new RuntimeException("Cannot load sizzle.js from classpath", e);
      }
    }
    executeJavaScript(sizzleSource);
  }
}

package com.codeborne.selenide.impl;

import org.apache.commons.io.IOUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.By.ByCssSelector;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;

import java.io.IOException;
import java.util.List;

import static com.codeborne.selenide.Configuration.SelectorMode.CSS;
import static com.codeborne.selenide.Configuration.selectorMode;
import static com.codeborne.selenide.Selenide.executeJavaScript;
import static java.lang.Thread.currentThread;

/**
 * Thanks to http://selenium.polteq.com/en/injecting-the-sizzle-css-selector-library/
 */
public class WebElementSelector {
  public static WebElementSelector instance = new WebElementSelector();
  
  protected String sizzleSource;

  public WebElement findElement(SearchContext context, By selector) {
    if (selectorMode == CSS || !(selector instanceof ByCssSelector)) {
      return context.findElement(selector);
    }
    
    List<WebElement> webElements = evaluateSizzleSelector(context, (ByCssSelector) selector);
    return webElements.isEmpty() ? null : webElements.get(0);
  }

  public List<WebElement> findElements(SearchContext context, By selector) {
    if (selectorMode == CSS || !(selector instanceof ByCssSelector)) {
      return context.findElements(selector);
    }
    
    return evaluateSizzleSelector(context, (ByCssSelector) selector);
  }

  protected List<WebElement> evaluateSizzleSelector(SearchContext context, ByCssSelector sizzleCssSelector) {
    injectSizzleIfNeeded();

    String sizzleSelector = sizzleCssSelector.toString()
        .replace("By.selector: ", "")
        .replace("By.cssSelector: ", "");
    
    if (context instanceof WebElement)
      return executeJavaScript("return Sizzle(arguments[0], arguments[1])", sizzleSelector, context);
    else
      return executeJavaScript("return Sizzle(arguments[0])", sizzleSelector);
  }

  protected void injectSizzleIfNeeded() {
    if (!sizzleLoaded()) {
      injectSizzle();
    }
  }

  protected Boolean sizzleLoaded() {
    try {
      return executeJavaScript("return typeof Sizzle != 'undefined'");
    } catch (WebDriverException e) {
      return false;
    }
  }
  
  protected synchronized void injectSizzle() {
    if (sizzleSource == null) {
      try {
        sizzleSource = IOUtils.toString(currentThread().getContextClassLoader().getResource("sizzle.js"));
      } catch (IOException e) {
        throw new RuntimeException("Cannot load sizzle.js from classpath", e);
      }
    }
    executeJavaScript(sizzleSource);
  }
}

Check if Sizzle is defined