GWT:How to figure out HTMLPanel rendering is complete - javascript

I am working on an application which fetches HTML content from the server and displays it to the user. The content fetched from the server is a complete HTML document. I have used UiBinder to specify UI for the view.
<g:HTMLPanel ui:field="mainPanel" styleName="ap-mainPanel">
</g:HTMLPanel>
In the view I have setViewerContent(String content) method and also a member panel for holding content[contentPanel]
public void setViewerContent(String content)
{
contentPanel = new HTMLPanel(content);
contentPanel.setStyleName("ap-mainPanel ap-scrollPanel"); //$NON-NLS-1$
contentPanel.addAttachHandler(new AttachEvent.Handler() {
#Override
public void onAttachOrDetach(AttachEvent event) {
if(event.isAttached())
{
System.out.println("<-- rendering complete -->");
isRenderComplete = true;
}
}
});
mainPanel.clear();
mainPanel.add(contentPanel);
addScrollHandler();
}
I add a scroll handler to the contentPanel which listens to the ScrollEvent and onScroll() calls the appropriate methods to fetch content from the server
based on whether scroll is at the top or bottom.
public void addScrollHandler() {
Event.sinkEvents(contentPanel.getElement(), Event.ONSCROLL);
contentPanel.addHandler(this, ScrollEvent.getType());
}
public void onScroll( ScrollEvent event )
{
if( HelperUtils.isScrollAtBottom( event.getSource() ) )
{
if(isRenderComplete)
{
System.out.println("<-- Process Down scroll START-->");
isRenderComplete = false;
getUiHandlers().reachedMaxVerticalScrollPostion();
System.out.println("<-- Process Down scroll END-->");
}
}
if( HelperUtils.isScrollAtTop( event.getSource() ) )
{
if(isRenderComplete)
{
System.out.println("<-- Process Up scroll START-->");
isRenderComplete = false;
getUiHandlers().reachedMinVerticalScrollPostion();
System.out.println("<-- Process Up scroll END -->");
}
}
}
The problem I was facing was as we render the content I see calls made to the server to fetch content continuously. New scroll events are being fired while the content fetched from the server is being rendered. We would not want this i.e while the content is being rendered we do not want the ScrollEvent to be fired. I tried the above code where I have attached AttachEvent.Handler() to contentPanel. A flag isRenderComplete is maintained which is turned true on contentPanel attach. This flag is used in the onScroll method before triggering any server call.This approach seems to work.
But I am not sure if this is the correct one. Does anyone has any better solution[s] ?
Also since we are creating new contentPanel everytime each fetch takes the scrollbar to the top. I tried to add a new HTMLPanel markerPanel with couple of line breaks to the contentPanel. Then in the onAttachOrDetach() of contentPanel tried to scroll to the markerPanel. This did not work.
public void setViewerContent(String content)
{
contentPanel = new HTMLPanel(content);
markerPanel = new HTMLPanel(" <br> <br> ");
contentPanel.setStyleName("ap-mainPanel ap-scrollPanel"); //$NON-NLS-1$
contentPanel.addAttachHandler(new AttachEvent.Handler() {
#Override
public void onAttachOrDetach(AttachEvent event) {
System.out.println("<-- rendering complete -->");
if(event.isAttached())
{
markerPanel.getElement().scrollIntoView();
isRenderComplete = true;
}
}
});
mainPanel.clear();
contentPanel.add(markerPanel);
mainPanel.add(contentPanel);
addScrollHandler();
}
Any suggestions ? we want the scroll to be at the bottom when we fetch new content and the scroll event should not be fired till the content fetched is rendered completely.

Add the scroll handler after content is rendered and remove it when the panel is detached
public void setViewerContent(String content)
{
contentPanel = new HTMLPanel(content);
markerPanel = new HTMLPanel(" <br> <br> ");
contentPanel.setStyleName("ap-mainPanel ap-scrollPanel"); //$NON-NLS-1$
contentPanel.addAttachHandler(new AttachEvent.Handler() {
#Override
public void onAttachOrDetach(AttachEvent event) {
System.out.println("<-- rendering complete -->");
if(event.isAttached())
{
markerPanel.getElement().scrollIntoView();
isRenderComplete = true;
addScrollHandler();
}
else
{
isRenderComplete = false;
removeScrollHandler();
}
}
});
mainPanel.clear();
contentPanel.add(markerPanel);
mainPanel.add(contentPanel);
}

Related

Refresh WebView contents from a JS bridge in JavaFX

