I order my imported CSS and JS with a JavascriptFilteredIntoFooterHeaderResponse. With this Class goes all my CSS in the header and all my JS to a separate container near the </body> tag. But now i need to add one JS to the header but wicket pushed every JS to the footer. Knows anybody a solution for this? JavascriptFilteredIntoFooterHeaderResponse is final and can't be overriden.
WicketApplication
#Override
public void init()
{
super.init();
setHeaderResponseDecorator( new IHeaderResponseDecorator()
{
#Override
public IHeaderResponse decorate( IHeaderResponse response )
{
return new JavascriptFilteredIntoFooterHeaderResponse( response, FOOTER_FILTER_NAME );
}
} );
}
BasePage.java
public BasePage()
{
add( new HeaderResponseFilteredResponseContainer( FOOTER_FILTER_NAME, FOOTER_FILTER_NAME ) );
}
BasePage.html
<body>
...
<div wicket:id="footerBucket" />
</body>
You don't need to override JavascriptFilteredIntoFooterHeaderResponse. Just use org.apache.wicket.resource.filtering.HeaderResponseContainerFilteringHeaderResponse with the following constructor:
HeaderResponseContainerFilteringHeaderResponse(IHeaderResponse response,
String headerFilterName, IHeaderResponseFilter[] filters)
For example, if you write:
IHeaderResponseFilter[] filters = new IHeaderResponseFilter[] {
new CssAcceptingHeaderResponseFilter(HEADER_FILTER_NAME),
new JavascriptAcceptingHeaderResponseFilter(FOOTER_FILTER_NAME) };
return new HeaderResponseContainerFilteringHeaderResponse(response,
HEADER_FILTER_NAME, filters);
it will be the same JavascriptFilteredIntoFooterHeaderResponse that you use in your code.
Here's an example of anonymous filter class that you can use instead of CssAcceptingHeaderResponseFilter to accomplish your task. File "script-for-the-header.js" is the script that you'd like to have in the header.
new CssAcceptingHeaderResponseFilter(HEADER_FILTER_NAME) {
#Override
public boolean acceptReference(ResourceReference ref) {
if (!Strings.isEmpty(ref.getName()) && ref.getName().equals("script-for-the-header.js")) {
return true;
} else {
return super.acceptReference(ref);
}
}
}
To better organize your code you may also consider overriding HeaderResponseContainerFilteringHeaderResponse (if you haven't guessed yet, it's also the parent of JavascriptFilteredIntoFooterHeaderResponse). See source code in JavascriptFilteredIntoFooterHeaderResponse.java for details.
Related
I am building widget in FlutterFlow using WebViewX, I use JavaScript inside WebViewX to hide part of the site I am showing. Below is my code:
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:webviewx/webviewx.dart';
class MyWebView extends StatefulWidget {
const MyWebView({
Key? key,
required this.url,
this.width,
this.height,
this.bypass = false,
this.horizontalScroll = false,
this.verticalScroll = false,
}) : super(key: key);
final bool bypass;
final bool horizontalScroll;
final bool verticalScroll;
final double? height;
final double? width;
final String url;
#override
_MyWebViewState createState() => _MyWebViewState();
}
class _MyWebViewState extends State<MyWebView> {
late WebViewXController webviewController;
#override
void dispose() {
webviewController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) => WebViewX(
key: webviewKey,
width: widget.width ?? MediaQuery.of(context).size.width,
height: widget.height ?? MediaQuery.of(context).size.height,
ignoreAllGestures: false,
initialContent: widget.url,
initialMediaPlaybackPolicy:
AutoMediaPlaybackPolicy.requireUserActionForAllMediaTypes,
initialSourceType:
widget.bypass ? SourceType.urlBypass : SourceType.url,
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (controller) => webviewController = controller,
onPageFinished: (String url) {
webviewController.evalRawJavascript(
"document.getElementsByClassName('news-info')[0].style.display='none';"),
},
webSpecificParams: const WebSpecificParams(
webAllowFullscreenContent: true,
),
mobileSpecificParams: MobileSpecificParams(
debuggingEnabled: false,
gestureNavigationEnabled: true,
mobileGestureRecognizers: {
if (widget.verticalScroll)
Factory<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer(),
),
if (widget.horizontalScroll)
Factory<HorizontalDragGestureRecognizer>(
() => HorizontalDragGestureRecognizer(),
),
},
androidEnableHybridComposition: true,
),
);
Key get webviewKey => Key(
[
widget.url,
widget.width,
widget.height,
widget.bypass,
widget.horizontalScroll,
widget.verticalScroll,
].map((s) => s?.toString() ?? '').join(),
);
}
Page loads just fine, but required div block is not hidden, I get Uncaught TypeError: Cannot read properties of null. I checked loaded site page in WebViewX frame and there is no original DOM structure so no div named "news-info" is present, so JS cannot find it. My questions are:
Is it normal WebView behaviour not to keep original DOM?
I have seen guides on using JS inside WebView to hide part of the site loaded, what is wrong with my implementation? Do I call JS code on the right time, after page is finished? Or should I do this earlier?
If there is no mistakes in the code could it be FlutterFlow or WebViewX issues, as guides I have seen are about WebView?
Thanks in advance for any advice.
I spent 2 days trying to find answer before posting here, but found in 5 minutes after posting. What a shame :)
So WebViewX implementation is able to inject some JS code if source type of URL you pass to WebViewX is defined as "urlBypass", while default is just "url". Added a line:
initialSourceType: SourceType.urlBypass,
and you are good to go.
I have an Android test app with a webView like so:
<WebView
android:alpha="0"
android:id="#+id/myWebView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
My Main activity loads a page that I have JS content running. JS is enabled using:
webSettings.setJavaScriptEnabled(true);
There is a button on my app that toggles the alpha value from 0 to 1 (show / hide webView).
Is there any "creative" way of detecting the change on the JS side?
Things I tried:
Checking requestAnimationFrame frame rate changes.
Visibility API
Update:
Clarification, I'm looking for a JS Only solution, the actual JS code is an SDK used inside a WebView environment.
I have no control over the Android app, full control over the WebView content.
You could subclass android's WebView, override its setAlpha method and add some logic to let the webpage know the current alpha value.
Here is a simplified example:
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.webkit.WebView;
public class MyWebView extends WebView {
public MyWebView(Context context) {
super(context);
init();
}
public MyWebView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public MyWebView(Context context,
AttributeSet attrs,
int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
#SuppressLint("SetJavaScriptEnabled")
private void init() {
getSettings().setJavaScriptEnabled(true);
}
#Override
public void setAlpha(float alpha) {
super.setAlpha(alpha);
propagateAlphaToWebPage(alpha);
}
private void propagateAlphaToWebPage(float alpha) {
// setWebViewAlpha is a JS function that should be defined in your html's js
String jssnippet = "setWebViewAlpha("+alpha+");";
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
evaluateJavascript(jssnippet, null);
} else {
loadUrl("javascript:"+jssnippet);
}
}
}
Other related questions you may find helpful:
Android WebView - detect whether a JS function is defined
Android Calling JavaScript functions in WebView
Declaring a custom android UI element using XML
Below is complete code which will tell to JavaScript if WebView is visible or not.
Steps:
Create a layout containing a ToggleButton and WebView.
Load your html using htmlWebView.loadUrl("file:///android_asset/index.html"); and related code
Create a function onToggleButtonPressed() which will be called onClick of toggle button
In onToggleButtonPressed() show/hide WebView and at the same time pass the status to JavaScript using htmlWebView.evaluateJavascript() method. Pass visibilityStatusFromAndroid() to JavaScript
Get the status in JavaScript from visibilityStatusFromAndroid() function
Android Layout xml code:
<ToggleButton
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:background="#color/red"
android:onClick="onToggleButtonPressed"
android:text="Button1"
android:textOn="Show"
android:textOff="Hide"
tools:ignore="MissingConstraints"
tools:layout_editor_absoluteX="148dp"
tools:layout_editor_absoluteY="0dp" />
<WebView
android:id="#+id/webView"
android:layout_width="368dp"
android:layout_height="447dp"
android:layout_alignParentStart="true"
android:layout_below="#+id/button"
tools:ignore="MissingConstraints"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="56dp"
android:layout_alignParentLeft="true" />
Java Code:
WebView htmlWebView; // put it outside onCreate() to make it accessible in onToggleButtonPressed()
htmlWebView = (WebView)findViewById(R.id.webView);
htmlWebView.setWebViewClient(new WebViewClient());
htmlWebView.loadUrl("file:///android_asset/index.html");
WebSettings webSetting = htmlWebView.getSettings();
webSetting.setJavaScriptEnabled(true);
public void onToggleButtonPressed(View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
boolean on = ((ToggleButton) view).isChecked();
String visibility;
if (on) {
htmlWebView.setVisibility(View.GONE);
visibility = "NOT VISIBLE";
htmlWebView.evaluateJavascript("javascript: " +
"visibilityStatusFromAndroid(\"" + visibility + "\")", null);
} else {
htmlWebView.setVisibility(View.VISIBLE);
visibility = "VISIBLE NOW";
htmlWebView.evaluateJavascript("javascript: " +
"visibilityStatusFromAndroid(\"" + visibility + "\")", null);
}
}
}
JavaScript file:
function visibilityStatusFromAndroid(message) {
if (message === "NOT VISIBLE") {
console.log("webview is not visible");
// do your stuff here
} else if (message === "VISIBLE NOW") {
console.log("webview is visible now");
// do your stuff here
}
}
That's it. You are DONE!
Here is Video of working code.
Update:
There is one option that you can use to detect visibility only using JavaScript is document.hasFocus() but this will not work if there is any EditText on the screen and user will focus that EditText.
Just in case below is the code you can use in for document.hasFocus():
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>TEST</title>
<style>
#message { font-weight: bold; }
</style>
<script>
setInterval( checkPageFocus, 200 ); // you can change interval time to check more frequently
function checkPageFocus() {
var info = document.getElementById("message");
if ( document.hasFocus() ) {
info.innerHTML = "The document is VISIBLE.";
} else {
info.innerHTML = "The document is INVISIBLE.";
}
}
</script>
</head>
<body>
<h1>JavaScript hasFocus example</h1>
<div id="message">Waiting for user action</div>
</body>
</html>
There is (currently) no JS-only solution.
The content of the WebView isn't supposed to be able to get information about its environment.
The only exception is information which was volunteered by the environment itself.
This would contradict basic sandboxing and could lead to security issues.
As there is no standard API that would give you the information you want you would have to change the environment yourself to expose it to the WebView.
If you can't change the environment what you are requesting is (theoretically) impossible with the current browsers/android.
#Hack To find an actual loophole we could exploit to get the alpha value we would need more detailed information about the JS SDK, the environment and how much influence on the android / webview side of the app you have. And even then after investing much effort the result would most likely be the same.
Other SO answers suggest overriding ApplicationServlet.writeAjaxPageHtmlHeader, but I cannot find those classes and methods in Vaadin 8.
I cannot find anything similar in com.vaadin.server.VaadinServlet or com.vaadin.ui.UI.
There is the #JavaScript annotation, but if I put that on my UI class, the script would be loaded for every page of my application. I need it only on one specific page.
The initial HTML page is called bootstrap page in Vaadin. There is some documentation that hints you to right direction in Book of Vaadin.
In Vaadin 8, you need to add BootstrapListener to session. You can get created sessions by adding SessionInitListener in VaadinServlet.
Register Session
This example is using Vaadin with Spring Boot but the same principle applies when not using Spring Boot.
#Component("vaadinServlet")
#WebServlet(urlPatterns = "/*", name = "BootstrapVaadinServlet", asyncSupported = true)
#VaadinServletConfiguration(ui = BoostrapUi.class, productionMode = false)
public class BootstrapVaadinServlet extends SpringVaadinServlet {
private static final Logger logger = LoggerFactory.getLogger(BootstrapVaadinServlet.class);
#Override
protected void servletInitialized() throws ServletException {
super.servletInitialized();
getService().addSessionInitListener(this::addBoostrapListenerOnSessionInit);
}
private void addBoostrapListenerOnSessionInit(SessionInitEvent sessionInitEvent) {
sessionInitEvent.getSession().addBootstrapListener(new AppBootstrapListener());
}
}
Implement html head tag modification
public class AppBootstrapListener implements BootstrapListener {
#Override
public void modifyBootstrapFragment(BootstrapFragmentResponse bootstrapFragmentResponse) {
}
#Override
public void modifyBootstrapPage(BootstrapPageResponse res) {
Elements headTags = res.getDocument().getElementsByTag("head");
Element head = headTags.get(0);
head.appendChild(metaExample(res.getDocument()));
}
private Node metaExample(Document document) {
Element meta = document.createElement("meta");
meta.attr("author", "Me");
return meta;
}
}
If using add-on is ok, try HeaderTags
Overview
Using this add-on, you can define tags to add to the host page by
adding annotation to your UI class.
Sample from add ons usage example
#MetaTags({
// Replaces the Vaadin X-UA-Compatible header
#Meta(httpEquiv = "X-UA-Compatible", content = "hello"),
#Meta(name = "test", content = "test") })
// And showing how to create a link tag as well
#Link(rel = "foobar", href = "about:blank")
public class DemoUI extends UI {
...
}
Say I have a simple view model (widget.js):
import {Behavior} from 'aurelia-framework';
export class Widget
{
static metadata() { return Behavior.customElement('widget') }
constructor()
{
this.title= "AwesomeWidget";
}
}
With the following view: (widget.html):
<template>
<div>${title}</div>
</template>
Now say I inject some markup like this into the DOM:
var markup = `<div><widget></widget></div>`;
var $markup = $(markup);
var $placeholder = $("#placeholder");
$placeholder.append($markup);
How can I now tell Aurelia to compile this newly added part of the DOM against a new instance of Widget? I know it involves ViewCompiler but need an example to help me along. I'd greatly appreciate any help. Thanks!
A few months ago the TemplatingEngine class got a newly accessible enhance API method. This shortcuts the need to manually use the viewCompiler and compile method which was originally the only easy approach. This blog post details how you can use the enhance API to Aureliaify dynamically added HTML in your pages.
This has the added benefit of not needing to clean up the compiled HTML or detach anything either.
Here's an example: https://gist.run/?id=762c00133d5d5be624f9
It uses Aurelia's view compiler to compile the html and create a view instance, bound to the supplied view-model.
view-factory.js
import {
inject,
ViewCompiler,
ViewResources,
Container,
ViewSlot,
createOverrideContext
} from 'aurelia-framework';
#inject(ViewCompiler, ViewResources)
export class ViewFactory {
constructor(viewCompiler, resources, container) {
this.viewCompiler = viewCompiler;
this.resources = resources;
this.container = container;
}
insert(containerElement, html, viewModel) {
let viewFactory = this.viewCompiler.compile(html);
let view = viewFactory.create(this.container);
let anchorIsContainer = true;
let viewSlot = new ViewSlot(containerElement, anchorIsContainer);
viewSlot.add(view);
view.bind(viewModel, createOverrideContext(viewModel));
return () => {
viewSlot.remove(view);
view.unbind();
};
}
}
Usage:
let view = this.viewFactory.insert(containerElement, viewHtml, viewModel);
I have created a new Tapestry 5.3 project using Maven. I noticed that Tapestry adds tons of different JS and CSS files to all pages:
<link type="text/css" rel="stylesheet" href="/tutorial1/assets/1.0-SNAPSHOT-DEV/tapestry/default.css"/>
<link type="text/css" rel="stylesheet" href="/tutorial1/assets/1.0-SNAPSHOT-DEV/ctx/layout/layout.css"/>
<link type="text/css" rel="stylesheet" href="/tutorial1/assets/1.0-SNAPSHOT-DEV/tapestry/tapestry-console.css"/>
<link type="text/css" rel="stylesheet" href="/tutorial1/assets/1.0-SNAPSHOT-DEV/tapestry/t5-alerts.css"/>
<link type="text/css" rel="stylesheet" href="/tutorial1/assets/1.0-SNAPSHOT-DEV/tapestry/tree.css"/>
<script src="/tutorial1/assets/1.0-SNAPSHOT-DEV/tapestry/underscore_1_3_3.js" type="text/javascript"></script>
<script src="/tutorial1/assets/1.0-SNAPSHOT-DEV/tapestry/scriptaculous_1_9_0/prototype.js" type="text/javascript"></script>
And many, MANY more...
Are these required for my site to work properly? If not, how can I remove them? I am pretty comfortable to write JS myself and I do not need Tapestry to add anything for me.
It's funny: Tapestry provides very rich functionality to override default service behavior but not in this case.
The main culprit JavaScriptSupport is created on the fly and can't be decorated.
MarkupRendererFilter javaScriptSupport = new MarkupRendererFilter() {
public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) {
DocumentLinker linker = environment.peekRequired(DocumentLinker.class);
//Surprise!;)
JavaScriptSupportImpl support = new JavaScriptSupportImpl(linker, javascriptStackSource, javascriptStackPathConstructor);
environment.push(JavaScriptSupport.class, support);
renderer.renderMarkup(writer);
environment.pop(JavaScriptSupport.class);
support.commit();
}
};
So the only way is to patch sources (as we did many times), or to try to override the javascriptStackSource which goes as param to JavaScriptSupportImpl:
Your AppModule.java
#Decorate(serviceInterface = JavaScriptStackSource.class)
public JavaScriptStackSource decorateJavaScriptStackSource(JavaScriptStackSource original) {
return new MyJavaScriptStackSource(original);
}
MyJavaScriptStackSource.java
public class MyJavaScriptStackSource implements JavaScriptStackSource {
// This bunch of stacks comes from got5
private Set<String> SKIP = new HashSet<String>(Arrays.asList("Slider", "AjaxUploadStack", "DataTableStack", "FormFragmentSupportStack", "FormSupportStack",
"SuperfishStack", "JQueryDateFieldStack", "GalleryStack"));
private class JavaScriptStackWraper implements JavaScriptStack {
private final JavaScriptStack original;
JavaScriptStackWraper(JavaScriptStack original) {
if (original != null) {
System.out.println("Wrap " + original.getClass().getName());
}
this.original = original;
}
#Override
public List<String> getStacks() {
return original != null ? original.getStacks() : Collections.<String>emptyList();
}
#Override
public List<Asset> getJavaScriptLibraries() {
return original != null ? original.getJavaScriptLibraries() : Collections.<Asset>emptyList();
}
// Always return empty list
#Override
public List<StylesheetLink> getStylesheets() {
return Collections.<StylesheetLink>emptyList();
}
#Override
public String getInitialization() {
return original != null ? original.getInitialization() : null;
}
}
private final JavaScriptStackSource original;
public MyJavaScriptStackSource(JavaScriptStackSource original) {
this.original = original;
}
#Override
public JavaScriptStack getStack(String name) {
JavaScriptStack stack = original.getStack(name);
if (!SKIP.contains(stack.getClass().getSimpleName())) {
return new JavaScriptStackWraper(stack);
}
return new JavaScriptStackWraper(null);
}
#Override
public List<String> getStackNames() {
return original.getStackNames();
}
}
It's a big piece of shitty code but it works.. I'd like to have more control of whats displaying in my pages in Tapestry.
Part of the advantage of Tapestry is the number of components that provide DHTML and Ajax behaviors out of the box without writing any JavaScript, but just configuring components.
It is possible to disable this, but that means many components you might like to use, such as Zone, will be broken. Likewise, all client-side input validation will be gone. I do have clients that have done this, but it is not a small undertaking.
Basically, you can override Tapestry's "core" JavaScriptStack. There isn't a FAQ for this, because it is not frequently asked. It is also not for an absolute beginner, more a journeyman thing (it is relatively easy to override things in Tapestry due to its inversion of control container, but knowing WHAT to override is trickier).
In any case, Tapestry 5.4 is well underway and is changing Tapestry's JavaScript to be much lighter and much more modular, as well as giving you the choice between Prototype (mostly for compatibility in existing Tapestry projects) or jQuery. Even then, however, there will be some amount of JavaScript built into the framework.