package com.abmash.api.query;
import com.abmash.api.Browser;
import com.abmash.api.HtmlElement;
import com.abmash.api.HtmlElements;
import com.abmash.core.element.Location;
import com.abmash.core.element.Size;
import com.abmash.core.jquery.JQuery;
import com.abmash.core.jquery.command.Command;
import com.abmash.core.jquery.command.CommandWithPredicates;
import com.abmash.core.query.BooleanType;
import com.abmash.core.query.predicate.*;
import com.abmash.core.tools.DataTypeConversion;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.remote.RemoteWebElement;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
/**
* Queries are used to find elements on the current web page. They contain {@link Predicates} which filter the elements
* by their visual properties. {@link HtmlElements} can be found by their location on screen and by their color. Predicates can be
* arbitrarily nested and combined with boolean operators.
*
* The {@link Query} can be executed by using the {@link #find()} method. In case of AJAX interaction, the {@link #findWithWait()}
* method repeatedly tries to find the elements until a timeout occurs.
*
* @author alp
*/
public class Query {
// TODO http://www.jarvana.com/jarvana/view/org/seleniumhq/selenium/selenium-support/2.0b3/selenium-support-2.0b3-javadoc.jar!/overview-summary.html
// TODO @CacheLookup - http://www.jarvana.com/jarvana/view/org/seleniumhq/selenium/selenium-support/2.0b3/selenium-support-2.0b3-javadoc.jar!/org/openqa/selenium/support/CacheLookup.html
// TODO ByChained - http://www.jarvana.com/jarvana/view/org/seleniumhq/selenium/selenium-support/2.0b3/selenium-support-2.0b3-javadoc.jar!/org/openqa/selenium/support/pagefactory/ByChained.html
// TODO hasParent(HtmlQuery), hasSibling(HtmlQuery)
// TODO inSameBlock(), inSameTable(), inNextBlock(), inPreviousBlock(), inThirdBlockAbove()
// TODO distinct text
// TODO if limit and no sorting condition, abort if there are enough results
private Browser browser = null;
private Predicates predicates = new Predicates();
private HashMap<String, HtmlElements> referenceElements = new HashMap<String, HtmlElements>();
public Query(Browser browser, Predicate... predicates) {
this.browser = browser;
addPredicates(predicates);
}
public Query(Browser browser, Predicates predicates) {
this(browser, (Predicate[]) predicates.toArray());
}
public void addPredicates(Predicates predicates) {
addPredicates((Predicate[]) predicates.toArray());
}
public void addPredicates(Predicate... predicates) {
for(Predicate predicate: predicates) {
this.predicates.add(predicate);
}
}
public void union(Query... queries) {
Predicate andPredicate = new BooleanPredicate(BooleanType.AND, (Predicate[]) predicates.toArray());
BooleanPredicate orPredicate = new BooleanPredicate(BooleanType.OR, andPredicate);
for(Query query: queries) {
orPredicate.addPredicates((Predicate[]) query.getPredicates().toArray());
}
predicates = new Predicates(andPredicate);
}
public Browser getBrowser() {
return browser;
}
public Predicates getPredicates() {
return predicates;
}
/**
* Finds all elements that match the given predicates.
*
* @return the {@link HtmlElements} result
*/
public HtmlElements find() {
return doFind(browser);
}
/**
* Finds all elements that match the given predicates and returns the first result.
*
* @return the {@link HtmlElements} result
*/
public HtmlElement findFirst() {
return find().first();
}
/**
* Finds all elements that match the given predicates. If the result is empty, the find process is
* repeatedly called until at least one element is found or a timeout exception is thrown. This can
* be useful to wait for an element to appear after triggering an AJAX request.
*
* @return the {@link HtmlElements} result
*/
public HtmlElements findWithWait() {
HtmlElements elements = doFind(browser);
if(elements == null || elements.isEmpty()) {
try {
browser.waitFor().query(this);
elements = doFind(browser);
} catch (TimeoutException e) {
// element not found
browser.log().info("Query: element not found for query " + this.toString());
}
}
return elements;
}
/**
* Finds all elements that match the given predicates and returns the first result. If the result
* is empty, the find process is repeatedly called until at least one element is found or a timeout exception
* is thrown. This can be useful to wait for an element to appear after triggering an AJAX request.
*
* @return the {@link HtmlElements} result
*/
public HtmlElement findFirstWithWait() {
return findWithWait().first();
}
@SuppressWarnings("unchecked")
private HtmlElements doFind(Browser browser) {
JSONArray jsonPredicates = new JSONArray();
try {
jsonPredicates = convertPredicatesToJSON(predicates);
// System.out.println(jsonPredicates.toString(2));
} catch (JSONException e) {
e.printStackTrace();
}
// send screenshot if color queries requested
if(hasColorPredicates(predicates)) {
try {
// TODO find way to take only one screenshot
byte[] pageAsPNGByteArray = ((TakesScreenshot) browser.getWebDriver()).getScreenshotAs(OutputType.BYTES);
BufferedImage image = ImageIO.read(new ByteArrayInputStream(pageAsPNGByteArray));
// encode image as base64 string
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
baos.flush();
pageAsPNGByteArray = baos.toByteArray();
baos.close();
// TODO find way to take only one screenshot
// String pageAsBase64PNG = "data:image/png;base64," + Base64.encodeBase64URLSafeString(pageAsPNGByteArray);
String pageAsBase64PNG = "data:image/png;base64," + ((TakesScreenshot) browser.getWebDriver()).getScreenshotAs(OutputType.BASE64);
// example base64 png
// pageAsBase64PNG = "data:image/png;base64," + "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAEWlJREFUeNrUmnmQXVWdxz/nnHvvu2/pfr2lk84CBAgQICxBZJMScdAQRhAhjBkdGUVLZ3UKqRKkBgWcKkDHWUpccHTGGkVxHEpwFBzAiYjIIiTEJBUC2Tq9b+/1W+9ylvnjvXSSpqOlg1XjqTp1X796957f9/x+3+/vd363hXOO33bEPC3aHw9eZfuzArz2DA6bmfYMgBB4e/v3TwAGSBaYcfuaAro9DeAAK15HAKINQLYB+O0ZXP62Gz8yNTX7Qc9TA9lsQKGQJZ/Psm79BUgp+J8nXiCOU+qNiGYjIooSnHN71pxx4l33fvFjjx0GJD0MgAXM6wlg/u77QHDFuo9dP3Rg4u4TV51ANpshnw8pFvN093RwyVvORCnJL57fRbXaYHa2TmW2TqMRE0UJu3fvTddfccGGT95+w4vzPGEOgpCvY/gs6ImxsZkNnu8xMTlOqTRNo1lFSE1Xd0hPb4G+RZ0sX9FDV3eIEJpmVKdUnmFqehLf9/yfPvnSh9obog577sF1hMfrO8R8EDrVpxe7Orn3i5/n1JNOQIgUZIyQEULWQThWHrcSXAZnQ3ABznnsfGUvf/Fnf0mjEZ17NONpI3s9jT94DYFlwPJLLj0nXLFiMUJNU2kKggB8HzwBon2HkDHWxmhXIU0gScAyzdvXncfY2EwfsBoYAobbIOaG9zswnna8DgPTT27aTFdXJ8eesJZSCXJZRbHo090d0N3tI4QgTS3VqqZUTqhWNI2mZnyixBOP/4JGI5LAK0BjofVe7xA66nAOZkpl4sTHkUGqLNmsRClBrZYwW0mYnY2oVGIaTd1WydZ97T8WVJvfGEDQ1Q9Atfy9I+ybd51b1DlHFMfcd++9dHTm6O0pMrC0l+XL+7ngwtPxfY+dO/czOVFmeHiSyYkSMzNVarUG1lraKnn4848A4wGsPOXsNloHzuGcxTqHswZrLNYarNEYY8h7CmP1gps8bxELWGtbXy/uX8zAwGLyhZCOjiyCDLVaipSaRs0iRIZisRdP5ejsjJmcmGL3nn0YYzlM9+d7wv3WIdSz6JpztLZ/KwTrgEBKiRAkQeD/ZNGi4rd/ueXLDwFGa4PWhpHRMWYrs3QWCxTyWXL5kGxeIaVk2/Y9JO1EVq83qdWaNOoNtDYYbQ4HYOdtEK/JAyMTowDrgG8Cv2in+S8DNwLZujb4uY7eNDWPA1c6R3B4hBljL5ucnP3qeRf89VUHAaSpJkk1WlvS1BDHhijSVGabzJYbNOopUaxJE4vRDmssafs+fQjAfBAATjjn5ofQm52zm4y1GJ1iTavscNZijLnfBpmbGo3GvwFvE6KlhQflUEqJkhKpJJ4n8X2PwPcIMh5hJiAT+AQZjyBofX/yycsRQvDq7lGssRhjSVJNkmiSJCWKEuJEkyatDbDWbskE/n2v7PjK1w4CEs45lHcoklYcf9ojWifroqgJzmGNwVqLECClMjVj9wEn9PUtpqeriOrowgs8OnJ5smEGP61RyGUJggA8D6UEnufheZIgUK2/fQ9PCVYM9CIFDI2XcM6htSVJNHGcEjVjojglSjRxnNCsN3l19x6mp6dRnvrz0f3//pWjARi1xiyRgY9SHs46krhJbXYGpTzqaYpNE3AOIQReroDneRTyOcJsloIH+VyWIBOQyWTwwixKSaRseUUpgZIS5Um6u/MIIZmdreMcGGNIU0OqDUmckCSaVBu0FWitieKEvXv3o0360vTot9ceTUaL+c4i2UInaZrgjKGjswubJjSbdVwSE+byJHGEsw4hJUoppFIEgY8SBoRAa4O1DVwzQggQEoQQSCnwVIu89UoFhKNej7DOYrRFa0s+nyfIBDTqNawTWBQWkEIS5rJUZpOTXqNCx5y4Zplz7macy85MjZGLGnQWezBAtTxFFDdbzBGSbJjHpAnamZZBvo9SisD3kU5gncOkKTpNSZIEbVKstdAGIFWbK1IghMA5cDiMcRitGUk1xkJ/fx+LFy+h2UyZqdaxzlEsdjNbLmcPEtlrG6+A7UARYNmyZczOlhnavwvnQArBkiVLyIYZtu3YSRAESKWQ1qKkwlcC3ySQRAhfodvGN5sRcRyT6hRtDELAG95wLhdc9CbOO/8ipFSsOukkRoaGaEQNhvbvY9OmTWz6yY+p16rsKs/y8suvcPppa1i6eBEHRsfJFfJH1jBtFSoCZSEVVqcEgU9fXy++r0hTTeB7jI+OMjIyTJwaliw/lvL0OEmSkuvuJpsJKXiQCTMopTA6pdFo0owi4riJ1oarrr6W99/wYToLObK5DCNjU1TKJWZK0xQKnfT399PXtwijU6SU/PzZZ7jzk7cxPTOFc3DmGWvw80VibXlpy2ZmJ78j53FA0NHRSa1SJkkSDgwNz2VinSbEUaNdlwi8NsGlsvh+gPIUQlgAtNYkcUocxzSbDZYMLOPuz/4Ti/v6qEcR//yFL/Hooz+kUa8DDtGuy1qPVqxfv57r3/vHXPDG83jk0R9x1z2f4T8euJ8XX9zMxW95K7F2+L4/l4mPSGRz5a0QR6k3W0nQ93w81ZJEpRSe583dY7QmjmOiOOaN57+J+/7l66xceQJf+uq/csUVl/PAt75BaWocYRMynkdHPks28FEYkmaZ7z/4Ha7dcB233XkXOrV8/OZb+dw/fhHnYPv27fhtvi1YzEkh5wwRQixc/jmH5ymE8lDKw2tLpHAtqUuSFnFXn7aGWz5xG1IFXLvhGvbsfhWJYdnyFRy3chVRM6LeqNO3aBE6TQmCDDMzkwzt30upPMMPHnqA5557lm9+/eu8+c2X8Mk77uL2T91K37IaUkoyXOhinj4SgGjLmzG/uixWSqE8hUxbQKRUOO1w1pGmKd29vXzi1tvJZwLecc27mBgdpNjZxalrziYMs5RK01Srs6RJTLk0jVKSTJhDIFh27PEcf9Jqdr+8g9HBV7jyXVfz8IPf4x1XvZNnnnmK57buOOIMewQAvw1goRASgHUHD4sCKVo7f9Cd1lqstWitufGmWzhm2RLe+/4PMjF2gIGBFaw6eTXVapWR4UHKM5PUa1XmL9PVu5iOtJtcocCas89j146tHBjax/U3fIAHv/sgt9/+af7wuj8ibR6KjSOPZ77XSkpHAdEaFiXaGi4FUspWyWEdxhjWnHkWZ5x2Go889gTbfrmFQr7A4qXLmJ6cYnJilJED+6jXqviBR64Q0tGZw8t4ZHIFytMTjA3tpTQ9wcTYKN29/XR0FBnZ/yr3feEL+Nk8d9zyceqNxsIAMkHQMl7Ko4SPO0gWoOUF58CJlgeMMVy7YSPNqME999yNdJaunl7KpWmq1TKjQ/sQQuJlFFJJuvxusoQE0iMrIcyHWKuZHB2iUilRrVXo6VuMkpJvPHA/o8NDXHzRhUeQ+AhLfd/H8xRSiF/hgXn+sAZrHdZafN/n1NWnsGnTk5RLU3R0dOJ5HgjB+MgBlPQQvsPzi/iFLmpZj6iQR3X1keY6iKMm0pMILDNTE3Mh2tFZJG5U+f5D/4UfZFl79loAOvs2zAegWvp+VA9YnJ2vTRJnHcZozjrnXHyl+NFjjyMRZHJZAJq1GtqkiEAgpY9QDoNHo2GoRp00GgpjDLliH2kS44chUaNG3GyVL9lCB1IIHnviv2lGKW/7g8sW9oAQqh3bC3PAHRTWORAt6UxN2or/08+gXq+zf3Av0g/wPB8QVCtlpGhxReGhMiFJs07cMAQmR1KdJGnEICVeEGKtwfMUzUYVgcD3fHzPY+/ePUxOTbP6lNULA/j1rYUjwVjXUh2dGpJUs2RgKbFOqdVqSNHijHWOJI7wgpbgJUmMMTFpGuMFeVKaCAHaplgHziqssUjfJ0mi1qIOlO+Bc8yWpujt6eHv7vrca2XUtON5oX7poe9aamO0wdlWmWGtxegEz/fIBCFpqlEK/EwGo/XB4xrCtffA0BIKFyMdoAKkskilsEajPIkUom0LGGcQQoEzGJPi+8HCHkjSBGM07lA7Y4HNFyS69TttNEmatKcmjqK584HWjmOWr2ypjufjdKtWUlKR1Or4fgatZ4ijMRCaMJsjrlQQCLxMtiUKXoBzjo5CEW0sDkchlyeO46MAiNNWJ2AhALbFACkhbiboJEGnmjTVGG3Qacrw0DDZbI58PkeSpMzMTGKtJZsrkKYJys+QWt1q2zTqZD2ffDYkG3iklTImSUBCNhOSxE3CXB6pBJ3FLpI0IRf6dPX0MD09yR2f/fvCawEkCVq3PLAgAQQooUiaTRKdoE2rg2adRQjJ5heex/c8Vq5chbUJlfIsxa5uMvkCQoBJktZZ2VpwjrTZJK5WSer1VvNAQLGrh3q1gu+FZLI5Ojt7mJmewiZNBpYuJcxk2P3qq9FtN32stiAAYwzGmAV5gGt5IUma6FRjbOt3UkikUmzZupnybIW3XnYZ1jgmpybo7e4lDLMUu/uJmw2cc/jZbCtMjcW111KeR3dPP3HUIG7W6e0fIAgCsmHI8NAg1ljWvf0dGGs5cGD/lxcMIa1bxh9NgkS7lDBp3Aoz65BCoKTA83yazYidO7ez7tJLWbp8BY1GhcHBfSxZspQwX6C7dwlxvYqOI8J8js7ubroXD1Ds6SYMQ8rT4zSrFRYvXUk234Hv+UyMj9GMIvoHlnH11e9kYnzCLF+y+PajANA45+bmIXXSR5TWOk1aYUCrM6E8he/7BJkMjz7yQ5wUbNz4PqTw2L9/H416nUX9A+QKnfQvPQ7lezSqFcozk0yPDVGanKA6WyIIQwaOPZFcvpMwl6Neq7J/cB9CCK7/wIcoFAoMDe557I677iktWI0691rypkmESdM5GRII0lQjBDgpyfk+2VyOXC5HPt9BIzEc119k43XX8szPn+LnTz/Jjh1bOeWU08iGOXTg44chzrSI77AIJEEmRPk+vvIIczlq5TKj42MAXL7+St69YQODg4Pp2Mjwx4+sA+ZlYiFkKwFZQ9xstGS13QNSqnXySuMmNo2xcZOpsSEO7NnFy9u28OKzP+WJRx9m3VVXs23rFj59x52sOeNMjLFs376V0swk+XyBnu4+cvkOit19FLsWUezuxQ8CMn6GIBMwMTrC3sG9WOu46OI3c9ONH6VUKtmd27Z8/m/+6sNbXwNg787Ns0anY81mnUyYmyOSdWauZSilbHfP0lYTNAhf1FFdmrghDp/nnXOWQEdieGjwM+Vy2X72M//AhRdcQJgJmC6V2LF9GwcG99GoVfB9j1wYEvg+zXqdkeH97Nj+S8Ynxgn8gGs2bOTO2z+F9Hz30ovP/UxrffP8CPEAlOex9NiTb61XS191uU5ynV2kSYxJE9Kkrfk6RWuNwJHJhCmIjwZd/c4vLiIpT8yl9nPXno61lhe27Lhl20svDJx+5jkb77n7HvXAd7/LIz/8AfsH91Muz1Aqw8jo6KEKy4F0FusMxx53Au/50xu4av3lVCoV+9zTP96SJsm7PvKh65PDAcRTw4c4cGD39q+tOOH0wahZvy9NopVpmmBMu7nbvikIMijlPWuduSmy4qmFtOrUU09Fa82qVatMkiR/8uBDj+yenlxxy3XXXuevv3y9eP6FzWx5aSujo8MsW7qEXbtexjiFh2Bg2VLWrl3LReefT3dPD7t37Up37dz2CPCe97/v3bWFCswjSHxg97bHgeOPXbXmGOvcMc6aU3WqC9akGhg3xjxVjePhI9+szutLFotzuSRJEt678ZpPfeNb//nT0ZEDn1t+zMrV55x1hrrk4otkFEU0owRjLcrz6OzIk8mEzJbLbnJ8VG9+/melNE3v3HjdOz+fy+UIw3DutPiaxtbB5q4q9PxWLzsOD6FfM64E3i2EuLSnt69bKY+u7m5Rr9VcFDWpVio6SeLHgR8A9wO1X/WwW2++seWBgxXjwfdfv8PxMPCwc05NT02eAWQnxkdXAweASWAPMPsbvR79v/yrwf+HIfk9H7/3AP53ACvaauGbV+7SAAAAAElFTkSuQmCC";
if(pageAsBase64PNG.length() >= 500000) {
for (int i = 0; i < pageAsBase64PNG.length(); i += 500000) {
browser.javaScript("abmash.buildImageDataForPageScreenshot(arguments[0])", pageAsBase64PNG.substring(i, Math.min(i + 499999, pageAsBase64PNG.length())));
}
browser.javaScript("abmash.updatePageScreenshot(arguments[0], arguments[1])", image.getWidth(), image.getHeight());
} else {
browser.javaScript("abmash.buildImageDataForPageScreenshot(arguments[0]); abmash.updatePageScreenshot(arguments[1], arguments[2])", pageAsBase64PNG, image.getWidth(), image.getHeight());
}
// browser.javaScript("abmash.updatePageScreenshot(arguments[0], arguments[1], arguments[2])", pageAsBase64PNG, image.getWidth(), image.getHeight());
} catch (IOException e) {
e.printStackTrace();
}
}
String script = "return abmash.query(arguments[0], arguments[1], arguments[2]);";
Object result = browser.javaScript(
script,
jsonPredicates.toString(),
referenceElements
// limit
).getReturnValue();
if(result.getClass().toString().equals("class java.lang.String")) {
browser.log().error("Query returned: {}", result);
return new HtmlElements();
}
ArrayList<Map<String, Object>> queryResult = (ArrayList<Map<String, Object>>) result;
// converting selenium elements to abmash elements
HtmlElements resultElements = new HtmlElements();
for(Map<String, Object> queryElement: queryResult) {
HtmlElement element = new HtmlElement(browser, (RemoteWebElement) queryElement.get("element"));
element.setTagName((String) queryElement.get("tag"));
element.setText((String) queryElement.get("text"));
element.setSourceText((String) queryElement.get("sourceText"));
element.setAttributeNames((ArrayList<String>) queryElement.get("attributeNames"));
element.setAttributes((Map<String,String>) queryElement.get("attributes"));
element.setUniqueSelector((String) queryElement.get("uniqueSelector"));
Map<String, Object> location = (Map<String, Object>) queryElement.get("location");
Double left = DataTypeConversion.longOrDoubleToDouble(location.get("left"));
Double top = DataTypeConversion.longOrDoubleToDouble(location.get("top"));
element.setLocation(new Location(left, top));
Map<String, Object> size = (Map<String, Object>) queryElement.get("size");
Double width = DataTypeConversion.longOrDoubleToDouble(size.get("width"));
Double height = DataTypeConversion.longOrDoubleToDouble(size.get("height"));
element.setSize(new Size(width, height));
if(queryElement.containsKey("iframe")) {
element.setFrameElement(new HtmlElement(browser, (RemoteWebElement) queryElement.get("iframe")));
}
resultElements.add(element);
}
return resultElements;
}
private boolean hasColorPredicates(Predicates predicates) {
for(Predicate predicate: predicates) {
if(predicate instanceof ColorPredicate) return true;
if(predicate instanceof RecursivePredicate) {
if(hasColorPredicates(((RecursivePredicate) predicate).getPredicates())) {
return true;
}
}
// TODO DirectionPredicate should be subclass of RecursivePredicate
if(predicate instanceof DirectionPredicate) {
if(hasColorPredicates(((DirectionPredicate) predicate).getPredicates())) {
return true;
}
}
}
return false;
}
private JSONArray convertPredicatesToJSON(Predicates predicates) throws JSONException {
JSONArray jsonPredicates = new JSONArray();
ArrayList<JSONObject> filteringPredicates = new ArrayList<JSONObject>();
for(Predicate predicate: predicates) {
JSONObject jsonPredicate = new JSONObject();
if(predicate instanceof RecursivePredicate) {
if(predicate instanceof BooleanPredicate) {
jsonPredicate.put("isBoolean", true);
jsonPredicate.put("type", ((BooleanPredicate) predicate).getType());
}
jsonPredicate.put("predicates", convertPredicatesToJSON(((RecursivePredicate) predicate).getPredicates()));
}
if(predicate instanceof JQueryPredicate) {
JSONArray jsonJQueryList = new JSONArray();
for(JQuery jQuery: ((JQueryPredicate) predicate).getJQueryList()) {
jsonJQueryList.put(convertJQueryToJSON(jQuery));
}
jsonPredicate.put("jQueryList", jsonJQueryList);
}
// predicates that contain preselected HtmlElements
if(predicate instanceof ElementPredicate) {
String referenceId = Long.toString(System.currentTimeMillis()) + Double.toString(Math.random());
jsonPredicate.put("referenceId", referenceId);
referenceElements.put(referenceId, ((ElementPredicate) predicate).getElements());
}
// finally add the predicate to the list
if(predicate instanceof DirectionPredicate || predicate instanceof ColorPredicate) {
filteringPredicates.add(jsonPredicate);
} else {
jsonPredicates.put(jsonPredicate);
}
}
// put all direction predicates at the end
for(JSONObject jsonPredicate: filteringPredicates) {
jsonPredicates.put(jsonPredicate);
}
return jsonPredicates;
}
private JSONObject convertJQueryToJSON(JQuery jQuery) throws JSONException {
JSONArray jsonCommands = new JSONArray();
for(Command command: jQuery.getCommands()) {
JSONObject jsonCommand = new JSONObject();
jsonCommand.put("method", command.getMethod());
if(command instanceof CommandWithPredicates) {
jsonCommand.put("predicates", convertPredicatesToJSON(((CommandWithPredicates) command).getPredicates()));
}
jsonCommand.put("selector", command.getSelector());
jsonCommands.put(jsonCommand);
}
JSONObject jsonJQuery = new JSONObject();
jsonJQuery.put("selector", jQuery.getSelector());
jsonJQuery.put("weight", jQuery.getWeight());
jsonJQuery.put("commands", jsonCommands);
return jsonJQuery;
}
@Override
public String toString() {
return toString(0);
}
public String toString(int intendationSpaces) {
return "Query:" + predicates.toString(intendationSpaces + 2);
}
}