I am creating an AppDrawer (all in one place to launch shortcuts).
My JSCallBack bridge class has a method that should delete a shortcut, then refresh the page. But, it is unable to successfully refresh the page.
AppDrawerMain.java
public class AppDrawerMain extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(AppDrawerMain.class.getResource("main.fxml"));
Parent root = (Parent) fxmlLoader.load();
Scene scene = new Scene(root, 1280, 720);
String css = this.getClass().getResource("application.css").toExternalForm();
scene.getStylesheets().add(css);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
AppDrawerController.java
public class AppDrawerController implements Initializable {
#FXML
private Button refreshButton;
#FXML
private WebView webView;
WebEngine webEngine;
JSCallBack jsCallBack;
//this function essentially generates the html code for the webview
public String loadApps(){
return "<div class=\"container\"><img src=\"file:/"+imagePath+"\"/><p class=\"title\">"+displayName+"</p><div class=\"overlay\"></div><div class=\"button-open\" onclick=\"app.processOnClickOpen(\'"+id+"\')\"> Open </div><div class=\"button-option\" onclick=\"app.processOnClickOption(\'"+id+"\')\"> Edit </div></div>"
}
//refresh the page
private void refreshPage(String html){
webEngine.loadContent(html);
webEngine.reload();
}
#SneakyThrows
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
refreshButton.setOnAction(event -> {
refreshPage(loadApps());
});
webEngine = webView.getEngine();
webEngine.getLoadWorker().stateProperty().addListener((obs, oldValue, newValue)-> {
if (newValue == Worker.State.SUCCEEDED) {
JSObject jsObject = (JSObject) webEngine.executeScript("window");
jsCallBack = new JSCallBack(webEngine); //declared this way to avoid GC
jsObject.setMember("app", jsCallBack);
}
});
webEngine.setJavaScriptEnabled(true);
var html = loadApps();
webEngine.loadContent(html);
}
//The bridge class
public class JSCallBack {
protected JSCallBack() {}
//no refresh needed here
public void processOnClickOpen(String id) {
log("Before open");
onAppOpen(id);
log("After open");
}
//The part that isnt working
public void processOnClickOption(String id) {
//deleting the apps works fine
webEngine.loadContent(loadApps()+loadApps()); //trying to refresh the page isnt fine
webEngine.reload();
}
}
}
The problem I am having is here:
public void processOnClickOption(String id) {
//refreshPage(loadApps()+loadApps()) (not working either)
//refreshButton.fire() (not working either)
webEngine.loadContent(loadApps()+loadApps()); //trying to refresh the page isnt fine
webEngine.reload(); //not working
}
I tried adding location.reload() to the JavaScript function in the script itself, but it did not work.
I tried adding the refreshButton.fire() to the processOnClickOption(), which should reload the page if clicked on manually, but it did work either.
I tried to set a new WebEngine in the bridge class itself. Also, it did not work.
I added a log to see if there was an issue with threading, but WebEngine mentions that it was on the JavaFX Application thread, so that is not the problem either.
This is the main WebView:
If I click on the edit button, it should remove it from the WebView and refresh the page like this expected outcome:
Unfortunately, after clicking the edit button it will delete the files on the backend side, but the WebView is not refreshed, so the app stays there. I would like to get help on this.

Using Ionic (angular).Unable to access Hardware back button of mobile devices which have buttons inside the display/screen

I am facing a problem when I trying to access hardware back button so that I can produce an alert message to the user before the app get closed.
Initially I was unable to do , so I raised a question on stack overflow (link of the question) and with the help of the answers I almost solved my problem until I observed on some particular devices I unable to access the back button.
Noticing those devices which has back button inside the display (I think we can call this type of buttons as soft back button ) in those device I unable to produce alert box,to be more precise unable to access the back button.
But If I touch on the screen and than press the button, it is working fine . basically if someone just launch the app and presses the back button the app gets exit.
It is very difficult for me to write and produce my issue properly, therefore I am sharing a link of video please watch and try to understand and ask for any clarification
Link-1, The type of devices on which alert is not working
Link-2 On this type devices working fine
When you are in the app on another screen and press back button that time you go to the back screen. and when your screen is home or login, and that time within two seconds you press twice the time back button app is closed.
public astTimeBackPress = 0;
public timePeriodToExit = 2000;
constructor(
public toastController: ToastController,
private platform: Platform,
private nav: NavController,
private router: Router,
) { }
handleBackButton() {
this.platform.backButton.subscribe(() => {
if (this.loaderOff) {
document.addEventListener(
'backbutton',
() => {},
false);
} else {
if (
this.router.url === '/tabs/home' ||
this.router.url === '/signin'
) {
if (new Date().getTime() - this.lastTimeBackPress < this.timePeriodToExit) {
navigator['app'].exitApp();
} else {
this.presentToast('Press again to exit');
this.lastTimeBackPress = new Date().getTime();
}
} else {
this.nav.back();
}
}
});
}
async presentToast(msg, color = 'dark') {
const toast = await this.toastController.create({
color,
message: msg,
duration: 3000,
showCloseButton: true,
closeButtonText: 'Close',
});
toast.present();
}
public void callAlert(){
AlertDialog.Builder builder1 = new AlertDialog.Builder(appCompatActivity);
builder1.setMessage("Do you want to close.");
builder1.setCancelable(true);
builder1.setPositiveButton(
"Yes",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
finish();
}
});
builder1.setNegativeButton(
"No",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert11 = builder1.create();
alert11.show();
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
callAlert();
return true;
}
return super.onKeyDown(keyCode, event);
}

