I'm working with a mix of legacy code that uses the jQuery DataTables plugin (version 1.10. Some of the old code passes around jQuery object, and some of the old code passes around DataTables API objects. I'd like to make all new code accept either a jQuery object or a DataTables API object, but I haven't been able to detect when a variable refers to a DataTables API object.
The only detection method I could find in the API, isDataTable, is not suitable - it take a string selector for a table and returns whether the selection has been turned into a DataTable.
The "class detection" methods in this answer don't work; typeof returns "object", constructor.name returns "Object", and I don't know of a constructor to use for instanceof or isPrototypeOf.
As suggested in this answer (to same question), Object.prototype.toString.call(var) returns "[object Object]"
In Chrome, console.log(var) produces a summary that looks like it has some other class information:
▶ _Api {context: Array[1], selector: Object, ajax: Object}
I imagine that means there is something I could use somewhere but this question about where Chrome gets that name has no useful answers.
How can I detect when a variable refers to a jQuery-DataTables API object?
You can dig around more by using a breakpoint in Chrome. If you set a breakpoint just after a DataTable variable, and evaluate the variable, you'll get a complete map. I did that, and found this:
var myDataTable = $('#myTable').DataTable(myOptions);
alert(myDataTable.$.__dt_wrapper);
The alert returns true. (myOptions is an object containing all the options for the DataTable.) So, I looked at an object (I used myOptions) to see what I could test for. You can't test directly for whether myOptions.$.__dt_wrapper) is false, because myOptions.$ evaluates to undefined. So:
if(undefined != myObjectVariable.$ && myObjectVariable.$.__dt_wrapper) {
alert('A DataTable Object')
} else {
alert('Not a DataTable Object')
}
Googling "__dt_wrapper" doesn't show any links that are unrelated to DataTables. Looking at datatables.js, it occurs twice. Both cases suggest that it's an internal attribute used to denote a DataTable object, or more specifically whether an object is a DataTable wrapper.
Since this is undocumented there's no guarantee that it will work in all future versions of DataTables. But this works in DataTables 1.10.11.
Edit: I asked about this on the DataTables forum (link), and there indeed is a cleaner way to do this:
myObjectVariable instanceof $.fn.DataTable.Api
Is true if the object is a DataTable. (This also works for the lower case dataTable object.)
Related
I want to understand in which case I should or have to use the evaluate function.
I have read the API doc about the evaluate function of CasperJS, but I'm unsure in which case I should use this function. And what does DOM context mean? Can somebody provide an example?
The CasperJS documentation has a pretty good description of what casper.evaluate() does.
To recap: You pass a function that will be executed in the DOM context (you can also call it the page context). You can pass some primitives as arguments to this function and return one primitive back. Keep in mind that this function that you pass to evaluate must be self contained. It cannot use variables or functions that are defined outside of this function.
CasperJS provides many good functions for everyday tasks, but you may run into a situation when you need a custom function to do something. evaluate is basically there to do
Retrieve some value from the page to take action based on it in your script
Manipulate the page in some way other than clicking or filling out a form
Combinations of points 1. and 2.
Examples
You may need a generic function to get the checked property from a checkbox. CasperJS currently only provides getElementAttribute function which will not work in this case.
function getChecked(cssSelector){
return document.querySelector(cssSelector).checked;
}
if (casper.evaluate(getChecked, selector)){
// do something
} else {
// do something else
}
In most of the cases it is just preference of what you want to use. If you have a list of users with data-uid on each li element then you have at least 2 possibilities to retrieve the uids.
Casper-only:
var uids = casper.getElementsAttribute('ul#user-list > li', 'data-uid');
Casper-Evaluate:
var uids = casper.evaluate(function(){
return Array.prototype.map.call(document.querySelectorAll('ul#user-list > li'), function(li){ return li["data-uid"]});
});
Regarding manipulation everything is possible but depends on what you want to do. Let's say you want to take screenshots of web pages, but there are some elements that you don't want to be there. Or you could add your own CSS to the document.
Remove elements:
function removeSelector(cssSelector){
var elements = document.querySelectorAll(cssSelector);
Array.prototype.forEach.call(elements, function(el){
el.parent.removeChild(el);
});
}
casper.evaluate(removeSelector, '.ad'); // if it would be that easy :)
Change site appearance through CSS:
function applyCSS(yourCss){
var style = document.createElement("style");
style.innerHTML = yourCss;
document.head.appendChild(style);
}
casper.evaluate(applyCSS, 'body { background-color: black; }'); // non-sense
Roots
CasperJS is built on top of PhantomJS and as such inherits some of its quirks. The PhantomJS documentation for page.evaluate() says this:
Note: The arguments and the return value to the evaluate function must be a simple primitive object. The rule of thumb: if it can be serialized via JSON, then it is fine.
Closures, functions, DOM nodes, etc. will not work!
I'm working on a vb.net application that interacts with a (third party provided) web app to provide additional functionality (e.g. removing menu items, pulling information from the pages, etc.). The web app is completely driven by javascript but is hosted in asp.net and is only used with Internet Explorer.
I'm trying to read properties from a javascript object and execute some of it's functions. I've managed to get hold of the javascript object by getting the mshtml.HTMLDocument of the iframe the script resides in and using the following code:
Dim jsObject as Object
jsObject = htmldoc.Script.jsObject
jsObject exists as a {System.__ComObject} and i can use it to execute any of it's functions or read it's properties as follows:
Dim value as String = jsObject.FunctionThatReturnsAString()
jsObject.FunctionTHatDoesSomethingInWebApp("Param1", "Param2")
This works great. However, when I leave the page/frame with jsObject in and return to it, the same code throws an exception when getting the javascript object from the frame again (i.e. executing the following line):
jsObject = htmldoc.Script.jsObject
Exception: Member not found. (Exception from HRESULT: 0x80020003 (DISP_E_MEMBERNOTFOUND))
If I stop debugging and restart, it works again (until i leave the page, etc.). I'm not sure what's happening that's causing the javascript object to disappear as far as my app's concerned. I'm presuming it's due to my app holding a reference to the COM object and i need to release it in some way (particulary as it's got a base type of MarshalByRefObject - which makes sense as it's being passed between app domains).
Why is this happening? Is there a better way of accessing a javascript object, it's properties and functions?
I've found what is, in my case, a better way of achieving what I need. Instead of accessing the jsObject directly as a COM Object (and worrying about Marshaling, etc.), I either use:
execScript to call functions with no return value or
create a hidden div element in the frame i'm working in, set the innerHTML of that div equal to whatever javascript variable/function return value that i'm interested in using execScript and then read that value seperately from the DOM
To read a variable/function return i use the following vb.net function:
Private Function getJScriptVariable(ByVal JScript As String)
Dim command As New StringBuilder()
command.Append("var e = document.getElementById('Matt_JScriptReturn');")
command.Append("if (e == null) {")
command.Append("var e = document.createElement('div');")
command.Append("e.id = 'Matt_JScriptReturn';")
command.Append("e.type = 'hidden';")
command.Append("document.body.appendChild(e);")
command.Append("}")
command.Append("e.innerHTML = ")
command.Append(JScript)
command.Append(";")
'fMaster is the frame containing the javascript's mshtml.IHTMLWindow2
fMaster.execScript(command.ToString(), "JScript")
'N.B. fMaster_Document is the fMaster's mshtml.HTMLDocument
Return fMaster_Document.getElementById("Matt_JScriptReturn").innerHTML
'Optionally execScript to remove element from DOM at this point
End Function
Then i would use that function as follows (respecting my example in the original question):
Dim value as String = getJScriptVariable("jsObject.FunctionThatReturnsAString()")
To execute javascript code without needing to return a value I simply execute it as follows:
fMaster.execScript("jsObject.FunctionTHatDoesSomethingInWebApp('Param1', 'Param2')")
I'd still be interesting in finding out why i had the problem earlier with the javascipt object being unable to access after leaving the page and returning, however this solves my problem so i'm happy for now! I hope this helps someone else at some point.
I'd like to use UUID as action parameters. However, unless I use .toString() on the UUID objects when generating the action URL's, Play seems to serialize the object differently; Something like this: referenceId.sequence=-1&referenceId.hashCode=1728064460&referenceId.version=-1&referenceId.variant=-1&referenceId.timestamp=-1&referenceId.node=-1
However, using toString "works", but when I redirect from one action to another by simply invoking the method directly, there's no way I can call toString, as the method expects a UUID. Therefore it gives me the representation shown above.
Is there any way I can intersect the serialization of a certain type?
aren't you able to just use string in your action parameter? you know that this string is an UUID, so you can always recreate UUID from it. Maybe this is not the solution for you but that's my first thought. As far as I know play serializes objects like that when passing them trough paremeters.
If this does not work for you try finding something here: http://www.playframework.org/documentation/1.2.4/controllers
I found a way to do this, but right now it means hacking a part of the frameworks code itself.
What you basically need is a TypeBinder for binding the value from the String to the UUID
and a small code change in
play/framework/src/play/data/binding/Unbinder.java
if (!isAsAnnotation) {
// We want to use that one so when redirecting it looks ok. We could as well use the DateBinder.ISO8601 but the url looks terrible
if (Calendar.class.isAssignableFrom(src.getClass())) {
result.put(name, new SimpleDateFormat(I18N.getDateFormat()).format(((Calendar) src).getTime()));
} else {
result.put(name, new SimpleDateFormat(I18N.getDateFormat()).format((Date) src));
}
}
}
//here's the changed code
else if (UUID.class.isAssignableFrom(src.getClass()))
{
result.put(name, src.toString());
}
else {
// this code is responsible for the behavior you're seeing right now
Field[] fields = src.getClass().getDeclaredFields();
for (Field field : fields) {
if ((field.getModifiers() & BeanWrapper.notwritableField) != 0) {
// skip fields that cannot be bound by BeanWrapper
continue;
}
I'm working with the framework authors on a fix for this. will come back later with results.
if you need this urgently, apply the change to the code yourself and rebuild the framework by issuing
ant
in the playframework/framework
directory.
I'm having trouble with a .NET Assembly that is com visible, and calling certain methods from VB6.
What I have found is that if the parameters are well defined types, (e.g. string), calls work fine. If they are higher level objects, it raises a runtime error '438' suggesting that the property or method is not present. I suspect that this is a question of having the correct signature on the call, but I can't see how to do this correctly.
I believe that I've done everything correct on the .NET side (ComVisible, public interfaces, etc. and even have it down to a simple enough case).
Looking at the output from the typelib viewer, I have the following:
dispinterface ISimple {
properties:
methods:
[id(0x60020000)]
void Add([in] ISimpleMember* member);
[id(0x60020001)]
ISimpleMember* Create();
};
OK. So I have 2 methods in my ISimple interface. One takes an ISimpleMember (Add), whilst the other, returns an ISimpleMember.
The corresponding code in VB looks like this:
Dim item As ISimpleMember
Dim simple As simple
Set item = New SimpleMember
item.S1 = "Hello"
item.S2 = "World"
Set simple = New simple
simple.Add (item) <---- This raised the run time error 438
Set item = simple.Create <---- This works fine, returning me an ISimpleMember
I've tried a couple of things:
1. Dim item as SimpleMember (makes no difference)
2. simple.Add(ObjPtr(item)) - Syntax error
3. simple.Add(ByRef item) - Syntax error
Basically, The run time error is the same as if I had
simple.AMethodThatIHaventWritten()
Also, If I browse References in the VB6 Environment, The Add method is well defined:
Sub Add(member As SimpleMember)
I've found the answer I believe. It was very simple:
When calling a SubRoutine, I shouldn't put the name in braces. the call should have been:
simple.add member
rather than
simple.add(member)
If I change it to a function (i.e. return a value rather than void) the braces are necessary
This seems to work
(Probably) The top 3 VB6 coding mistakes made by devs who now mainly code in C#, Javascript etc. Are:-
Placing ; at the end of lines. Its a syntax error very easily spotted and picked up the compiler.
Not placing Then on the other side of an If condition expression. Again its a syntax error.
Calling a method without retrieving a value and yet using ( ) to enclose the parameter list. With multiple parameters this is a syntax error and easily found. With only one parameter the use of ( ) is interpreted as an expression. Its the result of the ( ) expression which is passed as parameter. This causes problems when ByRef is expected by the callee.
I am setting up an expectation for a call to a method that builds and executes a query. I would like to interrogate the properties of the parameter used. Is this possible
using (mocks.Record())
{
Expect.Call(connection.Retrieve(SOMETHING_HERE)).Return(returnedDatay);
}
The bit I am after is the "SOMETHING HERE" bit.
(This is my first time using Rhino mocks)
You can set up constraints on your parameters and on the properties of the parameters. The following code sets up a constraint on a property named MyProperty on your connection object. The mock expects the MyProperty to be 42. Notice, that null is passed as the parameter since it is ignored.
Expect
.Call(connection.Retrieve(null))
.IgnoreArguments()
.Constraints(Property.Value("MyProperty", 42))
.Return(returnedData);
I am writing this from memory so it may not be absolutely correct.
UPDATE:
Rhino Mocks version 3.5 introduces a new extension method GetArgumentsForCallsMadeOn that lets you inspect the parameters passed to the mocked objects:
http://kashfarooq.wordpress.com/2009/01/10/rhino-mocks-and-getargumentsforcallsmadeon/