Created
November 30, 2012 16:49
-
-
Save biovisualize/4176908 to your computer and use it in GitHub Desktop.
Sankey Viz using R2D3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map | |
// Production steps of ECMA-262, Edition 5, 15.4.4.19 | |
// Reference: http://es5.github.com/#x15.4.4.19 | |
if (!Array.prototype.map) { | |
Array.prototype.map = function(callback, thisArg) { | |
var T, A, k; | |
if (this == null) { | |
throw new TypeError(" this is null or not defined"); | |
} | |
// 1. Let O be the result of calling ToObject passing the |this| value as the argument. | |
var O = Object(this); | |
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". | |
// 3. Let len be ToUint32(lenValue). | |
var len = O.length >>> 0; | |
// 4. If IsCallable(callback) is false, throw a TypeError exception. | |
// See: http://es5.github.com/#x9.11 | |
if ({}.toString.call(callback) != "[object Function]") { | |
throw new TypeError(callback + " is not a function"); | |
} | |
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined. | |
if (thisArg) { | |
T = thisArg; | |
} | |
// 6. Let A be a new array created as if by the expression new Array(len) where Array is | |
// the standard built-in constructor with that name and len is the value of len. | |
A = new Array(len); | |
// 7. Let k be 0 | |
k = 0; | |
// 8. Repeat, while k < len | |
while(k < len) { | |
var kValue, mappedValue; | |
// a. Let Pk be ToString(k). | |
// This is implicit for LHS operands of the in operator | |
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. | |
// This step can be combined with c | |
// c. If kPresent is true, then | |
if (k in O) { | |
// i. Let kValue be the result of calling the Get internal method of O with argument Pk. | |
kValue = O[ k ]; | |
// ii. Let mappedValue be the result of calling the Call internal method of callback | |
// with T as the this value and argument list containing kValue, k, and O. | |
mappedValue = callback.call(T, kValue, k, O); | |
// iii. Call the DefineOwnProperty internal method of A with arguments | |
// Pk, Property Descriptor {Value: mappedValue, Writable: true, Enumerable: true, Configurable: true}, | |
// and false. | |
// In browsers that support Object.defineProperty, use the following: | |
// Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true }); | |
// For best browser support, use the following: | |
A[ k ] = mappedValue; | |
} | |
// d. Increase k by 1. | |
k++; | |
} | |
// 9. return A | |
return A; | |
}; | |
} | |
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/forEach | |
// Production steps of ECMA-262, Edition 5, 15.4.4.18 | |
// Reference: http://es5.github.com/#x15.4.4.18 | |
if ( !Array.prototype.forEach ) { | |
Array.prototype.forEach = function( callback, thisArg ) { | |
var T, k; | |
if ( this == null ) { | |
throw new TypeError( " this is null or not defined" ); | |
} | |
// 1. Let O be the result of calling ToObject passing the |this| value as the argument. | |
var O = Object(this); | |
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". | |
// 3. Let len be ToUint32(lenValue). | |
var len = O.length >>> 0; // Hack to convert O.length to a UInt32 | |
// 4. If IsCallable(callback) is false, throw a TypeError exception. | |
// See: http://es5.github.com/#x9.11 | |
if ( {}.toString.call(callback) != "[object Function]" ) { | |
throw new TypeError( callback + " is not a function" ); | |
} | |
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined. | |
if ( thisArg ) { | |
T = thisArg; | |
} | |
// 6. Let k be 0 | |
k = 0; | |
// 7. Repeat, while k < len | |
while( k < len ) { | |
var kValue; | |
// a. Let Pk be ToString(k). | |
// This is implicit for LHS operands of the in operator | |
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. | |
// This step can be combined with c | |
// c. If kPresent is true, then | |
if ( k in O ) { | |
// i. Let kValue be the result of calling the Get internal method of O with argument Pk. | |
kValue = O[ k ]; | |
// ii. Call the Call internal method of callback with T as the this value and | |
// argument list containing kValue, k, and O. | |
callback.call( T, kValue, k, O ); | |
} | |
// d. Increase k by 1. | |
k++; | |
} | |
// 8. return undefined | |
}; | |
} | |
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf | |
if (!Array.prototype.indexOf) { | |
Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) { | |
"use strict"; | |
if (this == null) { | |
throw new TypeError(); | |
} | |
var t = Object(this); | |
var len = t.length >>> 0; | |
if (len === 0) { | |
return -1; | |
} | |
var n = 0; | |
if (arguments.length > 0) { | |
n = Number(arguments[1]); | |
if (n != n) { // shortcut for verifying if it's NaN | |
n = 0; | |
} else if (n != 0 && n != Infinity && n != -Infinity) { | |
n = (n > 0 || -1) * Math.floor(Math.abs(n)); | |
} | |
} | |
if (n >= len) { | |
return -1; | |
} | |
var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); | |
for (; k < len; k++) { | |
if (k in t && t[k] === searchElement) { | |
return k; | |
} | |
} | |
return -1; | |
} | |
} | |
// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter | |
if (!Array.prototype.filter) | |
{ | |
Array.prototype.filter = function(fun /*, thisp */) | |
{ | |
"use strict"; | |
if (this == null) | |
throw new TypeError(); | |
var t = Object(this); | |
var len = t.length >>> 0; | |
if (typeof fun != "function") | |
throw new TypeError(); | |
var res = []; | |
var thisp = arguments[1]; | |
for (var i = 0; i < len; i++) | |
{ | |
if (i in t) | |
{ | |
var val = t[i]; // in case fun mutates this | |
if (fun.call(thisp, val, i, t)) | |
res.push(val); | |
} | |
} | |
return res; | |
}; | |
} | |
if (!String.prototype.trim) { | |
String.prototype.trim = function() { | |
return this.replace(/^\s+|\s+$/g, ''); | |
}; | |
} | |
window.CSSStyleDeclaration = window.CSSStyleDeclaration || function() { } | |
// https://github.com/mbostock/d3/issues/199#issuecomment-1487129 | |
if (!CSSStyleDeclaration.prototype.getProperty) | |
CSSStyleDeclaration.prototype.getProperty = function(a) { | |
return this.getAttribute(a); | |
}; | |
if(!CSSStyleDeclaration.prototype.setProperty) | |
CSSStyleDeclaration.prototype.setProperty = function(a,b) { | |
return this.setAttribute(a,b); | |
}; | |
if(!CSSStyleDeclaration.prototype.removeProperty) | |
CSSStyleDeclaration.prototype.removeProperty = function(a) { | |
return this.removeAttribute(a); | |
}; | |
// https://github.com/mbostock/d3/issues/42#issuecomment-1265410 | |
if(!document.createElementNS) | |
document.createElementNS = function(ns, name) { | |
return document.createElement(name); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"nodes":[ | |
{"name":"Agricultural 'waste'"}, | |
{"name":"Bio-conversion"}, | |
{"name":"Liquid"}, | |
{"name":"Losses"}, | |
{"name":"Solid"}, | |
{"name":"Gas"}, | |
{"name":"Biofuel imports"}, | |
{"name":"Biomass imports"}, | |
{"name":"Coal imports"}, | |
{"name":"Coal"}, | |
{"name":"Coal reserves"}, | |
{"name":"District heating"}, | |
{"name":"Industry"}, | |
{"name":"Heating and cooling - commercial"}, | |
{"name":"Heating and cooling - homes"}, | |
{"name":"Electricity grid"}, | |
{"name":"Over generation / exports"}, | |
{"name":"H2 conversion"}, | |
{"name":"Road transport"}, | |
{"name":"Agriculture"}, | |
{"name":"Rail transport"}, | |
{"name":"Lighting & appliances - commercial"}, | |
{"name":"Lighting & appliances - homes"}, | |
{"name":"Gas imports"}, | |
{"name":"Ngas"}, | |
{"name":"Gas reserves"}, | |
{"name":"Thermal generation"}, | |
{"name":"Geothermal"}, | |
{"name":"H2"}, | |
{"name":"Hydro"}, | |
{"name":"International shipping"}, | |
{"name":"Domestic aviation"}, | |
{"name":"International aviation"}, | |
{"name":"National navigation"}, | |
{"name":"Marine algae"}, | |
{"name":"Nuclear"}, | |
{"name":"Oil imports"}, | |
{"name":"Oil"}, | |
{"name":"Oil reserves"}, | |
{"name":"Other waste"}, | |
{"name":"Pumped heat"}, | |
{"name":"Solar PV"}, | |
{"name":"Solar Thermal"}, | |
{"name":"Solar"}, | |
{"name":"Tidal"}, | |
{"name":"UK land based bioenergy"}, | |
{"name":"Wave"}, | |
{"name":"Wind"} | |
], | |
"links":[ | |
{"source":0,"target":1,"value":124.729}, | |
{"source":1,"target":2,"value":0.597}, | |
{"source":1,"target":3,"value":26.862}, | |
{"source":1,"target":4,"value":280.322}, | |
{"source":1,"target":5,"value":81.144}, | |
{"source":6,"target":2,"value":35}, | |
{"source":7,"target":4,"value":35}, | |
{"source":8,"target":9,"value":11.606}, | |
{"source":10,"target":9,"value":63.965}, | |
{"source":9,"target":4,"value":75.571}, | |
{"source":11,"target":12,"value":10.639}, | |
{"source":11,"target":13,"value":22.505}, | |
{"source":11,"target":14,"value":46.184}, | |
{"source":15,"target":16,"value":104.453}, | |
{"source":15,"target":14,"value":113.726}, | |
{"source":15,"target":17,"value":27.14}, | |
{"source":15,"target":12,"value":342.165}, | |
{"source":15,"target":18,"value":37.797}, | |
{"source":15,"target":19,"value":4.412}, | |
{"source":15,"target":13,"value":40.858}, | |
{"source":15,"target":3,"value":56.691}, | |
{"source":15,"target":20,"value":7.863}, | |
{"source":15,"target":21,"value":90.008}, | |
{"source":15,"target":22,"value":93.494}, | |
{"source":23,"target":24,"value":40.719}, | |
{"source":25,"target":24,"value":82.233}, | |
{"source":5,"target":13,"value":0.129}, | |
{"source":5,"target":3,"value":1.401}, | |
{"source":5,"target":26,"value":151.891}, | |
{"source":5,"target":19,"value":2.096}, | |
{"source":5,"target":12,"value":48.58}, | |
{"source":27,"target":15,"value":7.013}, | |
{"source":17,"target":28,"value":20.897}, | |
{"source":17,"target":3,"value":6.242}, | |
{"source":28,"target":18,"value":20.897}, | |
{"source":29,"target":15,"value":6.995}, | |
{"source":2,"target":12,"value":121.066}, | |
{"source":2,"target":30,"value":128.69}, | |
{"source":2,"target":18,"value":135.835}, | |
{"source":2,"target":31,"value":14.458}, | |
{"source":2,"target":32,"value":206.267}, | |
{"source":2,"target":19,"value":3.64}, | |
{"source":2,"target":33,"value":33.218}, | |
{"source":2,"target":20,"value":4.413}, | |
{"source":34,"target":1,"value":4.375}, | |
{"source":24,"target":5,"value":122.952}, | |
{"source":35,"target":26,"value":839.978}, | |
{"source":36,"target":37,"value":504.287}, | |
{"source":38,"target":37,"value":107.703}, | |
{"source":37,"target":2,"value":611.99}, | |
{"source":39,"target":4,"value":56.587}, | |
{"source":39,"target":1,"value":77.81}, | |
{"source":40,"target":14,"value":193.026}, | |
{"source":40,"target":13,"value":70.672}, | |
{"source":41,"target":15,"value":59.901}, | |
{"source":42,"target":14,"value":19.263}, | |
{"source":43,"target":42,"value":19.263}, | |
{"source":43,"target":41,"value":59.901}, | |
{"source":4,"target":19,"value":0.882}, | |
{"source":4,"target":26,"value":400.12}, | |
{"source":4,"target":12,"value":46.477}, | |
{"source":26,"target":15,"value":525.531}, | |
{"source":26,"target":3,"value":787.129}, | |
{"source":26,"target":11,"value":79.329}, | |
{"source":44,"target":15,"value":9.452}, | |
{"source":45,"target":1,"value":182.01}, | |
{"source":46,"target":15,"value":19.013}, | |
{"source":47,"target":15,"value":289.366} | |
]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(j){typeof define==="function"?define(function(){j()}):j()})(function(j){if(!Function.prototype.bind){var n=Array.prototype.slice;Function.prototype.bind=function(){function c(){if(this instanceof c){var d=Object.create(b.prototype);b.apply(d,a.concat(n.call(arguments)));return d}else return b.call.apply(b,a.concat(n.call(arguments)))}var b=this;if(typeof b.apply!=="function"||typeof b.call!=="function")return new TypeError;var a=n.call(arguments);c.length=typeof b==="function"?Math.max(b.length- | |
a.length,0):0;return c}}var l=Function.prototype.call,f=Object.prototype,h=l.bind(f.hasOwnProperty),q,r,o,p,m;if(m=h(f,"__defineGetter__"))q=l.bind(f.__defineGetter__),r=l.bind(f.__defineSetter__),o=l.bind(f.__lookupGetter__),p=l.bind(f.__lookupSetter__);if(!Array.isArray)Array.isArray=function(c){return Object.prototype.toString.call(c)==="[object Array]"};if(!Array.prototype.forEach)Array.prototype.forEach=function(c,b){for(var a=+this.length,d=0;d<a;d++)d in this&&c.call(b,this[d],d,this)};if(!Array.prototype.map)Array.prototype.map= | |
function(c,b){var a=+this.length;if(typeof c!=="function")throw new TypeError;for(var d=Array(a),e=0;e<a;e++)e in this&&(d[e]=c.call(b,this[e],e,this));return d};if(!Array.prototype.filter)Array.prototype.filter=function(c,b){for(var a=[],d=0;d<this.length;d++)c.call(b,this[d])&&a.push(this[d]);return a};if(!Array.prototype.every)Array.prototype.every=function(c,b){for(var a=0;a<this.length;a++)if(!c.call(b,this[a]))return!1;return!0};if(!Array.prototype.some)Array.prototype.some=function(c,b){for(var a= | |
0;a<this.length;a++)if(c.call(b,this[a]))return!0;return!1};if(!Array.prototype.reduce)Array.prototype.reduce=function(c){var b=+this.length;if(typeof c!=="function")throw new TypeError;if(b===0&&arguments.length===1)throw new TypeError;var a=0;if(arguments.length>=2)var d=arguments[1];else{do{if(a in this){d=this[a++];break}if(++a>=b)throw new TypeError;}while(1)}for(;a<b;a++)a in this&&(d=c.call(null,d,this[a],a,this));return d};if(!Array.prototype.reduceRight)Array.prototype.reduceRight=function(c){var b= | |
+this.length;if(typeof c!=="function")throw new TypeError;if(b===0&&arguments.length===1)throw new TypeError;var a;b-=1;if(arguments.length>=2)a=arguments[1];else{do{if(b in this){a=this[b--];break}if(--b<0)throw new TypeError;}while(1)}for(;b>=0;b--)b in this&&(a=c.call(null,a,this[b],b,this));return a};if(!Array.prototype.indexOf)Array.prototype.indexOf=function(c,b){var a=this.length;if(!a)return-1;var d=b||0;if(d>=a)return-1;for(d<0&&(d+=a);d<a;d++)if(d in this&&c===this[d])return d;return-1}; | |
if(!Array.prototype.lastIndexOf)Array.prototype.lastIndexOf=function(c,b){var a=this.length;if(!a)return-1;var d=b||a;d<0&&(d+=a);for(d=Math.min(d,a-1);d>=0;d--)if(d in this&&c===this[d])return d;return-1};if(!Object.getPrototypeOf)Object.getPrototypeOf=function(c){return c.__proto__||c.constructor.prototype};if(!Object.getOwnPropertyDescriptor)Object.getOwnPropertyDescriptor=function(c,b){if(typeof c!=="object"&&typeof c!=="function"||c===null)throw new TypeError("Object.getOwnPropertyDescriptor called on a non-object: "+ | |
c);if(!h(c,b))return j;var a,d,e;a={enumerable:!0,configurable:!0};if(m){var u=c.__proto__;c.__proto__=f;d=o(c,b);e=p(c,b);c.__proto__=u;if(d||e){if(d)a.get=d;if(e)a.set=e;return a}}a.value=c[b];return a};if(!Object.getOwnPropertyNames)Object.getOwnPropertyNames=function(c){return Object.keys(c)};if(!Object.create)Object.create=function(c,b){var a;if(c===null)a={__proto__:null};else{if(typeof c!=="object")throw new TypeError("typeof prototype["+typeof c+"] != 'object'");a=function(){};a.prototype= | |
c;a=new a;a.__proto__=c}typeof b!=="undefined"&&Object.defineProperties(a,b);return a};if(!Object.defineProperty)Object.defineProperty=function(c,b,a){if(typeof c!=="object"&&typeof c!=="function")throw new TypeError("Object.defineProperty called on non-object: "+c);if(typeof a!=="object"||a===null)throw new TypeError("Property description must be an object: "+a);if(h(a,"value")){if(m&&(o(c,b)||p(c,b)))c.__proto__=f,delete c[b];c[b]=a.value}else{if(!m)throw new TypeError("getters & setters can not be defined on this javascript engine"); | |
h(a,"get")&&q(c,b,a.get);h(a,"set")&&r(c,b,a.set)}return c};if(!Object.defineProperties)Object.defineProperties=function(c,b){for(var a in b)h(b,a)&&Object.defineProperty(c,a,b[a]);return c};if(!Object.seal)Object.seal=function(c){return c};if(!Object.freeze)Object.freeze=function(c){return c};try{Object.freeze(function(){})}catch(z){Object.freeze=function(c){return function(b){return typeof b==="function"?b:c(b)}}(Object.freeze)}if(!Object.preventExtensions)Object.preventExtensions=function(c){return c}; | |
if(!Object.isSealed)Object.isSealed=function(){return!1};if(!Object.isFrozen)Object.isFrozen=function(){return!1};if(!Object.isExtensible)Object.isExtensible=function(){return!0};if(!Object.keys){var s=!0,t=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],v=t.length,w;for(w in{toString:null})s=!1;Object.keys=function b(a){if(typeof a!=="object"&&typeof a!=="function"||a===null)throw new TypeError("Object.keys called on a non-object");var b= | |
[],d;for(d in a)h(a,d)&&b.push(d);if(s)for(d=0;d<v;d++){var e=t[d];h(a,e)&&b.push(e)}return b}}if(!Date.prototype.toISOString)Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+(this.getUTCMonth()+1)+"-"+this.getUTCDate()+"T"+this.getUTCHours()+":"+this.getUTCMinutes()+":"+this.getUTCSeconds()+"Z"};if(!Date.now)Date.now=function(){return(new Date).getTime()};if(!Date.prototype.toJSON)Date.prototype.toJSON=function(){if(typeof this.toISOString!=="function")throw new TypeError;return this.toISOString()}; | |
isNaN(Date.parse("T00:00"))&&(Date=function(b){var a=function(d,k,e,g,f,h,j){var i=arguments.length;if(this instanceof b)return i=i===1&&String(d)===d?new b(a.parse(d)):i>=7?new b(d,k,e,g,f,h,j):i>=6?new b(d,k,e,g,f,h):i>=5?new b(d,k,e,g,f):i>=4?new b(d,k,e,g):i>=3?new b(d,k,e):i>=2?new b(d,k):i>=1?new b(d):new b,i.constructor=a,i;return b.apply(this,arguments)},d=RegExp("^(?:((?:[+-]\\d\\d)?\\d\\d\\d\\d)(?:-(\\d\\d)(?:-(\\d\\d))?)?)?(?:T(\\d\\d):(\\d\\d)(?::(\\d\\d)(?:\\.(\\d\\d\\d))?)?)?(?:Z|([+-])(\\d\\d):(\\d\\d))?$"), | |
e;for(e in b)a[e]=b[e];a.now=b.now;a.UTC=b.UTC;a.prototype=b.prototype;a.prototype.constructor=a;a.parse=function(a){var e=d.exec(a);if(e){e.shift();for(var f=e[0]===j,g=0;g<10;g++)g!==7&&(e[g]=+(e[g]||(g<3?1:0)),g===1&&e[g]--);if(f)return((e[3]*60+e[4])*60+e[5])*1E3+e[6];f=(e[8]*60+e[9])*6E4;e[6]==="-"&&(f=-f);return b.UTC.apply(this,e.slice(0,7))+f}return b.parse.apply(this,arguments)};return a}(Date));if(!String.prototype.trim){var x=/^\s\s*/,y=/\s\s*$/;String.prototype.trim=function(){return String(this).replace(x, | |
"").replace(y,"")}}}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta http-equiv="X-UA-Compatible" content="IE=8" charset="utf-8"> | |
<title>Sankey Diagram</title> | |
<head> | |
<style> | |
@import url(style.css); | |
#chart { | |
height: 900px; | |
} | |
.node rect { | |
cursor: move; | |
fill-opacity: .9; | |
shape-rendering: crispEdges; | |
} | |
.node text { | |
pointer-events: none; | |
fill: #fff; | |
} | |
.link { | |
fill: none; | |
stroke: #fff; | |
stroke-opacity: .2; | |
} | |
g.node { | |
color: #fff; | |
} | |
.link:hover { | |
stroke-opacity: .7; | |
} | |
</style> | |
<script src="es5-shim.min.js" type="text/javascript"></script> | |
<script src="jquery.min.js" type="text/javascript"></script> | |
<script type="text/javascript" src="sizzle.js"></script> | |
<script type="text/javascript" src="compat.js"></script> | |
<script type="text/javascript" src="../../../r2d3-master/r2d3.v2.js"></script> | |
<script src="sankey.js"></script> | |
</head> | |
<body> | |
<div id="chart" style="background-color:#222222"/> | |
<script> | |
var margin = {top: 1, right: 1, bottom: 6, left: 1}, | |
width = 960 - margin.left - margin.right, | |
height = 900 - margin.top - margin.bottom; | |
var formatNumber = d3.format(",.0f"), | |
format = function(d) { return formatNumber(d) + " TWh"; }, | |
color = d3.scale.category20(); | |
var svg = d3.select("#chart").append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
var sankey = d3.sankey() | |
.nodeWidth(15) | |
.nodePadding(10) | |
.size([width, height]); | |
var path = sankey.link(); | |
d3.json("energy.json", function(energy) { | |
sankey | |
.nodes(energy.nodes) | |
.links(energy.links) | |
.layout(32); | |
var link = svg.append("g").selectAll(".link") | |
.data(energy.links) | |
.enter().append("path") | |
.attr("class", "link") | |
.attr("d", path) | |
.style("stroke-width", function(d) { return Math.max(1, d.dy); }) | |
.sort(function(a, b) { return b.dy - a.dy; }); | |
// link.append("title") | |
// .text(function(d) { return d.source.name + " → " + d.target.name + "\n" + format(d.value); }); | |
var node = svg.append("g").selectAll(".node") | |
.data(energy.nodes) | |
.enter().append("g") | |
.attr("class", "node") | |
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) | |
;//.call(d3.behavior.drag() | |
// .origin(function(d) { return d; }) | |
// .on("dragstart", function() { this.parentNode.appendChild(this); }) | |
// .on("drag", dragmove)); | |
node.append("rect") | |
.attr("height", function(d) { return d.dy; }) | |
.attr("width", sankey.nodeWidth()) | |
.style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); }) | |
.style("stroke", function(d) { return d3.rgb(d.color).darker(2); }) | |
;//.append("title") | |
// .text(function(d) { return d.name + "\n" + format(d.value); }); | |
node.append("text") | |
.attr("x", -6) | |
.attr("y", function(d) { return d.dy / 2; }) | |
.attr("dy", ".35em") | |
.attr("text-anchor", "end") | |
.attr("transform", null) | |
.text(function(d) { return d.name; }) | |
.filter(function(d) { return d.x < width / 2; }) | |
.attr("x", 6 + sankey.nodeWidth()) | |
.attr("text-anchor", "start"); | |
function dragmove(d) { | |
d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")"); | |
sankey.relayout(); | |
link.attr("d", path); | |
} | |
}); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d3.sankey = function() { | |
var sankey = {}, | |
nodeWidth = 24, | |
nodePadding = 8, | |
size = [1, 1], | |
nodes = [], | |
links = []; | |
sankey.nodeWidth = function(_) { | |
if (!arguments.length) return nodeWidth; | |
nodeWidth = +_; | |
return sankey; | |
}; | |
sankey.nodePadding = function(_) { | |
if (!arguments.length) return nodePadding; | |
nodePadding = +_; | |
return sankey; | |
}; | |
sankey.nodes = function(_) { | |
if (!arguments.length) return nodes; | |
nodes = _; | |
return sankey; | |
}; | |
sankey.links = function(_) { | |
if (!arguments.length) return links; | |
links = _; | |
return sankey; | |
}; | |
sankey.size = function(_) { | |
if (!arguments.length) return size; | |
size = _; | |
return sankey; | |
}; | |
sankey.layout = function(iterations) { | |
computeNodeLinks(); | |
computeNodeValues(); | |
computeNodeBreadths(); | |
computeNodeDepths(iterations); | |
computeLinkDepths(); | |
return sankey; | |
}; | |
sankey.relayout = function() { | |
computeLinkDepths(); | |
return sankey; | |
}; | |
sankey.link = function() { | |
var curvature = .5; | |
function link(d) { | |
var x0 = d.source.x + d.source.dx, | |
x1 = d.target.x, | |
xi = d3.interpolateNumber(x0, x1), | |
x2 = xi(curvature), | |
x3 = xi(1 - curvature), | |
y0 = d.source.y + d.sy + d.dy / 2, | |
y1 = d.target.y + d.ty + d.dy / 2; | |
return "M" + x0 + "," + y0 | |
+ "C" + x2 + "," + y0 | |
+ " " + x3 + "," + y1 | |
+ " " + x1 + "," + y1; | |
} | |
link.curvature = function(_) { | |
if (!arguments.length) return curvature; | |
curvature = +_; | |
return link; | |
}; | |
return link; | |
}; | |
// Populate the sourceLinks and targetLinks for each node. | |
// Also, if the source and target are not objects, assume they are indices. | |
function computeNodeLinks() { | |
nodes.forEach(function(node) { | |
node.sourceLinks = []; | |
node.targetLinks = []; | |
}); | |
links.forEach(function(link) { | |
var source = link.source, | |
target = link.target; | |
if (typeof source === "number") source = link.source = nodes[link.source]; | |
if (typeof target === "number") target = link.target = nodes[link.target]; | |
if (typeof source === "string") source = link.source = findNode(link.source); | |
if (typeof target === "string") target = link.target = findNode(link.target); | |
source.sourceLinks.push(link); | |
target.targetLinks.push(link); | |
}); | |
} | |
// Determine the Id of a node taking into account the type | |
function getNodeId(node) { | |
return node.dataType + "_" + node.Uid; | |
} | |
// Find a node with the given id | |
function findNode(id) { | |
for(var i = 0; i < nodes.length; i++) { | |
if (nodes[i].Uid && nodes[i].dataType && getNodeId(nodes[i]) == id) { | |
return nodes[i]; | |
} | |
} | |
// If we can't find a Node let's just stop and error | |
throw "Unable to find a node with the correct ID: " + id; | |
} | |
// Compute the value (size) of each node by summing the associated links. | |
function computeNodeValues() { | |
nodes.forEach(function(node) { | |
node.value = Math.max( | |
d3.sum(node.sourceLinks, value), | |
d3.sum(node.targetLinks, value) | |
); | |
}); | |
} | |
// Iteratively assign the breadth (x-position) for each node. | |
// Nodes are assigned the maximum breadth of incoming neighbors plus one; | |
// nodes with no incoming links are assigned breadth zero, while | |
// nodes with no outgoing links are assigned the maximum breadth. | |
function computeNodeBreadths() { | |
var remainingNodes = nodes, | |
nextNodes, | |
x = 0; | |
while (remainingNodes.length) { | |
nextNodes = []; | |
remainingNodes.forEach(function(node) { | |
node.x = x; | |
node.dx = nodeWidth; | |
node.sourceLinks.forEach(function(link) { | |
nextNodes.push(link.target); | |
}); | |
}); | |
remainingNodes = nextNodes; | |
++x; | |
} | |
// | |
moveSinksRight(x); | |
scaleNodeBreadths((width - nodeWidth) / (x - 1)); | |
} | |
function moveSourcesRight() { | |
nodes.forEach(function(node) { | |
if (!node.targetLinks.length) { | |
node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1; | |
} | |
}); | |
} | |
function moveSinksRight(x) { | |
nodes.forEach(function(node) { | |
if (!node.sourceLinks.length) { | |
node.x = x - 1; | |
} | |
}); | |
} | |
function scaleNodeBreadths(kx) { | |
nodes.forEach(function(node) { | |
node.x *= kx; | |
}); | |
} | |
function computeNodeDepths(iterations) { | |
var nodesByBreadth = d3.nest() | |
.key(function(d) { return d.x; }) | |
.sortKeys(d3.ascending) | |
.entries(nodes) | |
.map(function(d) { return d.values; }); | |
// | |
initializeNodeDepth(); | |
resolveCollisions(); | |
for (var alpha = 1; iterations > 0; --iterations) { | |
relaxRightToLeft(alpha *= .99); | |
resolveCollisions(); | |
relaxLeftToRight(alpha); | |
resolveCollisions(); | |
} | |
function initializeNodeDepth() { | |
var ky = d3.min(nodesByBreadth, function(nodes) { | |
return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value); | |
}); | |
nodesByBreadth.forEach(function(nodes) { | |
nodes.forEach(function(node, i) { | |
node.y = i; | |
node.dy = node.value * ky; | |
}); | |
}); | |
links.forEach(function(link) { | |
link.dy = link.value * ky; | |
}); | |
} | |
function relaxLeftToRight(alpha) { | |
nodesByBreadth.forEach(function(nodes, breadth) { | |
nodes.forEach(function(node) { | |
if (node.targetLinks.length) { | |
var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value); | |
node.y += (y - center(node)) * alpha; | |
} | |
}); | |
}); | |
function weightedSource(link) { | |
return center(link.source) * link.value; | |
} | |
} | |
function relaxRightToLeft(alpha) { | |
nodesByBreadth.slice().reverse().forEach(function(nodes) { | |
nodes.forEach(function(node) { | |
if (node.sourceLinks.length) { | |
var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value); | |
node.y += (y - center(node)) * alpha; | |
} | |
}); | |
}); | |
function weightedTarget(link) { | |
return center(link.target) * link.value; | |
} | |
} | |
function resolveCollisions() { | |
nodesByBreadth.forEach(function(nodes) { | |
var node, | |
dy, | |
y0 = 0, | |
n = nodes.length, | |
i; | |
// Push any overlapping nodes down. | |
nodes.sort(ascendingDepth); | |
for (i = 0; i < n; ++i) { | |
node = nodes[i]; | |
dy = y0 - node.y; | |
if (dy > 0) node.y += dy; | |
y0 = node.y + node.dy + nodePadding; | |
} | |
// If the bottommost node goes outside the bounds, push it back up. | |
dy = y0 - nodePadding - size[1]; | |
if (dy > 0) { | |
y0 = node.y -= dy; | |
// Push any overlapping nodes back up. | |
for (i = n - 2; i >= 0; --i) { | |
node = nodes[i]; | |
dy = node.y + node.dy + nodePadding - y0; | |
if (dy > 0) node.y -= dy; | |
y0 = node.y; | |
} | |
} | |
}); | |
} | |
function ascendingDepth(a, b) { | |
return a.y - b.y; | |
} | |
} | |
function computeLinkDepths() { | |
nodes.forEach(function(node) { | |
node.sourceLinks.sort(ascendingTargetDepth); | |
node.targetLinks.sort(ascendingSourceDepth); | |
}); | |
nodes.forEach(function(node) { | |
var sy = 0, ty = 0; | |
node.sourceLinks.forEach(function(link) { | |
link.sy = sy; | |
sy += link.dy; | |
}); | |
node.targetLinks.forEach(function(link) { | |
link.ty = ty; | |
ty += link.dy; | |
}); | |
}); | |
function ascendingSourceDepth(a, b) { | |
return a.source.y - b.source.y; | |
} | |
function ascendingTargetDepth(a, b) { | |
return a.target.y - b.target.y; | |
} | |
} | |
function center(node) { | |
return node.y + node.dy / 2; | |
} | |
function value(link) { | |
return link.value; | |
} | |
return sankey; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* Sizzle CSS Selector Engine | |
* Copyright 2012 jQuery Foundation and other contributors | |
* Released under the MIT license | |
* http://sizzlejs.com/ | |
*/ | |
(function( window, undefined ) { | |
var dirruns, | |
cachedruns, | |
assertGetIdNotName, | |
Expr, | |
getText, | |
isXML, | |
contains, | |
compile, | |
sortOrder, | |
hasDuplicate, | |
baseHasDuplicate = true, | |
strundefined = "undefined", | |
expando = ( "sizcache" + Math.random() ).replace( ".", "" ), | |
document = window.document, | |
docElem = document.documentElement, | |
done = 0, | |
slice = [].slice, | |
push = [].push, | |
// Augment a function for special use by Sizzle | |
markFunction = function( fn, value ) { | |
fn[ expando ] = value || true; | |
return fn; | |
}, | |
createCache = function() { | |
var cache = {}, | |
keys = []; | |
return markFunction(function( key, value ) { | |
// Only keep the most recent entries | |
if ( keys.push( key ) > Expr.cacheLength ) { | |
delete cache[ keys.shift() ]; | |
} | |
return (cache[ key ] = value); | |
}, cache ); | |
}, | |
classCache = createCache(), | |
tokenCache = createCache(), | |
compilerCache = createCache(), | |
// Regex | |
// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace | |
whitespace = "[\\x20\\t\\r\\n\\f]", | |
// http://www.w3.org/TR/css3-syntax/#characters | |
characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+", | |
// Loosely modeled on CSS identifier characters | |
// An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors) | |
// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier | |
identifier = characterEncoding.replace( "w", "w#" ), | |
// Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors | |
operators = "([*^$|!~]?=)", | |
attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + | |
"*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", | |
// Prefer arguments not in parens/brackets, | |
// then attribute selectors and non-pseudos (denoted by :), | |
// then anything else | |
// These preferences are here to reduce the number of selectors | |
// needing tokenize in the PSEUDO preFilter | |
pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)", | |
// For matchExpr.POS and matchExpr.needsContext | |
pos = ":(nth|eq|gt|lt|first|last|even|odd)(?:\\(((?:-\\d)?\\d*)\\)|)(?=[^-]|$)", | |
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter | |
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), | |
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), | |
rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ), | |
rpseudo = new RegExp( pseudos ), | |
// Easily-parseable/retrievable ID or TAG or CLASS selectors | |
rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/, | |
rnot = /^:not/, | |
rsibling = /[\x20\t\r\n\f]*[+~]/, | |
rendsWithNot = /:not\($/, | |
rheader = /h\d/i, | |
rinputs = /input|select|textarea|button/i, | |
rbackslash = /\\(?!\\)/g, | |
matchExpr = { | |
"ID": new RegExp( "^#(" + characterEncoding + ")" ), | |
"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), | |
"NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ), | |
"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), | |
"ATTR": new RegExp( "^" + attributes ), | |
"PSEUDO": new RegExp( "^" + pseudos ), | |
"CHILD": new RegExp( "^:(only|nth|last|first)-child(?:\\(" + whitespace + | |
"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + | |
"*(\\d+)|))" + whitespace + "*\\)|)", "i" ), | |
"POS": new RegExp( pos, "ig" ), | |
// For use in libraries implementing .is() | |
"needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" ) | |
}, | |
// Support | |
// Used for testing something on an element | |
assert = function( fn ) { | |
var div = document.createElement("div"); | |
try { | |
return fn( div ); | |
} catch (e) { | |
return false; | |
} finally { | |
// release memory in IE | |
div = null; | |
} | |
}, | |
// Check if getElementsByTagName("*") returns only elements | |
assertTagNameNoComments = assert(function( div ) { | |
div.appendChild( document.createComment("") ); | |
return !div.getElementsByTagName("*").length; | |
}), | |
// Check if getAttribute returns normalized href attributes | |
assertHrefNotNormalized = assert(function( div ) { | |
div.innerHTML = "<a href='#'></a>"; | |
return div.firstChild && typeof div.firstChild.getAttribute !== strundefined && | |
div.firstChild.getAttribute("href") === "#"; | |
}), | |
// Check if attributes should be retrieved by attribute nodes | |
assertAttributes = assert(function( div ) { | |
div.innerHTML = "<select></select>"; | |
var type = typeof div.lastChild.getAttribute("multiple"); | |
// IE8 returns a string for some attributes even when not present | |
return type !== "boolean" && type !== "string"; | |
}), | |
// Check if getElementsByClassName can be trusted | |
assertUsableClassName = assert(function( div ) { | |
// Opera can't find a second classname (in 9.6) | |
div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>"; | |
if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) { | |
return false; | |
} | |
// Safari 3.2 caches class attributes and doesn't catch changes | |
div.lastChild.className = "e"; | |
return div.getElementsByClassName("e").length === 2; | |
}), | |
// Check if getElementById returns elements by name | |
// Check if getElementsByName privileges form controls or returns elements by ID | |
assertUsableName = assert(function( div ) { | |
// Inject content | |
div.id = expando + 0; | |
div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>"; | |
docElem.insertBefore( div, docElem.firstChild ); | |
// Test | |
var pass = document.getElementsByName && | |
// buggy browsers will return fewer than the correct 2 | |
document.getElementsByName( expando ).length === 2 + | |
// buggy browsers will return more than the correct 0 | |
document.getElementsByName( expando + 0 ).length; | |
assertGetIdNotName = !document.getElementById( expando ); | |
// Cleanup | |
docElem.removeChild( div ); | |
return pass; | |
}); | |
// If slice is not available, provide a backup | |
try { | |
slice.call( docElem.childNodes, 0 )[0].nodeType; | |
} catch ( e ) { | |
slice = function( i ) { | |
var elem, results = []; | |
for ( ; (elem = this[i]); i++ ) { | |
results.push( elem ); | |
} | |
return results; | |
}; | |
} | |
function Sizzle( selector, context, results, seed ) { | |
results = results || []; | |
context = context || document; | |
var match, elem, xml, m, | |
nodeType = context.nodeType; | |
if ( nodeType !== 1 && nodeType !== 9 ) { | |
return []; | |
} | |
if ( !selector || typeof selector !== "string" ) { | |
return results; | |
} | |
xml = isXML( context ); | |
if ( !xml && !seed ) { | |
if ( (match = rquickExpr.exec( selector )) ) { | |
// Speed-up: Sizzle("#ID") | |
if ( (m = match[1]) ) { | |
if ( nodeType === 9 ) { | |
elem = context.getElementById( m ); | |
// Check parentNode to catch when Blackberry 4.6 returns | |
// nodes that are no longer in the document #6963 | |
if ( elem && elem.parentNode ) { | |
// Handle the case where IE, Opera, and Webkit return items | |
// by name instead of ID | |
if ( elem.id === m ) { | |
results.push( elem ); | |
return results; | |
} | |
} else { | |
return results; | |
} | |
} else { | |
// Context is not a document | |
if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && | |
contains( context, elem ) && elem.id === m ) { | |
results.push( elem ); | |
return results; | |
} | |
} | |
// Speed-up: Sizzle("TAG") | |
} else if ( match[2] ) { | |
push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) ); | |
return results; | |
// Speed-up: Sizzle(".CLASS") | |
} else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) { | |
push.apply( results, slice.call(context.getElementsByClassName( m ), 0) ); | |
return results; | |
} | |
} | |
} | |
// All others | |
return select( selector, context, results, seed, xml ); | |
} | |
Sizzle.matches = function( expr, elements ) { | |
return Sizzle( expr, null, null, elements ); | |
}; | |
Sizzle.matchesSelector = function( elem, expr ) { | |
return Sizzle( expr, null, null, [ elem ] ).length > 0; | |
}; | |
// Returns a function to use in pseudos for input types | |
function createInputPseudo( type ) { | |
return function( elem ) { | |
var name = elem.nodeName.toLowerCase(); | |
return name === "input" && elem.type === type; | |
}; | |
} | |
// Returns a function to use in pseudos for buttons | |
function createButtonPseudo( type ) { | |
return function( elem ) { | |
var name = elem.nodeName.toLowerCase(); | |
return (name === "input" || name === "button") && elem.type === type; | |
}; | |
} | |
/** | |
* Utility function for retrieving the text value of an array of DOM nodes | |
* @param {Array|Element} elem | |
*/ | |
getText = Sizzle.getText = function( elem ) { | |
var node, | |
ret = "", | |
i = 0, | |
nodeType = elem.nodeType; | |
if ( nodeType ) { | |
if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { | |
// Use textContent for elements | |
// innerText usage removed for consistency of new lines (see #11153) | |
if ( typeof elem.textContent === "string" ) { | |
return elem.textContent; | |
} else { | |
// Traverse its children | |
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { | |
ret += getText( elem ); | |
} | |
} | |
} else if ( nodeType === 3 || nodeType === 4 ) { | |
return elem.nodeValue; | |
} | |
// Do not include comment or processing instruction nodes | |
} else { | |
// If no nodeType, this is expected to be an array | |
for ( ; (node = elem[i]); i++ ) { | |
// Do not traverse comment nodes | |
ret += getText( node ); | |
} | |
} | |
return ret; | |
}; | |
isXML = Sizzle.isXML = function isXML( elem ) { | |
// documentElement is verified for cases where it doesn't yet exist | |
// (such as loading iframes in IE - #4833) | |
var documentElement = elem && (elem.ownerDocument || elem).documentElement; | |
return documentElement ? documentElement.nodeName !== "HTML" : false; | |
}; | |
// Element contains another | |
contains = Sizzle.contains = docElem.contains ? | |
function( a, b ) { | |
var adown = a.nodeType === 9 ? a.documentElement : a, | |
bup = b && b.parentNode; | |
return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) ); | |
} : | |
docElem.compareDocumentPosition ? | |
function( a, b ) { | |
return b && !!( a.compareDocumentPosition( b ) & 16 ); | |
} : | |
function( a, b ) { | |
while ( (b = b.parentNode) ) { | |
if ( b === a ) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
Sizzle.attr = function( elem, name ) { | |
var attr, | |
xml = isXML( elem ); | |
if ( !xml ) { | |
name = name.toLowerCase(); | |
} | |
if ( Expr.attrHandle[ name ] ) { | |
return Expr.attrHandle[ name ]( elem ); | |
} | |
if ( assertAttributes || xml ) { | |
return elem.getAttribute( name ); | |
} | |
attr = elem.getAttributeNode( name ); | |
return attr ? | |
typeof elem[ name ] === "boolean" ? | |
elem[ name ] ? name : null : | |
attr.specified ? attr.value : null : | |
null; | |
}; | |
Expr = Sizzle.selectors = { | |
// Can be adjusted by the user | |
cacheLength: 50, | |
createPseudo: markFunction, | |
match: matchExpr, | |
order: new RegExp( "ID|TAG" + | |
(assertUsableName ? "|NAME" : "") + | |
(assertUsableClassName ? "|CLASS" : "") | |
), | |
// IE6/7 return a modified href | |
attrHandle: assertHrefNotNormalized ? | |
{} : | |
{ | |
"href": function( elem ) { | |
return elem.getAttribute( "href", 2 ); | |
}, | |
"type": function( elem ) { | |
return elem.getAttribute("type"); | |
} | |
}, | |
find: { | |
"ID": assertGetIdNotName ? | |
function( id, context, xml ) { | |
if ( typeof context.getElementById !== strundefined && !xml ) { | |
var m = context.getElementById( id ); | |
// Check parentNode to catch when Blackberry 4.6 returns | |
// nodes that are no longer in the document #6963 | |
return m && m.parentNode ? [m] : []; | |
} | |
} : | |
function( id, context, xml ) { | |
if ( typeof context.getElementById !== strundefined && !xml ) { | |
var m = context.getElementById( id ); | |
return m ? | |
m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ? | |
[m] : | |
undefined : | |
[]; | |
} | |
}, | |
"TAG": assertTagNameNoComments ? | |
function( tag, context ) { | |
if ( typeof context.getElementsByTagName !== strundefined ) { | |
return context.getElementsByTagName( tag ); | |
} | |
} : | |
function( tag, context ) { | |
var results = context.getElementsByTagName( tag ); | |
// Filter out possible comments | |
if ( tag === "*" ) { | |
var elem, | |
tmp = [], | |
i = 0; | |
for ( ; (elem = results[i]); i++ ) { | |
if ( elem.nodeType === 1 ) { | |
tmp.push( elem ); | |
} | |
} | |
return tmp; | |
} | |
return results; | |
}, | |
"NAME": function( tag, context ) { | |
if ( typeof context.getElementsByName !== strundefined ) { | |
return context.getElementsByName( name ); | |
} | |
}, | |
"CLASS": function( className, context, xml ) { | |
if ( typeof context.getElementsByClassName !== strundefined && !xml ) { | |
return context.getElementsByClassName( className ); | |
} | |
} | |
}, | |
relative: { | |
">": { dir: "parentNode", first: true }, | |
" ": { dir: "parentNode" }, | |
"+": { dir: "previousSibling", first: true }, | |
"~": { dir: "previousSibling" } | |
}, | |
preFilter: { | |
"ATTR": function( match ) { | |
match[1] = match[1].replace( rbackslash, "" ); | |
// Move the given value to match[3] whether quoted or unquoted | |
match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" ); | |
if ( match[2] === "~=" ) { | |
match[3] = " " + match[3] + " "; | |
} | |
return match.slice( 0, 4 ); | |
}, | |
"CHILD": function( match ) { | |
/* matches from matchExpr.CHILD | |
1 type (only|nth|...) | |
2 argument (even|odd|\d*|\d*n([+-]\d+)?|...) | |
3 xn-component of xn+y argument ([+-]?\d*n|) | |
4 sign of xn-component | |
5 x of xn-component | |
6 sign of y-component | |
7 y of y-component | |
*/ | |
match[1] = match[1].toLowerCase(); | |
if ( match[1] === "nth" ) { | |
// nth-child requires argument | |
if ( !match[2] ) { | |
Sizzle.error( match[0] ); | |
} | |
// numeric x and y parameters for Expr.filter.CHILD | |
// remember that false/true cast respectively to 0/1 | |
match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) ); | |
match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" ); | |
// other types prohibit arguments | |
} else if ( match[2] ) { | |
Sizzle.error( match[0] ); | |
} | |
return match; | |
}, | |
"PSEUDO": function( match, context, xml ) { | |
var unquoted, excess; | |
if ( matchExpr["CHILD"].test( match[0] ) ) { | |
return null; | |
} | |
if ( match[3] ) { | |
match[2] = match[3]; | |
} else if ( (unquoted = match[4]) ) { | |
// Only check arguments that contain a pseudo | |
if ( rpseudo.test(unquoted) && | |
// Get excess from tokenize (recursively) | |
(excess = tokenize( unquoted, context, xml, true )) && | |
// advance to the next closing parenthesis | |
(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { | |
// excess is a negative index | |
unquoted = unquoted.slice( 0, excess ); | |
match[0] = match[0].slice( 0, excess ); | |
} | |
match[2] = unquoted; | |
} | |
// Return only captures needed by the pseudo filter method (type and argument) | |
return match.slice( 0, 3 ); | |
} | |
}, | |
filter: { | |
"ID": assertGetIdNotName ? | |
function( id ) { | |
id = id.replace( rbackslash, "" ); | |
return function( elem ) { | |
return elem.getAttribute("id") === id; | |
}; | |
} : | |
function( id ) { | |
id = id.replace( rbackslash, "" ); | |
return function( elem ) { | |
var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); | |
return node && node.value === id; | |
}; | |
}, | |
"TAG": function( nodeName ) { | |
if ( nodeName === "*" ) { | |
return function() { return true; }; | |
} | |
nodeName = nodeName.replace( rbackslash, "" ).toLowerCase(); | |
return function( elem ) { | |
return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; | |
}; | |
}, | |
"CLASS": function( className ) { | |
var pattern = classCache[ expando ][ className ]; | |
if ( !pattern ) { | |
pattern = classCache( className, new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)") ); | |
} | |
return function( elem ) { | |
return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" ); | |
}; | |
}, | |
"ATTR": function( name, operator, check ) { | |
if ( !operator ) { | |
return function( elem ) { | |
return Sizzle.attr( elem, name ) != null; | |
}; | |
} | |
return function( elem ) { | |
var result = Sizzle.attr( elem, name ), | |
value = result + ""; | |
if ( result == null ) { | |
return operator === "!="; | |
} | |
switch ( operator ) { | |
case "=": | |
return value === check; | |
case "!=": | |
return value !== check; | |
case "^=": | |
return check && value.indexOf( check ) === 0; | |
case "*=": | |
return check && value.indexOf( check ) > -1; | |
case "$=": | |
return check && value.substr( value.length - check.length ) === check; | |
case "~=": | |
return ( " " + value + " " ).indexOf( check ) > -1; | |
case "|=": | |
return value === check || value.substr( 0, check.length + 1 ) === check + "-"; | |
} | |
}; | |
}, | |
"CHILD": function( type, argument, first, last ) { | |
if ( type === "nth" ) { | |
var doneName = done++; | |
return function( elem ) { | |
var parent, diff, | |
count = 0, | |
node = elem; | |
if ( first === 1 && last === 0 ) { | |
return true; | |
} | |
parent = elem.parentNode; | |
if ( parent && (parent[ expando ] !== doneName || !elem.sizset) ) { | |
for ( node = parent.firstChild; node; node = node.nextSibling ) { | |
if ( node.nodeType === 1 ) { | |
node.sizset = ++count; | |
if ( node === elem ) { | |
break; | |
} | |
} | |
} | |
parent[ expando ] = doneName; | |
} | |
diff = elem.sizset - last; | |
if ( first === 0 ) { | |
return diff === 0; | |
} else { | |
return ( diff % first === 0 && diff / first >= 0 ); | |
} | |
}; | |
} | |
return function( elem ) { | |
var node = elem; | |
switch ( type ) { | |
case "only": | |
case "first": | |
while ( (node = node.previousSibling) ) { | |
if ( node.nodeType === 1 ) { | |
return false; | |
} | |
} | |
if ( type === "first" ) { | |
return true; | |
} | |
node = elem; | |
/* falls through */ | |
case "last": | |
while ( (node = node.nextSibling) ) { | |
if ( node.nodeType === 1 ) { | |
return false; | |
} | |
} | |
return true; | |
} | |
}; | |
}, | |
"PSEUDO": function( pseudo, argument, context, xml ) { | |
// pseudo-class names are case-insensitive | |
// http://www.w3.org/TR/selectors/#pseudo-classes | |
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters | |
var args, | |
fn = Expr.pseudos[ pseudo ] || Expr.pseudos[ pseudo.toLowerCase() ]; | |
if ( !fn ) { | |
Sizzle.error( "unsupported pseudo: " + pseudo ); | |
} | |
// The user may use createPseudo to indicate that | |
// arguments are needed to create the filter function | |
// just as Sizzle does | |
if ( !fn[ expando ] ) { | |
if ( fn.length > 1 ) { | |
args = [ pseudo, pseudo, "", argument ]; | |
return function( elem ) { | |
return fn( elem, 0, args ); | |
}; | |
} | |
return fn; | |
} | |
return fn( argument, context, xml ); | |
} | |
}, | |
pseudos: { | |
"not": markFunction(function( selector, context, xml ) { | |
// Trim the selector passed to compile | |
// to avoid treating leading and trailing | |
// spaces as combinators | |
var matcher = compile( selector.replace( rtrim, "$1" ), context, xml ); | |
return function( elem ) { | |
return !matcher( elem ); | |
}; | |
}), | |
"enabled": function( elem ) { | |
return elem.disabled === false; | |
}, | |
"disabled": function( elem ) { | |
return elem.disabled === true; | |
}, | |
"checked": function( elem ) { | |
// In CSS3, :checked should return both checked and selected elements | |
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked | |
var nodeName = elem.nodeName.toLowerCase(); | |
return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); | |
}, | |
"selected": function( elem ) { | |
// Accessing this property makes selected-by-default | |
// options in Safari work properly | |
if ( elem.parentNode ) { | |
elem.parentNode.selectedIndex; | |
} | |
return elem.selected === true; | |
}, | |
"parent": function( elem ) { | |
return !Expr.pseudos["empty"]( elem ); | |
}, | |
"empty": function( elem ) { | |
// http://www.w3.org/TR/selectors/#empty-pseudo | |
// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), | |
// not comment, processing instructions, or others | |
// Thanks to Diego Perini for the nodeName shortcut | |
// Greater than "@" means alpha characters (specifically not starting with "#" or "?") | |
var nodeType; | |
elem = elem.firstChild; | |
while ( elem ) { | |
if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) { | |
return false; | |
} | |
elem = elem.nextSibling; | |
} | |
return true; | |
}, | |
"contains": markFunction(function( text ) { | |
return function( elem ) { | |
return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; | |
}; | |
}), | |
"has": markFunction(function( selector ) { | |
return function( elem ) { | |
return Sizzle( selector, elem ).length > 0; | |
}; | |
}), | |
"header": function( elem ) { | |
return rheader.test( elem.nodeName ); | |
}, | |
"text": function( elem ) { | |
var type, attr; | |
// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) | |
// use getAttribute instead to test this case | |
return elem.nodeName.toLowerCase() === "input" && | |
(type = elem.type) === "text" && | |
( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type ); | |
}, | |
// Input types | |
"radio": createInputPseudo("radio"), | |
"checkbox": createInputPseudo("checkbox"), | |
"file": createInputPseudo("file"), | |
"password": createInputPseudo("password"), | |
"image": createInputPseudo("image"), | |
"submit": createButtonPseudo("submit"), | |
"reset": createButtonPseudo("reset"), | |
"button": function( elem ) { | |
var name = elem.nodeName.toLowerCase(); | |
return name === "input" && elem.type === "button" || name === "button"; | |
}, | |
"input": function( elem ) { | |
return rinputs.test( elem.nodeName ); | |
}, | |
"focus": function( elem ) { | |
var doc = elem.ownerDocument; | |
return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href); | |
}, | |
"active": function( elem ) { | |
return elem === elem.ownerDocument.activeElement; | |
} | |
}, | |
setFilters: { | |
"first": function( elements, argument, not ) { | |
return not ? elements.slice( 1 ) : [ elements[0] ]; | |
}, | |
"last": function( elements, argument, not ) { | |
var elem = elements.pop(); | |
return not ? elements : [ elem ]; | |
}, | |
"even": function( elements, argument, not ) { | |
var results = [], | |
i = not ? 1 : 0, | |
len = elements.length; | |
for ( ; i < len; i = i + 2 ) { | |
results.push( elements[i] ); | |
} | |
return results; | |
}, | |
"odd": function( elements, argument, not ) { | |
var results = [], | |
i = not ? 0 : 1, | |
len = elements.length; | |
for ( ; i < len; i = i + 2 ) { | |
results.push( elements[i] ); | |
} | |
return results; | |
}, | |
"lt": function( elements, argument, not ) { | |
return not ? elements.slice( +argument ) : elements.slice( 0, +argument ); | |
}, | |
"gt": function( elements, argument, not ) { | |
return not ? elements.slice( 0, +argument + 1 ) : elements.slice( +argument + 1 ); | |
}, | |
"eq": function( elements, argument, not ) { | |
var elem = elements.splice( +argument, 1 ); | |
return not ? elements : elem; | |
} | |
} | |
}; | |
function siblingCheck( a, b, ret ) { | |
if ( a === b ) { | |
return ret; | |
} | |
var cur = a.nextSibling; | |
while ( cur ) { | |
if ( cur === b ) { | |
return -1; | |
} | |
cur = cur.nextSibling; | |
} | |
return 1; | |
} | |
sortOrder = docElem.compareDocumentPosition ? | |
function( a, b ) { | |
if ( a === b ) { | |
hasDuplicate = true; | |
return 0; | |
} | |
return ( !a.compareDocumentPosition || !b.compareDocumentPosition ? | |
a.compareDocumentPosition : | |
a.compareDocumentPosition(b) & 4 | |
) ? -1 : 1; | |
} : | |
function( a, b ) { | |
// The nodes are identical, we can exit early | |
if ( a === b ) { | |
hasDuplicate = true; | |
return 0; | |
// Fallback to using sourceIndex (in IE) if it's available on both nodes | |
} else if ( a.sourceIndex && b.sourceIndex ) { | |
return a.sourceIndex - b.sourceIndex; | |
} | |
var al, bl, | |
ap = [], | |
bp = [], | |
aup = a.parentNode, | |
bup = b.parentNode, | |
cur = aup; | |
// If the nodes are siblings (or identical) we can do a quick check | |
if ( aup === bup ) { | |
return siblingCheck( a, b ); | |
// If no parents were found then the nodes are disconnected | |
} else if ( !aup ) { | |
return -1; | |
} else if ( !bup ) { | |
return 1; | |
} | |
// Otherwise they're somewhere else in the tree so we need | |
// to build up a full list of the parentNodes for comparison | |
while ( cur ) { | |
ap.unshift( cur ); | |
cur = cur.parentNode; | |
} | |
cur = bup; | |
while ( cur ) { | |
bp.unshift( cur ); | |
cur = cur.parentNode; | |
} | |
al = ap.length; | |
bl = bp.length; | |
// Start walking down the tree looking for a discrepancy | |
for ( var i = 0; i < al && i < bl; i++ ) { | |
if ( ap[i] !== bp[i] ) { | |
return siblingCheck( ap[i], bp[i] ); | |
} | |
} | |
// We ended someplace up the tree so do a sibling check | |
return i === al ? | |
siblingCheck( a, bp[i], -1 ) : | |
siblingCheck( ap[i], b, 1 ); | |
}; | |
// Always assume the presence of duplicates if sort doesn't | |
// pass them to our comparison function (as in Google Chrome). | |
[0, 0].sort( sortOrder ); | |
baseHasDuplicate = !hasDuplicate; | |
// Document sorting and removing duplicates | |
Sizzle.uniqueSort = function( results ) { | |
var elem, | |
i = 1; | |
hasDuplicate = baseHasDuplicate; | |
results.sort( sortOrder ); | |
if ( hasDuplicate ) { | |
for ( ; (elem = results[i]); i++ ) { | |
if ( elem === results[ i - 1 ] ) { | |
results.splice( i--, 1 ); | |
} | |
} | |
} | |
return results; | |
}; | |
Sizzle.error = function( msg ) { | |
throw new Error( "Syntax error, unrecognized expression: " + msg ); | |
}; | |
function tokenize( selector, context, xml, parseOnly ) { | |
var matched, match, tokens, type, | |
soFar, groups, group, i, | |
preFilters, filters, | |
checkContext = !xml && context !== document, | |
// Token cache should maintain spaces | |
key = ( checkContext ? "<s>" : "" ) + selector.replace( rtrim, "$1<s>" ), | |
cached = tokenCache[ expando ][ key ]; | |
if ( cached ) { | |
return parseOnly ? 0 : slice.call( cached, 0 ); | |
} | |
soFar = selector; | |
groups = []; | |
i = 0; | |
preFilters = Expr.preFilter; | |
filters = Expr.filter; | |
while ( soFar ) { | |
// Comma and first run | |
if ( !matched || (match = rcomma.exec( soFar )) ) { | |
if ( match ) { | |
soFar = soFar.slice( match[0].length ); | |
tokens.selector = group; | |
} | |
groups.push( tokens = [] ); | |
group = ""; | |
// Need to make sure we're within a narrower context if necessary | |
// Adding a descendant combinator will generate what is needed | |
if ( checkContext ) { | |
soFar = " " + soFar; | |
} | |
} | |
matched = false; | |
// Combinators | |
if ( (match = rcombinators.exec( soFar )) ) { | |
group += match[0]; | |
soFar = soFar.slice( match[0].length ); | |
// Cast descendant combinators to space | |
matched = tokens.push({ | |
part: match.pop().replace( rtrim, " " ), | |
string: match[0], | |
captures: match | |
}); | |
} | |
// Filters | |
for ( type in filters ) { | |
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || | |
( match = preFilters[ type ](match, context, xml) )) ) { | |
group += match[0]; | |
soFar = soFar.slice( match[0].length ); | |
matched = tokens.push({ | |
part: type, | |
string: match.shift(), | |
captures: match | |
}); | |
} | |
} | |
if ( !matched ) { | |
break; | |
} | |
} | |
// Attach the full group as a selector | |
if ( group ) { | |
tokens.selector = group; | |
} | |
// Return the length of the invalid excess | |
// if we're just parsing | |
// Otherwise, throw an error or return tokens | |
return parseOnly ? | |
soFar.length : | |
soFar ? | |
Sizzle.error( selector ) : | |
// Cache the tokens | |
slice.call( tokenCache(key, groups), 0 ); | |
} | |
function addCombinator( matcher, combinator, context, xml ) { | |
var dir = combinator.dir, | |
doneName = done++; | |
if ( !matcher ) { | |
// If there is no matcher to check, check against the context | |
matcher = function( elem ) { | |
return elem === context; | |
}; | |
} | |
return combinator.first ? | |
function( elem ) { | |
while ( (elem = elem[ dir ]) ) { | |
if ( elem.nodeType === 1 ) { | |
return matcher( elem ) && elem; | |
} | |
} | |
} : | |
xml ? | |
function( elem ) { | |
while ( (elem = elem[ dir ]) ) { | |
if ( elem.nodeType === 1 ) { | |
if ( matcher( elem ) ) { | |
return elem; | |
} | |
} | |
} | |
} : | |
function( elem ) { | |
var cache, | |
dirkey = doneName + "." + dirruns, | |
cachedkey = dirkey + "." + cachedruns; | |
while ( (elem = elem[ dir ]) ) { | |
if ( elem.nodeType === 1 ) { | |
if ( (cache = elem[ expando ]) === cachedkey ) { | |
return elem.sizset; | |
} else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) { | |
if ( elem.sizset ) { | |
return elem; | |
} | |
} else { | |
elem[ expando ] = cachedkey; | |
if ( matcher( elem ) ) { | |
elem.sizset = true; | |
return elem; | |
} | |
elem.sizset = false; | |
} | |
} | |
} | |
}; | |
} | |
function addMatcher( higher, deeper ) { | |
return higher ? | |
function( elem ) { | |
var result = deeper( elem ); | |
return result && higher( result === true ? elem : result ); | |
} : | |
deeper; | |
} | |
// ["TAG", ">", "ID", " ", "CLASS"] | |
function matcherFromTokens( tokens, context, xml ) { | |
var token, matcher, | |
i = 0; | |
for ( ; (token = tokens[i]); i++ ) { | |
if ( Expr.relative[ token.part ] ) { | |
matcher = addCombinator( matcher, Expr.relative[ token.part ], context, xml ); | |
} else { | |
matcher = addMatcher( matcher, Expr.filter[ token.part ].apply(null, token.captures.concat( context, xml )) ); | |
} | |
} | |
return matcher; | |
} | |
function matcherFromGroupMatchers( matchers ) { | |
return function( elem ) { | |
var matcher, | |
j = 0; | |
for ( ; (matcher = matchers[j]); j++ ) { | |
if ( matcher(elem) ) { | |
return true; | |
} | |
} | |
return false; | |
}; | |
} | |
compile = Sizzle.compile = function( selector, context, xml ) { | |
var group, i, len, | |
cached = compilerCache[ expando ][ selector ]; | |
// Return a cached group function if already generated (context dependent) | |
if ( cached && cached.context === context ) { | |
return cached; | |
} | |
// Generate a function of recursive functions that can be used to check each element | |
group = tokenize( selector, context, xml ); | |
for ( i = 0, len = group.length; i < len; i++ ) { | |
group[i] = matcherFromTokens(group[i], context, xml); | |
} | |
// Cache the compiled function | |
cached = compilerCache( selector, matcherFromGroupMatchers(group) ); | |
cached.context = context; | |
cached.runs = cached.dirruns = 0; | |
return cached; | |
}; | |
function multipleContexts( selector, contexts, results, seed ) { | |
var i = 0, | |
len = contexts.length; | |
for ( ; i < len; i++ ) { | |
Sizzle( selector, contexts[i], results, seed ); | |
} | |
} | |
function handlePOSGroup( selector, posfilter, argument, contexts, seed, not ) { | |
var results, | |
fn = Expr.setFilters[ posfilter.toLowerCase() ]; | |
if ( !fn ) { | |
Sizzle.error( posfilter ); | |
} | |
if ( selector || !(results = seed) ) { | |
multipleContexts( selector || "*", contexts, (results = []), seed ); | |
} | |
return results.length > 0 ? fn( results, argument, not ) : []; | |
} | |
function handlePOS( groups, context, results, seed ) { | |
var group, part, j, groupLen, token, selector, | |
anchor, elements, match, matched, | |
lastIndex, currentContexts, not, | |
i = 0, | |
len = groups.length, | |
rpos = matchExpr["POS"], | |
// This is generated here in case matchExpr["POS"] is extended | |
rposgroups = new RegExp( "^" + rpos.source + "(?!" + whitespace + ")", "i" ), | |
// This is for making sure non-participating | |
// matching groups are represented cross-browser (IE6-8) | |
setUndefined = function() { | |
var i = 1, | |
len = arguments.length - 2; | |
for ( ; i < len; i++ ) { | |
if ( arguments[i] === undefined ) { | |
match[i] = undefined; | |
} | |
} | |
}; | |
for ( ; i < len; i++ ) { | |
group = groups[i]; | |
part = ""; | |
elements = seed; | |
for ( j = 0, groupLen = group.length; j < groupLen; j++ ) { | |
token = group[j]; | |
selector = token.string; | |
if ( token.part === "PSEUDO" ) { | |
// Reset regex index to 0 | |
rpos.exec(""); | |
anchor = 0; | |
while ( (match = rpos.exec( selector )) ) { | |
matched = true; | |
lastIndex = rpos.lastIndex = match.index + match[0].length; | |
if ( lastIndex > anchor ) { | |
part += selector.slice( anchor, match.index ); | |
anchor = lastIndex; | |
currentContexts = [ context ]; | |
if ( rcombinators.test(part) ) { | |
if ( elements ) { | |
currentContexts = elements; | |
} | |
elements = seed; | |
} | |
if ( (not = rendsWithNot.test( part )) ) { | |
part = part.slice( 0, -5 ).replace( rcombinators, "$&*" ); | |
anchor++; | |
} | |
if ( match.length > 1 ) { | |
match[0].replace( rposgroups, setUndefined ); | |
} | |
elements = handlePOSGroup( part, match[1], match[2], currentContexts, elements, not ); | |
} | |
part = ""; | |
} | |
} | |
if ( !matched ) { | |
part += selector; | |
} | |
matched = false; | |
} | |
if ( part ) { | |
if ( rcombinators.test(part) ) { | |
multipleContexts( part, elements || [ context ], results, seed ); | |
} else { | |
Sizzle( part, context, results, seed ? seed.concat(elements) : elements ); | |
} | |
} else { | |
push.apply( results, elements ); | |
} | |
} | |
// Do not sort if this is a single filter | |
return len === 1 ? results : Sizzle.uniqueSort( results ); | |
} | |
function select( selector, context, results, seed, xml ) { | |
// Remove excessive whitespace | |
selector = selector.replace( rtrim, "$1" ); | |
var elements, matcher, cached, elem, | |
i, tokens, token, lastToken, findContext, type, | |
match = tokenize( selector, context, xml ), | |
contextNodeType = context.nodeType; | |
// POS handling | |
if ( matchExpr["POS"].test(selector) ) { | |
return handlePOS( match, context, results, seed ); | |
} | |
if ( seed ) { | |
elements = slice.call( seed, 0 ); | |
// To maintain document order, only narrow the | |
// set if there is one group | |
} else if ( match.length === 1 ) { | |
// Take a shortcut and set the context if the root selector is an ID | |
if ( (tokens = slice.call( match[0], 0 )).length > 2 && | |
(token = tokens[0]).part === "ID" && | |
contextNodeType === 9 && !xml && | |
Expr.relative[ tokens[1].part ] ) { | |
context = Expr.find["ID"]( token.captures[0].replace( rbackslash, "" ), context, xml )[0]; | |
if ( !context ) { | |
return results; | |
} | |
selector = selector.slice( tokens.shift().string.length ); | |
} | |
findContext = ( (match = rsibling.exec( tokens[0].string )) && !match.index && context.parentNode ) || context; | |
// Reduce the set if possible | |
lastToken = ""; | |
for ( i = tokens.length - 1; i >= 0; i-- ) { | |
token = tokens[i]; | |
type = token.part; | |
lastToken = token.string + lastToken; | |
if ( Expr.relative[ type ] ) { | |
break; | |
} | |
if ( Expr.order.test(type) ) { | |
elements = Expr.find[ type ]( token.captures[0].replace( rbackslash, "" ), findContext, xml ); | |
if ( elements == null ) { | |
continue; | |
} else { | |
selector = selector.slice( 0, selector.length - lastToken.length ) + | |
lastToken.replace( matchExpr[ type ], "" ); | |
if ( !selector ) { | |
push.apply( results, slice.call(elements, 0) ); | |
} | |
break; | |
} | |
} | |
} | |
} | |
// Only loop over the given elements once | |
if ( selector ) { | |
matcher = compile( selector, context, xml ); | |
dirruns = matcher.dirruns++; | |
if ( elements == null ) { | |
elements = Expr.find["TAG"]( "*", (rsibling.test( selector ) && context.parentNode) || context ); | |
} | |
for ( i = 0; (elem = elements[i]); i++ ) { | |
cachedruns = matcher.runs++; | |
if ( matcher(elem) ) { | |
results.push( elem ); | |
} | |
} | |
} | |
return results; | |
} | |
if ( document.querySelectorAll ) { | |
(function() { | |
var disconnectedMatch, | |
oldSelect = select, | |
rescape = /'|\\/g, | |
rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g, | |
// qSa(:focus) reports false when true (Chrome 21), | |
// A support test would require too much code (would include document ready) | |
rbuggyQSA = [":focus"], | |
// matchesSelector(:focus) reports false when true (Chrome 21), | |
// matchesSelector(:active) reports false when true (IE9/Opera 11.5) | |
// A support test would require too much code (would include document ready) | |
// just skip matchesSelector for :active | |
rbuggyMatches = [ ":active", ":focus" ], | |
matches = docElem.matchesSelector || | |
docElem.mozMatchesSelector || | |
docElem.webkitMatchesSelector || | |
docElem.oMatchesSelector || | |
docElem.msMatchesSelector; | |
// Build QSA regex | |
// Regex strategy adopted from Diego Perini | |
assert(function( div ) { | |
// Select is set to empty string on purpose | |
// This is to test IE's treatment of not explictly | |
// setting a boolean content attribute, | |
// since its presence should be enough | |
// http://bugs.jquery.com/ticket/12359 | |
div.innerHTML = "<select><option selected=''></option></select>"; | |
// IE8 - Some boolean attributes are not treated correctly | |
if ( !div.querySelectorAll("[selected]").length ) { | |
rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" ); | |
} | |
// Webkit/Opera - :checked should return selected option elements | |
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked | |
// IE8 throws error here (do not put tests after this one) | |
if ( !div.querySelectorAll(":checked").length ) { | |
rbuggyQSA.push(":checked"); | |
} | |
}); | |
assert(function( div ) { | |
// Opera 10-12/IE9 - ^= $= *= and empty values | |
// Should not select anything | |
div.innerHTML = "<p test=''></p>"; | |
if ( div.querySelectorAll("[test^='']").length ) { | |
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" ); | |
} | |
// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) | |
// IE8 throws error here (do not put tests after this one) | |
div.innerHTML = "<input type='hidden'/>"; | |
if ( !div.querySelectorAll(":enabled").length ) { | |
rbuggyQSA.push(":enabled", ":disabled"); | |
} | |
}); | |
// rbuggyQSA always contains :focus, so no need for a length check | |
rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") ); | |
select = function( selector, context, results, seed, xml ) { | |
// Only use querySelectorAll when not filtering, | |
// when this is not xml, | |
// and when no QSA bugs apply | |
if ( !seed && !xml && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { | |
if ( context.nodeType === 9 ) { | |
try { | |
push.apply( results, slice.call(context.querySelectorAll( selector ), 0) ); | |
return results; | |
} catch(qsaError) {} | |
// qSA works strangely on Element-rooted queries | |
// We can work around this by specifying an extra ID on the root | |
// and working up from there (Thanks to Andrew Dupont for the technique) | |
// IE 8 doesn't work on object elements | |
} else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { | |
var groups, i, len, | |
old = context.getAttribute("id"), | |
nid = old || expando, | |
newContext = rsibling.test( selector ) && context.parentNode || context; | |
if ( old ) { | |
nid = nid.replace( rescape, "\\$&" ); | |
} else { | |
context.setAttribute( "id", nid ); | |
} | |
groups = tokenize(selector, context, xml); | |
// Trailing space is unnecessary | |
// There is always a context check | |
nid = "[id='" + nid + "']"; | |
for ( i = 0, len = groups.length; i < len; i++ ) { | |
groups[i] = nid + groups[i].selector; | |
} | |
try { | |
push.apply( results, slice.call( newContext.querySelectorAll( | |
groups.join(",") | |
), 0 ) ); | |
return results; | |
} catch(qsaError) { | |
} finally { | |
if ( !old ) { | |
context.removeAttribute("id"); | |
} | |
} | |
} | |
} | |
return oldSelect( selector, context, results, seed, xml ); | |
}; | |
if ( matches ) { | |
assert(function( div ) { | |
// Check to see if it's possible to do matchesSelector | |
// on a disconnected node (IE 9) | |
disconnectedMatch = matches.call( div, "div" ); | |
// This should fail with an exception | |
// Gecko does not error, returns false instead | |
try { | |
matches.call( div, "[test!='']:sizzle" ); | |
rbuggyMatches.push( matchExpr["PSEUDO"].source, matchExpr["POS"].source, "!=" ); | |
} catch ( e ) {} | |
}); | |
// rbuggyMatches always contains :active and :focus, so no need for a length check | |
rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") ); | |
Sizzle.matchesSelector = function( elem, expr ) { | |
// Make sure that attribute selectors are quoted | |
expr = expr.replace( rattributeQuotes, "='$1']" ); | |
// rbuggyMatches always contains :active, so no need for an existence check | |
if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && (!rbuggyQSA || !rbuggyQSA.test( expr )) ) { | |
try { | |
var ret = matches.call( elem, expr ); | |
// IE 9's matchesSelector returns false on disconnected nodes | |
if ( ret || disconnectedMatch || | |
// As well, disconnected nodes are said to be in a document | |
// fragment in IE 9 | |
elem.document && elem.document.nodeType !== 11 ) { | |
return ret; | |
} | |
} catch(e) {} | |
} | |
return Sizzle( expr, null, null, [ elem ] ).length > 0; | |
}; | |
} | |
})(); | |
} | |
// Deprecated | |
Expr.setFilters["nth"] = Expr.setFilters["eq"]; | |
// Back-compat | |
Expr.filters = Expr.pseudos; | |
// EXPOSE | |
if ( typeof define === "function" && define.amd ) { | |
define(function() { return Sizzle; }); | |
} else { | |
window.Sizzle = Sizzle; | |
} | |
// EXPOSE | |
})( window ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment