Skip to content

Instantly share code, notes, and snippets.

@homerhanumat
Last active March 3, 2018 18:06
Show Gist options
  • Save homerhanumat/4f315ccae8707b64236500da7e35810a to your computer and use it in GitHub Desktop.
Save homerhanumat/4f315ccae8707b64236500da7e35810a to your computer and use it in GitHub Desktop.
R^2 Illustration
license: MIT
height: 1000
<html>
<head>
<style>
body {
margin: 0 auto;
margin-top: 1em;
display: table;
font-family: "Helvetica Neue", sans-serif;
}
button {
position: relative;
top: 0;
border: solid red 2px;
border-radius: 3px;
box-shadow: 0px 4px rgba(82, 29, 29, 0.2);
margin: 0 7px 11px 0;
}
button:hover {
background-color: burlywood;
box-shadow: 0px 3px rgba(22, 4, 4, 0.2);
}
button:active {
top: 2px;
;
}
svg {
padding-bottom: 16px;
}
.groupMean {
stroke-width: 2px;
stroke: rgb(90, 90, 206);
cursor: none;
pointer-events: none;
}
.groupMeanUse {
stroke: transparent;
stroke-width: 16px;
z-index: 99;
}
.groupMeanUse.active {
cursor: move;
pointer-events: all;
}
.inactive {
visibility: hidden;
cursor: none;
pointer-events: none;
}
.groupYbar {
stroke-width: 2px;
stroke: red;
stroke-dasharray: 10, 5;
cursor: none;
pointer-events: none;
}
.ybb {
stroke-width: 3px;
stroke: red;
stroke-dasharray: 10, 5;
cursor: none;
pointer-events: none;
}
.point {
cursor: none;
pointer-events: none;
}
.vertical {
stroke-width: 1px;
stroke: darkgreen;
}
.axistext {
font-size: 14px;
}
#selector {
margin-left: 50px;
}
.meanpoint {
visibility: hidden;
}
.report span {
width: 50px;
padding-right: 1.5em;
}
#sigmaValue {
display: block;
font-size: 1.2em;
font-weight: bold;
text-align: center;
margin: 15px 0;
width: 100%;
}
.info {
max-width: 600px;
}
</style>
<link rel="stylesheet" type="text/css" href="range-slider-flat.css">
</head>
<body>
<div>
<button id="resample" name="button">Resample</button>
<button id="sse" name="button">Error SS</button>
<button id="ssm" name="button">Treatment SS</button>
<button id="sst" name="button">Total SS</button>
<button id="groupMeans" name="button">Group Means</button>
</div>
<div>
<input id="sigma" type="range" min="0" max="5" step="0.1" data-rangeSlider />
<ouput id="sigmavalue">
</output>
</div>
<div class="chart"></div>
<p=c lass="report">
<span>Error SS:
<output id="sse-value"></output>
</span>
<span>Treatment SS:
<output id="ssm-value"></output>
</span>
</p>
<p class="report">
<span>Total SS:
<output id="sst-value"></output>
</span>
<span>R^2:
<output id="r2-value"></output>
</span>
</p>
<p class = "info">Blue lines represent the population mean for each group.
Dotted green lines are the sample means. Drag a blue line
to change the population mean. The slider controls the
standard devition of the group populations.
</p>
<script src="range-slider.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
const width = 500,
height = 500;
const margin = { top: 33, right: 5, bottom: 35, left: 50 },
innerWidth = width - margin.left - margin.right,
innerHeight = width - margin.top - margin.bottom;
const n = 10;
// number of groups (limit 12, please)
const groupNumber = 3;
const groups = "abcdefghijkl".split("").slice(0, groupNumber);
const minY = 0, maxY = 40;
const minAllowedGroupMean = 10;
const maxAllowedGroupMean = 30;
let groupMeans = Array(groupNumber).fill(20);
const maxSigma = 5;
let sigma = 2.5;
// set up x-scale now
const x = d3.scaleBand()
.range([0, innerWidth]);
x.domain(groups);
const data = generateData(x, groups, groupMeans, n, sigma);
const [sse, ssm, sst, r2] = sumSquares(data);
document.querySelector("#sse-value").innerHTML = precisionRound(sse, 2);
document.querySelector("#ssm-value").innerHTML = precisionRound(ssm, 2);
document.querySelector("#sst-value").innerHTML = precisionRound(sst, 2);
document.querySelector("#r2-value").innerHTML = precisionRound(r2, 4);
document.querySelector("#resample")
.addEventListener("click", resampleHandler);
document.querySelector("#sigma")
.addEventListener("input", sigmaHandler);
document.querySelector("#sse")
.addEventListener("click", sseHandler);
document.querySelector("#ssm")
.addEventListener("click", ssmHandler);
document.querySelector("#sst")
.addEventListener("click", sstHandler);
document.querySelector("#groupMeans")
.addEventListener("click", groupMeansHandler);
const svg = d3.select(".chart")
.append("svg")
.attr("width", innerWidth + margin.left + margin.right)
.attr("height", innerHeight + margin.top + margin.bottom);
const svgInside = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
.classed("innersvg", true);
const y = d3.scaleLinear()
.range([innerHeight, 0]);
const xAxis = d3.axisBottom()
.scale(x);
const yAxis = d3.axisLeft()
.scale(y);
const yaxistext = "y";
svgInside.append("text")
.attr("class", "axistext")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (innerHeight / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text(yaxistext);
const [minMean, maxMean] = d3.extent(groupMeans);
y.domain([minY, maxY]);
svgInside.append("g")
.attr("class", "x axis")
.attr("transform", `translate(0,${innerHeight})`)
.call(xAxis);
svgInside.append("g")
.attr("class", "y axis")
.call(yAxis);
drawAll(data);
document.querySelector("#sigmavalue")
.innerHTML = `Sigma: ${sigma}`;
document.querySelector("#sigma").value = sigma.toFixed(1);
// FUNCTIONS .........................................................
function gaussian(a, b) {
return {
u: Math.sqrt(-2 * Math.log(a)) * Math.cos(2 * Math.PI * b),
v: Math.sqrt(-2 * Math.log(a)) * Math.sin(2 * Math.PI * b)
};
}
function gaussianArray(n, mu, sigma) {
let data = [];
while (data.length < n) {
let u1 = Math.random();
let u2 = Math.random();
let { u, v } = gaussian(u1, u2);
let x = mu + sigma * u;
let y = mu + sigma * v;
data.push(x);
if (data.length < n) data.push(y);
}
return data;
}
function meanOfAll(data) {
let allYs = [];
data.forEach(group => {
allYs = allYs.concat(group.yvals);
});
return d3.mean(allYs);
}
function sumSquares(data) {
let SSM = 0, SST = 0;
let allYs = [];
data.forEach(group => {
allYs = allYs.concat(group.yvals);
});
const ybb = d3.mean(allYs);
//SST = allYs.reduce((acc, y) => acc + (y - ybb) * (y - ybb));
for (let i = 0; i < n * data.length; i++) {
SST += (allYs[i] - ybb) * (allYs[i] - ybb);
}
for (let i = 0; i < data.length; i++) {
let groupMean = d3.mean(data[i].yvals);
SSM += (n * (groupMean - ybb) * (groupMean - ybb));
}
const SSE = SST - SSM;
const R2 = SSM / SST
return [SSE, SSM, SST, R2];
}
function precisionRound(number, precision) {
const factor = Math.pow(10, precision);
return Math.round(number * factor) / factor;
}
function generateData(x, groups, groupMeans, n, sigma) {
const spacing = x(groups[1]) - x(groups[0]);
const padding = spacing / 8;
let data = [];
for (let i = 0; i < groups.length; i++) {
const start = x(groups[i]) + padding;
const end = x(groups[i]) + spacing - padding;
data.push({
group: groups[i],
groupMean: groupMeans[i],
start: start,
end: end,
xvals: spreadEvenly(n, start, end),
yvals: gaussianArray(n, groupMeans[i], sigma)
});
}
return data;
}
function updateDataGroup(index) {
const gm = data[index].groupMean;
data[index].yvals = gaussianArray(n, gm, sigma);
}
function updateData() {
for (let i = 0; i < data.length; i++) {
updateDataGroup(i);
}
}
function updateOutputs() {
const [sse, ssm, sst, r2] = sumSquares(data);
document.querySelector("#sse-value")
.innerHTML = precisionRound(sse, 2);
document.querySelector("#ssm-value")
.innerHTML = precisionRound(ssm, 2);
document.querySelector("#sst-value")
.innerHTML = precisionRound(sst, 2);
document.querySelector("#r2-value")
.innerHTML = precisionRound(r2, 4);
}
function resampleHandler() {
clearErrorLines();
clearTreatmentLines();
clearTotalLines();
updateData();
updateOutputs();
drawAll(data);
}
function sigmaHandler() {
clearErrorLines();
clearTreatmentLines();
clearTotalLines();
sigma = parseFloat(this.value);
document.querySelector("#sigmavalue")
.innerHTML = `Sigma: ${sigma.toFixed(1)}`;
updateData();
updateOutputs();
drawAll(data);
}
function clearTreatmentLines() {
svgInside.selectAll(".yb-ybb").remove()
svgInside.select("#ybbLine").remove()
svgInside.selectAll(".point")
.style("opacity", 1);
}
function clearErrorLines() {
svgInside.selectAll(".point-yb").remove()
}
function clearTotalLines() {
svgInside.selectAll(".point-ybb").remove()
svgInside.select("#ybbLine").remove()
}
function groupMeansHandler() {
clearTreatmentLines();
clearTotalLines();
clearErrorLines();
svgInside.selectAll(".groupYbar")
.classed("active", true)
.classed("inactive", false);
svgInside.selectAll(".groupMean")
.classed("active", true)
.classed("inactive", false);
svgInside.selectAll(".groupMeanUse")
.classed("active", true)
.classed("inactive", false);
}
function sseHandler() {
clearTreatmentLines();
clearTotalLines();
svgInside.selectAll(".groupYbar")
.classed("active", true)
.classed("inactive", false);
svgInside.selectAll(".groupMeanUse")
.classed("inactive", true);
svgInside.selectAll(".groupMean")
.classed("inactive", true);
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < n; j++) {
addErrorLine(i, j);
}
}
}
function ssmHandler() {
clearErrorLines();
clearTotalLines();
svgInside.selectAll(".point")
.style("opacity", 0.3);
svgInside.selectAll(".groupYbar")
.classed("active", true)
.classed("inactive", false);
svgInside.selectAll(".groupMeanUse")
.classed("inactive", true);
svgInside.selectAll(".groupMean")
.classed("inactive", true);
const ybb = meanOfAll(data);
svgInside.append("line")
.attr("id", "ybbLine")
.attr("x1", data[0].start)
.attr("x2", data[data.length - 1].end)
.attr("y1", y(ybb))
.attr("y2", y(ybb))
.attr("stroke", "red")
.attr("stroke-width", 3)
;
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < n; j++) {
addTreatmentLine(i, j);
}
}
}
function sstHandler() {
clearErrorLines();
clearTreatmentLines();
svgInside.selectAll(".groupYbar")
.classed("active", false)
.classed("inactive", true);
svgInside.selectAll(".groupMeanUse")
.classed("inactive", true);
svgInside.selectAll(".groupMean")
.classed("inactive", true);
const ybb = meanOfAll(data);
svgInside.append("line")
.attr("id", "ybbLine")
.attr("x1", data[0].start)
.attr("x2", data[data.length - 1].end)
.attr("y1", y(ybb))
.attr("y2", y(ybb))
.attr("stroke", "red")
.attr("stroke-width", 3)
;
for (let i = 0; i < data.length; i++) {
for (let j = 0; j < n; j++) {
addTotalLine(i, j);
}
}
}
function addErrorLine(i, j) {
svgInside.append("line")
.attr("class", "vertical")
.classed("point-yb", true)
.attr("x1", data[i].xvals[j])
.attr("x2", data[i].xvals[j])
.attr("y1", y(data[i].yvals[j]))
.attr("y2", y(d3.mean(data[i].yvals)))
;
}
function addTreatmentLine(i, j) {
svgInside.append("line")
.attr("class", "vertical")
.classed("yb-ybb", true)
.attr("x1", data[i].xvals[j])
.attr("x2", data[i].xvals[j])
.attr("y1", y(d3.mean(data[i].yvals)))
.attr("y2", y(meanOfAll(data)))
;
}
function addTotalLine(i, j) {
svgInside.append("line")
.attr("class", "vertical")
.classed("point-ybb", true)
.attr("x1", data[i].xvals[j])
.attr("x2", data[i].xvals[j])
.attr("y1", y(data[i].yvals[j]))
.attr("y2", y(meanOfAll(data)))
;
}
function spreadEvenly(n, a, b) {
const step = (b - a) / (n + 1);
let arr = [];
for (let i = 0; i < n; i++) {
arr.push(a + (i + 1) * step);
}
return arr;
}
function drawGroup(group) {
const [dataGroup] = data.filter(elem => elem.group == group);
drawInsideGroup(group);
svgInside.selectAll(`[data-group=${group}]`).remove();
const groupMeanLine = svgInside.append("line")
.attr("class", "groupMeanUse")
.attr("id", `groupMeanUse-${group}`)
.attr("data-group", dataGroup.group)
.attr("x1", dataGroup.start)
.attr("x2", dataGroup.end)
.attr("y1", y(dataGroup.groupMean))
.attr("y2", y(dataGroup.groupMean))
;
drawInsideGroup(group);
const dragHandler = d3.drag().on("drag", function () {
const okToMove = y.invert(d3.event.y) >= minAllowedGroupMean &&
y.invert(d3.event.y) <= maxAllowedGroupMean;
if (okToMove) {
newGroupMean = y.invert(d3.event.y);
const group = this.getAttribute("data-group");
const index = groups.indexOf(group);
data[index].groupMean = newGroupMean;
updateDataGroup(index);
this.setAttribute("y1", y(dataGroup.groupMean));
this.setAttribute("y2", y(dataGroup.groupMean));
d3.select(`#groupMean-${group}`).remove();
drawInsideGroup(group);
updateOutputs();
}
});
dragHandler(groupMeanLine);
}
function drawInsideGroup(group) {
const [dataGroup] = data.filter(elem => elem.group == group);
const index = groups.indexOf(group);
const groupMeanLine = svgInside.append("line")
.attr("class", "groupMean")
.attr("id", `groupMean-${group}`)
.attr("data-group", dataGroup.group)
.attr("x1", dataGroup.start)
.attr("x2", dataGroup.end)
.attr("y1", y(dataGroup.groupMean))
.attr("y2", y(dataGroup.groupMean))
;
svgInside.selectAll(`#groupYbarLine-${group}`)
.remove()
svgInside.append("line")
.attr("class", "groupYbar")
.attr("id", `groupYbarLine-${group}`)
.attr("data-group", dataGroup.group)
.attr("x1", dataGroup.start)
.attr("x2", dataGroup.end)
.attr("y1", y(d3.mean(dataGroup.yvals)))
.attr("y2", y(d3.mean(dataGroup.yvals)))
;
svgInside.selectAll(`.point[data-group=${group}]`)
.remove()
for (let j = 0; j < n; j++) {
svgInside.append("circle")
.attr("class", "point")
.attr("data-group", dataGroup.group)
.attr("r", 2)
.attr("cx", dataGroup.xvals[j])
.attr("cy", y(dataGroup.yvals[j]))
;
}
}
function drawAll(data) {
for (let i = 0; i < groups.length; i++) {
const group = data[i].group;
drawGroup(group);
}
svgInside.selectAll(".groupMeanUse")
.classed("active", true);
}
// Slider ............................................
const slider = document.querySelector('#sigma');
rangeSlider.create(slider, {
polyfill: true, //
rangeClass: 'rangeSlider',
disabledClass: 'rangeSlider--disabled',
fillClass: 'rangeSlider__fill',
bufferClass: 'rangeSlider__buffer',
handleClass: 'rangeSlider__handle',
startEvent: ['mousedown', 'touchstart', 'pointerdown'],
moveEvent: ['mousemove', 'touchmove', 'pointermove'],
endEvent: ['mouseup', 'touchend', 'pointerup'],
vertical: false, // Boolean,
min: 0, // Number , 0
max: maxSigma, // Number, 100
step: 0.1, // Number, 1
value: sigma, // Number, center of slider
buffer: null, // Number, in percent, 0 by default
stick: null, // [Number stickTo, Number stickRadius]
borderRadius: 10, // Number,
onInit: function () {
console.info('onInit')
}
});
</script>
</body>
</html>
/************************************************
* Source:
* https://github.com/Stryzhevskyi/rangeSlider
* License: MIT
*************************************************/
.rangeSlider,
.rangeSlider__fill {
background: #7f8c8d;
display: block;
height: 8px;
width: 100%;
-webkit-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3);
-moz-box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.3);
box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.5);
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
-ms-border-radius: 10px;
-o-border-radius: 10px;
border-radius: 4px;
}
.rangeSlider {
position: relative;
}
.rangeSlider--disabled {
filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
opacity: 0.4;
}
.rangeSlider__fill {
background: #FFFFFF;
position: absolute;
top: 0;
z-index: 2;
}
.rangeSlider__handle {
background: white;
border: 1px solid #ccc;
cursor: pointer;
display: inline-block;
width: 22px;
height: 21px;
position: absolute;
top: -7px;
z-index: 3;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(100%, rgba(0, 0, 0, 0.1)));
background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0), rgba(0, 0, 0, 0.1));
background-image: -moz-linear-gradient(rgba(255, 255, 255, 0), rgba(0, 0, 0, 0.1));
background-image: -o-linear-gradient(rgba(255, 255, 255, 0), rgba(0, 0, 0, 0.1));
background-image: linear-gradient(rgba(255, 255, 255, 0), rgba(0, 0, 0, 0.1));
-webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.3);
-moz-box-shadow: 0 0 8px rgba(0, 0, 0, 0.3);
box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
-ms-border-radius: 50%;
-o-border-radius: 50%;
border-radius: 50%;
}
.rangeSlider__handle:after {
content: "";
display: block;
width: 10px;
height: 10px;
margin: auto;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0, 0, 0, 0.13)), color-stop(100%, rgba(255, 255, 255, 0)));
background-image: -webkit-linear-gradient(rgba(0, 0, 0, 0.13), rgba(255, 255, 255, 0));
background-image: -moz-linear-gradient(rgba(0, 0, 0, 0.13), rgba(255, 255, 255, 0));
background-image: -o-linear-gradient(rgba(0, 0, 0, 0.13), rgba(255, 255, 255, 0));
background-image: linear-gradient(rgba(0, 0, 0, 0.13), rgba(255, 255, 255, 0));
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
-ms-border-radius: 50%;
-o-border-radius: 50%;
border-radius: 50%;
}
.rangeSlider__handle:active {
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, rgba(0, 0, 0, 0.1)), color-stop(100%, rgba(0, 0, 0, 0.12)));
background-image: -webkit-linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.12));
background-image: -moz-linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.12));
background-image: -o-linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.12));
background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.12));
outline: none;
}
input[type="range"]:focus + .rangeSlider .rangeSlider__handle {
-webkit-box-shadow: 0 0 8px rgba(142, 68, 173, 0.9);
-moz-box-shadow: 0 0 8px rgba(142, 68, 173, 0.9);
box-shadow: 0 0 8px rgba(142, 68, 173, 0.9);
}
.rangeSlider__buffer {
z-index: 1;
position: absolute;
top: 2px;
height: 4px;
background: #2c3e50;
border-radius: 2px;
}
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("rangeSlider",[],t):"object"==typeof exports?exports.rangeSlider=t():e.rangeSlider=t()}(this,function(){return function(e){function t(n){if(i[n])return i[n].exports;var s=i[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,t),s.l=!0,s.exports}var i={};return t.m=e,t.c=i,t.d=function(e,i,n){t.o(e,i)||Object.defineProperty(e,i,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var i=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(i,"a",i),i},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=1)}([function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=(t.delay=function(e,t){for(var i=arguments.length,n=Array(i>2?i-2:0),s=2;s<i;s++)n[s-2]=arguments[s];return setTimeout(function(){return e.apply(null,n)},t)},t.debounce=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:100;return function(){for(var i=arguments.length,n=Array(i),s=0;s<i;s++)n[s]=arguments[s];return e.debouncing||(e.lastReturnVal=e.apply(window,n),e.debouncing=!0),clearTimeout(e.debounceTimeout),e.debounceTimeout=setTimeout(function(){e.debouncing=!1},t),e.lastReturnVal}},t.isString=function(e){return e===""+e}),s=(t.isArray=function(e){return"[object Array]"===Object.prototype.toString.call(e)},t.isNumberLike=function(e){return null!==e&&void 0!==e&&(n(e)&&isFinite(parseFloat(e))||isFinite(e))});t.getFirsNumberLike=function(){for(var e=arguments.length,t=Array(e),i=0;i<e;i++)t[i]=arguments[i];if(!t.length)return null;for(var n=0,r=t.length;n<r;n++)if(s(t[n]))return t[n];return null},t.isObject=function(e){return"[object Object]"===Object.prototype.toString.call(e)},t.simpleExtend=function(e,t){var i={};for(var n in e)i[n]=e[n];for(var s in t)i[s]=t[s];return i},t.between=function(e,t,i){return e<t?t:e>i?i:e}},function(e,t,i){"use strict";function n(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i]);return t.default=e,t}function s(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){for(var i=0;i<t.length;i++){var n=t[i];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}return function(t,i,n){return i&&e(t.prototype,i),n&&e(t,n),t}}(),o=i(2),a=n(o),l=i(0),h=n(l);i(3);var u=new RegExp("/[\\n\\t]/","g"),d=100,f="rangeSlider",c=0,v=a.supportsRange(),p={polyfill:!0,rangeClass:"rangeSlider",disabledClass:"rangeSlider--disabled",fillClass:"rangeSlider__fill",bufferClass:"rangeSlider__buffer",handleClass:"rangeSlider__handle",startEvent:["mousedown","touchstart","pointerdown"],moveEvent:["mousemove","touchmove","pointermove"],endEvent:["mouseup","touchend","pointerup"],min:null,max:null,step:null,value:null,buffer:null,stick:null,borderRadius:10,vertical:!1},m=function(){function e(t,i){s(this,e);var n=void 0,r=void 0,o=void 0,l=void 0,u=void 0;if(this.element=t,this.options=h.simpleExtend(p,i),this.polyfill=this.options.polyfill,this.vertical=this.options.vertical,this.onInit=this.options.onInit,this.onSlide=this.options.onSlide,this.onSlideStart=this.options.onSlideStart,this.onSlideEnd=this.options.onSlideEnd,this.onSlideEventsCount=-1,this.isInteractsNow=!1,this.needTriggerEvents=!1,this.polyfill||!v){this.options.buffer=this.options.buffer||parseFloat(this.element.getAttribute("data-buffer")),this.identifier="js-"+f+"-"+c++,this.min=h.getFirsNumberLike(this.options.min,parseFloat(this.element.getAttribute("min")),n=0),this.max=h.getFirsNumberLike(this.options.max,parseFloat(this.element.getAttribute("max")),r=d),this.value=h.getFirsNumberLike(this.options.value,this.element.value,parseFloat(this.element.value||this.min+(this.max-this.min)/2)),this.step=h.getFirsNumberLike(this.options.step,parseFloat(this.element.getAttribute("step"))||(o=1)),this.percent=null,h.isArray(this.options.stick)&&this.options.stick.length>=1?this.stick=this.options.stick:(l=this.element.getAttribute("stick"))&&(u=l.split(" "),u.length>=1&&(this.stick=u.map(parseFloat))),this.stick&&1===this.stick.length&&this.stick.push(1.5*this.step),this._updatePercentFromValue(),this.toFixed=this._toFixed(this.step);var m=void 0;this.container=document.createElement("div"),a.addClass(this.container,this.options.fillClass),m=this.vertical?this.options.fillClass+"__vertical":this.options.fillClass+"__horizontal",a.addClass(this.container,m),this.handle=document.createElement("div"),a.addClass(this.handle,this.options.handleClass),m=this.vertical?this.options.handleClass+"__vertical":this.options.handleClass+"__horizontal",a.addClass(this.handle,m),this.range=document.createElement("div"),a.addClass(this.range,this.options.rangeClass),this.range.id=this.identifier,this.range.appendChild(this.handle),this.range.appendChild(this.container),m=this.vertical?this.options.rangeClass+"__vertical":this.options.rangeClass+"__horizontal",a.addClass(this.range,m),this.options.bufferClass&&(this.buffer=document.createElement("div"),a.addClass(this.buffer,this.options.bufferClass),this.range.appendChild(this.buffer),m=this.vertical?this.options.bufferClass+"__vertical":this.options.bufferClass+"__horizontal",a.addClass(this.buffer,m)),h.isNumberLike(this.options.value)&&(this._setValue(this.options.value,!0),this.element.value=this.options.value),h.isNumberLike(this.options.buffer)&&this.element.setAttribute("data-buffer",this.options.buffer),(h.isNumberLike(this.options.min)||n)&&this.element.setAttribute("min",""+this.min),(h.isNumberLike(this.options.max)||r)&&this.element.setAttribute("max",""+this.max),(h.isNumberLike(this.options.step)||o)&&this.element.setAttribute("step",""+this.step),a.insertAfter(this.element,this.range),a.setCss(this.element,{position:"absolute",width:"1px",height:"1px",overflow:"hidden",opacity:"0"}),this._handleDown=this._handleDown.bind(this),this._handleMove=this._handleMove.bind(this),this._handleEnd=this._handleEnd.bind(this),this._startEventListener=this._startEventListener.bind(this),this._changeEventListener=this._changeEventListener.bind(this),this._handleResize=this._handleResize.bind(this),this._init(),window.addEventListener("resize",this._handleResize,!1),a.addEventListeners(document,this.options.startEvent,this._startEventListener),this.element.addEventListener("change",this._changeEventListener,!1)}}return r(e,[{key:"update",value:function(e,t){return t&&(this.needTriggerEvents=!0),h.isObject(e)&&(h.isNumberLike(e.min)&&(this.element.setAttribute("min",""+e.min),this.min=e.min),h.isNumberLike(e.max)&&(this.element.setAttribute("max",""+e.max),this.max=e.max),h.isNumberLike(e.step)&&(this.element.setAttribute("step",""+e.step),this.step=e.step,this.toFixed=this._toFixed(e.step)),h.isNumberLike(e.buffer)&&this._setBufferPosition(e.buffer),h.isNumberLike(e.value)&&this._setValue(e.value)),this._update(),this.onSlideEventsCount=0,this.needTriggerEvents=!1,this}},{key:"destroy",value:function(){a.removeAllListenersFromEl(this,document),window.removeEventListener("resize",this._handleResize,!1),this.element.removeEventListener("change",this._changeEventListener,!1),this.element.style.cssText="",delete this.element[f],this.range&&this.range.parentNode.removeChild(this.range)}},{key:"_toFixed",value:function(e){return(e+"").replace(".","").length-1}},{key:"_init",value:function(){this.onInit&&"function"==typeof this.onInit&&this.onInit(),this._update()}},{key:"_updatePercentFromValue",value:function(){this.percent=(this.value-this.min)/(this.max-this.min)}},{key:"_startEventListener",value:function(e,t){var i=this,n=e.target,s=!1;(1===e.which||"touches"in e)&&(a.forEachAncestors(n,function(e){return s=e.id===i.identifier&&!a.hasClass(e,i.options.disabledClass)},!0),s&&this._handleDown(e,t))}},{key:"_changeEventListener",value:function(e,t){if(!t||t.origin!==this.identifier){var i=e.target.value,n=this._getPositionFromValue(i);this._setPosition(n)}}},{key:"_update",value:function(){var e=this.vertical?"offsetHeight":"offsetWidth";this.handleSize=a.getDimension(this.handle,e),this.rangeSize=a.getDimension(this.range,e),this.maxHandleX=this.rangeSize-this.handleSize,this.grabX=this.handleSize/2,this.position=this._getPositionFromValue(this.value),this.element.disabled?a.addClass(this.range,this.options.disabledClass):a.removeClass(this.range,this.options.disabledClass),this._setPosition(this.position),this.options.bufferClass&&this.options.buffer&&this._setBufferPosition(this.options.buffer),this._updatePercentFromValue(),a.triggerEvent(this.element,"change",{origin:this.identifier})}},{key:"_handleResize",value:function(){var e=this;return h.debounce(function(){h.delay(function(){e._update()},300)},50)()}},{key:"_handleDown",value:function(e){if(this.isInteractsNow=!0,e.preventDefault(),a.addEventListeners(document,this.options.moveEvent,this._handleMove),a.addEventListeners(document,this.options.endEvent,this._handleEnd),!((" "+e.target.className+" ").replace(u," ").indexOf(this.options.handleClass)>-1)){var t=this.range.getBoundingClientRect(),i=this._getRelativePosition(e),n=this.vertical?t.bottom:t.left,s=this._getPositionFromNode(this.handle)-n,r=i-this.grabX;this._setPosition(r),i>=s&&i<s+2*this.options.borderRadius&&(this.grabX=i-s),this._updatePercentFromValue()}}},{key:"_handleMove",value:function(e){var t=this._getRelativePosition(e);this.isInteractsNow=!0,e.preventDefault(),this._setPosition(t-this.grabX)}},{key:"_handleEnd",value:function(e){e.preventDefault(),a.removeEventListeners(document,this.options.moveEvent,this._handleMove),a.removeEventListeners(document,this.options.endEvent,this._handleEnd),a.triggerEvent(this.element,"change",{origin:this.identifier}),(this.isInteractsNow||this.needTriggerEvents)&&this.onSlideEnd&&"function"==typeof this.onSlideEnd&&this.onSlideEnd(this.value,this.percent,this.position),this.onSlideEventsCount=0,this.isInteractsNow=!1}},{key:"_setPosition",value:function(e){var t=void 0,i=void 0,n=void 0,s=void 0,r=this._getValueFromPosition(h.between(e,0,this.maxHandleX));this.stick&&(s=this.stick[0],i=this.stick[1]||.1,n=r%s,n<i?r-=n:Math.abs(s-n)<i&&(r=r-n+s)),t=this._getPositionFromValue(r),this.vertical?(this.container.style.height=t+this.grabX+"px",this.handle.style.transform="translateY(-"+t+"px)",this.handle.style["-ms-transform"]="translateY(-"+t+"px)"):(this.container.style.width=t+this.grabX+"px",this.handle.style.transform="translateX("+t+"px)",this.handle.style["-ms-transform"]="translateX("+t+"px)"),this._setValue(r),this.position=t,this.value=r,this._updatePercentFromValue(),(this.isInteractsNow||this.needTriggerEvents)&&(this.onSlideStart&&"function"==typeof this.onSlideStart&&0===this.onSlideEventsCount&&this.onSlideStart(this.value,this.percent,this.position),this.onSlide&&"function"==typeof this.onSlide&&this.onSlide(this.value,this.percent,this.position)),this.onSlideEventsCount++}},{key:"_setBufferPosition",value:function(e){var t=!0;if(isFinite(e))e=parseFloat(e);else{if(!h.isString(e))return void console.warn("New position must be XXpx or XX%");e.indexOf("px")>0&&(t=!1),e=parseFloat(e)}if(isNaN(e))return void console.warn("New position is NaN");if(!this.options.bufferClass)return void console.warn("You disabled buffer, it's className is empty");var i=t?e:e/this.rangeSize*100;i<0&&(i=0),i>100&&(i=100),this.options.buffer=i;var n=this.options.borderRadius/this.rangeSize*100,s=i-n;s<0&&(s=0),this.vertical?(this.buffer.style.height=s+"%",this.buffer.style.bottom=.5*n+"%"):(this.buffer.style.width=s+"%",this.buffer.style.left=.5*n+"%"),this.element.setAttribute("data-buffer",i)}},{key:"_getPositionFromNode",value:function(e){for(var t=this.vertical?this.maxHandleX:0;null!==e;)t+=this.vertical?e.offsetTop:e.offsetLeft,e=e.offsetParent;return t}},{key:"_getRelativePosition",value:function(e){var t=this.range.getBoundingClientRect(),i=this.vertical?t.bottom:t.left,n=0,s=this.vertical?"pageY":"pageX";return void 0!==e[s]?n=e.touches&&e.touches.length?e.touches[0][s]:e[s]:void 0!==e.originalEvent?void 0!==e.originalEvent[s]?n=e.originalEvent[s]:e.originalEvent.touches&&e.originalEvent.touches[0]&&void 0!==e.originalEvent.touches[0][s]&&(n=e.originalEvent.touches[0][s]):e.touches&&e.touches[0]&&void 0!==e.touches[0][s]?n=e.touches[0][s]:!e.currentPoint||void 0===e.currentPoint.x&&void 0===e.currentPoint.y||(n=this.vertical?e.currentPoint.y:e.currentPoint.x),this.vertical&&(n-=window.pageYOffset),this.vertical?i-n:n-i}},{key:"_getPositionFromValue",value:function(e){var t=(e-this.min)/(this.max-this.min),i=t*this.maxHandleX;return isNaN(i)?0:i}},{key:"_getValueFromPosition",value:function(e){var t=e/(this.maxHandleX||1),i=this.step*Math.round(t*(this.max-this.min)/this.step)+this.min;return Number(i.toFixed(this.toFixed))}},{key:"_setValue",value:function(e,t){(e!==this.value||t)&&(this.element.value=e,this.value=e,a.triggerEvent(this.element,"input",{origin:this.identifier}))}}],[{key:"create",value:function(t,i){var n=function(t){var n=t[f];n||(n=new e(t,i),t[f]=n)};t.length?Array.prototype.slice.call(t).forEach(function(e){n(e)}):n(t)}}]),e}();t.default=m,e.exports=t.default},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.supportsRange=t.removeAllListenersFromEl=t.removeEventListeners=t.addEventListeners=t.insertAfter=t.triggerEvent=t.forEachAncestors=t.removeClass=t.addClass=t.hasClass=t.setCss=t.getDimension=t.getHiddenParentNodes=t.isHidden=t.detectIE=void 0;var n=i(0),s=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i]);return t.default=e,t}(n),r=t.detectIE=function(){var e=window.navigator.userAgent,t=e.indexOf("MSIE ");if(t>0)return parseInt(e.substring(t+5,e.indexOf(".",t)),10);if(e.indexOf("Trident/")>0){var i=e.indexOf("rv:");return parseInt(e.substring(i+3,e.indexOf(".",i)),10)}var n=e.indexOf("Edge/");return n>0&&parseInt(e.substring(n+5,e.indexOf(".",n)),10)},o=r(),a=!(!window.PointerEvent||o)&&{passive:!1},l=t.isHidden=function(e){return 0===e.offsetWidth||0===e.offsetHeight||!1===e.open},h=t.getHiddenParentNodes=function(e){for(var t=[],i=e.parentNode;l(i);)t.push(i),i=i.parentNode;return t},u=(t.getDimension=function(e,t){var i=h(e),n=i.length,s=[],r=e[t],o=function(e){void 0!==e.open&&(e.open=!e.open)};if(n){for(var a=0;a<n;a++)s[a]=i[a].style.display,i[a].style.display="block",i[a].style.height="0",i[a].style.overflow="hidden",i[a].style.visibility="hidden",o(i[a]);r=e[t];for(var l=0;l<n;l++)o(i[l]),i[l].style.display=s[l],i[l].style.height="",i[l].style.overflow="",i[l].style.visibility=""}return r},t.setCss=function(e,t){for(var i in t)e.style[i]=t[i];return e.style},t.hasClass=function(e,t){return new RegExp(" "+t+" ").test(" "+e.className+" ")});t.addClass=function(e,t){u(e,t)||(e.className+=" "+t)},t.removeClass=function(e,t){var i=" "+e.className.replace(/[\t\r\n]/g," ")+" ";if(u(e,t)){for(;i.indexOf(" "+t+" ")>=0;)i=i.replace(" "+t+" "," ");e.className=i.replace(/^\s+|\s+$/g,"")}},t.forEachAncestors=function(e,t,i){for(i&&t(e);e.parentNode&&!t(e);)e=e.parentNode;return e},t.triggerEvent=function(e,t,i){if(!s.isString(t))throw new TypeError("event name must be String");if(!(e instanceof HTMLElement))throw new TypeError("element must be HTMLElement");t=t.trim();var n=document.createEvent("CustomEvent");n.initCustomEvent(t,!1,!1,i),e.dispatchEvent(n)},t.insertAfter=function(e,t){return e.parentNode.insertBefore(t,e.nextSibling)},t.addEventListeners=function(e,t,i){t.forEach(function(t){e.eventListenerList||(e.eventListenerList={}),e.eventListenerList[t]||(e.eventListenerList[t]=[]),e.addEventListener(t,i,a),e.eventListenerList[t].indexOf(i)<0&&e.eventListenerList[t].push(i)})},t.removeEventListeners=function(e,t,i){t.forEach(function(t){var n=void 0;e.removeEventListener(t,i,!1),e.eventListenerList&&e.eventListenerList[t]&&(n=e.eventListenerList[t].indexOf(i))>-1&&e.eventListenerList[t].splice(n,1)})},t.removeAllListenersFromEl=function(e,t){function i(t){t===e._startEventListener&&this.el.removeEventListener(this.eventName,t,!1)}if(t.eventListenerList){for(var n in t.eventListenerList)t.eventListenerList[n].forEach(i,{eventName:n,el:t});t.eventListenerList={}}},t.supportsRange=function(){var e=document.createElement("input");return e.setAttribute("type","range"),"text"!==e.type}},function(e,t){}])});
//# sourceMappingURL=range-slider.min.js.map
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment