I am running with multiCapabilities, and would like to know if it is possible to know what capability is currently used, both in the onPrepare function and/or the testcase itself.
The use case is that I am planning to run my tests both on chrome and on android. For Chrome the window should be resized to required dimensions, however running the same code on selendroid gives an exception because the method is not implemented (also resizing a window on a device does not really make sense):
So, the idea was to somehow wrap the offending code in a simple check like so:
if(browser != 'android')
browser.driver.manage().window().setSize(480, 800);
There are also other use cases, but that's the most important one for now.
I do stuff like that within the onPrepare section, e.g.
// Return if current browser is IE, optionally specifying if it is a particular IE version
browser.isInternetExplorer = function(ver) {
var browserName, version, ie;
return browser.getCapabilities().then(function(s) {
browserName = s.caps_.browserName;
version = s.caps_.version;
ie = /i.*explore/.test(browserName);
if (ver == null) {
return ie;
} else {
return ie && ver.toString() === version;
}
});
};
Then, later on, i use it like this:
if (browser.isInternetExplorer()) {...}
For android this should work:
browser.isAndroid = function(ver) {
var browserName, version;
return browser.getCapabilities().then(function(s) {
browserName = s.caps_.browserName;
version = s.caps_.version;
return /droid/.test(browserName);
});
};
Related
I am developing a GTK application in GJS and like to reuse parts of the GTK
code inside a Gnome Shell extension. However, I did not find a way to add a
Gtk.Widget to the menu of my Gnome Shell panel icon.
I tried to use GtkClutter.Actor from clutter-gtk, but the library seems to
be out-dated and does neither work in a Wayland or X11 Gnome Shell, since it
requires Clutter 1.0 but sees 7 already loaded. When importing
imports.gi.GtkClutter in an extension, Gnome Shell yields this error:
Requiring GtkClutter, version none: Requiring namespace 'Clutter' version '1.0', but '7' is already loaded.
Here is some code to demonstrate that clutter-gtk actually works, if directly
running it via gjs; probably because I can enforce GTK 3.0 here.
gtkclutter.js:
imports.gi.versions.Gtk = '3.0' // fails if set to 4.0
const { Gtk, GLib, Clutter, GtkClutter } = imports.gi
// gtkUI returns a Gtk.Widget tree. This should be the reusable code.
function gtkUI() {
return new Gtk.Label({
label: '<span size="100000">🎉</span>',
use_markup: true,
})
}
// embedClutterActor returns a Gtk.Widget with an embedded Clutter.Actor.
function embedClutterActor(clutter_actor) {
let embed = new GtkClutter.Embed()
embed.get_stage().add_child(clutter_actor)
return embed
}
// embedGtkWidget returns a Clutter.Actor with an embedded Gtk.Widget.
function embedGtkWidget(gtk_widget) {
return new GtkClutter.Actor({ contents: gtk_widget })
}
class App {
constructor() {
this.title = 'GtkClutter'
GLib.set_prgname(this.title)
}
onActivate() { this.window.show_all() }
onStartup() { this.buildUI() }
run(ARGV=[]) {
this.app = new Gtk.Application()
this.app.connect('activate', () => this.onActivate())
this.app.connect('startup', () => this.onStartup())
this.app.run(ARGV)
}
buildUI() {
let w = this.window = new Gtk.ApplicationWindow({
application: this.app, title: this.title, icon_name: 'face-smile',
default_height: 160, default_width: 160, window_position: Gtk.WindowPosition.CENTER,
})
// Just to demonstrate that GtkClutter embedding works, we use both embeds here to create
// a Gtk.Widget from a Clutter.Actor from the actual Gtk.Widget that we want to show.
GtkClutter.init(null)
Clutter.init(null)
w.add(embedClutterActor(embedGtkWidget(gtkUI())))
// In the actual GTK App, we would just have used `w.add(gtkUI())`
// and not imported Clutter and GtkClutter at all.
}
}
new App().run(ARGV)
Here is the companion extension to the GTK app, trying (and failing)
to reuse the GTK code as contents of a GtkClutter.Actor.
extension.js:
const { Clutter, Gtk, Gio, St } = imports.gi
let GtkClutter = null // lazy import for debugging
const Main = imports.ui.main
const PanelMenu = imports.ui.panelMenu
const PopupMenu = imports.ui.popupMenu
const Me = imports.misc.extensionUtils.getCurrentExtension()
const VERSION = 'dev-version' // filled during install
const NAME = 'GtkClutterExt'
// gtkUI returns a Gtk.Widget tree. This should be the reusable code.
function gtkUI() {
return new Gtk.Button({ child: Gtk.Label({
label: `<span size="100000">🎉</span>`,
use_markup: true,
})})
}
// stUI returns an Gnome Shell widget tree that works only in Gnome Shell.
function stUI(icon_name='face-sad') {
return new St.Icon({ icon_name })
}
function statusIcon(icon_name) {
let box = new St.BoxLayout()
let icon = new St.Icon({ icon_name, style_class: 'system-status-icon emotes-icon' })
box.add_child(icon)
box.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM))
return box
}
class Ext {
constructor() { this.panel_widget = null }
enable() {
log(`enabling extension ${Me.uuid}`)
try {
// Use St only for the status icon and the menu container (not the menu content).
let btn = this.panel_widget = new PanelMenu.Button(0.0, NAME, false)
let item = new PopupMenu.PopupBaseMenuItem({ reactive: false, can_focus: false })
btn.menu.addMenuItem(item)
Main.panel.addToStatusArea(NAME, btn)
try { GtkClutter = imports.gi.GtkClutter }
catch (e) { log(`failed to load clutter-gtk, err=${e.message}`) }
if (GtkClutter) {
// Using St for the status icon is OK, since it is only used by the extension.
btn.add_child(statusIcon('face-happy'))
// But for the menu, I like to reuse my GTK code from the GTK app.
// That is what GtkClutter was designed for, I believe.
item.actor.add_child(new GtkClutter.Actor({ contents: gtkUI() }))
} else {
// As fallback we show our mood regarding GtkClutter support in Gnome Shell ;)
btn.add_child(statusIcon('face-sad'))
item.actor.add_child(stUI('face-angry'))
}
} catch (e) {
log(`failed to enable ${Me.uuid}, err=${e.message}`)
}
}
disable() {
debug(`disabling extension ${Me.uuid}`)
if (this.panel_widget == null) return
this.panel_widget.destroy()
this.panel_widget = null
}
}
function init() { return new Ext() }
I know that clutter-gtk is quite dated (see https://gitlab.gnome.org/GNOME/clutter-gtk),
but I did not find a better way to lift GTK into my extension.
Questions
Does Gnome Shell provide something similar to GtkClutter.Actor that allows
extension programmers to reuse their GJS/GTK code?
Which alternative way to reuse GTK/GJS code do you see?
If GTK is such a universal and cross-platform library, why does Gnome Shell not
support it out-of-the box? (Bonus question 😉, more out of curiosity)
TL;DR You can not use GTK widgets in a GNOME Shell extension.
The toolkit used in GNOME Shell is Clutter, not GTK. Clutter is an internal library of Mutter, while GTK3 is only used in GNOME Shell for a handful of utilities.
Clutter used to be a standalone library, but is now developed specifically as a compositor toolkit for Mutter. GTK is an application toolkit, not suited for use in a compositor.
The standalone Clutter project is effectively unmaintained now, making GtkClutter pretty much the same.
Is it possible to show users of your extension or color theme notifications in Visual Studio Code? For someone who has my color theme or extension installed and is getting updates, I would like to possibly show this person a notification after they update the extension (That could be on launch of VSCode, or right after they go into the market to update & reload the extension and client themselves.)
For example: I think it would be beneficial to me and not invasive if they saw a notification after updating the extension saying "Feedback? Suggestions? Fixes?..on the theme?" OR notifying them of something changed in the theme that may not be favorable. So they can "opt out" of that change if they want (Like an extra set of borders around something or the color change of something.)
Obviously people with all notifications off would not be affected, but I thought an occasional notification after a rare update wouldn't be too bad. I have not been able to find info on if this is possible, and if it was, how to do it. Any info on this is appreciated. And if it is possible, those reading this, whether you've done it or not, would you recommend showing a notification to your theme users in that way?
Thanks :)
Show a notification on bottom-right corner, whenever your extension is updated. You can also control to show it only for major/minor releases.
That's how it looks:
Add below code to extension.ts:
import { window, ExtensionContext, extensions, env, Uri } from "vscode";
const extensionId = "jerrygoyal.shortcut-menu-bar";
// this method is called when your extension is activated
export function activate(context: ExtensionContext) {
showWhatsNew(context); // show notification in case of a major release i.e. 1.0.0 -> 2.0.0
}
// https://stackoverflow.com/a/66303259/3073272
function isMajorUpdate(previousVersion: string, currentVersion: string) {
// rain-check for malformed string
if (previousVersion.indexOf(".") === -1) {
return true;
}
//returns int array [1,1,1] i.e. [major,minor,patch]
var previousVerArr = previousVersion.split(".").map(Number);
var currentVerArr = currentVersion.split(".").map(Number);
if (currentVerArr[0] > previousVerArr[0]) {
return true;
} else {
return false;
}
}
async function showWhatsNew(context: ExtensionContext) {
const previousVersion = context.globalState.get<string>(extensionId);
const currentVersion = extensions.getExtension(extensionId)!.packageJSON
.version;
// store latest version
context.globalState.update(extensionId, currentVersion);
if (
previousVersion === undefined ||
isMajorUpdate(previousVersion, currentVersion)
) {
// show whats new notificatin:
const actions = [{ title: "See how" }];
const result = await window.showInformationMessage(
`Shortcut Menubar v${currentVersion} — Add your own buttons!`,
...actions
);
if (result !== null) {
if (result === actions[0]) {
await env.openExternal(
Uri.parse(
"https://github.com/GorvGoyl/Shortcut-Menu-Bar-VSCode-Extension#create-buttons-with-custom-commands"
)
);
}
}
}
}
You can see this implementation in my VSCode extension repo Shortcut Menu Bar
I think you can register the version during activation event and check for it on each activation. Then you can do whatever you want. For instance GitLens is migrating settings https://github.com/eamodio/vscode-gitlens/blob/master/src/extension.ts#L52 and i'm pretty sure I remember that they were opening a notification (but i have not found immediately in the code)
regards,
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.
I have just finished building a website for client, instantinteriors.com.au. (Visit this site for source code.)
For some reason, when I view the website in Firefox offline as a file it looks like it should. Upon upload the sizing becomes different, such as the font and div sizes. There is also a failure in some of my scripts to operate.
The website appears to work fine in Safari and Chrome, haven't checked on a Windows yet. There also seems to be something wrong with the form, as I have everything ready working and testing, although as soon as it went onto the host server the client uses it fails.
The reason I emphasise client's server is that when I tested it earlier on the host I use it worked fine.
Here is one of many scripts appearing to fail...
<script type="text/javascript">
$.fn.hasOverflow = function() {
var $this = $(this);
var $children = $this.find('*');
var len = $children.length;
if (len) {
var maxWidth = 0;
var maxHeight = 0
$children.map(function(){
maxWidth = Math.max(maxWidth, $(this).outerWidth(true));
maxHeight = Math.max(maxHeight, $(this).outerHeight(true));
});
return maxWidth > $this.width() || maxHeight > $this.height();
}
return false;
};
$(function() {
var $content = $('#wrap4').children().wrapAll('<div>');
if($content.hasOverflow()){
$("#arrows").css("display", "none");
} else {
$("#arrows").css("display", "block");
}
});
</script>
Is your logic correct? Your hasOverflow function returns true when either your maxWidth or maxHeight is greater than your current width and height. If you have overflow shouldn't the test be if either the current values are greater than the maximums?
You should switch the way you use the hasOverflow result so the arrows are shown when you have overflow. You can use the show() and hide() methods as shorthand.
if($content.hasOverflow()) {
$("#arrows").show();
} else {
$("#arrows").hide();
}
Is this being called when the document is ready? The source formatting makes it hard to tell.
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.