I was experimenting with nightmarejs tonight and I wanted to have a step during runtime that required user input. Is there a way to get nightmarejs to allow something like this? I modified the type() method that comes with the lib, and I was able to stop execution to enter some text. The script even finishes, but I don't see the input I typed, displayed in the text box it's supposed to go into. I'm certain the dom selector is correct because when I use the same one and the built in type() method, the static text is there. In the code below, prompt(msg) is a method from sync-prompt Here's the method I wrote, but it doesn't update the element on the page:
exports.type_async = function(selector, msg, done) {
this.page.evaluate(function(selector) {
return document.querySelector(selector);
}, function(element){
text = prompt(msg);
element.text = text;
done();
}, selector);
};
What am I doing wrong?
The original type contains the line element.value = text; in the page context (inside of evaluate, but not the result callback), but you have element.text = text;.
Also, the value should be probably set after you have prompted for the input.
exports.typeAsync = function(selector, done) {
var text = prompt('dynamic, blocking input:');
debug('.type() %s into %s', text, selector);
this.page.evaluate(function(selector, text) {
var element = document.querySelector(selector);
element.value = text;
}, done, selector, text);
};
and it's called like this:
nightmare.typeAsync('#foo')
Related
i'm working on a simple note-taking app for my portfolio using JS and Firebase. Before i tell you what's happening i feel like i need to show you how my code works, if you have any tips and concerns please tell me as it would be GREATLY appreciated. That being said, let's have a look "together". I'm using this class to create the notes:
const htmlElements = [document.querySelector('.notes'), document.querySelector('.note')];
const [notesDiv, noteDiv] = htmlElements;
class CreateNote {
constructor(title, body) {
this.title = title;
this.body = body;
this.render = () => {
const div1 = document.createElement('div');
div1.className = 'notes-prev-container';
div1.addEventListener('click', () => { this.clickHandler(this) });
const div2 = document.createElement('div');
div2.className = 'notes-prev';
const hr = document.createElement('hr');
hr.className = 'notes__line';
// Nest 'div2' inside 'div1'
div1.appendChild(div2);
div1.appendChild(hr);
/*
Create Paragraph 1 & 2 and give them the same
class name and some text
*/
const p1 = document.createElement('p');
p1.className = 'notes-prev__title';
p1.innerText = this.title;
const p2 = document.createElement('p');
p2.className = 'notes-prev__body';
p2.innerText = this.body;
// Nest p 1 & 2 inside 'div2'
div2.appendChild(p1);
div2.appendChild(p2);
// Finally, render the div to its root tag
notesDiv.appendChild(div1);
}
}
/*
Every time this method is called, it creates 2 textareas,
one for the note title and the other for its body then it
appends it to the DOM.
*/
renderNoteContent () {
const title = document.createElement('textarea');
title.placeholder = 'Title';
title.value = this.title;
title.className = 'note__title';
const body = document.createElement('textarea');
body.placeholder = 'Body';
body.value = this.body;
body.className = 'note__body';
noteDiv.appendChild(title);
noteDiv.appendChild(body);
}
/*
When this method is called, it checks to see if there's a
note rendered already (childElementCount === 1 because there's a
button, so if there's only this button it means there's no
textareas rendered).
If yes, then merely call the renderNoteContent method. Else
get the tags with the classes 'note__title' and 'note__body'
and remove them from the DOM, then call renderNoteContent to
create the textareas with the clicked notes values.
This function gets mentioned at line 19.
*/
clickHandler(thisClass) {
if (noteDiv.childElementCount === 1) {
thisClass.renderNoteContent();
} else {
document.querySelector('.note__title').remove();
document.querySelector('.note__body').remove();
thisClass.renderNoteContent();
}
}
}
Now i need 2 buttons, createNotesButton and saveNotesButton respectively. These 2 buttons must be inside a function that will be called inside .onAuthStateChanged (why? because they will be needing access to the currentUser on firebase auth).
I want the createNotesButton to create a note prototype, render it to the DOM and create a new document on firestore, where this note contents will be stored. Here's how i did it:
PS: I feel like i'm not using this class correctly, so again if you have any tips i appreciate it.
import {db} from '../../firebase_variables/firebase-variables.js';
import {CreateNote} from '../create_notes_class/create_notes_class.js';
const htmlElements = [
document.querySelector('.createNotes-button'),
document.querySelector('.saveNotes-button')
];
const [createNotesButton, saveNotesButton] = htmlElements;
function clickHandler(user) {
/*
1. Creates a class.
2. Creates a new document on firebase with the class's empty value.
3. Renders the empty class to the DOM.
*/
createNotesButton.addEventListener('click', () => {
const note = new CreateNote('', '');
note.render();
// Each user has it's own note collection, said collection has their `uid` as name.
db.collection(`${user.uid}`).doc().set({
title: `${note.title}`,
body: `${note.body}`
})
})
}
Now i need a saveNotesButton, he's the one i'm having issues with. He needs to save the displayed note's content on firestore. Here's what i tried doing:
import {db} from '../../firebase_variables/firebase-variables.js';
import {CreateNote} from '../create_notes_class/create_notes_class.js';
const htmlElements = [
document.querySelector('.createNotes-button'),
document.querySelector('.saveNotes-button')
];
const [createNotesButton, saveNotesButton] = htmlElements;
function clickHandler(user) {
createNotesButton.addEventListener('click', () => {...})
/*
1. Creates 2 variables, `title` and `body, if there's not a note being displayed
their values will be null, which is why the rest of the code is inside an if
statement
2. If statement to check if there's a note being displayed, if yes then:
1. Call the user's note collection. Any document who has the title field equal to the
displayed note's value gets returned as a promise.
2. Then call an specific user document and update the fields `title` and `body` with
the displayed note's values.
3. If no then do nothing.
*/
saveNotesButton.addEventListener('click', () => {
const title = document.querySelector('.note__title');
const body = document.querySelector('.note__body');
db.collection(`${user.uid}`).where('title', '==', `${title.value}`)
.get()
.then(userCollection => {
db.collection(`${user.uid}`).doc(`${userCollection.docs[0].id}`).update({
title: `${title.value}`,
body: `${body.value}`
})
})
.catch(error => {
console.log('Error getting documents: ', error);
});
});
}
This didn't work because i'm using title.value as a query, so if i change it's value it will also change the queries direction to a path that doesn't exist.
So here's the question: how can i make it so the saveNotesButton does its job? I was thinking of adding another field to each note, something that won't change so i can easily identify and edit each note. Again, if there's something in my code that you think can or should be formatted please let me know, i'm using this project as a way to solidify my native JS knowledge so please be patient. I feel like if i had used React i would've finished this sometime ago but definitely wouldn't have learned as much, anyway thanks for your help in advance.
I was thinking of adding another field to each note, something that won't change so i can easily identify and edit each note.
Yes, you absolutely need an immutable identifier for each note document in the firestore so you can unambiguously reference it. You almost always want this whenever you're storing a data object, in any application with any database.
But, the firestore already does this for you: after calling db.collection(user.uid).doc() you should get a doc with an ID. That's the ID you want to use when updating the note.
The part of your code that interacts with the DOM will need to keep track of this. I suggest moving the code the creates the firestore document into the constructor of CreateNote and storing it on this. You'll need the user id there as well.
constructor(title, body, userId) {
this.title = title;
this.body = body;
const docRef = db.collection(userId).doc();
this.docId = docRef.id;
/* etc. */
Then any time you have an instance of CreateNote, you'll know the right user and document to reference.
Other suggestions (since you asked)
Use JsPrettier. It's worth the setup, you'll never go back.
Use HTML semantics correctly. Divs shouldn't be appended as children of hrs, because they're for "a thematic break between paragraph-level elements: for example, a change of scene in a story, or a shift of topic within a section." MDN
For your next project, use a framework. Essentially no one hand-codes event listeners and appends children to get things done. I see the value for basic understanding, but there's a rich and beautiful world of frameworks out there; don't limit yourself by avoiding them :-)
I am currently working with Cypress and have created a function that randomly clicks an element from a list. I am trying to also get the text from that same function for future assertions. The problem is I am unable to return the text properly. The code I currently have is:
export function selectRandomFromList(listLocator, elementLocator) {
cy.get(listLocator).within(() => {
let numberOfElements = Cypress.$(listLocator + ' ' + elementLocator).length
let selected = Cypress._.random(0, numberOfElements - 1)
cy.get(elementLocator).eq(selected).then(($text) => {
const text = $text.text()
return text
}).click()
})
}
I was hoping that I could then in my test run this function, do the click and then store the returned text in a variable for future checking. What am I doing wrong? Also tried some other stuff with promises and such, when the code was saying I am trying to mix sync and async..
Forgot to add. This is in the support file and I want to use the text variable in the test file. Something like this:
var text = function.selectRandomFromList('[class*=***]', 'li ul li button')
After which I should have the text of the clicked button in text.
Cypress commands work the same way as Promises. You need to return a Promise when inside a then() callback. See doc here.
You may use cy.wrap($text).invoke('text') inside your then() callback instead of returning a string. But then, your click() would not work because your then() would yield a text value that cannot be clicked.
I suggest not using the within() and working directly with the elements. You'll end up with the same result, but with less complexity. I use the within() command when I need to perform several actions on several elements within a div container for example.
You can perform what you need with default commands instead of a function:
let mixLocator = listLocator + ' ' + elementLocator;
cy.get(mixLocator).its('length').then(elementCount => {
let selected = Cypress._.random(elementCount - 1); // lower = 0 is default
cy.get(mixLocator).eq(selected).click().invoke('text').as('selectedText'); // saving the text as an alias to be used later
});
Here, the invoke('text') should still work even if it is after the click(), but it is impossible to do the opposite invoke('text').click() because the invoke('text') command yields a string. If it doesn't, call it once to get the text and again to click it:
cy.get(mixLocator).eq(selected).invoke('text').as('selectedText');
cy.get(mixLocator).eq(selected).click();
or:
cy.get(mixLocator).eq(selected).then(selectedElement => {
cy.wrap(selectedElement).invoke('text').as('selectedText');
cy.wrap(selectedElement).click();
});
You may then use your saved alias later:
cy.get('#selectedText').then(selectedText => {
// use selectedText here
});
I often prefer to chose random elements in our tests to have a better coverage. I came up with a custom commands:
commands.js
Cypress.Commands.add('any', { prevSubject: 'element' }, (subject, size = 1) => {
cy.wrap(subject).then(elementList => {
elementList = (elementList.jquery) ? elementList.get() : elementList;
elementList = Cypress._.sampleSize(elementList, size);
elementList = (elementList.length > 1) ? elementList : elementList[0];
cy.wrap(elementList);
});
});
I use it like this:
cy.get(elementLocator).any().click();
or
cy.get(elementLocator).any(5).each(randomElement => {
cy.wrap(randomElement).click();
});
I want to parse some html with htmlparser2 module for Node.js. My task is to find a precise element by its ID and extract its text content.
I have read the documentation (quite limited) and I know how to setup my parser with the onopentag function but it only gives access to the tag name and its attributes (I cannot see the text). The ontext function extracts all text nodes from the given html string, but ignores all markup.
So here's my code.
const htmlparser = require("htmlparser2");
const file = '<h1 id="heading1">Some heading</h1><p>Foobar</p>';
const parser = new htmlparser.Parser({
onopentag: function(name, attribs){
if (attribs.id === "heading1"){
console.log(/*how to extract text so I can get "Some heading" here*/);
}
},
ontext: function(text){
console.log(text); // Some heading \n Foobar
}
});
parser.parseComplete(file);
I expect the output of the function call to be 'Some heading'. I believe that there is some obvious solution but somehow it misses my mind.
Thank you.
You can do it like this using the library you asked about:
const htmlparser = require('htmlparser2');
const domUtils = require('domutils');
const file = '<h1 id="heading1">Some heading</h1><p>Foobar</p>';
var handler = new htmlparser.DomHandler(function(error, dom) {
if (error) {
console.log('Parsing had an error');
return;
} else {
const item = domUtils.findOne(element => {
const matches = element.attribs.id === 'heading1';
return matches;
}, dom);
if (item) {
console.log(item.children[0].data);
}
}
});
var parser = new htmlparser.Parser(handler);
parser.write(file);
parser.end();
The output you will get is "Some Heading". However, you will, in my opinion, find it easier to just use a querying library that is meant for it. You of course, don't need to do this, but you can note how much simpler the following code is: How do I get an element name in cheerio with node.js
Cheerio OR a querySelector API such as https://www.npmjs.com/package/node-html-parser if you prefer the native query selectors is much more lean.
You can compare that code to something more lean, such as the node-html-parser which supports simply querying:
const { parse } = require('node-html-parser');
const file = '<h1 id="heading1">Some heading</h1><p>Foobar</p>';
const root = parse(file);
const text = root.querySelector('#heading1').text;
console.log(text);
I'm working on an application that uses some client-side templates to render data and most of the javascript template engines return a simple string with the markup and it's up to the developer to insert that string into the DOM.
I googled around and saw a bunch of people suggesting the use of an empty div, setting its innerHTML to the new string and then iterating through the child nodes of that div like so
var parsedTemplate = 'something returned by template engine';
var tempDiv = document.createElement('div'), childNode;
var documentFragment = document.createDocumentFragment;
tempDiv.innerHTML = parsedTemplate;
while ( childNode = tempDiv.firstChild ) {
documentFragment.appendChild(childNode);
}
And TADA, documentFragment now contains the parsed template. However, if my template happens to be a tr, adding the div around it doesn't achieve the expected behaviour, as it adds the contents of the td's inside the row.
Does anybody know of a good way of to solve this? Right now I'm checking the node where the parsed template will be inserted and creating an element from its tag name. I'm not even sure there's another way of doing this.
While searching I came across this discussion on the w3 mailing lists, but there was no useful solution, unfortunately.
You can use a DOMParser as XHTML to avoid the HTML "auto-correction" DOMs like to perform:
var parser = new DOMParser(),
doc = parser.parseFromString('<tr><td>something returned </td><td>by template engine</td></tr>', "text/xml"),
documentFragment = document.createDocumentFragment() ;
documentFragment.appendChild( doc.documentElement );
//fragment populated, now view as HTML to verify fragment contains what's expected:
var temp=document.createElement('div');
temp.appendChild(documentFragment);
console.log(temp.outerHTML);
// shows: "<div><tr><td>something returned </td><td>by template engine</td></tr></div>"
this is contrasted to using naive innerHTML with a temp div:
var temp=document.createElement('div');
temp.innerHTML='<tr><td>something returned </td><td>by template engine</td></tr>';
console.log(temp.outerHTML);
// shows: '<div>something returned by template engine</div>' (bad)
by treating the template as XHTML/XML (making sure it's well-formed), we can bend the normal rules of HTML.
the coverage of DOMParser should correlate with the support for documentFragment, but on some old copies (single-digit-versions) of firefox, you might need to use importNode().
as a re-usable function:
function strToFrag(strHTML){
var temp=document.createElement('template');
if( temp.content ){
temp.innerHTML=strHTML;
return temp.content;
}
var parser = new DOMParser(),
doc = parser.parseFromString(strHTML, "text/xml"),
documentFragment = document.createDocumentFragment() ;
documentFragment.appendChild( doc.documentElement );
return documentFragment;
}
This is one for the future, rather than now, but HTML5 defines a <template> element that will create the fragment for you. You will be able to do:
var parsedTemplate = '<tr><td>xxx</td></tr>';
var tempEL = document.createElement('template');
tempEl.innerHTML = parsedTemplate;
var documentFragment = tempEl.content;
It currently works in Firefox. See here
The ideal approach is to use the <template> tag from HTML5. You can create a template element programmatically, assign the .innerHTML to it and all the parsed elements (even fragments of a table) will be present in the template.content property. This does all the work for you. But, this only exists right now in the latest versions of Firefox and Chrome.
If template support exists, it as simple as this:
function makeDocFragment(htmlString) {
var container = document.createElement("template");
container.innerHTML = htmlString;
return container.content;
}
The return result from this works just like a documentFragment. You can just append it directly and it solves the problem just like a documentFragment would except it has the advantage of supporting .innerHTML assignment and it lets you use partially formed pieces of HTML (solving both problems we need).
But, template support doesn't exist everywhere yet, so you need a fallback approach. The brute force way to handle the fallback is to peek at the beginning of the HTML string and see what type of tab it starts with and create the appropriate container for that type of tag and use that container to assign the HTML to. This is kind of a brute force approach, but it works. This special handling is needed for any type of HTML element that can only legally exist in a particular type of container. I've included a bunch of those types of elements in my code below (though I've not attempted to make the list exhaustive). Here's the code and a working jsFiddle link below. If you use a recent version of Chrome or Firefox, the code will take the path that uses the template object. If some other browser, it will create the appropriate type of container object.
var makeDocFragment = (function() {
// static data in closure so it only has to be parsed once
var specials = {
td: {
parentElement: "table",
starterHTML: "<tbody><tr class='xx_Root_'></tr></tbody>"
},
tr: {
parentElement: "table",
starterHTML: "<tbody class='xx_Root_'></tbody>"
},
thead: {
parentElement: "table",
starterHTML: "<tbody class='xx_Root_'></tbody>"
},
caption: {
parentElement: "table",
starterHTML: "<tbody class='xx_Root_'></tbody>"
},
li: {
parentElement: "ul",
},
dd: {
parentElement: "dl",
},
dt: {
parentElement: "dl",
},
optgroup: {
parentElement: "select",
},
option: {
parentElement: "select",
}
};
// feature detect template tag support and use simpler path if so
// testing for the content property is suggested by MDN
var testTemplate = document.createElement("template");
if ("content" in testTemplate) {
return function(htmlString) {
var container = document.createElement("template");
container.innerHTML = htmlString;
return container.content;
}
} else {
return function(htmlString) {
var specialInfo, container, root, tagMatch,
documentFragment;
// can't use template tag, so lets mini-parse the first HTML tag
// to discern if it needs a special container
tagMatch = htmlString.match(/^\s*<([^>\s]+)/);
if (tagMatch) {
specialInfo = specials[tagMatch[1].toLowerCase()];
if (specialInfo) {
container = document.createElement(specialInfo.parentElement);
if (specialInfo.starterHTML) {
container.innerHTML = specialInfo.starterHTML;
}
root = container.querySelector(".xx_Root_");
if (!root) {
root = container;
}
root.innerHTML = htmlString;
}
}
if (!container) {
container = document.createElement("div");
container.innerHTML = htmlString;
root = container;
}
documentFragment = document.createDocumentFragment();
// start at the actual root we want
while (root.firstChild) {
documentFragment.appendChild(root.firstChild);
}
return documentFragment;
}
}
// don't let the feature test template object hang around in closure
testTemplate = null;
})();
// test cases
var frag = makeDocFragment("<tr><td>Three</td><td>Four</td></tr>");
document.getElementById("myTableBody").appendChild(frag);
frag = makeDocFragment("<td>Zero</td><td>Zero</td>");
document.getElementById("emptyRow").appendChild(frag);
frag = makeDocFragment("<li>Two</li><li>Three</li>");
document.getElementById("myUL").appendChild(frag);
frag = makeDocFragment("<option>Second Option</option><option>Third Option</option>");
document.getElementById("mySelect").appendChild(frag);
Working demo with several test cases: http://jsfiddle.net/jfriend00/SycL6/
Use this function
supports IE11
has not to be xml-conform e.g. '<td hidden>test'
function createFragment(html){
var tmpl = document.createElement('template');
tmpl.innerHTML = html;
if (tmpl.content == void 0){ // ie11
var fragment = document.createDocumentFragment();
var isTableEl = /^[^\S]*?<(t(?:head|body|foot|r|d|h))/i.test(html);
tmpl.innerHTML = isTableEl ? '<table>'+html : html;
var els = isTableEl ? tmpl.querySelector(RegExp.$1).parentNode.childNodes : tmpl.childNodes;
while(els[0]) fragment.appendChild(els[0]);
return fragment;
}
return tmpl.content;
}
The solution from #dandavis will accept only xml-conform content in ie11.
I dont know if there are other tag which must be taken into account?
i have been trying to figure this out for the past couple hours and was hoping someone here could help out. I am using JQuery 1.6.4 to make an ajax call when a button is clicked and populate the contents of a table with the results. The code works as it should in all browsers except IE. When i run it in IE, some values are populated and the rest are not, very strange!
When i run the script and use IE's debugging tool i get the following error;
Unexpected call to method or property access (line 3)
Does anyone know what i'm doing wrong? Here is my code that runs the ajax;
$(document).ready(function(){
$("#find").click(function () {
//Set kinase
var kinaseEntry = $("#kinaseEntry").val();
var dataString = "kinaseEntry=" + kinaseEntry;
//Fetch list from database
$.ajax({
type : "GET",
url : "http://www.webaddress/php/post.php",
datatype: "json",
data: dataString,
success : function(datas) {
//SET VARIABLES TO BE USED THROUGHOUT PAGE
var kinaseName = (datas.skuName);
var molecularWeight = (datas.molecularWeight);
var kinaseConc = (datas.kinaseConc);
var anti1Name = (datas.antiName);
var anti2Name = (datas.antiName2);
var antiConc = (datas.antiConc);
var antiConc2 = (datas.antiConc2);
var tracerName = (datas.tracerName);
var tracerConc = (datas.tracerConc);
var tracerStockConc = (datas.tracerStockConc);
var tracerSku = (datas.tracerSku);
var kinaseSku = (datas.kinaseSku);
var antiSku = (datas.antiSku);
var antiSku2 = (datas.antiSku2);
var bufferName = (datas.bufferName);
var bufferSku = (datas.bufferSku);
//REAGENT NAMES
$(".kinaseName").html(kinaseName + " (" + molecularWeight + " kDa)");
$(".anti1Name").html(anti1Name);
$(".anti2Name").html(anti2Name);
$(".tracerName").html(tracerName);
$(".bufferName").html(bufferName);
//DEFAULT VALUES
$(".defaultKinaseConc").html(kinaseConc);
$(".defaultAntiConc").html(antiConc);
$(".defaultAnti2Conc").html(antiConc2);
$(".defaultTracerConc").html(tracerConc);
$("#molecularWeight").val(molecularWeight);
$("#tracerStockConc").val(tracerStockConc);
//INSERT DEFAULTS INTO INPUT
$("#userKinaseConc").val(kinaseConc);
$("#userAntibodyConc1").val(antiConc);
$("#userAntibodyConc2").val(antiConc2);
$("#userTracerConc").val(tracerConc);
},
error : function (XMLHttpRequest, textStatus, errorThrown) {
console.log("error :"+XMLHttpRequest.responseText);
}
});
return false;
}); });
The names populate fine and the first default value gets populated, but the rest breaks. Any help would be greatly appreciated.
Here are the results of my console.log();
antiConc
"2"
antiConc2
"2"
antiName
"Biotin-anti-His"
antiName2
"Eu-Streptavidin"
antiSku
"PV6089"
antiSku2
"PV5899"
bufferName
"Kinase Buffer A"
bufferSku
"PV3189"
cleanSKU
null
kinaseConc
"5"
kinaseSku
"P3049"
molecularWeight
"125.4"
skuName
"ABL1"
tracerConc
"100"
tracerName
"Kinase Tracer 1710"
tracerSku
"PV6088"
tracerStockConc
"25"
Are you sure it's working properly in other browsers, or are they just not breaking as significantly.
I'm guessing you have mixed updates happening here (a distinction presumed from val() and html() use). I notice a pattern whereby elements retrieved by id (e.g., #userKinaseConc) and those retrieved by class (e.g., .kinaseName) are using val() and html() respectively. However, #defAntiCon breaks that pattern. You're using html() for this element. Is this element an input or other form field? Should this be val()?
The experience you describe can be re-created by attempting to apply a value to an input field via html() setter rather than val(), with the exception that it still doesn't work for that specific field in all values... browsers other than IE just fail differently and the error becomes non-breaking. In IE, it will break execution. Is #defAntiCon actually being populated based on data returned in other browsers?
IE8 Is strict about HTML insertion. If your markup is invalid, or either have HTML5 specific tags, it will throw an error.
I have no real solution, but I'd follow this steps to make it works.
Make sure your datas contains valid HTML, make sure you have no extra closing tags or syntax error (you can use a validator if needed)
If you are using HTML5 tags, you could try to use html5shiv to make sure the browser will understand all the tags
Make sur you don't use .val() instead of .html() in some cases
You should replace .html(datas.foo) by .text(datas.foo) since you are passing string to the method.
In case you don't find any solutions, you could add a catch block in jQuery resolveWith method (referring to your comment) by doing the following changes :
replace this :
resolveWith: function ( context, args ) {
if ( !cancelled && !fired && !firing ) {
// make sure args are available (#8421)
args = args || []; firing = 1;
try {
while ( callbacks[ 0 ] ) { callbacks.shift().apply( context, args ); }
} finally {
fired = [ context, args ];
firing = 0;
}
}
return this ;
},
with this :
resolveWith: function ( context, args ) {
if ( !cancelled && !fired && !firing ) {
// make sure args are available (#8421)
args = args || []; firing = 1;
try {
while ( callbacks[ 0 ] ) { callbacks.shift().apply( context, args ); }
} catch (err) {
} finally {
fired = [ context, args ];
firing = 0;
}
}
return this ;
},
Bug on the try-finally in IE is reported here
Hope this helps.
Edit : As #Josh mentionned in the comment, the bug has been reported and resolved in this commit for jQuery 1.7.x. A simple update of your jQuery should works.