-
-
Save mhawksey/1285471 to your computer and use it in GitHub Desktop.
Guardian Tag Explorer
This file contains hidden or 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> | |
<html> | |
<head> | |
<title>Guardian Tag Explorer</title> | |
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?1.29.1"></script> | |
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js?1.29.1"></script> | |
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?1.29.1"></script> | |
<link href='http://fonts.googleapis.com/css?family=Ubuntu:500' rel='stylesheet' type='text/css'> | |
<link href="http://jqueryui.com/themes/base/jquery.ui.all.css" type="text/css" rel="stylesheet" /> | |
<script type="text/javascript" src="http://www.google.com/jsapi"></script> | |
<script type="text/javascript"> | |
google.load('visualization', '1', {packages: ['corechart']}); | |
</script> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script> | |
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js"></script> | |
<style type="text/css"> | |
body { | |
background:white; | |
overflow: hidden; | |
margin: 0; | |
font: 14px "Ubuntu", sans-serif; | |
} | |
.titleLink{ | |
font-family: georgia,serif; | |
color: #005689; | |
font-size: 18px; | |
line-height: 21px; | |
} | |
.articleTitle{ | |
margin-top:15px; | |
margin-bottom:3px; | |
} | |
a.titleLink{ | |
text-decoration: none; | |
} | |
a.titleLink:hover{ | |
text-decoration: underline; | |
} | |
.info { | |
display:inline; | |
} | |
.topbar{ | |
text-align:right; | |
} | |
svg { | |
position:absolute; | |
top:0px; | |
z-index:0; | |
} | |
#popup { | |
display:none; | |
position:absolute; | |
z-index:9999; | |
opacity:0.9; | |
filter:alpha(opacity=90); | |
text-align:left; | |
max-width:300px; | |
max-height:300px; | |
overflow:auto; | |
} | |
.topic { | |
padding: 10px; | |
margin-bottom:10px; | |
background: gainsboro; | |
font-size: 10px; | |
cursor:pointer; | |
} | |
.fieldname { | |
font-family:Arial, Helvetica, sans-serif; | |
color: gray; | |
} | |
.connections { | |
font-size:42px; | |
color:#069; | |
} | |
.popup { | |
background:white; | |
border:1px solid gainsboro; | |
padding:5px; | |
min-width: 150px; | |
} | |
#sidepanel, #tagpanel { | |
border:5px solid gray; | |
position:absolute; | |
top:80px; | |
right:10px; | |
padding:10px; | |
width:300px; | |
height:400px; | |
overflow:auto; | |
background:white; | |
opacity:0.95; | |
display:none; | |
} | |
#tagpanel { | |
left:10px; | |
} | |
rect { | |
fill: none; | |
pointer-events: all; | |
} | |
line { | |
stroke: #000; | |
stroke-width: 1.5px; | |
} | |
.string, .regexp { | |
color: #f39; | |
} | |
.keyword { | |
color: #00c; | |
} | |
.comment { | |
color: #555; | |
} | |
.number { | |
color: #369; | |
} | |
.class, .special { | |
color: #1181B8; | |
} | |
path.link { | |
fill: none; | |
stroke:#999; | |
stroke-width: 2px; | |
} | |
marker#licensing { | |
fill: lightgreen; | |
} | |
path.link.licensing { | |
stroke: lightgreen; | |
} | |
path.link.resolved { | |
stroke-dasharray: 0,2 1; | |
} | |
circle { | |
fill: #ccc; | |
stroke: #333; | |
stroke-width: 2px; | |
opacity: 1; | |
} | |
text { | |
font:10px Tahoma, Geneva, sans-serif; | |
pointer-events: none; | |
} | |
text.shadow { | |
stroke: #fff; | |
stroke-width: 3px; | |
} | |
.result{ | |
width:75px; | |
float:left; | |
} | |
.rowInfo{ | |
margin:5px 0; | |
} | |
.link { | |
stroke: #ccc; | |
} | |
.nodetext { | |
pointer-events: none; | |
font: 10px sans-serif; | |
color: white; | |
stroke: #ccc; | |
} | |
path.link { | |
fill: none; | |
stroke: #666; | |
stroke-width: 1.5px; | |
} | |
marker#licensing { | |
fill: green; | |
} | |
path.link.licensing { | |
stroke: green; | |
} | |
path.link.resolved { | |
stroke-dasharray: 0,2 1; | |
} | |
circle { | |
fill: #ccc; | |
stroke: #333; | |
stroke-width: 1.5px; | |
} | |
text { | |
font: 10px sans-serif; | |
pointer-events: none; | |
} | |
text.shadow { | |
stroke: #fff; | |
stroke-width: 3px; | |
stroke-opacity: .8; | |
} | |
</style> | |
<script type="text/javascript"> | |
/* =============================================================================== ** | |
jQuery UI | |
** =============================================================================== */ | |
/** | |
* | |
* credits for this plugin go to brandonaaron.net | |
* | |
* unfortunately his site is down | |
* | |
* @param {Object} up | |
* @param {Object} down | |
* @param {Object} preventDefault | |
*/ | |
jQuery.fn.extend({ | |
mousewheel: function(up, down, preventDefault) { | |
return this.hover( | |
function() { | |
jQuery.event.mousewheel.giveFocus(this, up, down, preventDefault); | |
}, | |
function() { | |
jQuery.event.mousewheel.removeFocus(this); | |
} | |
); | |
}, | |
mousewheeldown: function(fn, preventDefault) { | |
return this.mousewheel(function(){}, fn, preventDefault); | |
}, | |
mousewheelup: function(fn, preventDefault) { | |
return this.mousewheel(fn, function(){}, preventDefault); | |
}, | |
unmousewheel: function() { | |
return this.each(function() { | |
jQuery(this).unmouseover().unmouseout(); | |
jQuery.event.mousewheel.removeFocus(this); | |
}); | |
}, | |
unmousewheeldown: jQuery.fn.unmousewheel, | |
unmousewheelup: jQuery.fn.unmousewheel | |
}); | |
jQuery.event.mousewheel = { | |
giveFocus: function(el, up, down, preventDefault) { | |
if (el._handleMousewheel) jQuery(el).unmousewheel(); | |
if (preventDefault == window.undefined && down && down.constructor != Function) { | |
preventDefault = down; | |
down = null; | |
} | |
el._handleMousewheel = function(event) { | |
if (!event) event = window.event; | |
if (preventDefault) | |
if (event.preventDefault) event.preventDefault(); | |
else event.returnValue = false; | |
var delta = 0; | |
if (event.wheelDelta) { | |
delta = event.wheelDelta/120; | |
if (window.opera) delta = -delta; | |
} else if (event.detail) { | |
delta = -event.detail/3; | |
} | |
if (up && (delta > 0 || !down)) | |
up.apply(el, [event, delta]); | |
else if (down && delta < 0) | |
down.apply(el, [event, delta]); | |
}; | |
if (window.addEventListener) | |
window.addEventListener('DOMMouseScroll', el._handleMousewheel, false); | |
window.onmousewheel = document.onmousewheel = el._handleMousewheel; | |
}, | |
removeFocus: function(el) { | |
if (!el._handleMousewheel) return; | |
if (window.removeEventListener) | |
window.removeEventListener('DOMMouseScroll', el._handleMousewheel, false); | |
window.onmousewheel = document.onmousewheel = null; | |
el._handleMousewheel = null; | |
} | |
}; | |
$(document).ready(function() { | |
$(document).mousemove(function(e){ | |
$('#popup').css('left', e.pageX+10); | |
$('#popup').css('top', e.pageY-30); | |
//$('#popup').css('padding', '5px'); | |
}); | |
$(window).resize(function() { | |
resize(); | |
}); | |
//allow pressing "enter" | |
$('#search').keypress(function(e) { | |
if(e.which == 13) { | |
doBits(); | |
} | |
}); | |
$("body").mousewheel(function(i,intDelta){ | |
if (intDelta > 0 ) gravityUp(); | |
if (intDelta < 0 ) gravityDown(); | |
}); | |
var q = $.getUrlVar('q'); | |
if(typeof q !== 'undefined') | |
{ | |
$('#search').val(decodeURI(q)); | |
$("#permlink").attr('href', 'http://hawksey.info/tagexplorer/guardian.html?q='+encodeURI(q)); | |
getLiveData(q); | |
} | |
}); | |
$(function() { | |
$("#dialog").dialog({modal:true, height:400, width: 600, autoOpen:false}); | |
}); | |
$.extend({ | |
getUrlVars: function(){ | |
var vars = [], hash; | |
var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); | |
for(var i = 0; i < hashes.length; i++) | |
{ | |
hash = hashes[i].split('='); | |
vars.push(hash[0]); | |
vars[hash[0]] = hash[1]; | |
} | |
return vars; | |
}, | |
getUrlVar: function(name){ | |
return $.getUrlVars()[name]; | |
} | |
}); | |
//+ Jonas Raoni Soares Silva | |
//@ http://jsfromhell.com/geral/utf-8 [rev. #1] | |
UTF8 = { | |
encode: function(s){ | |
for(var c, i = -1, l = (s = s.split("")).length, o = String.fromCharCode; ++i < l; | |
s[i] = (c = s[i].charCodeAt(0)) >= 127 ? o(0xc0 | (c >>> 6)) + o(0x80 | (c & 0x3f)) : s[i] | |
); | |
return s.join(""); | |
}, | |
decode: function(s){ | |
for(var a, b, i = -1, l = (s = s.split("")).length, o = String.fromCharCode, c = "charCodeAt"; ++i < l; | |
((a = s[i][c](0)) & 0x80) && | |
(s[i] = (a & 0xfc) == 0xc0 && ((b = s[i + 1][c](0)) & 0xc0) == 0x80 ? | |
o(((a & 0x03) << 6) + (b & 0x3f)) : o(128), s[++i] = "") | |
); | |
return s.join(""); | |
} | |
}; | |
function doBits(){ | |
qterm = $('#search').val(); | |
$("#permlink").attr('href', 'http://hawksey.info/tagexplorer/guardian.html?q='+encodeURI(qterm)); | |
getLiveData(qterm); | |
} | |
function init(){ | |
force = d3.layout.force() | |
.charge(-100) | |
.gravity(0.1) | |
.linkDistance(150) | |
.size([w, h]); | |
nodes = force.nodes(), | |
links = force.links(); | |
svg = d3.select("body").append("svg:svg") | |
.attr("width", w) | |
.attr("height", h); | |
force.on("tick", function() { | |
svg.selectAll("path").attr("d", function(d) { | |
var dx = (d.target.x - d.source.x), | |
dy = (d.target.y - d.source.y), | |
dr = Math.sqrt(dx * dx + dy * dy); | |
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y; | |
}); | |
svg.selectAll("circle").attr("transform", function(d) { | |
return "translate(" + d.x + "," + d.y + ")"; | |
}); | |
svg.selectAll("text").attr("transform", function(d) { | |
return "translate(" + d.x + "," + d.y + ")"; | |
}); | |
// If zooming, change transformation | |
}) | |
// | |
restart(); | |
} | |
/* =============================================================================== ** | |
Restart | |
** =============================================================================== */ | |
function restart() { | |
// Per-type markers, as they don't inherit styles. | |
var path = svg.append("svg:g").selectAll("path") | |
.data(force.links()) | |
.enter().append("svg:path") | |
.attr("class", function(d) { return "link " + d.type; }); | |
var circle = svg.append("svg:g").selectAll("circle") | |
.data(force.nodes()) | |
.enter().append("svg:circle") | |
.attr("r", 6) | |
.on("mouseover", function(d) {showThumb(d.name,d)}) | |
.on("click", function(d) { findTag(d.name,d,d.type);}) | |
//getLiveData(d.name); | |
.on("mouseout", function() { | |
$('#popup').hide(); | |
$('#popup').html(''); | |
}) | |
.call(force.drag); | |
var text = svg.append("svg:g").selectAll("g") | |
.data(force.nodes()) | |
.enter().append("svg:g"); | |
// A copy of the text with a thick white stroke for legibility. | |
text.append("svg:text") | |
.attr("x", 8) | |
.attr("y", ".31em") | |
.attr("class", "shadow") | |
.attr("style", function(d) { | |
var size = parseInt(d.connections); | |
if(size > 35) size = 35; | |
//size = Math.sqrt(d.connections)*8; | |
if(size == 0) size = 4; | |
return "font-size:"+size+"px" }) | |
.text(function(d) { return d.name; }); | |
text.append("svg:text") | |
.attr("x", 8) | |
.attr("y", ".31em") | |
.attr("style", function(d) { | |
var size = parseInt(d.connections); | |
if(size > 35) size = 35; | |
//size = Math.sqrt(d.connections)*8; | |
if(size == 0) size = 4; | |
return "font-size:"+size+"px" }) | |
.text(function(d) { return d.name; }); | |
//circle.exit().remove(); | |
force.start(); | |
} | |
/* =============================================================================== ** | |
Globals | |
** =============================================================================== */ | |
var w,h,force,nodes,links,svg | |
var w = $(window).width(), | |
h = $(window).height(); | |
var next_page = '1'; | |
var refresh_url = ''; | |
var q = ''; | |
var doMore = false; | |
var nodes = []; | |
var links = []; | |
var nodeList = []; | |
/* =============================================================================== ** | |
Get tweets | |
** =============================================================================== */ | |
function getLiveData(q,doMore) | |
{ | |
console.log('next...' + q); | |
//console.log("Getting more " +doMore); | |
if (doMore == true){ | |
doMore = false; | |
console.log("Getting more"); | |
q = q; | |
} else { | |
nodes = []; | |
links = []; | |
nodeList = []; | |
nodeCount = []; | |
next_page = 1; | |
$('#searchCount').html(0); | |
$('svg').remove(); | |
init(); | |
} | |
console.log('counting'); | |
var url = 'http://pipes.yahoo.com/pipes/pipe.run?_id=3710670cffd070d96242785c7a4a727d&_render=json&q='+encodeURI(q)+'&page='+next_page; | |
$.getJSON(url, function(res) | |
{ | |
var data = res.value.items[0]; | |
console.log('Results ' + data.results.length); | |
if(typeof data.didYouMean != "undefined"){ | |
alert('Did you mean ' + data.didYouMean); | |
did_you_mean = data.didYouMean; | |
} | |
if(data.pages == "0") | |
{ | |
alert('no results found'); | |
return false; | |
} | |
var searchCount = parseInt($('#searchCount').html())+data.results.length; | |
$('#searchCount').html(searchCount); | |
$('#searchTotal').html(data.total); | |
if(data.currentPage != data.pages){ | |
next_page = parseInt(data.currentPage)+1; | |
console.log("Next page "+next_page); | |
} | |
var dr=data['results']; | |
$.each(dr,function(i,result){ | |
// Collect a list of tags associated with the current article | |
var tags=[]; | |
var story = { id: result['id'], | |
sectionId: result['sectionName'], | |
sectionName: result['sectionName'], | |
webPublicationDate: result['webPublicationDate'], | |
webTitle: result['webTitle'], | |
webUrl: result['webUrl'], | |
apiUrl: result['apiUrl'] | |
}; | |
// Now handle the article tags | |
if (typeof result['tags'] != "undefined") { | |
if($.isArray(result['tags'])){ | |
$.each(result['tags'],function(j,tag){ | |
processTag(tag,story); | |
$.each(result['tags'],function(k,tagB){ | |
if (tag['id'] != tagB['id']){ | |
links.push({source: {id: tag['id']}, target: {id: tagB['id']}}); | |
} | |
}); | |
}); | |
} else { | |
processTag(result['tags'],story); | |
} | |
} | |
}); | |
// got all nodes and links count connections and add source target | |
$.each(nodes,function(l,val2){ | |
nodes[l].connections = 0; | |
}); | |
$.each(links,function(l,aLink){ | |
$.each(nodes,function(l,val2){ | |
if (aLink.source.id == val2.id || aLink.target.id == val2.id){ | |
nodes[l].connections = nodes[l].connections +1; | |
} | |
}); | |
aLink.source = findNodePos(aLink.source.id); | |
aLink.target = findNodePos(aLink.target.id); | |
}); | |
$('#nodeCount').html(nodes.length); | |
$('#linkCount').html(links.length); | |
restart(); | |
}); | |
} | |
function processTag(tag,story){ | |
tag.stories = [story]; | |
if($.inArray(tag['id'],nodeList) == -1){ | |
tag.connections = 0; | |
tag.name = tag.webTitle; | |
nodes.push(tag); | |
nodeList.push(tag['id']); | |
} else { | |
if (typeof nodes[findNodePos(tag['id'])].stories != "undefined") { | |
nodes[findNodePos(tag['id'])].stories.push(story); | |
} | |
} | |
} | |
function findNodePos(element) | |
{ | |
var foundin = 0; | |
$.each(nodes,function(i,val){ | |
if (val.id == element) | |
{ | |
foundin = i; | |
} | |
}); | |
return foundin; | |
} | |
/* =============================================================================== ** | |
Display stuff | |
** =============================================================================== */ | |
function showThumb(index,d) | |
{ | |
var box = '<table cellpadding="2" width="100%"><tr><td align="center"><span class="connections">'+d.connections+'</span><br><span class="fieldname">connections</span></td>'; | |
box += '<td align="center"><span class="connections">'+d.stories.length+'</span><br><span class="fieldname">articles</span></td></tr></table>'; | |
box += '<table cellpadding="2"><tr><td align="right"><span class="fieldname">tag </span></td><td>'+d.name+'</td></tr>'; | |
box += '<tr><td align="right"><span class="fieldname">tag id</span></td><td>'+d.id+'</td></tr>'; | |
box += '</table>'; | |
$('#popup').fadeIn(); | |
$('#popup').html('<div class="popup">'+box+'</div>'); | |
} | |
var type = ''; | |
function findTag(id,d,type) | |
{ | |
$('#sidepanel').fadeOut(); | |
$('#sidepanel').fadeIn('slow'); | |
resize(); | |
var box = '<div class="topbar"><a href="javascript:void()" onClick="javascript:$(\'#sidepanel\').fadeOut()">X</a></div>'; | |
box += '<table cellpadding="2" width="100%"><tr><td align="center"><span class="connections">'+d.connections+'</span><br><span class="fieldname">connections</span></td>'; | |
box += '<td align="center"><span class="connections">'+d.stories.length+'</span><br><span class="fieldname">articles</span></td></tr></table>'; | |
box += '<table cellpadding="2"><tr><td align="right"><span class="fieldname">tag </span></td><td>'+d.name+'</td></tr>'; | |
box += '<tr><td align="right"><span class="fieldname">tag id</span></td><td>'+d.id+'</td></tr>'; | |
box += '</table>'; | |
$.each(d.stories,function(i,val){ | |
box += '<div class="articleLink"><div class="articleTitle"><a href="'+val.webUrl+'" target="_blank" class="titleLink">'+val.webTitle+'</a></div>'; | |
box += '<div class="infoRow"><div class="fieldname result">Section</div> <div class="info">'+val.sectionName+'</div></div>'; | |
box += '<div class="infoRow"><div class="fieldname result">Publication</div> <div class="info">'+$.datepicker.formatDate('D d MM yy', new Date(val.webPublicationDate))+'</div></div></div>'; | |
}); | |
$('#sidepanel').html(box); | |
} | |
function showTagDist() | |
{ | |
$('#tagpanel').fadeOut(); | |
$('#tagpanel').fadeIn('slow'); | |
drawVisualization(); | |
resize(); | |
} | |
function cmp(a, b) { | |
return b[1].localeCompare(a[1]); | |
} | |
function drawVisualization() { | |
// Create and populate the data table. | |
var data = new google.visualization.DataTable(); | |
data.addColumn('string', 'Tag'); | |
data.addColumn('number', 'Connections'); | |
data.addRows(nodes.length); | |
//for (var i = 0; i < raw_data.length; ++i) { | |
for (var j = 0; j < nodes.length; ++j) { | |
data.setValue(j, 0, nodes[j].name); | |
data.setValue(j, 1, nodes[j].connections); | |
} | |
//} | |
data.sort([{column: 1, desc: true}]); | |
// Create and draw the visualization. | |
new google.visualization.BarChart(document.getElementById('visualization')). | |
draw(data, | |
{ width:290, | |
height:nodes.length*10, | |
title:'Tag Occurences', | |
chartArea: {left:0,top:20,width:"100%",height:"100%"}, | |
colors: ['#069'], | |
legend: 'none'} | |
); | |
} | |
/* =============================================================================== ** | |
Redraw | |
** =============================================================================== */ | |
function resize() | |
{ | |
var h = $(window).height(); | |
var w = $(window).width(); | |
//svg window | |
//svg.attr("width",w); | |
//svg.attr("height",h); | |
//side panel | |
h = h - 145; | |
$('#sidepanel').height(h); | |
$('#tagpanel').height(h); | |
} | |
/* =============================================================================== ** | |
Controls | |
** =============================================================================== */ | |
function gravityUp() | |
{ | |
var value = force.gravity()+0.01; | |
//var dis = force.linkDistance()-0.01; | |
var chrg = force.charge()+1; | |
if(value > 1) value = 1; | |
force.gravity(value); | |
//force.linkDistance(dis); | |
//force.charge(chrg); | |
force.start(); | |
} | |
function gravityDown() | |
{ | |
var value = force.gravity()-0.01; | |
var dis = force.linkDistance()+0.01; | |
var chrg = force.charge()-1; | |
if(value < 0) value = 0; | |
force.gravity(value); | |
//force.linkDistance(dis); | |
//force.charge(chrg); | |
force.start(); | |
} | |
</script> | |
<body style="" onLoad="init()"> | |
<div style="padding:5px;position:absolute;top:0px; text-align:center height:60px; background:#444; width:100%; z-index:1; opacity:0.9;"> | |
<table align="" cellpadding="0" cellspacing="0" border="0"> | |
<tr> | |
<td valign="top" rowspan="2"><span style="padding:5px; font-size:32px;color:gainsboro;">GuardianTagExplorer</span></td> | |
<td valign="top"><span style="padding:5px; font-size:12px;color:gainsboro;">What</span> </td> | |
<td valign="top"></td> | |
<td valign="top"></td> | |
<td rowspan="2" valign="bottom"><span style="padding:5px; font-size:12px;color:gainsboro;">[<a id="permlink" href="http://hawksey.info/tagexplorer/guardian.html?q=" style="color:#fff;">permalink</a>]</span></td> | |
</tr> | |
<tr> | |
<td valign="top"><input type="text" class="search" id="search" style="width:250px;" value=""the open university""> | |
</td> | |
<td valign="top"><input value="go" class="" type="submit" onClick="doBits();"> | |
</td> | |
<td valign="top"> </td> | |
</tr> | |
</table> | |
</div> | |
<div style="position:absolute;bottom:10px;left:-3px;background-color:#ffffff;display:block;z-index:2;width:100%;text-align:right;">displaying: <span id="searchCount">0</span> records of a possible <span id="searchTotal">-</span> | <span id="nodeCount">-</span> <a href="javascript:void(0);" onClick="showTagDist()">tags</a> <span id="linkCount">-</span> edges [<a href="javascript:void(0);" onClick="getLiveData($('#search').val(),true)">+</a>] | |
<div style="float:left;text-align:left"> <a href="http://mbostock.github.com/d3/" target="_blank">d3.js</a> and the <a href="http://www.guardian.co.uk/open-platform" target="_blank">Guardian Open Platform</a> glued togther with lots of ideas and code from <a href="http://ouseful.info">Tony Hirst</a> and <a href="http://gis.yohman.com" target="_blank">yohman</a> by <a href="http://mashe.hawksey.info" target="_blank">mhawksey</a>. <a href="http://mashe.hawksey.info/2011/10/guardian-tag-explorer/" target="_blank">More info</a>... </div> | |
</div> | |
<!-- | |
Side panel for tweet display | |
--> | |
<div id="sidepanel" style="z-index:100"></div> | |
<div id="tagpanel" style="z-index:100"><div class="topbar"><a href="javascript:void()" onClick="javascript:$('#tagpanel').fadeOut()">X</a></div><div id="visualization" style="height: 1000px; width: 290px;"></div></div> | |
<!-- | |
Popup | |
--> | |
<div id="popup" style=""></div> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment