Make Webview's auto links visible - android-webview

A Webview will display links in the content HTML as having blue underlines. So if you have something in the HTML like
blah blah
... it is clearly visible as a link.
The Webview also allows you to click on phone numbers and addresses (even if those are just text in the HTML, not links) to launch the Dialer or Maps.
How can one get Webview to display those (Linkify, probably) links with underlines etc? It's easy enough in a TextView since one can get the spans from a TextView and style them, but Webview doesn't expose any way to retrieve that data... at least not that I can see looking through the docs.

Here is some JS code which can be injected to linkify phone numbers, emails and urls:
function linkify() {
linkifyTexts(linkifyPhoneNumbers);
linkifyTexts(linkifyEmails);
linkifyTexts(linkifyWebAddresses1);
linkifyTexts(linkifyWebAddresses2);
}
function linkifyPhoneNumbers(text) {
text = text.replace(/\b\+?[0-9\-]+\*?\b/g, '$&');
return text;
}
function linkifyEmails(text) {
text = text.replace(/(\w+#[a-zA-Z_]+?\.[a-zA-Z]{2,6})/gim, '$1');
return text;
}
function linkifyWebAddresses1(text) {
text = text.replace(/(\b(https?|ftp):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/gim, '$1');
return text;
}
function linkifyWebAddresses2(text) {
text = text.replace(/(^|[^\/])(www\.[\S]+(\b|$))/gim, '$1$2');
return text;
}
var linkifyTexts = function(replaceFunc)
{
var tNodes = [];
getTextNodes(document.body,false,tNodes,false);
var l = tNodes.length;
while(l--)
{
wrapNode(tNodes[l], replaceFunc);
}
}
function getTextNodes(node, includeWhitespaceNodes,textNodes,match) {
if (node.nodeType == 3) {
if (includeWhitespaceNodes || !/^\s*$/.test(node.nodeValue)) {
if(match){
if(match.test(node.nodeValue))
textNodes.push(node);
}
else {
textNodes.push(node);
}
}
} else {
for (var i = 0, len = node.childNodes.length; i < len; ++i) {
var subnode = node.childNodes[i];
if (subnode.nodeName != "A") {
getTextNodes(subnode,includeWhitespaceNodes,textNodes,match);
}
}
}
}
function wrapNode(n, replaceFunc) {
var temp = document.createElement('div');
if(n.data)
temp.innerHTML = replaceFunc(n.data);
else{
//whatever
}
while (temp.firstChild) {
n.parentNode.insertBefore(temp.firstChild,n);
}
n.parentNode.removeChild(n);
}

Given this:
http://code.google.com/p/android/issues/detail?id=742
it still doesn't seem to be a way to do this from Java directly. One thing that might work is to write some JavaScript code and run it after page is loaded, e.g. as given here:
In Android Webview, am I able to modify a webpage's DOM?
Here's an example of a similar thing:
Disabling links in android WebView
where the idea is to disable links. You may be able to use a similar approach to add some CSS, including underlining. A couple of other SOqs / links that might help:
Android: Injecting Javascript into a Webview outside the onPageFinished Event
Android: Injecting Javascript into a Webview outside the onPageFinished Event
http://iphoneincubator.com/blog/windows-views/how-to-inject-javascript-functions-into-a-uiwebview
Injecting Javascript into a Webview outside the onPageFinished Event (Using DatePicker to set a date on an input of a WebView)
Hope this helps.

Related

Jodit -> Hide Toolbar after initialization

i use Jodit as wysiwyg editor and have a beginner question.
At initialization of the editor, i want to hide the toolbar.
This works perfect:
var editor = new Jodit('#freitext_oben', {
fullsize: false,
toolbar: false,
editorCssClass: 'editor_freitext'
});
Now i want a custom button which calls a function that toggles the toolbar but i don't know how to set this option.
i need something like
function toggle_toolbar() {
editor.toggleToolbar();
}
But i don't know the exact syntax...
Please help ;-(
So here's a way that I've found to do this:
public ToggleToolbarVisibility()
{
if(!editor.editor.toolbar.container.style.display != "none")
{
editor.toolbar.container.style.display = "none";
}
else
{
editor.toolbar.container.style.display = "";
}
}

How can I prefill links with http in a Quill editor?

When adding links with the Quill editor I must include the protocol or the link is treated as a relative link.
When someone clicks to add a link I would like to have the field prepopulate with http:// so when a user types google.com it will create a link to http://google.com instead of http://myapp.net/something/google.com.
Stack overflow does this...
The above solution won't work when you try to save an existing link. Also, it ignores other protocols such as (mailto, tel, https)
Here is a better solution:
let Link = window.Quill.import('formats/link');
class CustomLink extends Link {
static sanitize(url) {
let value = super.sanitize(url);
if (value) {
for (let i = 0; i < this.PROTOCOL_WHITELIST.length; i++)
if(value.startsWith(this.PROTOCOL_WHITELIST[i]))
return value;
return `http://${value}`
}
return value;
}
}
Quill.register(CustomLink);
You can extend the link format with custom logic:
var Link = Quill.import('formats/link');
class MyLink extends Link {
static create(value) {
let node = super.create(value);
value = this.sanitize(value);
if(!value.startsWith("http")) {
value = "http://" + value;
}
node.setAttribute('href', value);
return node;
}
}
Quill.register(MyLink);

How to integrate Google Picker with TinyMCE?

I am using the latest version of TinyMCE and I would like to integrate Google Drive Picker whenever an user clicks on Insert Image.
From the TinyMCE documentation I saw that I should use the file_browser_callback parameter but I am stuck with a couple of problems. First of all, I managed to attach the Google Picker but the Insert Image popup stays on top and there's no way for me to select a file. Even if I solve this problem, how can I set the textbox value from the Google Picker callback function? Below you can see my code, Google Picker code is pretty standard so I won't paste it.
var picker;
tinymce.init({
//other init parameters...
file_browser_callback: function(field_name, url, type, win) {
picker.setVisible(true);
win.document.getElementById(field_name).value = 'my browser value';
}
});
function createPicker() {
// Here I build the Picker...
// var picker = ...
}
function pickerCallback(data) {
//TODO: Find a way to set textbox value with the URL of the Image selected from Drive
}
I think you can hide the modal when you call createPicker():
function createPicker() {
document.getElementById('modal_id').style.display = 'none';
// your picker code here!
}
and then display it back once you get the callback i.e. pickerCallback(data):
function pickerCallback(data) {
document.getElementById('modal_id').style.display = 'block';
var url = 'nothing';
if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
var doc = data[google.picker.Response.DOCUMENTS][0];
url = doc[google.picker.Document.URL];
}
// set the input text value with the URL:
document.getElementById('source_input_id').value = url;
}
Change modal_id to your modal's div id and source_input_id with your source input field's id.

PhoneGap ChildBrowser Executing JavaScript

I wonder if this is possible to execute JavaScript inside phonegap childbrowser window so we can manipulate websites under phonegap app?
Looking at the big picture as one can create a function in Objective-C which executes that JS into childbrowser (modifying childbrowser.m and childbrowser.h files) and creating JS wrapper of it so one can call JS function to execute JS inside childbrowser.
I want you to modify ChildBrowser for me to have that functionality so I shouldn't lost doing it. At least give me initial steps.
Alright I just tried and it worked in a single go. That was amazing! I just modified ChildBrowser plugin of PhoneGap and it worked.
UPDATED
I finally got few minutes to update the answer for those who will encounter the same issue.
ChildBrowserCommand.h
- (void) jsExec:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
ChildBrowserCommand.m
- (void) jsExec:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options; {
[childBrowser executeJS:(NSString *)[arguments objectAtIndex:0]];
}
ChildBrowserViewController.h
- (void)executeJS:(NSString *)js;
ChildBrowserViewController.m
- (void) executeJS:(NSString *)js {
[webView stringByEvaluatingJavaScriptFromString:js];
}
ChildBrowser.js
/* MIT licensed */
// (c) 2010 Jesse MacFadyen, Nitobi
function ChildBrowser()
{
}
// Callback when the location of the page changes
// called from native
ChildBrowser._onLocationChange = function(newLoc)
{
window.plugins.childBrowser.onLocationChange(newLoc);
}
// Callback when the user chooses the 'Done' button
// called from native
ChildBrowser._onClose = function()
{
window.plugins.childBrowser.onClose();
}
// Callback when the user chooses the 'open in Safari' button
// called from native
ChildBrowser._onOpenExternal = function()
{
window.plugins.childBrowser.onOpenExternal();
}
// Pages loaded into the ChildBrowser can execute callback scripts, so be careful to
// check location, and make sure it is a location you trust.
// Warning ... don't exec arbitrary code, it's risky and could cause your app to fail.
// called from native
ChildBrowser._onJSCallback = function(js, loc)
{
// Not Implemented
window.plugins.childBrowser.onJSCallback(js, loc);
}
/* The interface that you will use to access functionality */
// Show a webpage, will result in a callback to onLocationChange
ChildBrowser.prototype.showWebPage = function(loc)
{
PhoneGap.exec("ChildBrowserCommand.showWebPage",loc);
}
// close the browser, will NOT result in close callback
ChildBrowser.prototype.close = function()
{
PhoneGap.exec("ChildBrowserCommand.close");
}
// Not Implemented
ChildBrowser.prototype.jsExec = function(jsString)
{
// Not Implemented!!
PhoneGap.exec("ChildBrowserCommand.jsExec", jsString);
}
// Note: this plugin does NOT install itself, call this method some time after deviceready to install it
// it will be returned, and also available globally from window.plugins.childBrowser
ChildBrowser.install = function()
{
if(!window.plugins)
{
window.plugins = {};
}
window.plugins.childBrowser = new ChildBrowser();
return window.plugins.childBrowser;
}
My global variable.
var CB = null;
On my DeviceReady event.
CB = ChildBrowser.install();
if (CB != null) {
CB.onLocationChange = onCBLocationChanged;
}
I can execute any JS into webpage using.
CB.jsExec("alert('I am from ChildBrowser!');");
I hope my contribution to this will bring smile on your face.

What is the proper way in OpenLayers (OSM) to trigger a popup for a feature?

I have the feature ID, I can grab the marker layer on GeoRSS loadend, but I'm still not sure how to cause the popup to appear programmatically.
I'll create the popup on demand if that's necessary, but it seems as though I should be able to get the id of the marker as drawn on the map and call some event on that. I've tried using jQuery and calling the $(marker-id).click() event on the map elements, but that doesn't seem to be working. What am I missing?
Since I was asked for code, and since I presumed it to be boilerplate, here's where I am so far:
map = new OpenLayers.Map('myMap');
map.addLayer(new OpenLayers.Layer.OSM());
map.addLayer(new OpenLayers.Layer.GeoRSS(name,url));
//I've done some stuff as well in re: projections and centering and
//setting extents, but those really don't pertain to this question.
Elsewhere I've done a bit of jQuery templating and built me a nice list of all the points that are being shown on the map. I know how to do a callback from the layer loadend and get the layer object, I know how to retrieve my layer out of the map manually, I know how to iter over the layers collection and find my layer. So I can grab any of those details about the popup, but I still don't know how to go about using the built-in methods of the DOM or of this API to make it as easy as element.click() which is what I would prefer to do.
You don't have to click the feature to open a popup.
First you need a reference to the feature from the feature id. I would do that in the loadend event of the GeoRSS layer, using the markers property on the layer.
Assuming you have a reference to your feature, I would write a method which handles the automatic popup:
var popups = {}; // to be able to handle them later
function addPopup(feature) {
var text = getHtmlContent(feature); // handle the content in a separate function.
var popupId = evt.xy.x + "," + evt.xy.y;
var popup = popups[popupId];
if (!popup || !popup.map) {
popup = new OpenLayers.Popup.Anchored(
popupId,
feature.lonlat,
null,
" ",
null,
true,
function(evt) {
delete popups[this.id];
this.hide();
OpenLayers.Event.stop(evt);
}
);
popup.autoSize = true;
popup.useInlineStyles = false;
popups[popupId] = popup;
feature.layer.map.addPopup(popup, true);
}
popup.setContentHTML(popup.contentHTML + text);
popup.show();
}
fwiw I finally came back to this and did something entirely different, but his answer was a good one.
//I have a list of boxes that contain the information on the map (think google maps)
$('.paginatedItem').live('mouseenter', onFeatureSelected).live('mouseleave',onFeatureUnselected);
function onFeatureSelected(event) {
// I stuff the lookup attribute (I'm lazy) into a global
// a global, because there can be only one
hoveredItem = $(this).attr('lookup');
/* Do something here to indicate the onhover */
// find the layer pagination id
var feature = findFeatureById(hoveredItem);
if (feature) {
// use the pagination id to find the event, and then trigger the click for that event to show the popup
// also, pass a null event, since we don't necessarily have one.
feature.marker.events.listeners.click[0].func.call(feature, event)
}
}
function onFeatureUnselected(event) {
/* Do something here to indicate the onhover */
// find the layer pagination id
var feature = findFeatureById(hoveredItem);
if (feature) {
// use the pagination id to find the event, and then trigger the click for that event to show the popup
// also, pass a null event, since we don't necessarily have one.
feature.marker.events.listeners.click[0].func.call(feature, event)
}
/* Do something here to stop the indication of the onhover */
hoveredItem = null;
}
function findFeatureById(featureId) {
for (var key in map.layers) {
var layer = map.layers[key];
if (layer.hasOwnProperty('features')) {
for (var key1 in layer.features) {
var feature = layer.features[key1];
if (feature.hasOwnProperty('id') && feature.id == featureId) {
return feature;
}
}
}
}
return null;
}
also note that I keep map as a global so I don't have to reacquire it everytime I want to use it