Commit 8e4975ab authored by arturo's avatar arturo

>>> continue

parent 6014c01f
......@@ -12,6 +12,7 @@ import Data.HTTP.Method (Method(..))
import Data.Maybe (Maybe(..))
import Effect.Aff (Aff, launchAff_)
import Effect.Class (liftEffect)
import Gargantext.Components.PhyloExplorer.Layout (layout)
import Gargantext.Components.PhyloExplorer.Types (PhyloDataset)
import Gargantext.Sessions (Session)
import Gargantext.Types (NodeID)
......@@ -44,30 +45,12 @@ phyloLayoutCpt = here.component "phyloLayout" cpt where
Left err -> log2 "error" err
Right res -> T.write_ (Just res) fetchedDataBox
pure $ case fetchedData of
pure case fetchedData of
Nothing -> mempty
Just fdata ->
H.div
{ className:"phyloCorpus" }
[ H.text $ show fdata ]
-- ,
-- infoCorpusR
-- ,
-- infoPhyloR
-- ,
-- timelineR
-- ,
-- isolineR
-- ,
-- wordcloudR
-- ,
-- phyloR
Just phyloDataset -> layout { phyloDataset } []
fetchPhyloJSON :: Aff (Either String PhyloDataset)
-- fetchPhyloJSON :: ReadForeign PhyloDataset => Aff Unit
fetchPhyloJSON =
let
request = AX.defaultRequest
......
module Gargantext.Components.PhyloExplorer.Draw where
module Gargantext.Components.PhyloExplorer.Layout
( layout
) where
import Gargantext.Prelude
import DOM.Simple.Console (log2)
import Data.Array as Array
import Data.Int (fromString)
import Data.Maybe (maybe)
import Data.String as String
import Gargantext.Components.PhyloExplorer.Types (PhyloDataset(..))
import Gargantext.Utils (nbsp)
import Gargantext.Utils.Reactix as R2
import Reactix as R
import Reactix.DOM.HTML as H
import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.PhyloExplorer"
type Props =
( phyloDataset :: PhyloDataset
)
layout :: R2.Component Props
layout = R.createElement layoutCpt
layoutCpt :: R.Component Props
layoutCpt = here.component "layout" cpt where
cpt { phyloDataset: (PhyloDataset phyloDataset)
} _ = do
-- States
let
{ phyloDocs
, phyloBranches
, phyloGroups
, phyloTerms
, phyloPeriods
, phyloFoundations
, phyloSources
} = phyloDataset
nbDocs = parseInt phyloDocs
nbBranches = parseInt phyloBranches
nbGroups = parseInt phyloGroups
nbTerms = parseInt phyloTerms
nbPeriods = parseInt phyloPeriods
nbFoundations = parseInt phyloFoundations
sourcesBox <- T.useBox (mempty :: Array String)
sources <- T.useLive T.unequal sourcesBox
-- Hooks
R.useEffectOnce' $ do
sources' <- pure $ stringArrToArr phyloSources
T.write_ sources' sourcesBox
-- @hightlightSource
let
highlightSource = \_ -> unit
-- Render
pure $
H.div
{ className: "phylo" }
[
-- <!-- row 1 -->
H.div
{ className: "phylo-title font-bold" }
[ H.text "Mèmiescape" ]
,
H.div
{ className: "phylo-folder" }
[
-- <!-- title bar (static mode) -->
H.label
{ id: "phyloName"
, className: "phylo-name"
}
[]
,
-- <!-- folder bar -->
-- H.label
-- { id: "file-label"
-- , for: "file-path"
-- , className: "input-file"
-- }
-- [ H.text "load a phylomemy →" ]
-- ,
-- H.input
-- { id: "file-path"
-- , type: "file"
-- , maxLength: "10"
-- }
-- ,
-- H.label
-- { id: "file-name"
-- , className: "input-name"
-- }
-- []
-- ,
-- H.button
-- { id: "draw"
-- , className: "button draw"
-- }
-- [ H.text "draw" ]
-- ,
-- <!-- source selector -->
R2.select
{ id: "checkSource"
, className: "select-source"
, defaultValue: ""
, on: { change: \_ -> unit }
} $
[
H.option
{ disabled: true
, value: ""
}
[ H.text "select a source ↴" ]
,
H.option
{ value: "unselect" }
[ H.text "unselect source ✕" ]
]
<>
flip Array.mapWithIndex sources
( \idx val ->
H.option
{ value: idx }
[ H.text val ]
)
,
-- <!-- search bar -->
H.label
{ id: "search-label"
, className: "search-label"
}
[ H.text "find a term →" ]
,
H.input
{ id: "search-box"
, type: "text"
, className: "search"
}
,
H.input
{ id: "search-autocomplete"
, text: "text"
, className: "autocomplete"
, disabled: true
, value: ""
}
]
,
-- <!-- row 2 & 3 -->
phyloCorpus {} []
,
phyloCorpusInfo
{ nbDocs, nbFoundations, nbPeriods }
[]
,
-- H.div
-- { id: "phyloHow"
-- , className: "phylo-how"
-- }
-- []
-- ,
phyloPhylo {} []
,
phyloPhyloInfo
{ nbTerms, nbGroups, nbBranches }
[]
,
H.div
{ id: "phyloIsoLine"
, className: "phylo-isoline-info"
}
[
H.div
{ className: "btn-group" }
[
H.button
{ id: "reset"
, className: "button reset"
}
[
H.i
{ className: "fas fa-expand-arrows-alt" }
[]
]
,
H.button
{ id: "label"
, className: "button label"
}
[
H.i
{ className: "fas fa-dot-circle" }
[]
]
,
H.button
{ id: "heading"
, className: "button heading"
}
[
H.i
{ className: "fas fa-sort-alpha-down" }
[]
]
,
H.button
{ id: "export"
, className: "button export"
}
[
H.i
{ className: "fas fa-camera" }
[]
]
]
]
,
-- <!-- row 4 -->
H.div
{ id: "phyloScape"
, className: "phylo-scape"
}
[]
,
H.div
{ id: "phyloTimeline"
, className: "phylo-timeline"
}
[]
,
H.div
{ id: "phyloGraph"
, className: "phylo-graph"
}
[]
,
-- <!-- row 5 -->
H.div
{ className: "phylo-footer font-bold font-small"
}
[ H.text "iscpif // cnrs // 2021" ]
]
parseInt :: String -> Int
parseInt s = maybe 0 identity $ fromString s
stringArrToArr :: String -> Array String
stringArrToArr
= String.replace (String.Pattern "[") (String.Replacement "")
>>> String.replace (String.Pattern "]") (String.Replacement "")
>>> String.split (String.Pattern ",")
>>> Array.filter (\s -> not eq 0 $ String.length s)
--------------------------------------------------------
type PhyloCorpusProps = ()
phyloCorpus :: R2.Component PhyloCorpusProps
phyloCorpus = R.createElement phyloCorpusCpt
phyloCorpusCpt :: R.Component PhyloCorpusProps
phyloCorpusCpt = here.component "phyloCorpus" cpt where
cpt _ _ = do
-- Render
pure $
H.div
{ id: "phyloCorpus"
, className: "phylo-corpus"
}
[ H.text "corpus" ]
---------------------------------------------------------
type PhyloPhyloProps = ()
phyloPhylo :: R2.Component PhyloPhyloProps
phyloPhylo = R.createElement phyloPhyloCpt
phyloPhyloCpt :: R.Component PhyloPhyloProps
phyloPhyloCpt = here.component "phyloPhylo" cpt where
cpt _ _ = do
-- Render
pure $
H.div
{ id: "phyloPhylo"
, className: "phylo-phylo"
}
[ H.text "phylomemy" ]
---------------------------------------------------------
type PhyloCorpusInfoProps =
( nbDocs :: Int
, nbFoundations :: Int
, nbPeriods :: Int
)
phyloCorpusInfo :: R2.Component PhyloCorpusInfoProps
phyloCorpusInfo = R.createElement phyloCorpusInfoCpt
phyloCorpusInfoCpt :: R.Component PhyloCorpusInfoProps
phyloCorpusInfoCpt = here.component "phyloCorpusInfo" cpt where
cpt props _ = do
-- Render
pure $
H.div
{ id: "phyloCorpusInfo"
, className: "phylo-corpus-info"
}
[
H.span
{}
[
H.b {} [ H.text $ show props.nbDocs ]
, H.text $ nbsp 1 <> "docs"
]
,
H.span
{}
[
H.b {} [ H.text $ show props.nbFoundations ]
, H.text $ nbsp 1 <> "foundations"
]
,
H.span
{}
[
H.b {} [ H.text $ show props.nbPeriods ]
, H.text $ nbsp 1 <> "periods"
]
]
---------------------------------------------------------
type PhyloPhyloInfoProps =
( nbTerms :: Int
, nbGroups :: Int
, nbBranches :: Int
)
phyloPhyloInfo :: R2.Component PhyloPhyloInfoProps
phyloPhyloInfo = R.createElement phyloPhyloInfoCpt
phyloPhyloInfoCpt :: R.Component PhyloPhyloInfoProps
phyloPhyloInfoCpt = here.component "phyloPhyloInfo" cpt where
cpt props _ = do
-- Render
pure $
H.div
{ id: "phyloPhyloInfo"
, className: "phylo-phylo-info"
}
[
H.span
{}
[
H.b
{ id: "phyloTerms" }
[ H.text $ show props.nbTerms ]
, H.text $ nbsp 1 <> "terms"
]
,
H.span
{}
[
H.b
{ id: "phyloGroups" }
[ H.text $ show props.nbGroups ]
, H.text $ nbsp 1 <> "groups"
]
,
H.span
{}
[
H.b
{ id: "phyloBranches" }
[ H.text $ show props.nbBranches ]
, H.text $ nbsp 1 <> "branches"
]
]
'use strict';
// set javascript date from a string year
function yearToDate(year) {
var d = new Date()
d.setYear(parseInt(year));
d.setMonth(0);
d.setDate(1);
return d;
}
function stringToDate(str) {
var arr = (str.replace('"','')).split('-');
var d = new Date();
d.setYear(parseInt(arr[0]));
d.setMonth(parseInt(arr[1]));
d.setMonth(d.getMonth() - 1);
d.setDate(parseInt(arr[2]));
return d;
}
function utcStringToDate(str) {
var arr = ((str.replace('"','')).replace(' UTC','')).split(/[\s-:]+/);
var d = new Date();
d.setYear(parseInt(arr[0]));
d.setMonth(parseInt(arr[1]));
d.setDate(parseInt(arr[2]));
d.setHours(parseInt(arr[3]),parseInt(arr[4]),parseInt(arr[5]))
return d;
}
function stringArrToArr(str) {
var arr = ((str.replace('["','')).replace('"]','')).split('","');
return arr;
}
function intArrToArr(int) {
var arr = ((int.replace('[','')).replace(']','')).split(',');
return arr;
}
function yearToDateHacked(w) {
var d = new Date(2020,0,0);
d.setDate(d.getDate() + (w * 7));
return d
}
function toCoord(id) {
var x = parseFloat(d3.select("#group" + id).attr("cx")),
y = parseFloat(d3.select("#group" + id).attr("cy"));
return [x,y];
}
function toXLabels(branches, groups, xMax) {
var xLabels = branches.map(function(b) {
var bId = b.bId,
xs = groups.filter(g => g.bId == bId).map(g => g.x),
inf = Math.min(...xs),
sup = Math.max(...xs);
return { x : b.x2,
label : b.label.replace(/\"/g, '').replace(/\|/g, ''),
inf : inf,
sup : sup,
bId : bId};
})
return xLabels.map(function(b,i){
var prec = 0,
succ = xMax;
if (i != 0)
prec = xLabels[i -1].sup
if (i != (xLabels.length - 1))
succ = xLabels[i + 1].inf
var w = Math.min(...[(b.x - prec) / 2,(succ - b.x) / 2]),
inf = b.x - w,
sup = b.x + w;
inf = (b.inf < inf) ? b.inf : inf + (w / 10);
sup = (b.sup > sup) ? b.sup : sup - (w / 10);
return { x : (sup + inf) / 2,
label : b.label,
inf : inf,
sup : sup,
bId : b.bId};
})
}
function xOverFlow(ticks,arr) {
ticks.each(function(t,i){
var text = d3.select(this),
chars = d3.select(this).text().split('').reverse(),
nb = chars.length,
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
line = [],
tspan = text.attr("bId",arr[i][1]).text(null).append("tspan").attr("x", 0).attr("y", -14).attr("dy", dy + "em").attr("bId","");
while ((char = chars.pop()) && (tspan.node().getComputedTextLength() < (arr[i][0] - 2))) {
line.push(char);
tspan.text(line.join(''));
}
if (line.length != nb) {
line.slice(-3)
tspan.text(line.join('') + '...')
}
})
}
var branchFocus = [];
function addMarkX(ticks,ws,ids) {
ticks.each(function(t,i){
d3.select(this)
.append("rect")
.attr("x","-" + (ws[i]/2 + 1))
.attr("y","-4")
.attr("height","8")
.attr("width",ws[i] + 1)
.attr("class","x-mark")
.attr("id", "xmark-" + ids[i])
if (branchFocus.includes("" + ids[i])) {
d3.select("#xmark-" + ids[i]).style("fill","#F0684D");
}
})
}
function setMarkYLabel(labels) {
labels.each(function(l,i){
d3.select(this).attr("dx","-5").attr("class","y-label").attr("id","y-label-" + d3.timeYear(l).getFullYear());
})
}
function addMarkY(ticks) {
ticks.each(function(d,i){
if (d3.timeYear(d) < d) {
// month
d3.select(this)
.append("circle").attr("cx",0).attr("cy",0).attr("r",3).attr("class","y-mark-month");
} else {
var from = d3.timeYear(d).getFullYear();
// year
d3.select(this)
.append("circle").attr("cx",0).attr("cy",0).attr("r",6).attr("class","y-mark-year-outer").attr("id","y-mark-year-outer-" + from);
d3.select(this)
.append("circle").attr("cx",0).attr("cy",0).attr("r",3).attr("class","y-mark-year-inner").attr("id","y-mark-year-inner-" + from);
}
})
}
function addDays(date, days) {
var result = new Date(date);
result.setDate(result.getDate() + days);
return result;
}
function removeDays(date, days) {
var result = new Date(date);
result.setDate(result.getDate() - days);
return result;
}
function setYDomain(labels) {
var ts = ["week","month","day","year","epoch"];
console.log(labels)
if (ts.includes(window.timeScale)) {
labels = labels.sort(function(d1,d2){return d1.from - d2.from;})
}
var inf = (labels[0]).from,
sup = (labels[labels.length - 1]).to;
if (window.timeScale == "week") {
inf = addDays(inf,7)
sup = addDays(sup,7)
} else if (window.timeScale == "month") {
inf = removeDays(inf,31)
sup = addDays(sup,31)
} else if (window.timeScale == "day") {
inf = removeDays(inf,1)
sup = addDays(sup,1)
} else if (window.timeScale == "year") {
inf = removeDays(inf,365)
sup = addDays(sup,365)
} else if (window.timeScale == "epoch") {
inf = inf
sup = sup
} else {
inf = new Date((inf.getFullYear() - 1),0,0);
sup = new Date((sup.getFullYear() + 1),0,0);
}
// inf = new Date((inf - 1),6,0);
// inf = new Date((1950 - 1),6,0);
// sup = new Date((sup + 1),0,0);
return [inf,sup];
}
function groupTermsBy(elements, attr) {
let grouped = {},
curr = "";
for (var i = 0; i < elements.length; i++) {
let from = elements[i].getAttribute(attr)
if (curr != from) {
grouped[from] = [[(elements[i]).getAttribute("gx"),(elements[i]).getAttribute("gy"),(elements[i]).getAttribute("bid")]];
curr = from
} else {
grouped[from].push([(elements[i]).getAttribute("gx"),(elements[i]).getAttribute("gy"),(elements[i]).getAttribute("bid")]);
}
}
return Object.values(grouped);
};
function findValueByPrefix(prefix) {
for(var i = 0; i < window.terms.length; i++) {
var object = (window.terms)[i];
if(object.label.toLowerCase().startsWith(prefix.toLowerCase()))
{
return object;
}
}
return null;
}
function highlightSource() {
let checkSource = document.getElementById("checkSource");
let value = checkSource.options[checkSource.selectedIndex].value;
let groups = d3.selectAll(".group-inner");
if (window.highlighted == true) {
(groups.filter(".source-focus").nodes()).map(g => g.classList.add("group-unfocus"))
}
// unselected all the groups
(groups.nodes()).map(g => g.classList.remove("source-focus"));
if (window.ldView == true) {
(groups.nodes()).map(function(g){return g.style.fill = "#f5eee6";});
} else {
(groups.nodes()).map(function(g){return g.style.fill = "#fff";});
}
d3.selectAll(".peak").classed("peak-focus-source", false);
// select the relevant ones
if (value != "unselect") {
let selected = groups.filter(".source-" + value).nodes();
drawWordCloud(selected)
selected.map(function(g){
g.classList.remove("group-unfocus");
g.classList.add("source-focus");
g.style.fill = "#a6bddb";
let bid = g.getAttribute("bId")
d3.select("#peak-" + bid)
.classed("peak-focus-source", true);
})
}
}
function drawWordCloud (groups) {
let labels = {},
count = 0;
d3.selectAll(".word-cloud").remove();
groups.forEach(function(g){
let gid = (g.getAttribute("id")).replace("group","");
let terms = d3.selectAll(".term").filter(".g-" + gid).nodes();
terms.forEach(function(t){
count ++;
if (labels[t.getAttribute("fdt")] == undefined) {
labels[t.getAttribute("fdt")] = {"freq" : 1, "label" : t.getAttribute("label")}
} else {
labels[t.getAttribute("fdt")].freq = labels[t.getAttribute("fdt")].freq + 1
}
})
});
labels = (Object.values(labels)).map(function(l){
return {"freq":(l.freq / count),"label":l.label};
}).sort(function(l1,l2){
return l2.freq - l1.freq;
})
let y = 20
let opacity = d3.scaleLinear().domain([Math.log((labels[labels.length - 1]).freq),Math.log((labels[0]).freq)]).range([0.5,1]);
labels.forEach(function(l){
y = y + 12;
window.svg3.append("text")
.attr("class","word-cloud")
.attr("x", 10)
.attr("y", y)
.style("opacity", opacity(Math.log(l.freq)))
.text(l.label);
})
}
function drawPhylo(branches, periods, groups, links, aLinks, bLinks, frame) {
/* ** draw the sources box ** */
document.querySelector("#checkSource").style.display = "inline-block";
/* ** draw the search box ** */
var inputSearch = document.getElementById("search-box");
inputSearch.style.visibility = "visible";
inputSearch.addEventListener("keyup", autocomplete);
document.getElementById("search-autocomplete").style.visibility = "visible";
document.getElementById("search-label").style.visibility = "visible";
/* ** draw the isoline ** */
d3.select('#phyloIsoLine').style("background","#EBE4DD");
var div0 = d3.select('#phyloIsoLine').node().getBoundingClientRect(),
m0 = {t:5,r:5,b:5,l:5},
w0 = div0.width,
h0 = div0.height;
var svg0 = d3
.select('#phyloIsoLine')
.append("svg")
.attr("width", w0)
.attr("height",h0)
.append("g");
var xScale0 = d3.scaleLinear().domain([0,Math.max(...branches.map(b => b.x1))]).range([2 * m0.l, w0 - 2 * m0.l]),
yScale0 = d3.scaleLinear().domain(d3.extent(branches, b => b.y)).nice().range([2 * m0.t, h0 - 2 * m0.t]);
var density =
d3.contourDensity()
.x(function(b) { return xScale0(b.x1); })
.y(function(b) { return yScale0(b.y); })
.size([w0, h0])
.thresholds(Math.round(branches.length / 2))
(branches)
/* shadows and lights */
svg0.append("g")
.selectAll("circle")
.data(branches)
.enter()
.append("circle")
.attr("cx", b => xScale0(b.x1))
.attr("cy", b => yScale0(b.y))
.attr("r","55")
.attr("id",b => "peak-shadow" + b.bId)
.attr("visibility","visible")
.style("fill","#f5eee6");
svg0.selectAll("path")
.data(density)
.enter()
.append("path")
.attr("d", d3.geoPath())
.attr("fill", "none")
.attr("stroke", "#74B5FF")
.attr("stroke-width", (d, i) => i % 2 ? 0.25 : 1)
.attr("stroke-linejoin", "round");
var label =
d3.select("#phyloIsoLine")
.append("div")
.attr("class","peak-label");
svg0.append("g")
.selectAll("text")
.data(branches)
.enter()
.append("text")
.attr("x", b => xScale0(b.x1))
.attr("y", b => yScale0(b.y) + 4)
.attr("class","peak")
.attr("id",b => "peak-" + b.bId)
.style("fill","#0d1824")
.attr("visibility","visible")
.text("▲")
.on("mouseover", peakOver)
.on("mouseout", peakOut)
.on("click", peakClick);
/* *** draw the phylo *** */
var div1 = d3.select('#phyloScape')
.node().getBoundingClientRect(),
div2 = d3.select('#phyloTimeline')
.node().getBoundingClientRect(),
m = {t:10, b:10, l:10, r:10},
w = div1.width,
h = div1.height
ydiv = div1.y;
const svg = d3
.select('#phyloScape')
.append("svg")
.attr("viewBox",[0,0,w,h]);
var xo = div2.width + m.l,
yo = 2.5 * m.t,
wo = w - xo - m.r,
ho = h - yo - m.b;
/* *** draw the graph *** */
var div3 = d3.select('#phyloGraph')
.node().getBoundingClientRect();
window.svg3 = d3
.select('#phyloGraph')
.append("svg")
.attr("width", div3.width)
.attr("height",div3.height)
.append("g");
/* labels */
var firstDate = Math.min(...groups.map(g => (g.from).getFullYear()))
var yLabels = (periods.map(p => ({y:p.y,from:p.from,to:p.to,label:(p.from).getFullYear()}))).filter(p => p.label >= firstDate);
var xLabels = toXLabels(branches,groups,frame[2]);
/* weight */
if (window.weighted == true) {
var wInf = Math.min(...groups.map(g => g.weight))
var wSup = Math.max(...groups.map(g => g.weight))
var wScale = d3.scaleLog().domain([1,wSup]).range([3,10])
}
/* scales */
var xScale = d3.scaleLinear().domain([0,frame[2]]).range([xo + m.t,wo + xo]),
yScale = d3.scaleTime().domain(setYDomain(yLabels)).range([m.t + yo, ho + yo]);
/* panel and mask */
var mask = svg
.append("defs")
.append("svg:clipPath")
.attr("id","mask")
.append("svg:rect")
.attr("width", wo)
.attr("height",ho)
.attr("x",xo)
.attr("y",yo);
const panel = svg.append("g").attr("clip-path", "url(#mask)").attr("id","panel")
/* highlight */
xLabels.forEach(b =>
panel.append("rect")
.attr("class","branch-hover")
.attr("x", xScale(b.inf))
.attr("y", -10000)
.attr("width", xScale(b.sup) - xScale(b.inf))
.attr("height", 20000)
.attr("id","hover-" + b.bId)
.style("visibility","hidden"))
yLabels.forEach(l =>
panel.append("line")
.attr("class","y-highlight")
.attr("id","y-highlight-" + l.label)
.attr("x1", -10000)
.attr("y1", yScale(l.from))
.attr("x2", 10000)
.attr("y2", yScale(l.from))
.style("visibility","hidden"))
/* links */
function findGroup (id, xsc, ysc) {
var group = groups.find(g => g.gId == id),
x = xsc(group.x);
y = ysc(group.to);
return [x,y]
}
var linkGen = d3.linkVertical();
var groupLinks = links.map(l => ({source: findGroup(l.from, xScale, yScale), target: findGroup(l.to, xScale, yScale),from: l.from, to: l.to, label: l.label}));
var groupAncestors = aLinks.map(l => ({source: findGroup(l.from, xScale, yScale), target: findGroup(l.to, xScale, yScale),from: l.from, to: l.to, label: l.label}));
panel
.selectAll("path")
.data(groupLinks.concat(groupAncestors))
.join("path")
.attr("d", linkGen)
.attr("fill", "none")
.attr("stroke","#0d1824")
.attr("class", "group-path")
.attr("source",d => d.from)
.attr("target",d => d.to)
.attr("label", d => d.label)
// .on("click", function(){
// // console.log(this)
// })
var colors = ["#F0684D","#aa8c58","#74b5ff","#0d1824"];
/* groups */
groups.forEach(g => setGroup(g));
/* axis */
var xAxis = svg.append("g").attr("class","x-axis").attr("transform", "translate(0," + yo + ")"),
yAxis = svg.append("g").attr("class","y-axis").attr("transform", "translate(" + xo + ",0)");
setAxisX(xScale,xLabels);
setAxisY(yScale,yLabels);
/* zoom */
var zoom = d3.zoom()
.scaleExtent([1,50])
.extent([[xo,yo],[wo,ho]])
.on("zoom",onZoom)
svg.call(zoom).on("dblclick.zoom",null).on("dblclick",doubleClick);
d3.select("#reset").on("click",reset);
function reset() {
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity);
}
function onZoom() {
var zoomX = d3.event.transform.rescaleX(xScale),
zoomY = d3.event.transform.rescaleY(yScale),
zoomXLabels = xLabels
.filter(b => (zoomX(b.x) >= xo) && (zoomX(b.x) <= (wo + xo))),
zoomYLabels = yLabels
.filter(p => (zoomY(p.y) >= yo) && (zoomY(p.y) <= (ho + yo)));
setAxisX(zoomX,zoomXLabels);
setAxisY(zoomY,zoomYLabels);
panel.selectAll("circle").attr("transform", d3.event.transform);
panel.selectAll("text").attr("transform", d3.event.transform);
panel.selectAll("path").attr("transform", d3.event.transform);
panel.selectAll(".branch-hover").attr("transform", d3.event.transform);
panel.selectAll(".y-highlight").attr("transform", d3.event.transform);
panel.selectAll(".ngrams").attr("transform", d3.event.transform);
panel.selectAll(".term-path").attr("transform", d3.event.transform);
panel.selectAll(".emergence").attr("transform", d3.event.transform);
panel.selectAll(".header").attr("transform", d3.event.transform);
showPeak()
}
/* label */
// https://observablehq.com/@d3/parallel-coordinates
d3.select("#label").on("click",showLabel);
function autocomplete(e) {
var txt = e.target.value;
if (txt.length < 1) {
document.getElementById("search-autocomplete").value = '';
return;
}
var placeholder = findValueByPrefix(txt);
if (placeholder !== null) {
document.getElementById("search-autocomplete").value = placeholder.label;
} else {
document.getElementById("search-autocomplete").value = '';
}
// press enter
if (e.keyCode === 13) {
e.preventDefault();
if (placeholder !== null) {
showLabel("search")
termClick(placeholder.label,placeholder.fdt,0,"search")
}
}
}
function showLabel(type) {
if ((document.getElementsByClassName("header"))[0].style.visibility != "hidden") {
showHeading()
}
doubleClick()
let ngrams = document.getElementsByClassName("ngrams")
let groups = document.getElementsByClassName("group-inner")
if (ngrams[0].style.visibility == "hidden") {
document.getElementById("label").classList.add("labeled")
for (var i = 0; i < groups.length; i++) {
groups[i].style.fill = "#fff";
}
for (var i = 0; i < ngrams.length; i++){
ngrams[i].style.visibility = "visible";
}
} else {
if (type != "search") {
document.getElementById("label").classList.remove("labeled")
for (var i = 0; i < groups.length; i++) {
groups[i].style.fill = "#61a3a9";
}
for (var i = 0; i < ngrams.length; i++){
ngrams[i].style.visibility = "hidden";
}
}
}
}
/* role & dynamic */
d3.select("#heading").on("click",showHeading);
var emergences = {};
var branchByGroup = {};
groups.forEach(function(g){
// is a term in many branches ?
for (var i = 0; i < (g.foundation).length; i++) {
var fdt = parseInt(((g.foundation)[i]).trim());
if (fdt in branchByGroup) {
(branchByGroup[fdt]).push(g.bId);
} else {
branchByGroup[fdt] = [g.bId];
}
}
// is emerging ?
if ((g.role).includes(0)) {
for (var i = 0; i < (g.role).length; i++) {
if ((g.role)[i] == 0) {
var gf = parseInt(((g.foundation)[i]).trim())
if (gf in emergences) {
(emergences[gf].x).push(xScale(g.x));
(emergences[gf].y).push(yScale(g.to));
} else {
emergences[gf] = {"label":g.label[i],"x":[xScale(g.x)],"y":[yScale(g.to)],"bid":g.bId}
}
}
}
}
});
var keys = Object.keys(emergences);
var freqs = (keys.map(k => window.freq[k])).filter(f => f != null);
const arraySum = (acc, curr) => acc + curr;
function rdm() {
if (Math.random() > 0.5) {
return 1;
} else {
return -1;
}
}
// var fontScale = d3.scaleLinear().domain([0,Math.max(...freqs)]).range([2,10]);
var fontScale = d3.scaleLinear().domain([0,Math.sqrt(Math.max(...freqs))]).range([2,20]);
var opacityScale = d3.scaleLinear().domain([0,1/Math.sqrt(Math.max(...freqs))]).range([0.1,1]);
keys.forEach(function(k){
let x = ((emergences[k]).x).reduce(arraySum) / ((emergences[k]).x).length;
let y = ((emergences[k]).y).reduce(arraySum) / ((emergences[k]).y).length;
let bid = Array.from(new Set(branchByGroup[k]));
var freq = 0;
// console.log(k)
if (k in window.freq) {
freq = window.freq[k];
}
panel.append("text")
.attr("x",x + (rdm() * Math.random() * 10))
.attr("y",y + (rdm() * Math.random() * 10))
.attr("fdt",k)
.attr("id","head" + k)
.attr("mem-size", fontScale(Math.sqrt(freq)))
.attr("mem-opac", opacityScale(Math.sqrt(freq)))
.attr("bid",(emergences[k]).bid)
.style("font-size", fontScale(Math.sqrt(freq)) + "px")
.style("opacity", opacityScale(1/Math.sqrt(freq)))
.attr("class","header")
.style("visibility","hidden")
.style("text-anchor", "middle")
// .style("fill",(bid.length > 1) ? "#012840" : "#CC382F")
.style("fill",(bid.length > 1) ? "#012840" : "#012840")
.text((emergences[k]).label)
.on("click",function(){
showLabel("header")
termClick((emergences[k]).label,k,k,"head");
});
});
function landingView() {
window.ldView = true;
doubleClick()
let headers = document.getElementsByClassName("header")
let groups = document.getElementsByClassName("group-inner")
if (headers[0].style.visibility == "hidden") {
document.getElementById("heading").classList.add("headed")
for (var i = 0; i < groups.length; i++) {
groups[i].style.fill = "#f5eee6";
groups[i].classList.add("group-heading")
}
d3.selectAll(".group-path").classed("path-heading",true);
for (var i = 0; i < headers.length; i++){
headers[i].style.visibility = "visible";
}
} else {
document.getElementById("heading").classList.remove("headed")
for (var i = 0; i < groups.length; i++) {
groups[i].style.fill = "#61a3a9";
groups[i].classList.remove("group-heading")
}
d3.selectAll(".group-path").classed("path-heading",false);
for (var i = 0; i < headers.length; i++){
headers[i].style.visibility = "hidden";
}
}
}
landingView()
function showHeading() {
if ((document.getElementsByClassName("ngrams"))[0].style.visibility != "hidden") {
showLabel("header")
}
landingView()
}
/* groups */
function textWidth(text) {
const context = document.createElement("canvas").getContext("2d");
return context.measureText(text).width;
}
function toLines(words,fdt,role,targetWidth) {
let line;
let lineWidth0 = Infinity;
const lines = [];
for (let i = 0, n = words.length; i < n; ++i) {
let lineText1 = (line ? line.text + " " : "") + words[i];
let lineFdt1 = (line ? line.fdt + " " : "") + fdt[i];
let lineRole1 = (line ? line.role + " " : "") + role[i];
let lineWidth1 = textWidth(lineText1);
if ((lineWidth0 + lineWidth1) / 2 < targetWidth + 10) {
line.width = lineWidth0 = lineWidth1;
// line.text = lineText1;
line.text.push(words[i])
line.fdt.push(fdt[i])
line.role.push(role[i])
} else {
lineWidth0 = textWidth(words[i]);
line = {width: lineWidth0, text: [words[i]], fdt: [fdt[i]], role: [role[i]]};
lines.push(line);
}
}
return lines;
}
function toTextRadius(lines,lineHeight) {
let radius = 0;
for (let i = 0, n = lines.length; i < n; ++i) {
const dy = (Math.abs(i - n / 2 + 0.5) + 2) * lineHeight;
const dx = lines[i].width / 2;
radius = Math.max(radius, Math.sqrt(dx ** 2 + dy ** 2));
}
return radius;
}
function findFreq(fdt) {
let freq = 0;
if (window.freq[fdt.trim()] != null) {
freq = window.freq[fdt.trim()]
}
return freq;
}
function setGroupClass(g) {
var str = "group-inner" + " " + "branch-" + g.bId;
for (var i = 0; i < g.source.length; i++) {
str += " source-" + g.source[i];
}
return str;
}
function setGroup(g) {
// console.log(window.weighted)
if(window.weighted == true) {
var radius = wScale(g.weight)
} else {
var radius = 5;
}
// var radius = 5;
// var col = Math.round(Math.random() * 3) - 1
panel
.append("circle")
.attr("class","group-outer")
.attr("cx", xScale(g.x))
.attr("cy", yScale(g.to))
.attr("r" , radius + 0.5);
panel
.append("circle")
.attr("class", setGroupClass(g))
.attr("cx", xScale(g.x))
.attr("cy", yScale(g.to))
.attr("bId", g.bId)
.attr("id" , "group" + g.gId)
.attr("gid" , g.gId)
.attr("r" ,radius)
// .attr("stroke",colors[col])
.attr("stroke","#0d1824")
.style("fill", "#61a3a9")
.attr("from",(g.to).getFullYear())
// .on("mouseover",groupOver)
// .on("mouseout" ,groupOut)
/* group label */
var lineHeight = 12,
targetWidth = Math.sqrt(textWidth(g.label.join('').trim()) * radius),
lines = toLines(g.label,g.foundation,g.role,targetWidth),
textRadius = toTextRadius(lines,lineHeight)
textRatio = (radius - 0.5) / textRadius;
for (let i = 0; i < lines.length; i++) {
let words = lines[i].text,
fdt = lines[i].fdt,
roles = lines[i].role,
terms = mergeLists(words,fdt,roles)
toSpan = (acc, w) => acc + "<tspan fdt=" + (w[1]).trim()
+ " class='term fdt-" + (w[1]).trim() + " " + "g-" + g.gId + findRole(w[2]) + "'"
+ " gy=" + yScale(g.to)
+ " gx=" + xScale(g.x)
+ " freq=" + findFreq(w[1])
+ " label='" + w[0] + "'"
+ " gid=" + g.gId
+ " bid=" + g.bId
+ " from=" + (g.to).getFullYear()
+ ">" + w[0] + "</tspan>";
panel
.append("text")
.attr("class","ngrams")
.attr("text-anchor", "middle")
.style("font-size", 12 * textRatio + "px")
.style("visibility", "hidden")
.append("tspan")
.attr("x", xScale(g.x))
.attr("y", yScale(g.to) + (i - lines.length / 2.8) * (lineHeight * textRatio))
.html(terms.reduce(toSpan,""));
d3.selectAll(".term")
.on("click",function(){
termClick(this.textContent,this.getAttribute("fdt"),this.getAttribute("gid"),"group");
})
// .on("mouseover",function(){
// d3.selectAll(".term").classed("term-unfocus",true);
// d3.selectAll(".term").filter(".g-" + this.getAttribute("gid")).classed("term-focus",true);
// })
// .on("mouseout",function(){
// d3.selectAll(".term").classed("term-unfocus",false);
// d3.selectAll(".term").classed("term-focus",false);
// });
}
}
d3.selectAll(".header").raise();
function findRole(r) {
if (r == 0) {
return " emerging";
} else if (r == 2) {
return " decreasing";
} else {
return "";
}
}
function mergeLists(l1,l2,l3) {
let merged = [];
for (let i = 0; i < l1.length; i++) {
merged.push([l1[i],l2[i],l3[i]])
}
return merged;
}
function setAxisY(scale,labels) {
yAxis.call(d3.axisLeft(scale)
.tickFormat(function(d){
if (d3.timeYear(d) < d) {
// '%B'
return d3.timeFormat('%d %B')(d);
} else {
return d3.timeFormat('%Y')(d);
}
})
.tickSizeOuter(0));
yAxis.selectAll(".tick line").remove();
yAxis.selectAll(".tick circle").remove();
yAxis.selectAll(".tick")
.call(addMarkY)
yAxis.selectAll(".tick text")
.call(setMarkYLabel)
}
function setAxisX(scale,labels) {
xAxis.call(d3.axisTop(scale)
.tickValues(labels.map(l => l.x))
.tickFormat((l, i) => labels[i].label)
.tickSizeOuter(0));
xAxis.selectAll(".tick text")
.call(xOverFlow, labels.map(l => [scale(l.sup) - scale(l.inf),l.bId]))
.on("mouseover", tickOver)
.on("click", tickClick)
.on("mouseout" , tickOut);
xAxis.selectAll(".tick line").remove();
xAxis.selectAll(".tick rect").remove();
xAxis.selectAll(".tick")
.call(addMarkX, labels.map(l => scale(l.sup) - scale(l.inf)),labels.map(l => l.bId));
}
function showPeak() {
d3.selectAll(".peak").style("fill",function(peak,i){
var isVisible = d3
.selectAll(".branch-" + i)
.nodes()
.map(function(g){
var x = g.getBoundingClientRect().x,
y = g.getBoundingClientRect().y;
if ((x >= xo) && (x <= (wo + xo)) && (y >= (div1.y + yo - m.t)) && (y <= (div1.y + ho + yo))) {
return true;
} else {
return false;
}})
.reduce((mem,cur) => {return mem || cur;})
if (isVisible) {
d3.select("#peak-shadow" + i).attr("visibility","visible");
return "#0d1824";
} else {
d3.select("#peak-shadow" + i).attr("visibility","hidden");
return "#A9A9A9";
}
})
}
function countTerms(groups) {
var terms = [];
for (var i = 0; i < groups.length; i++) {
let gid = ((groups[i].getAttribute("id")).split("group"))[1]
d3.selectAll(".g-" + gid).nodes().forEach(e => terms.push(e.getAttribute("fdt")))
}
return (Array.from(new Set(terms))).length;
}
function countBranches(groups) {
var branches = [];
for (var i = 0; i < groups.length; i++) {
branches.push(groups[i].getAttribute("bId"));
}
return (Array.from(new Set(branches))).length;
}
function highlightGroups (groups) {
window.ldView = false;
// console.log(groups)
let paths = document.getElementsByClassName("group-path"),
gids = [];
for (var i = 0; i < groups.length; i++) {
// highlight the groups
groups[i]
.classList.add("group-focus");
groups[i]
.classList.remove("group-unfocus");
// .classed("group-unfocus", false)
// .classed("group-focus", true);
gids.push(groups[i].getAttribute("gid"))
// highlight the branches peak
let bid = groups[i].getAttribute("bId")
d3.select("#peak-" + bid)
.classed("peak-focus", true);
d3.select("#xmark-" + bid)
.style("fill", "#F0684D");
}
// facets
document.querySelector("#phyloGroups").innerHTML = groups.length;
document.querySelector("#phyloTerms").innerHTML = countTerms(groups);
document.querySelector("#phyloBranches").innerHTML = countBranches(groups);
document.querySelector("#phyloGroups").classList.add("phylo-focus");
document.querySelector("#phyloTerms").classList.add("phylo-focus");
document.querySelector("#phyloBranches").classList.add("phylo-focus");
// highlight the links
for (var i = 0; i < paths.length; i++) {
if (gids.includes((paths[i]).getAttribute("source")) && (paths[i]).getAttribute("target")) {
paths[i].classList.add("path-focus");
paths[i].classList.remove("path-unfocus");
}
}
}
function termClick (txt,idx,nodeId,typeNode) {
// remove old focus
initPath()
// catch the last transformations
if (typeNode == "group") {
var transform = d3.select("#group" + nodeId).node().getAttribute("transform");
} else if (typeNode == "head") {
var transform = d3.select("#head" + nodeId).node().getAttribute("transform");
} else {
var transform = (d3.selectAll(".header").nodes())[0].getAttribute("transform");
}
// focus
document.querySelector("#phyloPhylo").innerHTML = txt;
document.querySelector("#phyloPhylo").classList.add("phylo-focus");
document.querySelector("#phyloSearch").setAttribute("href",'https://en.wikipedia.org/w/index.php?search="' + txt + '"')
// highlight the groups
var terms = document.getElementsByClassName("fdt-" + idx),
periods = groupTermsBy(terms,"from");
var groups = [];
for (var i = 0; i < terms.length; i++) {
groups.push(d3.select("#group" + (terms[i]).getAttribute("gid")));
branchFocus.push((terms[i]).getAttribute("bid"));
}
highlightGroups(groups.map(g => g.node()));
drawWordCloud(groups.map(g => g.node()));
// highlight the cross branches links
var bids = [];
for (var i = 0; i < periods.length; i++) {
if (i != periods.length - 1) {
for (var j = 0; j < periods[i].length; j++) {
bids.push(periods[i][j][2])
var x1 = periods[i][j][0],
y1 = periods[i][j][1];
for (var k = 0; k < periods[i + 1].length; k++) {
var x2 = periods[i + 1][k][0],
y2 = periods[i + 1][k][1];
if ((periods[i][j][2] != periods[i + 1][k][2]) && (!bids.includes(periods[i + 1][k][2]))) {
// draw the links between branches
panel
.append("path")
.attr("class","term-path")
.attr("d", function(d) {
return "M" + x1 + "," + y1
+ "C" + x2 + "," + y1
+ " " + x2 + "," + y2
+ " " + x2 + "," + y2;
})
.attr("transform",transform)
.style("stroke-opacity", 0.4)
.lower();
}
bids.push(periods[i + 1][k][2])
}
}
}
}
d3.selectAll(".path-unfocus").lower();
}
function peakOver (b,i) {
d3.select("#peak-" + i).classed("peak-focus",false);
d3.select("#peak-" + i).classed("peak-over",true);
label.text(b.label.replace(/"/g,''))
.style("visibility", "visible")
.style("top", (yScale0(b.y) + div0.top - 18) + "px")
.style("left",(xScale0(b.x1) + div0.left + 12) + "px");
branchOver(b.bId);
}
function peakOut (b,i) {
d3.select("#peak-" + i).classed("peak-over",false);
if (branchFocus.includes("" + b.bId)) {
d3.select("#peak-" + i).classed("peak-focus",true);
}
branchOut();
}
function peakClick (b,i) {
initPath()
let groups = d3.selectAll(".group-inner").filter(".branch-" + b.bId).nodes()
branchFocus.push(b.bId);
/* word cloud */
drawWordCloud(groups);
highlightGroups(groups);
/* rescale */
let tx = (groups[0]).getAttribute("cx")
svg.transition()
.duration(750)
.call(zoom.transform, d3.zoomIdentity.translate(((wo + xo) / 2) - tx, 0).scale(1));
d3.selectAll(".path-unfocus").lower();
}
function branchOver(bId) {
// headers
if (d3.select("#heading").classed("headed")) {
d3.selectAll(".header").nodes().forEach(function(header){
if (header.getAttribute("bid") == bId) {
header.style["font-size"] = "10px";
header.style["opacity"] = 1;
} else {
header.style["opacity"] = 0.3;
}
})
}
// branches
d3.select("#xmark-" + bId).style("fill","#f3be54");
d3.select("#hover-" + bId).style("visibility","visible");
}
function headerOut() {
d3.selectAll(".header").nodes().forEach(function(header){
header.style["font-size"] = header.getAttribute("mem-size") + "px";
header.style["opacity"] = header.getAttribute("mem-opac");
})
}
function branchOut(bId) {
d3.selectAll(".peak-label").style("visibility","hidden");
d3.selectAll(".branch-hover").style("visibility","hidden");
d3.selectAll(".x-mark").style("fill","#4A5C70");
for (var i = 0; i < branchFocus.length; i++) {
d3.select("#xmark-" + branchFocus[i]).style("fill","#F24C3D");
}
headerOut();
}
function tickClick() {
initPath()
let bid = this.getAttribute("bId"),
groups = d3.selectAll(".group-inner").filter(".branch-" + bid).nodes();
// draw the word cloud
branchFocus.push(bid);
drawWordCloud(groups);
// highlight the groups
highlightGroups(groups);
d3.selectAll(".path-unfocus").lower();
}
function tickOver() {
var ego = this.getAttribute("bId"),
branch = branches.find(b => b.bId == ego);
if (d3.select("#peak-" + ego).node().style.visibility != "hidden") {
branchOver(ego);
peakOver(branch,ego);
}
}
function tickOut() {
var ego = this.getAttribute("bId"),
branch = branches.find(b => b.bId == ego);
branchOut();
peakOut(branch,ego)
}
// function groupOver() {
// var from = this.getAttribute("from");
// d3.select("#y-highlight-" + from).style("visibility","visible");
// // d3.select("#y-mark-year-inner-" + from).node().setAttribute("class","y-mark-year-inner-highlight");
// // d3.select("#y-mark-year-outer-" + from).node().setAttribute("class","y-mark-year-outer-highlight");
// // d3.select("#y-label-" + from).node().setAttribute("class","y-label-bold");
// }
// function groupOut() {
// var from = this.getAttribute("from");
// d3.select("#y-highlight-" + from).style("visibility","hidden");
// // d3.select("#y-mark-year-inner-" + from).node().setAttribute("class","y-mark-year-inner");
// // d3.select("#y-mark-year-outer-" + from).node().setAttribute("class","y-mark-year-outer");
// // d3.select("#y-label-" + from).node().setAttribute("class","y-label");
// }
function initPath () {
window.highlighted = true;
window.ldView = false;
let groups = d3.selectAll(".group-inner");
(groups.nodes()).map(function(g){
if (!g.classList.contains("source-focus")) {
g.classList.add("group-unfocus");
g.classList.remove("group-focus");
}
})
d3.selectAll(".group-path")
.classed("path-unfocus",true)
.classed("path-focus",false);
d3.selectAll(".term-path").remove();
d3.selectAll(".peak").classed("peak-focus",false);
d3.selectAll(".peak").classed("peak-focus-source",false);
d3.selectAll(".x-mark").style("fill","#4A5C70");
branchFocus = [];
}
function doubleClick() {
window.highlighted = false;
headerOut();
d3.selectAll(".group-inner")
.classed("group-unfocus",false)
.classed("group-focus",false);
d3.selectAll(".group-path")
.classed("path-unfocus",false)
.classed("path-focus",false);
d3.selectAll(".term-path").remove();
document.querySelector("#phyloPhylo").innerHTML = "phylomemy";
document.querySelector("#phyloPhylo").classList.remove("phylo-focus");
document.querySelector("#phyloGroups").innerHTML = window.nbGroups;
document.querySelector("#phyloTerms").innerHTML = window.nbTerms;
document.querySelector("#phyloBranches").innerHTML = window.nbBranches;
document.querySelector("#phyloGroups").classList.remove("phylo-focus");
document.querySelector("#phyloTerms").classList.remove("phylo-focus");
document.querySelector("#phyloBranches").classList.remove("phylo-focus");
d3.selectAll(".peak").classed("peak-focus",false);
d3.selectAll(".peak").classed("peak-focus-source",false);
d3.selectAll(".x-mark").style("fill","#4A5C70");
branchFocus = [];
}
/* export */
d3.select("#export").on("click",exportViz);
function exportViz() {
const xmlns = "http://www.w3.org/2000/xmlns/";
const xlinkns = "http://www.w3.org/1999/xlink";
const svgns = "http://www.w3.org/2000/svg";
var time = new Date();
serialize(svg.node(),"phylomemy-" + Date.parse(time.toString()) + ".svg")
function serialize(graph,name) {
graph = graph.cloneNode(true);
const fragment = window.location.href + "#";
const walker = document.createTreeWalker(graph, NodeFilter.SHOW_ELEMENT, null, false);
while (walker.nextNode()) {
for (const attr of walker.currentNode.attributes) {
if (attr.value.includes(fragment)) {
attr.value = attr.value.replace(fragment, "#");
}
}
}
graph.setAttributeNS(xmlns, "xmlns", svgns);
graph.setAttributeNS(xmlns, "xmlns:xlink", xlinkns);
var cssStyleText = getCSSStyles( graph );
appendCSS( cssStyleText, graph );
const serializer = new window.XMLSerializer;
const string = serializer.serializeToString(graph);
var svgBlob = new Blob([string], {type: "image/svg+xml"});
var svgUrl = URL.createObjectURL(svgBlob);
var downloadLink = document.createElement("a");
downloadLink.href = svgUrl;
downloadLink.download = name;
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
};
function getCSSStyles( parentElement ) {
var selectorTextArr = [];
// Add Parent element Id and Classes to the list
selectorTextArr.push( '#'+parentElement.id );
for (var c = 0; c < parentElement.classList.length; c++)
if ( !contains('.'+parentElement.classList[c], selectorTextArr) )
selectorTextArr.push( '.'+parentElement.classList[c] );
// Add Children element Ids and Classes to the list
var nodes = parentElement.getElementsByTagName("*");
for (var i = 0; i < nodes.length; i++) {
var id = nodes[i].id;
if ( !contains('#'+id, selectorTextArr) )
selectorTextArr.push( '#'+id );
var classes = nodes[i].classList;
for (var c = 0; c < classes.length; c++)
if ( !contains('.'+classes[c], selectorTextArr) )
selectorTextArr.push( '.'+classes[c] );
}
// Extract CSS Rules
var extractedCSSText = "";
for (var i = 0; i < document.styleSheets.length; i++) {
var s = document.styleSheets[i];
try {
if(!s.cssRules) continue;
} catch( e ) {
if(e.name !== 'SecurityError') throw e; // for Firefox
continue;
}
var cssRules = s.cssRules;
for (var r = 0; r < cssRules.length; r++) {
if ( contains( cssRules[r].selectorText, selectorTextArr ) )
extractedCSSText += cssRules[r].cssText;
}
}
return extractedCSSText;
function contains(str,arr) {
return arr.indexOf( str ) === -1 ? false : true;
}
}
function appendCSS( cssText, element ) {
var styleElement = document.createElement("style");
styleElement.setAttribute("type","text/css");
styleElement.innerHTML = cssText;
var refNode = element.hasChildNodes() ? element.children[0] : null;
element.insertBefore( styleElement, refNode );
}
}
}
'use strict';
function readJson(file, callback) {
var raw = new XMLHttpRequest();
raw.overrideMimeType("application/json");
raw.open("GET", file, true);
raw.onreadystatechange = function() {
if (raw.readyState === 4 && raw.status == "200") {
callback(raw.responseText);
}
}
raw.send(null);
}
function unhide(mode) {
document.querySelector("#reset").style.visibility = "visible";
document.querySelector("#label").style.visibility = "visible";
document.querySelector("#heading").style.visibility = "visible";
if (mode != "static") {
document.querySelector("#export").style.visibility = "visible";
}
}
window.addEventListener("load", function() {
// read the config
readJson("./config.json",function(data1){
var conf = JSON.parse(data1);
// available config modes are "static" or "explorable"
if (conf.mode == "static") {
var path = "";
var name = "";
if (conf.path == null || conf.path == "") {
path = conf.defaultPath;
name = conf.defaultName;
} else {
path = conf.path;
name = conf.pathName;
}
document.querySelector("#file-label").style.display = "none";
document.querySelector("#spin").style.visibility = "visible";
document.getElementById("phyloName").innerHTML = name;
document.querySelector("#phyloName").style.visibility = "visible";
readJson(path,function(data2){
phylo = JSON.parse(data2);
unhide("static");
draw(phylo);
})
}
})
})
function readPhylo(file) {
var reader = new FileReader();
reader.onload = (function(f) {
return function(e) {
try {
json = JSON.parse(e.target.result);
unhide("explorable");
draw(json)
} catch (error) {
console.log(error)
}
};
})(file);
reader.readAsText(file, "UTF-8");
}
// display the Draw button after loading a phylo
document.querySelector("#file-path").onchange = function(){
document.querySelector("#file-name").textContent = (this.files[0].name).substring(0,15) + "...";
// document.querySelector("#file-name").textContent = this.files[0].name;
document.querySelector("#draw").style.display = "inline-block";
}
// draw the phylo
document.querySelector("#draw").onclick = function() {
document.querySelector("#spin").style.visibility = "visible";
readPhylo(document.getElementById("file-path").files[0]);
}
function drawPhyloInfo(elemClass, docs, foundations, branches, groups, terms, periods) {
ReactDOM.render(React.createElement(phyloCorpus,{}),document.getElementById('phyloCorpus'));
ReactDOM.render(React.createElement(phyloPhylo,{}),document.getElementById('phyloPhylo'));
ReactDOM.render(React.createElement(phyloHow,{}),document.getElementById('phyloHow'));
ReactDOM.render(React.createElement(phyloCorpusInfo,{
nbDocs : docs,
nbFoundations : foundations,
nbPeriods : periods
}),document.getElementById('phyloCorpusInfo'));
ReactDOM.render(React.createElement(phyloPhyloInfo,{
nbTerms : terms,
nbGroups : groups,
nbBranches : branches
}),document.getElementById('phyloPhyloInfo'));
}
function draw(json) {
// draw PhyloInfo
window.freq = {};
window.terms = {};
window.sources = [];
window.nbDocs = parseFloat(json.phyloDocs);
window.nbBranches = parseFloat(json.phyloBranches);
window.nbGroups = parseFloat(json.phyloGroups);
window.nbTerms = parseFloat(json.phyloTerms);
window.nbPeriods = parseFloat(json.phyloPeriods);
window.nbFoundations = parseFloat(json.phyloFoundations);
window.timeScale = json.phyloTimeScale;
if (json.phyloSources != undefined) {
var sources = stringArrToArr(json.phyloSources);
var checkSource = document.getElementById("checkSource");
for (var i = 0; i < sources.length; i++) {
window.sources.push({source:sources[i],index:i});
}
window.sources.sort(function(a, b){
if(a.source < b.source) { return -1; }
if(a.source > b.source) { return 1; }
return 0;
})
for (var i = 0; i < window.sources.length; i++) {
var option = document.createElement("option");
option.text = window.sources[i].source;
option.value = window.sources[i].index;
checkSource.add(option);
}
}
// original bounding box
bb = ((json.bb).split(',')).map(xy => parseFloat(xy))
drawPhyloInfo("", window.nbDocs, window.nbFoundations, window.nbBranches, window.nbGroups, window.nbTerms, window.nbPeriods)
// draw PhyloIsoline
var branches = json.objects.filter(node => node.nodeType == "branch").map(function(b){
return { x1 : b.branch_x ,
y : b.branch_y ,
x2 : parseFloat(((b.pos).split(','))[0]) ,
label : b.label,
bId : parseInt(b.bId),
gvid : parseInt(b._gvid)
}
});
var periods = json.objects.filter(node => node.nodeType == "period").map(function(p){
var from = yearToDate(p.from),
to = yearToDate(p.to);
if (p.strFrom != undefined) {
if (window.timeScale == "epoch") {
from = utcStringToDate(p.strFrom)
} else {
from = stringToDate(p.strFrom)
}
}
if (p.strTo != undefined) {
if (window.timeScale == "epoch") {
to = utcStringToDate(p.strTo)
} else {
to = stringToDate(p.strTo)
}
}
return { from : from,
to : to,
y : parseFloat(((p.pos).split(','))[1])}
});
// groups
window.weighted = false;
var groups = json.objects.filter(node => node.nodeType == "group").map(function(g){
// console.log(g.weight)
if ((g.weight != undefined) && (g.weight != "Nothing"))
window.weighted = true;
var keys = (g.foundation.slice(1, g.foundation.length - 1)).split('|')
var labels = (g.lbl.slice(1, g.lbl.length - 1)).split('|')
for (var i = 0; i < keys.length; i++) {
// freq
if (!((keys[i]).trim() in window.freq)) {
window.freq[(keys[i]).trim()] = 0;
} else {
window.freq[(keys[i]).trim()] += 1;
}
// terms
if (!((keys[i]).trim() in window.terms)) {
window.terms[(keys[i]).trim()] = {label:(labels[i]).trim(),fdt:(keys[i]).trim()}
}
}
var from = yearToDate(g.from),
to = yearToDate(g.to),
weight = 0;
source = [];
if (g.strFrom != undefined) {
if (window.timeScale == "epoch") {
from = utcStringToDate(g.strFrom)
} else {
from = stringToDate(g.strFrom)
}
}
if (g.strTo != undefined) {
if (window.timeScale == "epoch") {
to = utcStringToDate(g.strTo)
} else {
to = stringToDate(g.strTo)
}
}
if (g.source != undefined)
source = intArrToArr(g.source);
if (g.weight != undefined)
weight = parseFloat((g.weight).replace("Just ",""));
return { from : from,
to : to,
x : parseFloat(((g.pos).split(','))[0]) ,
y : parseFloat(((g.pos).split(','))[1]) ,
bId : parseInt(g.bId) ,
gId : parseInt(g._gvid) ,
size : parseInt(g.support),
source : source,
weight : weight,
label : labels,
foundation : keys,
role : ((g.role.slice(1, g.role.length - 1)).split('|')).map(e => parseInt(e.trim()))}
});
var links = json.edges.filter(edges => edges.edgeType == "link").map(function(l){
return { lId : parseInt(l._gvid),
from : parseInt(l.tail) ,
to : parseInt(l.head) ,
label : l.label}
});
var aLinks = json.edges.filter(edges => edges.edgeType == "ancestorLink").map(function(l){
return { lId : parseInt(l._gvid),
from : parseInt(l.tail) ,
to : parseInt(l.head) ,
label : l.label }
});
var bLinks = json.edges.filter(edges => edges.edgeType == "branchLink").map(function(l){
return { from : parseInt(l.tail) ,
to : parseInt(l.head) }
});
window.terms = Object.values(window.terms)
// draw the phylo
drawPhylo(branches,periods,groups,links,aLinks,bLinks,bb);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment