<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
(function(exposeAs){
var Dex = function(selector, nodes){
// Return Dex object
return new Dex.fn.init(selector, nodes);
};
Dex.fn = Dex.prototype = {
init: function(selector, nodes){
this.selector = selector;
if(nodes){
this.nodes = nodes;
} else {
this.nodes = document.querySelectorAll(selector);
}
return this;
}
};
// CONSTANTS
var UNDEFINED = "undefined";
// HIDDEN VARIABLES
var cachedSelections = {},
delegatedEventListeners = {},
mutationObservers = {},
DOMMutationCallback = function(mutations, selector, parentNodes){
var n, x, y,
mutationRecord,
addedNode,
removedNode,
modifiedNodeAttribute,
callbacks,
modifiedSelector,
_target,
executeCallbacks = function(queue, node, arg1, arg2){
var n = 0;
while(queue[n]){
// Call callback function
queue[n].callback.call(node, arg1, arg2);
if(!queue[n].persistant){
queue.splice(n, 1);
}
n++;
}
},
executeAttributecallbacks = function(queue, node, attrKey, attrValue){
y = 0;
while(queue[y]){
// Call callback function
if(queue[y].attrKey == attrKey){
queue[y].callback.call(node, attrKey, attrValue);
if(!queue[y].persistant){
queue.splice(y, 1);
}
}
y++;
}
};
// Loop through mutation records
for(n = 0; n < mutations.length; n++){
mutationRecord = mutations[n];
// Loop through added nodes
if(mutationRecord.addedNodes.length > 0){
callbacks = mutationObservers[selector].callbacks.onOpen;
if(callbacks){
for(x = 0; x < mutationRecord.addedNodes.length; x++){
addedNode = mutationRecord.addedNodes[x];
// Check if node type is valid
if(addedNode.nodeType == 1){
// Loop through callbacks
for(_target in callbacks){
modifiedSelector = callbacks[_target].matchType.modifiedSelector;
// See if addedNode matches the _target of the callback
switch(callbacks[_target].matchType.type){
case "tag":
if(addedNode.tagName.toUpperCase() == modifiedSelector){
// Loop through all callbacks
executeCallbacks(callbacks[_target].queue, addedNode);
}
break;
case "id":
if(addedNode.id == modifiedSelector){
// Loop through all callbacks
executeCallbacks(callbacks[_target].queue, addedNode);
}
break;
case "classname":
if(addedNode.classList.contains(modifiedSelector)){
// Loop through all callbacks
executeCallbacks(callbacks[_target].queue, addedNode, mutationRecord.addedNodes);
}
break;
case "complex":
if(addedNode.matches(modifiedSelector)){
// Loop through all callbacks
executeCallbacks(callbacks[_target].queue, addedNode);
}
break;
}
}
}
}
}
}
if(mutationRecord.removedNodes.length > 0){
callbacks = mutationObservers[selector].callbacks.onClose;
if(callbacks){
// Loop through removed nodes
for(x = 0; x < mutationRecord.removedNodes.length; x++){
removedNode = mutationRecord.removedNodes[x];
// Check if node type is valid
if(removedNode.nodeType == 1){
// Loop through callbacks
for(_target in callbacks){
modifiedSelector = callbacks[_target].matchType.modifiedSelector;
// See if removedNode matches the _target of the callback
switch(callbacks[_target].matchType.type){
case "tag":
if(removedNode.tagName.toUpperCase() == modifiedSelector){
// Loop through all callbacks
executeCallbacks(callbacks[_target].queue, removedNode);
}
break;
case "id":
if(removedNode.id == modifiedSelector){
// Loop through all callbacks
executeCallbacks(callbacks[_target].queue, removedNode);
}
break;
case "classname":
if(removedNode.classList.contains(modifiedSelector)){
// Loop through all callbacks
executeCallbacks(callbacks[_target].queue, removedNode);
}
break;
case "complex":
if(removedNode.matches(modifiedSelector)){
// Loop through all callbacks
executeCallbacks(callbacks[_target].queue, removedNode);
}
break;
}
}
}
}
}
}
if(mutationRecord.attributeName){
callbacks = mutationObservers[selector].callbacks.onAttribute;
if(callbacks){
// Loop through modified attributes
modifiedNodeAttribute = mutationRecord.target;
// Loop through callbacks
for(_target in callbacks){
modifiedSelector = callbacks[_target].matchType.modifiedSelector;
// See if modifiedNodeAttribute matches the _target of the callback
switch(callbacks[_target].matchType.type){
case "tag":
if(modifiedNodeAttribute.tagName.toUpperCase() == modifiedSelector){
// Loop through all callbacks
if(mutationRecord.target.attributes[mutationRecord.attributeName]){ // Its a live list, so check it hasnt been removed
executeAttributecallbacks(callbacks[_target].queue, modifiedNodeAttribute, mutationRecord.attributeName, mutationRecord.target.attributes[mutationRecord.attributeName].value);
}
}
break;
case "id":
if(modifiedNodeAttribute.id == modifiedSelector){
// Loop through all callbacks
if(mutationRecord.target.attributes[mutationRecord.attributeName]){ // Its a live list, so check it hasnt been removed
executeAttributecallbacks(callbacks[_target].queue, modifiedNodeAttribute, mutationRecord.attributeName, mutationRecord.target.attributes[mutationRecord.attributeName].value);
}
}
break;
case "classname":
if(modifiedNodeAttribute.classList.contains(modifiedSelector)){
// Loop through all callbacks
if(mutationRecord.target.attributes[mutationRecord.attributeName]){ // Its a live list, so check it hasnt been removed
executeAttributecallbacks(callbacks[_target].queue, modifiedNodeAttribute, mutationRecord.attributeName, mutationRecord.target.attributes[mutationRecord.attributeName].value);
}
}
break;
case "complex":
if(modifiedNodeAttribute.matches(modifiedSelector)){
// Loop through all callbacks
if(mutationRecord.target.attributes[mutationRecord.attributeName]){ // Its a live list, so check it hasnt been removed
executeAttributecallbacks(callbacks[_target].queue, modifiedNodeAttribute, mutationRecord.attributeName, mutationRecord.target.attributes[mutationRecord.attributeName].value);
}
}
break;
}
}
}
}
}
},
createEventListener = function(dexObject, eventType, selector, target, callback, persistant){
// Check that the event type exists in queue
if(!delegatedEventListeners[eventType]){
delegatedEventListeners[eventType] = {};
}
// Check that this selector is registered
if(!delegatedEventListeners[eventType][selector]){
delegatedEventListeners[eventType][selector] = {};
// Setup listener
attachEventListeners(eventType, dexObject);
}
// Check that this target is registered
if(!delegatedEventListeners[eventType][selector][target]){
delegatedEventListeners[eventType][selector][target] = [];
}
// register delegated event listener
delegatedEventListeners[eventType][selector][target].push({
callback: callback,
persistant: persistant
});
},
attachEventListeners = function(eventType, dexObject){
var n,
selector = {
string: dexObject.selector
};
for(n = 0; n < dexObject.nodes.length; n++){
selector.node = dexObject.nodes[n];
selector.node.addEventListener(eventType, function(e){
var eventHandlers = delegatedEventListeners[eventType][selector.string];
for(handlerSelector in eventHandlers){
clickedNode = e.target;
while( clickedNode &&
clickedNode !== document &&
clickedNode !== selector.node) { // Stop looking when we hit the node that contains the event listener
if (clickedNode.matches(handlerSelector)) {
var handlerIndex = 0;
while (eventHandlers[handlerSelector][handlerIndex]){
eventHandlers[handlerSelector][handlerIndex].callback.call(clickedNode, e);
if(!eventHandlers[handlerSelector][handlerIndex].persistant){
eventHandlers[handlerSelector].splice(handlerIndex, 1);
}
handlerIndex++;
}
}
clickedNode = clickedNode.parentNode;
}
}
});
}
};
// EXPOSED SELECTOR FUNCTIONS
Dex.fn.init.prototype = {
first: function(){
/* Remove all nodes except first from node list */
this.nodes = [this.nodes[0]];
return this;
},
index: function(index){
/* Remove all nodes except requested index */
this.nodes = [this.nodes[index]];
return this;
},
getNode: function(index){
/* Return DOM node */
return this.nodes[index];
},
exists: function(){
/* See if the node list contains at least one node */
return this.nodes[0] != null;
},
appendClone: function(node){
var n;
for(n = 0; n < this.nodes.length; n++){
this.nodes[n].appendChild(node.cloneNode(true));
}
return this;
},
append: function(node){
var n;
for(n = 0; n < this.nodes.length; n++){
this.nodes[n].appendChild(node);
}
return this;
},
remove: function(){
var n;
for(n = 0; n < this.nodes.length; n++){
this.nodes[n];
if (this.nodes[n].parentNode) {
this.nodes[n].parentNode.removeChild(this.nodes[n]);
}
}
return this;
},
clone: function(){
var n,
clonedNodes = [];
for(n = 0; n < this.nodes.length; n++){
clonedNodes.push(this.nodes[n].cloneNode(true));
}
this.nodes = clonedNodes;
return this;
},
parent: function(){
this.nodes = [this.nodes[0].parentNode];
return this;
},
closest: function(matchSelector){
var closest = [];
node = this.nodes[0];
// Go through parents
while(node && node !== document) {
if (node.matches(matchSelector)) {
closest = [node];
break;
}
node = node.parentNode;
}
this.nodes = closest;
return this;
},
filter: function(selector){
/* Filter then current node list - note: only filters on the first node in list */
this.nodes = this.nodes[0].querySelectorAll(selector);
return this;
},
// DOM modification
setHTML: function(value){
/* Set innerHTML of all nodes in nodelist */
var n;
for(n = 0; n < this.nodes.length; n++){
this.nodes[n].innerHTML = value;
}
return this;
},
getHTML: function(value){
/* Get innerHTML of all first node in nodelist */
return this.nodes[0].innerHTML;
},
css: function(styles){
/* Set CSS of all nodes in nodelist */
var style,
n;
for(n = 0; n < this.nodes.length; n++){
for(style in styles){
this.nodes[n].style[style] = styles[style];
}
}
return this;
},
setAttribute: function(key, value){
/* Set attribute of all nodes in nodelist */
var n;
for(n = 0; n < this.nodes.length; n++){
this.nodes[n].setAttribute(key, value);
}
return this;
},
getAttribute: function(key){
/* Get attribute of first node in nodelist */
return this.nodes[0].getAttribute(key);
},
toggleAttribute: function(key, value){
/* Get attribute of first node in nodelist */
var n,
attrValue;
for(n = 0; n < this.nodes.length; n++){
attrValue = this.nodes[n].getAttribute(key);
if(attrValue == value[0]){
this.nodes[n].setAttribute(key, value[1]);
} else {
this.nodes[n].setAttribute(key, value[0]);
}
}
return this;
},
cache: function(cacheID){
/* Cache node list with cacheID as ID */
cachedSelections[cacheID] = this;
},
// Classes
addClass: function(classname){
/* Add class to all nodes in nodelist */
var n;
for(n = 0; n < this.nodes.length; n++){
this.nodes[n].classList.add(classname);
}
return this;
},
removeClass: function(classname){
/* Remove class from all nodes in nodelist */
var n;
for(n = 0; n < this.nodes.length; n++){
this.nodes[n].classList.remove(classname);
}
return this;
},
hasClass: function(classname){
/* Check whether first node in list contains a classname */
var result = false;
if(this.nodes[0]){
result = this.nodes[0].classList.contains(classname);
}
return result;
},
toggleClass: function(classname){
/* Toggle classnames on all nodes in nodelist */
var n;
for(n = 0; n < this.nodes.length; n++){
this.nodes[n].classList.toggle(classname);
}
return this;
},
replaceClass: function(old_classname, new_classname){
/* Replace old_classname with new_classname on all nodes in nodelist */
var n;
for(n = 0; n < this.nodes.length; n++){
this.nodes[n].classList.replace(old_classname, new_classname);
}
return this;
},
trigger: function(eventType){
/* Trigger eventType (click, mouseover, etc, ...) on all nodes in nodelist */
if(this.nodes[0]){
var clickEvent = document.createEvent("MouseEvents");
clickEvent.initEvent(eventType, true, true);
this.nodes[0].dispatchEvent(clickEvent);
}
return this;
},
onOpen: function(target, callback){
this.onMutation("onOpen", target, callback, {
children: false,
persistant: true
});
return this;
},
onceOpen: function(target, callback){
this.onMutation("onOpen", target, callback, {
children: false,
persistant: false
});
return this;
},
onClose: function(target, callback){
this.onMutation("onClose", target, callback, {
children: false,
persistant: true
});
return this;
},
onceClose: function(target, callback){
this.onMutation("onClose", target, callback, {
children: false,
persistant: false
});
return this;
},
onAttribute: function(target, attrKey, callback){
this.onMutation("onAttribute", target, callback, {
attrKey: attrKey,
persistant: true
});
return this;
},
onceAttribute: function(target, attrKey, callback){
this.onMutation("onAttribute", target, callback, {
attrKey: attrKey,
persistant: false
});
return this;
},
onMutation: function(mutationType, target, callback, parameters){
var selector = this.selector,
parentNodes = this.nodes,
matchType = function(target){
// This needs updating to cater for complex selectors.
var result;
if(target[0] == "."){
result = {
type: "classname",
modifiedSelector: target.slice(1)
}
} else if(target[0] == "#"){
result = {
type: "id",
modifiedSelector: target.slice(1)
}
} else if(target[0] == "["){
result = {
type: "complex",
modifiedSelector: target
}
} else {
result = {
type: "tag",
modifiedSelector: target.toUpperCase()
}
}
return result;
}(target),
n,
parameters = parameters || {};
// See if we need to attach a mutation observer
if(!mutationObservers[this.selector]){
mutationObservers[this.selector] = {
observer: new MutationObserver(function(mutations){
DOMMutationCallback(mutations, selector, parentNodes);
}),
callbacks: {}
}
// Attach observer to all matches nodes
for(n = 0; n < this.nodes.length; n++){
mutationObservers[this.selector].observer.observe(this.nodes[n], {
attributes: true,
childList: true,
characterData: false,
subtree: true
});
}
}
// See if there are already callbacks for mutationType
if(!mutationObservers[this.selector].callbacks[mutationType]){
mutationObservers[this.selector].callbacks[mutationType] = {}
}
// See if there are already calbacks for this target
if(!mutationObservers[this.selector].callbacks[mutationType][target]){
// mutationObservers[this.selector][childtree].callbacks[mutationType][target] = []
mutationObservers[this.selector].callbacks[mutationType][target] = {
matchType: matchType,
queue: []
}
}
// Save callback
mutationObservers[this.selector].callbacks[mutationType][target].queue.push({
callback: callback,
attrKey: parameters.attrKey,
persistant: parameters.persistant
});
},
onClick: function(target, callback){
createEventListener(this, "click", this.selector, target, callback, true);
return this;
},
oneClick: function(target, callback){
createEventListener(this, "click", this.selector, target, callback, false);
return this;
},
onMouseUp: function(target, callback){
createEventListener(this, "mouseup", this.selector, target, callback, true);
return this;
},
oneMouseUp: function(target, callback){
createEventListener(this, "mouseup", this.selector, target, callback, false);
return this;
},
onMouseDown: function(target, callback){
createEventListener(this, "mousedown", this.selector, target, callback, true);
return this;
},
oneMouseDown: function(target, callback){
createEventListener(this, "mousedown", this.selector, target, callback, false);
return this;
},
onMouseEnter: function(target, callback){
createEventListener(this, "mouseenter", this.selector, target, callback, true);
return this;
},
oneMouseEnter: function(target, callback){
createEventListener(this, "mouseenter", this.selector, target, callback, false);
return this;
},
onMouseLeave: function(target, callback){
createEventListener(this, "mouseleave", this.selector, target, callback, true);
return this;
},
oneMouseLeave: function(target, callback){
createEventListener(this, "mouseleave", this.selector, target, callback, false);
return this;
},
onMouseOver: function(target, callback){
createEventListener(this, "mouseover", this.selector, target, callback, true);
return this;
},
oneMouseOver: function(target, callback){
createEventListener(this, "mouseover", this.selector, target, callback, false);
return this;
},
onMouseOut: function(target, callback){
createEventListener(this, "mouseout", this.selector, target, callback, true);
return this;
},
oneMouseOut: function(target, callback){
createEventListener(this, "mouseout", this.selector, target, callback, false);
return this;
},
}
Dex.appendCSS = function(url){
var head = document.head,
link = document.createElement('link');
link.type = "text/css";
link.rel = "stylesheet";
link.href = url;
head.appendChild(link);
return link;
}
Dex.getCached = function(cacheID){
return cachedSelections[cacheID];
}
Dex.clearCache = function(cacheID){
delete cachedSelections[cacheID];
}
Dex.tag = function(tag){
/* Use Tag() to select nodes using the getElementsByTagName */
var nodes = document.getElementsByTagName(tag);
return Dex(tag, nodes);
}
Dex.class = function(classname){
/* Use Tag() to select nodes using the getElementsByClassName */
var nodeCollection = document.getElementsByClassName(classname),
nodes = Array.prototype.slice.call(nodeCollection);
return Dex("." + classname, nodes);
}
Dex.iframe = function(selector){
var iframe = document.querySelectorAll(selector)[0];
return Dex.node(iframe.contentDocument || iframe.contentWindow.document);
}
Dex.id = function(id){
/* Use Tag() to select nodes using the getElementById */
var nodes = [document.getElementById(id)];
return Dex("#" + id, nodes);
}
Dex.node = function(node){
/* Use Node to create a Dex object with a DOM node directly */
return Dex("node", [node]);
}
Dex.collection = function(nodeCollection){
/* Use Node to create a Dex object with an HTML Node Collection directly */
var nodes = [];
for(n = 0; n < nodeCollection.length; n++){
nodes.push(nodeCollection[n]);
}
return Dex("node", nodes)
}
Dex.dump = function(object){
console.log(mutationObservers);
};
if(exposeAs){
window[exposeAs] = Dex;
}
})("DexV2");
</script>
<style>
.add-colour {
background: red;
}
</style>
<section>
<table>
<tbody>
<tr>
<td>
<div class="app-container">
<table>
<tbody>
<tr>
<td>
<div class="app-window">
App window
<table>
<tbody>
<tr>
<td>
<div class="app-menu">
app menu
<div>
<div>
<ul>
<li>
<span class="title">This is a list item in an unordered list</span>
</li>
<li>
<button class="click-me-ul">ul .click-me</button>
<button class="and-me">ul .and-me</button>
</li>
</ul>
<ol>
<li>
<span class="title">This is a list item in an ordered list</span>
</li>
<li>
<button class="click-me-ol">ol .click-me</button>
<button class="and-me">ol .and-me</button>
</li>
</ol>
</div>
<span id="output" class="check-me">This is the output area</span>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</tbody>
</table>
</section>
DexV2.id("output").addClass("add-colour");
DexV2.id("output").removeClass("check-me");
DexV2.id("output").hasClass("check-me")
DexV2.id("output").toggleClass("check-me")
DexV2.id("output").replaceClass("check-me", "updated-class")
$("#output").addClass("add-colour");
$("#output").removeClass("check-me");
$("#output").hasClass("check-me")
$("#output")
.removeClass("check-me")
.addClass("updated-class");
$("#output").toggleClass("check-me")
--enable-precise-memory-info
flag.
Test case name | Result |
---|---|
Dex Add Class | |
Dex Remove Class | |
Dex Has Class | |
Dex Toggle Class | |
Dex Replace Class | |
jQuery Add Class | |
jQuery Remove Class | |
jQuery Has Class | |
jQuery Replace Class | |
jQuery Toggle Class |
Test name | Executions per second |
---|---|
Dex Add Class | 1039579.9 Ops/sec |
Dex Remove Class | 1375333.4 Ops/sec |
Dex Has Class | 3342226.0 Ops/sec |
Dex Toggle Class | 925498.1 Ops/sec |
Dex Replace Class | 2631932.2 Ops/sec |
jQuery Add Class | 540185.7 Ops/sec |
jQuery Remove Class | 517865.7 Ops/sec |
jQuery Has Class | 849267.9 Ops/sec |
jQuery Replace Class | 335583.7 Ops/sec |
jQuery Toggle Class | 199303.6 Ops/sec |
It looks like we're about to dive into a JSON data dump!
To summarize, it appears to be a list of test results for different browser and device configurations on a Mac OS X 10.12.5 platform. Each result is represented by an object with several key-value pairs:
RawUAString
: the original U.A. string (User Agent) from the browser's HTTP requestBrowser
, DevicePlatform
, and OperatingSystem
: metadata about the browser, device platform, and operating system used for the testExecutionsPerSecond
and corresponding TestName
: metrics related to the test execution, including the number of executions per second and the name of the test (e.g., "Dex Replace Class")Can I help you with something specific from this data?