Context: Azure, C#, ClearScript, JavaScript, HtmlAgilityPack, HtmlAgilityPack.CssSelectors
I do this a lot: add scripting to C# apps using ClearScript. Ordinarily this just works.
...
using Microsoft.ClearScript;
using Microsoft.ClearScript.Windows;
...
class Program
{
static JScriptEngine JSengine = null;
...
JSengine = new JScriptEngine(WindowsScriptEngineFlags.EnableDebugging | WindowsScriptEngineFlags.EnableJITDebugging);
....
JSengine.AddHostType("CSHtmlDocument", typeof(HtmlAgilityPack.HtmlDocument));
...
and then later, in the JavaScript code itself, there are things like
...
var hap = new CSHtmlDocument();
hap.LoadHtml(html);
...
So this is going really well until I add HtmlAgilityPack.CssSelectors into the mix. On the C# side, this adds extra methods to the HtmlDocument object, specifically QuerySelector and QuerySelectorAll. They're visible on the C# side. However, on the JavaScript side they're not and code such as
...
var selection = hap.QuerySelector(".reduced");
...
throws an error and looking at the object from a debugging session in Visual Studio 2015 shows no QuerySelector method in the hap var.
So what's the story? Is it a ClearScript issue or a C# issue? And what do I do about it? I'm quite happy to write a wrapper class, I was just expecting everything to work as before.
These new methods are most likely extension methods defined by a particular class. To make them accessible from script code, you must expose that class to the script engine.
EDIT: I'm not familiar with them, but it looks like the methods you're talking about are provided by the HapCssExtensionMethods class (or something very similar). To expose the methods, simply expose the class:
// C#
JSengine.AddHostType(typeof(HapCssExtensionMethods));
Once you've done that, your JavaScript sample above should work as is.
Related
I am new in the development of Confluence Add-Ons. I want to use the example from the API. For instance look at this Page Dialog 2, there are the HTML-Code and the JS-Code separated.
I know how to define a JS-File and how to refer to the atlassian-plugin.xml.
Look here
<web-resource key="Confluence-resources" name="Confluence-Web-Resources">
<dependency>com.atlassian.auiplugin:ajs</dependency>
<resource type="download" name="confluence.js" location="/js/confluence.js"/>
</web-resource>
But where I must define the HTML-Code?
In essence, you need to render the HTML from execute method in your Macro class. But, practically speaking, there're a few "Atlassian-approved" approaches.
You might want to consider built-in Velocity templating support from VelocityUtils.getRenderedTemplate method.
Quoting Atlassian's Documentation, you can probably write something like:
public String execute(Map params, String body, RenderContext renderContext) throws MacroException {
// do something with params ...
Map context = MacroUtils.defaultVelocityContext();
context.put("page", page);
context.put("labels", labels);
return VelocityUtils.getRenderedTemplate("com/atlassian/confluence/example/sample-velocity.vm", context);
}
In recent years, they started to promote the usage Soy Template too.
when I use WWW::Scripter normally everything works fine:
use WWW::Scripter;
my $m = WWW::Scripter->new();
$m->use_plugin('JavaScript');
$m->get('http://some-site-with-java-script.com');
But when I try to subclass WWW::Scripter like so:
package MyScripter;
use parent qw(WWW::Scripter);
sub new {
my ($class, #args) = #_;
my $self = $class->SUPER::new(#args);
return $self;
}
package main;
my $m = MyScripter->new();
$m->use_plugin('JavaScript');
$m->get('http://some-site-with-java-script.com');
I get this strange error message:
Error: Can't locate object method "prop" via package "MyScripter" at /home/myuser/localperl/lib/site_perl/5.18.2/JE/LValue.pm line 91
What is going on?
WWW::Scripter::VERSION => 0.030
JE::VERSION => 0.060
I guessed that the problem was dependent on the site being accessed, as I tried it with http://www.bbc.co.uk/ with no problems at all (except that it doesn't like the JavaScript apearing in XHTML CDATA sections).
This is because of a broken inheritance hierarchy. I haven't found the exact problem, but I believe it is because of the way the JavaScript plugin works. It expects to be (insists on being) called in the context of a WWW::Scripter object, and has an explicit
use WWW::Scripter 0.022
at the start, which will load all the method definitions from the basic module instead of from your subclass.
There is all sorts of nasty stuff in there, such as using the Perl 4 package name separator, like use LWP'UserAgent, and literal path loads like
require "WWW/Scripter/Plugin/JavaScript/" . "$$self[benm].pm"
which could hardly be more anti-inheritance.
Without some more work it is hard to tell whether it's an easy fix or a rewrite, but to solve your problem I suggest using roles rather than inheritance, which allows the role model much more freedom in how it does its thing. It's described nicely in perldoc perlootut and you do a lot worse than experimenting with Role::Tiny.
I'm not sure this is supported but wanted to see if any of you had come up with something creative to work around this.
Is there a way to call javascript from silverlight, without having to define any javascript functions on the aspx page/external js?
I'd like to be able to do something like:
HtmlPage.Window.Invoke("(function () { window.lastErrorMessage = 'foo'; })();")
Which, to forgo conversations about style, I'd agree goes against many best practices rules, but my current purpose is brainstorm some quick-and-dirty error reporting (could you even call it reporting?) before we implement the existing database-centric error logging in this solution.
Any thoughts? The idea is to generate something discreet that a user wouldn't feel intruded upon or something too technical (like IE's script error dialog), but something our app support could get a little more info from without access to code bases etc.
Thanks, Matthew
The method you are looking for is Eval not Invoke:-
HtmlPage.Window.Eval("(function () { window.lastErrorMessage = 'foo'; })();")
You could add your JavaScript dynamically to the hosting page DOM using HtmlPage.Document and then execute your added methods. Or are you trying not to modify the page at all?
HtmlElement head = HtmlPage.Document.GetElementsByTagName("head")[0] as HtmlElement;
HtmlDocument htmlDocument = HtmlPage.Document;
HtmlElement scriptElement = htmlDocument.CreateElement("script");
scriptElement.SetAttribute("type", #"text/javascript");
scriptElement.SetProperty("text", "function testMe(p) { alert(p); }");
head.AppendChild(scriptElement);
// Invoke like this
HtmlPage.Window.Invoke("testMe", "hello");
Most of the examples you find on the web, of using javascript from ASP.NET pages puts the javascript in the markup file (*.aspx). This is, of course, a really bad idea(tm), for all but the simplest uses of javascript.
What we want, of course, is to wrap the javascript up into a class, and to instantiate an instance of that class and tie it to the code-behind.
Microsoft provides a framework for doing this for user controls and server controls, in its IScriptControl interface. This allows a developer to create a javascript "component" - to define a javascript class in a *.js file, to include the *.js file on the page that contains the control, to instantiate an instance of the component, to set variables in the component from values in the code-behind, and to get a reference to the component in javascript on the client side.
The thing is - IScriptControl only works for user and server controls. It cannot be used to instantiate javascript objects at the page level.
So - how do people do this? We have some patterns we've been using, that seem to work. I was wondering what everyone thought of them, and what other people were using.
We start by defining a javascript class in a *.js file. In the code-behind, we create a loadJavascript() function, that we call from Page_Load on initial load or full postback (but not on partial postbacks).
In loadJavascript(), we include the *.js file with ScriptManager.RegisterClientScriptInclude(), and then construct a bit of javascript that instantiates an instance of the class, assigns a reference to a known name, and registers the object's initialize() and dispose() methods as handlers for window.load and window.unload.
E.g.:
string url = this.ResolveUrl("./FooBar.js");
ScriptManager.RegisterClientScriptInclude(this, this.GetType(), url, url);
string script = #"
if (typeof {0}_obj == 'undefined')
{0}_obj = {{}};
{0}_obj.fooBar = new FooBar();
Sys.UI.DomEvent.addHandler(window, 'load',
function()
{{
{0}_obj.fooBar.initialize('{1}', '{2}');
}}
);
Sys.UI.DomEvent.addHandler(window, 'unload', {0}_obj.fooBar.dispose);
";
script = String.Format(script,
new object[]
{
this.ClientID,
this.foo.ClientID,
this.bar.ClientID
});
ScriptManager.RegisterStartupScript(
this, this.GetType(), this.ClientID, script, true);
We construct an object name in the global namespace, based on the ClientID of the page, if we haven't already. We add an instance of our new class as a member of our global object. We add a window.load handler that calls our object's intialize() method, passing the clientIDs of the controls on the page that the object's methods need to access. And we add a window.unload handler that calls our object's dispose() method, that does whatever cleanup that is necessary.
This seems to be working, for us. We've used this pattern on a number of pages, some of which did significant amounts of partial-postbacks, without any problems.
I was wondering, first, what people thought of the pattern.
But more, I was wondering if we'd been reinventing the wheel, and if there were other approaches to dealing with the issues we were addressing, that we weren't aware of.
Anyone have any better ideas?
But more, I was wondering if we'd been reinventing the wheel, and if there were other approaches to dealing with the issues we were addressing, that we weren't aware of.
I think this the most good approaches, I use the same way some years now with out any problem in very complex javascript code. I do not see why you question your self :)
The idea is this you follow, now maybe there are some variations, maybe I not call the unload, nether create an object to keep the foobar and call the foobar rightway, but the idea is the same. I also check if the Javascript file have been loaded...
string script = #"
if (typeof (FooBar) != "undefined") {{
var {0}fooBar = new FooBar();
Sys.UI.DomEvent.addHandler(window, 'load',
function()
{{
{0}fooBar.initialize('{1}', '{2}');
}}
);
}}
I would like to ask if there is a liveconnect equivalent for ActionScript 3. I understand that there is the ExternalInterface class inside AS3 but it only supports calling a method by name. The really cool thing about Java and LiveConnect is that you can do something like
function jsFunc(name) = {
this.name = name;
this.talk = function(){
alert('hello world my name is ' + this.name);
}
}
javaapplet.function(new jsFunc("bob"));
The above approaches pseudo code since I never tested it but I've seen it in action. In AS3, while I am able to pass in an instance of JavaScript "object" into AS, it is often converted into an ActionScript Object instance which does away with all the functions as far as I'm aware.
I saw an implementation of JSInterface but I don't think it does specifically that. Is there any way to make OO like javascript work with ActionScript 3?
Try this library on Google code:
http://code.google.com/p/jsobject/
ExternalInterface.call("f = function() { alert('Is this like live connect?'); }");
Actually the main usage scenario is to have JS "objects" interacting with the Flex SWF application. Therefore when the JS "object" wants to say wait for something happening in the SWF object, it will put in a "this" with a callback.
After researching, the way I used to accomplish this is via the Flex Ajax bridge. It may not be a direct answer to the way I phrased the question but it was sufficient for my needs.
Basically what I do is via FABridge, after initializing, I'll attach event listeners to the object.
// JS
FlexApp.addEventListeners('flexDidSomething', this.doSomething().bind(this)); //using mootools;
and in Flex, the main application itself
// AS
dispatchEvent(new CustomCreatedEvent(param1, param2));
And inside the JS function I'll access the get methods of the event object to retrieve the params.
There's tight coupling in that sense but it works at least for what I need.
Hope this is helpful!
JSInterface designed exactly for such things.