Waiting for element visibility instead of page load with Neustar WPM

I am trying to write a Neustar WPM script to measure the time taken from clicking a button to the appearance of a button in a overlay that opens. The script looks something like below.
var webDriver = test.openBrowser();
var selenium = webDriver.getSelenium();
webDriver.get('https://www.mywebsite.com');
selenium.waitForPageToLoad(30000);
// Start logging HTTP traffic and timings
test.beginTransaction(); 
test.beginStep("Open SignUp");
selenium.click("link=Sign Up");
selenium.waitForElementPresent("name=nextStep");
test.endStep();
test.endTransaction();
The problem I am facing is that click does not return immediately and waits for the overlay to completely load. However I want to stop as soon as desired element is visible. How can I ensure that selenium.click return immediately instead of waiting till entire page is loaded.
you can try using this Java method,
public WebElement waitForVisibilityOfElementLocatedBy(final By locator) {
return waitFor(visibilityOfElementLocated(locator));
}
public static ExpectedCondition<WebElement> visibilityOfElementLocated(final By locator) {
return new ExpectedCondition<WebElement>() {
public WebElement apply(WebDriver driver) {
try {
return ExpectedConditions.elementIfVisible(ExpectedConditions.findElement(locator, driver));
} catch (StaleElementReferenceException var2) {
return null;
}
}
public String toString() {
return "visibility of element located by " + locator;
}
};
}`

cefsharp execute javascript

I want to execute JavaScript code by using CefSharp in Windows Forms, but it does not work. The code is as following, and the message test is not shown. Did I miss something?
var browser = new ChromiumWebBrowser("http://localhost:50056/simple.aspx");
browser.Name = "Simple Page";
browser.Dock = DockStyle.Fill;
this.Controls.Add(browser);
browser.ExecuteScriptAsync("alert('test');");
You must wait for the browser to have sufficiently loaded before executing JavaScript. It's tempting to start trying to access the DOM in OnFrameLoadStart, whilst the V8Context will have been created and you will be able to execute a script the DOM will not have finished loading. If you need to access the DOM at its earliest possible point, subscribe to DOMContentLoaded.
Some examples of executing JavaScript are below.
browser.RenderProcessMessageHandler = new RenderProcessMessageHandler();
public class RenderProcessMessageHandler : IRenderProcessMessageHandler
{
// Wait for the underlying JavaScript Context to be created. This is only called for the main frame.
// If the page has no JavaScript, no context will be created.
void IRenderProcessMessageHandler.OnContextCreated(IWebBrowser browserControl, IBrowser browser, IFrame frame)
{
const string script = "document.addEventListener('DOMContentLoaded', function(){ alert('DomLoaded'); });";
frame.ExecuteJavaScriptAsync(script);
}
}
//Wait for the page to finish loading (all resources will have been loaded, rendering is likely still happening)
browser.LoadingStateChanged += (sender, args) =>
{
//Wait for the Page to finish loading
if (args.IsLoading == false)
{
browser.ExecuteJavaScriptAsync("alert('All Resources Have Loaded');");
}
}
//Wait for the MainFrame to finish loading
browser.FrameLoadEnd += (sender, args) =>
{
//Wait for the MainFrame to finish loading
if(args.Frame.IsMain)
{
args.Frame.ExecuteJavaScriptAsync("alert('MainFrame finished loading');");
}
};
I think, in the case of calling a JavaScript function that exists inside HTML, and passing input arguments, one can simply use the Browser.LoadingStateChanged event in the MainWindow constructor to make sure loading is initiated. This event will be called after the Browser_Loaded, where the HTML file is declared. Following is an example of the code:
public MainWindow()
{
InitializeComponent();
//Wait for the page to finish loading (all resources will have been loaded, rendering is likely still happening)
Browser.LoadingStateChanged += (sender, args) =>
{
//Wait for the Page to finish loading
if (args.IsLoading == false)
{
Browser.ExecuteScriptAsync("JavaScripFunctionName1", new object[] { arg1, arg2});
}
};
}
private void Browser_Loaded(object sender, RoutedEventArgs e)
{
Browser.LoadHtml(File.ReadAllText(GetFilePath("YourHTMLFileName.html")));
}
However, if you want to execute the JavaScript code and get results, you should use:
var result = await Browser.EvaluateScriptAsync("JavaScripFunctionName2", new object[] { });
MessageBox.Show(result.Result.ToString());
In HTML:
<html>
<body>
<script>
function JavaScripFunctionName1(arg1, arg2)
{
// something here
}
function JavaScripFunctionName2()
{
// something here
return result;
}
</script>
</body>
</html>

Dynamically injecting swfupload.js into a GWT project. JS code is unintentionally called before <span> exists in DOM

I'm attempting to inject a swfupload.js into a GWT project to support multifile upload. I have a version of the swfupload.js running locally on a non-GWT project but I'm having difficulty integrating it into the GWT project.
I suspect that the JS is being called before the <span> element exists in the DOM. The JS script is injected when an upload modal dialog appears, after adding a breakpoint at swfupload.js > loadFlash() and inspecting the targetElement it comes back as undefined when it should be the #btnFileUpload span. Additionally, I could see the <span> on screen when the script stops on the breakpoint.
With the breakpoint, querying for $('#btnFileUpload') in console I
get [].
Without the breakpoint, querying for $('#btnFileUpload')
in console I get <span id="btnFileUpload">...</span>.
Script Injector code in GWT/Java (works to the best of my knowledge)
#UiFactory
FormPanel createForm() {
[...]
String baseURL = GWT.getHostPageBaseURL();
ScriptInjector.fromUrl(baseURL + "js/jquery.min.js").inject();
String[] files = {
"swfupload.js",
"handlers.js",
"swfupload.queue.js",
"fileprogress.js",
"swfupload.impl.js"
};
for (String file : files) {
ScriptInjector.fromUrl(baseURL + "js/swfupload/" + file).inject();
}
[...]
}
JavaScript: swfupload.impl.js
var swfu;
$('#btnFileUpload').ready(function() {
var settings = {
button_placeholder_id: "btnFileUpload"
[...]
};
swfu = new SWFUpload(settings);
}
JavaScript: swfupload.js
// Gets called from SWFUpload 'constructor'
SWFUpload.prototype.loadFlash = function () {
var targetElement, tempParent;
[...]
// Get the element where we will be placing the flash movie
targetElement = document.getElementById(this.settings.button_placeholder_id) || this.settings.button_placeholder;
if (targetElement == undefined) {
throw "Could not find the placeholder element: " + this.settings.button_placeholder_id;
}
[...]
};
Note: I know there is an implentation of SWFUpload for GWT but I'd rather not go that route.
When you use ScriptInjector the scripts get added to the page using an IFrame so your scripts are running within their own browsing context. The document.getElementById in swfupload.js is accessing it's "own DOM" rather than the top-level one. You can try accessing the parent context, that probably has the span you're looking for, by using window.parent.
Here is an example for loading multiple js into GWT
ScriptInjector.fromUrl(GWT.getModuleBaseURL() + "lodash.js").setWindow(ScriptInjector.TOP_WINDOW)
.setCallback(new Callback<Void, Exception>() {
#Override
public void onFailure(Exception reason) {
System.out.println("lodash loading failed");
}
#Override
public void onSuccess(Void result) {
System.out.println("lodash loaded");
}
}).inject();
ScriptInjector.fromUrl(GWT.getModuleBaseURL() + "jquery.js").setWindow(ScriptInjector.TOP_WINDOW)
.setCallback(new Callback<Void, Exception>() {
#Override
public void onFailure(Exception reason) {
System.out.println("jquery loading failed");
}
#Override
public void onSuccess(Void result) {
System.out.println("jquery loaded");
}
}).inject();
ScriptInjector.fromUrl(GWT.getModuleBaseURL() + "backbone.js").setWindow(ScriptInjector.TOP_WINDOW)
.setCallback(new Callback<Void, Exception>() {
#Override
public void onFailure(Exception reason) {
System.out.println("backbone loading failed");
}
#Override
public void onSuccess(Void result) {
System.out.println("backbone loaded");
}
}).inject();
ScriptInjector.fromUrl(GWT.getModuleBaseURL() + "joint.min.js").setWindow(ScriptInjector.TOP_WINDOW)
.setCallback(new Callback<Void, Exception>() {
#Override
public void onFailure(Exception reason) {
System.out.println("joint.min loading failed");
}
#Override
public void onSuccess(Void result) {
System.out.println("joint.min loaded");
}
}).inject();

Categories

Resources