This commit is contained in:
relikd
2020-09-25 02:56:16 +02:00
parent bd4a5f321f
commit 69b2875177
15 changed files with 340 additions and 193 deletions

View File

@@ -31,11 +31,14 @@ function lookup_domain_js(fname_a, fname_b, id1, id2, id3) {
let bid = apps[i][0];
let item = template.cloneNode(true);
item.href = '/app/'+bid+'/';
item.querySelector('img').src = '/app/'+bid+'/icon.png';
let img = item.querySelector('img');
img.classList = 'lozad';
img.setAttribute('data-src', '/app/'+bid+'/icon.png');
item.querySelector('.name').innerHTML = apps[i][1];
item.querySelector('.detail').innerHTML = bid;
dom_app_list.appendChild(item);
}
const observer = lozad(); observer.observe();
});
});
}

View File

@@ -20,16 +20,6 @@ function lookup_rank_js(bundle_id) {
meta[1].innerHTML = fmt(best[i]);
meta[2].innerHTML = fmt(worst[i]);
}
// formatting
function dot1(x) { return Math.round(x * 10) / 10; }
function as_percent(x) { return dot1(x * 100) + '%'; }
function as_pm(x) { return dot1(x) + '/min'; }
function HHmmss(seconds) {
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = Math.round(seconds % 60);
return (h<10?'0'+h:h)+':'+(m<10?'0'+m:m)+':'+(s<10?'0'+s:s);
}
// order is important!
update(0, 'sum_rec');
update(1, 'avg_time', HHmmss);

9
out/static/lozad.js Normal file
View File

@@ -0,0 +1,9 @@
/*! lozad.js - v1.9.0 - 2019-02-09
* https://github.com/ApoorvSaxena/lozad.js
* Copyright (c) 2019 Apoorv Saxena; Licensed MIT */
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):t.lozad=e()}(this,function(){"use strict";var g=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var r=arguments[e];for(var o in r)Object.prototype.hasOwnProperty.call(r,o)&&(t[o]=r[o])}return t},n="undefined"!=typeof document&&document.documentMode,l={rootMargin:"0px",threshold:0,load:function(t){if("picture"===t.nodeName.toLowerCase()){var e=document.createElement("img");n&&t.getAttribute("data-iesrc")&&(e.src=t.getAttribute("data-iesrc")),t.getAttribute("data-alt")&&(e.alt=t.getAttribute("data-alt")),t.appendChild(e)}if("video"===t.nodeName.toLowerCase()&&!t.getAttribute("data-src")&&t.children){for(var r=t.children,o=void 0,a=0;a<=r.length-1;a++)(o=r[a].getAttribute("data-src"))&&(r[a].src=o);t.load()}t.getAttribute("data-src")&&(t.src=t.getAttribute("data-src")),t.getAttribute("data-srcset")&&t.setAttribute("srcset",t.getAttribute("data-srcset")),t.getAttribute("data-background-image")&&(t.style.backgroundImage="url('"+t.getAttribute("data-background-image")+"')"),t.getAttribute("data-toggle-class")&&t.classList.toggle(t.getAttribute("data-toggle-class"))},loaded:function(){}};
/**
* Detect IE browser
* @const {boolean}
* @private
*/function f(t){t.setAttribute("data-loaded",!0)}var b=function(t){return"true"===t.getAttribute("data-loaded")};return function(){var r,o,a=0<arguments.length&&void 0!==arguments[0]?arguments[0]:".lozad",t=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{},e=g({},l,t),n=e.root,i=e.rootMargin,d=e.threshold,c=e.load,u=e.loaded,s=void 0;return window.IntersectionObserver&&(s=new IntersectionObserver((r=c,o=u,function(t,e){t.forEach(function(t){(0<t.intersectionRatio||t.isIntersecting)&&(e.unobserve(t.target),b(t.target)||(r(t.target),f(t.target),o(t.target)))})}),{root:n,rootMargin:i,threshold:d})),{observe:function(){for(var t=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:document;return t instanceof Element?[t]:t instanceof NodeList?t:e.querySelectorAll(t)}(a,n),e=0;e<t.length;e++)b(t[e])||(s?s.observe(t[e]):(c(t[e]),f(t[e]),u(t[e])))},triggerLoad:function(t){b(t)||(c(t),f(t),u(t))},observer:s}}});

59
out/static/ranking.js Normal file
View File

@@ -0,0 +1,59 @@
function th(cur_col, cur_asc, names) {
var txt = '<tr>';
for (var i = 0; i < names.length; i++) {
var v = names[i];
txt += '\n<th>' + v;
if (v) {
let cls = (i == cur_col) ? ' class="active"' : '';
txt += ' <span><a' + (cur_asc == -1 ? cls : '') + ' onClick="sort_by('+i+', -1)">';
txt += '<svg viewBox="0 0 16 14" width="12"><g><polygon points="8,14 0,0 16,0"/></g></svg>';
txt += '</a><a' + (cur_asc == 1 ? cls : '') + ' onClick="sort_by('+i+', 1)">';
txt += '<svg viewBox="0 0 16 14" width="12"><g><polygon points="8,0 0,14 16,14"/></g></svg>';
txt += '</a></span>';
}
txt += '</th>';
}
return txt + '</tr>';
}
function td(cols, id) {
var txt = '\n<tr id="' + id + '">';
for (var i = 0; i < cols.length; i++) {
txt += '<td>' + cols[i] + '</td>';
}
return txt + '</tr>';
}
function prep(x, i) {
return [
i+1 + '. <img class="lozad" data-src="/app/'+x[0]+'/icon.png"/>', '<a href="/app/'+x[0]+'/">' + x[1] + '</a>',
x[2], HHmmss(x[3]), HHmmss(x[4]), as_pm(x[5]), as_pm(x[6]), x[7], x[8],
as_percent(x[9]), x[10], dot1(x[11]), new Date(x[12] * 1000).toISOString().slice(0, 16).replace('T',' ')
];
}
function update(col, asc) {
let table = document.getElementById('rank-list');
let len = _data.length;
let txt = '';
for (var i = 0; i < len; i++) {
txt += td(prep(_data[i], i), _data[i][0])
}
table.innerHTML = th(col, asc, [
'', 'Application', 'Number of recordings', 'Average recording time',
'Cumulative recording time', 'Average requests per minute',
'Total requests per minute', 'Number of domains', 'Number of subdomains',
'Tracker percentage', 'Total number of requests', 'Average number of requests',
'Last Update'
]) + txt;
const observer = lozad(); observer.observe();
}
function sort_by(col, asc) {
let i = col;
let o = asc;
_data.sort(function(a, b){ return a[i] < b[i] ? -o : a[i] > b[i] ? o : 0; });
update(col, asc);
}
function rank_js(fname) {
loadJSON(fname, function(response) {
_data = JSON.parse(response);
update(12,-1);
});
}

View File

@@ -3,23 +3,33 @@
addEventListener('touchstart', function() {}); // :hover
})();
function updateViewport() {// show at least 2 columns on mobile devices
var viewport = document.head.querySelector("meta[name=viewport]");
var viewport = document.head.querySelector('meta[name=viewport]');
if (viewport && screen.width < 372) {
document.head.removeChild(viewport);
var x = document.createElement("meta");
x.setAttribute("name", "viewport");
x.setAttribute("content", "width=372");
var x = document.createElement('meta');
x.setAttribute('name', 'viewport');
x.setAttribute('content', 'width=372');
document.head.appendChild(x);
}
}
function loadJSON(url, callback, async=true) {
var xobj = new XMLHttpRequest();
xobj.overrideMimeType("application/json");
xobj.overrideMimeType('application/json');
xobj.open('GET', url, async);
xobj.onreadystatechange = function () {
if (xobj.readyState == 4 && xobj.status == "200") {
if (xobj.readyState == 4 && xobj.status == '200') {
callback(xobj.responseText);
}
};
xobj.send(null);
}
// formatting
function dot1(x) { return Math.round(x * 10) / 10; }
function as_percent(x) { return dot1(x * 100) + '%'; }
function as_pm(x) { return dot1(x) + '/min'; }
function HHmmss(seconds) {
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = Math.round(seconds % 60);
return (h<10?'0'+h:h)+':'+(m<10?'0'+m:m)+':'+(s<10?'0'+s:s);
}

View File

@@ -100,7 +100,7 @@ footer .links {
margin: .5em auto 1em;
display: block;
}
#app-toc img, img.app-icon {
#app-toc img, #rank-list img, img.app-icon {
border-radius: 21.5%;
border: .7px solid #ccc;
}
@@ -150,6 +150,11 @@ p.subtitle { margin-top: .2em; }
.border { border: 1pt solid #CCC; }
.large { font-size: 1.2em; }
.stick-top { top: 0; position: sticky; padding: .8em 0 .5em; background: #FFF; }
.xscroll { overflow-x: scroll; max-width: 100%; }
.yscroll { overflow-y: scroll; max-height: 80vh; }
table.alternate td, table.alternate th { padding: .5em; }
table.alternate tr:nth-child(even) { background: #DDD; }
table.alternate tr:nth-child(odd) { background: #F9F9F9; }
/*#meta { margin-bottom: 2em; }*/
#meta .icons { margin-bottom: 2em; }
@@ -243,13 +248,27 @@ p.trckr { font-size: .9em; margin-left: .5em; }
.cs1{stroke:#CA0D3A}
/* Help needed */
#help-links td { padding: .5em; }
#help-links tr:nth-child(even) { background: #DDD; }
#help-links tr:nth-child(odd) { background: #F9F9F9; }
#help-links span.snd { display: table; }
#help-links .notyet { color: #D11; }
#help-links .done { color: #52C840; }
/* rank-list */
#rank-list th span { display: block; margin-top: 4px; }
#rank-list th { vertical-align: bottom; }
#rank-list th a { cursor:pointer; border: unset; padding: 0 5px; margin: 0 2px; }
#rank-list th a:hover { fill: #F00; }
#rank-list th a.active { fill: #CA0D3A; }
#rank-list th:not(:first-child) { min-width: 12ex; }
#rank-list img { width: 25px; height: 25px; vertical-align: bottom; }
#rank-list td { text-align: center; min-width: max-content; }
#rank-list td:nth-child(1) {
position: sticky; left: 0;
background: inherit;
text-align: right;
}
#rank-list td:nth-child(2) { text-align: left; }
/* responsive */
@media(max-width: 900px) {
#stats { grid-template-columns: repeat(2, max-content); }

View File

@@ -40,22 +40,23 @@ Given A → B, B depends on A
```
digraph Dependency {
"." -> html_root
"." -> download_tracker
"." -> download_itunes
"." -> bundle_combine
download_itunes -> index_app_names
download_itunes -> index_categories
index_categories -> html_categories
index_app_names -> html_bundle
index_app_names -> html_index_apps
index_app_names -> html_index_domains
index_app_names -> html_categories
index_rank -> html_index_domains
"." -> bundle_combine
bundle_combine -> index_rank
index_rank -> html_bundle
bundle_combine -> index_domains
index_categories -> html_categories
index_app_names -> html_index_apps
index_app_names -> html_categories
index_app_names -> index_rank
index_rank -> html_bundle
index_rank -> html_rank
index_rank -> html_index_domains
index_domains -> html_index_domains
"." -> download_tracker
"." -> html_ranking
"." -> html_root
}
```
[graphviz](http://www.webgraphviz.com/)

View File

@@ -15,7 +15,7 @@ def process(affected=None, per_page=60):
arr.append((cid, cat))
if affected and cid not in affected:
continue
pre = HTML.h2(HTML.a_path([(parent, '..')], cat))
pre = HTML.h2(HTML.a_path([(parent, '../')], cat))
_, a = HTML.write_app_pages(mylib.path_add(base, cid), apps, cat,
per_page, pre=pre)
print(' {} ({})'.format(cat, a))

View File

@@ -7,9 +7,10 @@ import lib_html as HTML
def process(per_page=60):
print('generating html: app-index ...')
title = 'Apps (AZ)'
header = HTML.h2(HTML.a_path([('Results', '/results/')], title))
p, a = HTML.write_app_pages(mylib.path_out('index', 'apps'),
mylib.appids_in_out(), title,
per_page=per_page, pre=HTML.h2(title))
per_page=per_page, pre=header)
print(' {} apps'.format(a))
print(' {} pages'.format(p))
print('')

View File

@@ -98,6 +98,7 @@ def gen_lookup(html_dir, doms_dict, names_dict, title):
{}
</div>
<script type="text/javascript" src="/static/lookup-domain.js"></script>
<script type="text/javascript" src="/static/lozad.js"></script>
<script type="text/javascript">
lookup_domain_js('doms.json', 'apps.json', 'name', 'num-apps', 'app-toc');
</script>
@@ -122,7 +123,9 @@ def gen_results(c_apps, c_domains, title):
<li>List of <a href="/category/">Categories</a></li>
<li>List of <a href="/index/domains/all/">Requested Domains</a></li>
<li>List of <a href="/index/domains/tracker/">Trackers</a></li>
</ul>'''.format(title, c_apps, c_domains, c_recordings, c_logs), title=title)
</ul>
<p>Or go ahead to the <b><a href="/ranking/">Ranking</a></b> directly.</p>
'''.format(title, c_apps, c_domains, c_recordings, c_logs), title=title)
mylib.symlink(index_rank.fname_app_rank(),
mylib.path_out('results', 'rank.json')) # after HTML.write

34
src/html_ranking.py Executable file
View File

@@ -0,0 +1,34 @@
#!/usr/bin/env python3
import lib_common as mylib
import lib_html as HTML
import index_rank
def process():
print('generating html: ranking ...')
title = 'Ranking'
header = HTML.a_path([('Results', '/results/')], title)
base = mylib.path_out('ranking')
HTML.write(base, '''
<h2>{}</h2>
<p>
This ranking shows only the 500 most recently updated applications.<br>
If you're missing an app, feel free to contribute a new app recording.
</p>
<div class="xscroll yscroll">
<table id="rank-list" class="alternate"><tr><td>Loading …</td></tr></table>
</div>
<p class="right snd">Download: <a href="data.json" download="ranking-all.json">json</a></p>
<script type="text/javascript" src="/static/ranking.js"></script>
<script type="text/javascript" src="/static/lozad.js"></script>
<script type="text/javascript">
rank_js('data.json');
</script>'''.format(header), title=title)
mylib.symlink(index_rank.fname_ranking_list(),
mylib.path_add(base, 'data.json'))
print('')
if __name__ == '__main__':
process()

View File

@@ -67,7 +67,7 @@ def gen_help():
obj = mylib.json_read(mylib.path_root('src', 'help.json'))
for land in sorted(obj.keys()):
txt += '\n<h3>{}:</h3>\n<table>'.format(land)
txt += '\n<h3>{}:</h3>\n<table class="alternate">'.format(land)
txt += HTML.tr(['', 'App Name', 'pre iOS 14', 'post iOS 14'], 'th')
for i, x in enumerate(obj[land]):
bid = x[2]

View File

@@ -3,6 +3,7 @@
import sys
import lib_common as mylib
import bundle_combine # get_evaluated
import index_app_names
def fname_app_summary():
@@ -13,40 +14,27 @@ def fname_app_rank():
return mylib.path_data_index('app_rank.json')
def fname_ranking_list():
return mylib.path_data_index('ranking_list.json')
def json_to_list(json):
return [
json['sum_rec'],
json['avg_time'],
json['sum_time'],
json['avg_logs_pm'],
json['sum_logs_pm'],
len(json['pardom']),
len(json['subdom']),
json['tracker_percent'],
json['sum_rec'], # 0
json['avg_time'], # 1
json['sum_time'], # 2
json['avg_logs_pm'], # 3
json['sum_logs_pm'], # 4
len(json['pardom']), # 5
len(json['subdom']), # 6
json['tracker_percent'], # 7
# v- not part of rank -v
json['sum_logs'],
json['avg_logs'],
json['last_date'],
json['sum_logs'], # 8
json['avg_logs'], # 9
json['last_date'], # 10
]
def list_to_json(list):
return {
'sum_rec': list[0],
'avg_time': list[1],
'sum_time': list[2],
'avg_logs_pm': list[3],
'sum_logs_pm': list[4],
'pardom': list[5],
'subdom': list[6],
'tracker_percent': list[7],
# v- not part of rank -v
'sum_logs': list[8],
'avg_logs': list[9],
'last_date': list[10],
}
def write_summary_index(index, bundle_ids, deleteOnly=False):
for bid in bundle_ids:
# delete old value
@@ -64,10 +52,24 @@ def write_summary_index(index, bundle_ids, deleteOnly=False):
total[1] += val[8]
index['_sum'] = total
mylib.json_write(fname_app_summary(), index, pretty=False)
mylib.try_del(index, ['_sum'])
def write_ranking_list(index):
ret = []
for bid, values in index.items():
ret.append([bid, index_app_names.get_name(bid)] + values)
del(values[8:])
ret.sort(key=lambda x: -x[2 + 10]) # sort by last update
# TODO: doesnt scale well, 100'000 apps ~> 12mb
if len(ret) > 500: # limit to most recent X entries
ret = ret[:500]
# ret.sort(key=lambda x: x[1].lower()) # sort by name
mylib.json_write(fname_ranking_list(), ret, pretty=False)
def write_rank_index(index):
mylib.try_del(index, ['_sum', '_ranks', '_min', '_max'])
mylib.try_del(index, ['_ranks', '_min', '_max'])
mins = []
maxs = []
if len(index) > 0:
@@ -111,6 +113,7 @@ def process(bundle_ids, deleteOnly=False):
index = mylib.json_safe_read(fname, {})
ids = mylib.appids_in_data(bundle_ids)
write_summary_index(index, ids, deleteOnly=deleteOnly)
write_ranking_list(index)
write_rank_index(index)
print('')

View File

@@ -65,7 +65,7 @@ def h2(inner, attr=None):
def a_path(parts, suffix):
''' expects (name, url) tuples '''
return ' / '.join(['<a href="{}/">{}</a>'.format(url, title)
return ' / '.join(['<a href="{}">{}</a>'.format(url, title)
for title, url in parts] + [suffix])

View File

@@ -1,151 +1,166 @@
<svg width="686pt" height="260pt" viewBox="0.00 0.00 686.00 260.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph1" class="graph" transform="scale(1 1) rotate(0) translate(4 256)">
<svg width="661pt" height="332pt" viewBox="0.00 0.00 661.00 332.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph1" class="graph" transform="scale(1 1) rotate(0) translate(4 328)">
<title>Dependency</title>
<polygon fill="white" stroke="white" points="-4,5 -4,-256 683,-256 683,5 -4,5"></polygon>
<polygon fill="white" stroke="white" points="-4,5 -4,-328 658,-328 658,5 -4,5"></polygon>
<!-- . -->
<g id="node1" class="node"><title>.</title>
<ellipse fill="none" stroke="black" cx="338" cy="-234" rx="27" ry="18"></ellipse>
<text text-anchor="middle" x="338" y="-229.8" font-family="Times,serif" font-size="14.00">.</text>
<ellipse fill="none" stroke="black" cx="361" cy="-306" rx="27" ry="18"></ellipse>
<text text-anchor="middle" x="361" y="-301.8" font-family="Times,serif" font-size="14.00">.</text>
</g>
<!-- html_root -->
<g id="node3" class="node"><title>html_root</title>
<ellipse fill="none" stroke="black" cx="109" cy="-162" rx="49.1927" ry="18"></ellipse>
<text text-anchor="middle" x="109" y="-157.8" font-family="Times,serif" font-size="14.00">html_root</text>
<!-- download_tracker -->
<g id="node3" class="node"><title>download_tracker</title>
<ellipse fill="none" stroke="black" cx="80" cy="-234" rx="80.1284" ry="18"></ellipse>
<text text-anchor="middle" x="80" y="-229.8" font-family="Times,serif" font-size="14.00">download_tracker</text>
</g>
<!-- .&#45;&gt;html_root -->
<g id="edge2" class="edge"><title>.-&gt;html_root</title>
<path fill="none" stroke="black" d="M313.926,-225.641C276.403,-214.171 203.308,-191.828 155.329,-177.162"></path>
<polygon fill="black" stroke="black" points="156.08,-173.731 145.493,-174.155 154.033,-180.426 156.08,-173.731"></polygon>
<!-- .&#45;&gt;download_tracker -->
<g id="edge2" class="edge"><title>.-&gt;download_tracker</title>
<path fill="none" stroke="black" d="M336.063,-298.788C292.739,-287.996 202.38,-265.486 141.44,-250.305"></path>
<polygon fill="black" stroke="black" points="142.098,-246.862 131.549,-247.841 140.406,-253.655 142.098,-246.862"></polygon>
</g>
<!-- download_itunes -->
<g id="node5" class="node"><title>download_itunes</title>
<ellipse fill="none" stroke="black" cx="253" cy="-162" rx="77.0235" ry="18"></ellipse>
<text text-anchor="middle" x="253" y="-157.8" font-family="Times,serif" font-size="14.00">download_itunes</text>
<ellipse fill="none" stroke="black" cx="256" cy="-234" rx="77.0235" ry="18"></ellipse>
<text text-anchor="middle" x="256" y="-229.8" font-family="Times,serif" font-size="14.00">download_itunes</text>
</g>
<!-- .&#45;&gt;download_itunes -->
<g id="edge4" class="edge"><title>.-&gt;download_itunes</title>
<path fill="none" stroke="black" d="M321.611,-219.503C310.105,-210.028 294.431,-197.12 280.968,-186.033"></path>
<polygon fill="black" stroke="black" points="282.873,-183.067 272.929,-179.412 278.423,-188.471 282.873,-183.067"></polygon>
<path fill="none" stroke="black" d="M342.228,-292.485C327.324,-282.55 306.132,-268.422 288.487,-256.658"></path>
<polygon fill="black" stroke="black" points="290.415,-253.737 280.153,-251.102 286.532,-259.561 290.415,-253.737"></polygon>
</g>
<!-- bundle_combine -->
<g id="node22" class="node"><title>bundle_combine</title>
<ellipse fill="none" stroke="black" cx="423" cy="-162" rx="75.1062" ry="18"></ellipse>
<text text-anchor="middle" x="423" y="-157.8" font-family="Times,serif" font-size="14.00">bundle_combine</text>
<g id="node7" class="node"><title>bundle_combine</title>
<ellipse fill="none" stroke="black" cx="430" cy="-162" rx="75.1062" ry="18"></ellipse>
<text text-anchor="middle" x="430" y="-157.8" font-family="Times,serif" font-size="14.00">bundle_combine</text>
</g>
<!-- .&#45;&gt;bundle_combine -->
<g id="edge22" class="edge"><title>.-&gt;bundle_combine</title>
<path fill="none" stroke="black" d="M354.389,-219.503C365.895,-210.028 381.569,-197.12 395.032,-186.033"></path>
<polygon fill="black" stroke="black" points="397.577,-188.471 403.071,-179.412 393.127,-183.067 397.577,-188.471"></polygon>
<g id="edge6" class="edge"><title>.-&gt;bundle_combine</title>
<path fill="none" stroke="black" d="M361.903,-288.008C363.494,-269.383 367.885,-239.083 380,-216 385.683,-205.171 394.205,-194.962 402.611,-186.45"></path>
<polygon fill="black" stroke="black" points="405.121,-188.892 409.894,-179.433 400.264,-183.851 405.121,-188.892"></polygon>
</g>
<!-- download_tracker -->
<g id="node29" class="node"><title>download_tracker</title>
<ellipse fill="none" stroke="black" cx="597" cy="-162" rx="80.1284" ry="18"></ellipse>
<text text-anchor="middle" x="597" y="-157.8" font-family="Times,serif" font-size="14.00">download_tracker</text>
<!-- html_ranking -->
<g id="node30" class="node"><title>html_ranking</title>
<ellipse fill="none" stroke="black" cx="452" cy="-234" rx="63.108" ry="18"></ellipse>
<text text-anchor="middle" x="452" y="-229.8" font-family="Times,serif" font-size="14.00">html_ranking</text>
</g>
<!-- .&#45;&gt;download_tracker -->
<g id="edge32" class="edge"><title>.-&gt;download_tracker</title>
<path fill="none" stroke="black" d="M362.834,-226.288C402.847,-215.474 482.678,-193.898 537.928,-178.965"></path>
<polygon fill="black" stroke="black" points="538.86,-182.339 547.6,-176.351 537.034,-175.582 538.86,-182.339"></polygon>
<!-- .&#45;&gt;html_ranking -->
<g id="edge32" class="edge"><title>.-&gt;html_ranking</title>
<path fill="none" stroke="black" d="M378.116,-291.834C390.684,-282.166 408.061,-268.799 422.805,-257.457"></path>
<polygon fill="black" stroke="black" points="425.334,-259.928 431.126,-251.057 421.066,-254.38 425.334,-259.928"></polygon>
</g>
<!-- html_root -->
<g id="node32" class="node"><title>html_root</title>
<ellipse fill="none" stroke="black" cx="582" cy="-234" rx="49.1927" ry="18"></ellipse>
<text text-anchor="middle" x="582" y="-229.8" font-family="Times,serif" font-size="14.00">html_root</text>
</g>
<!-- .&#45;&gt;html_root -->
<g id="edge34" class="edge"><title>.-&gt;html_root</title>
<path fill="none" stroke="black" d="M385.072,-297.376C421.237,-285.92 490.243,-264.063 536.199,-249.507"></path>
<polygon fill="black" stroke="black" points="537.444,-252.784 545.921,-246.428 535.331,-246.111 537.444,-252.784"></polygon>
</g>
<!-- index_app_names -->
<g id="node7" class="node"><title>index_app_names</title>
<ellipse fill="none" stroke="black" cx="253" cy="-90" rx="80.1456" ry="18"></ellipse>
<text text-anchor="middle" x="253" y="-85.8" font-family="Times,serif" font-size="14.00">index_app_names</text>
<g id="node9" class="node"><title>index_app_names</title>
<ellipse fill="none" stroke="black" cx="256" cy="-162" rx="80.1456" ry="18"></ellipse>
<text text-anchor="middle" x="256" y="-157.8" font-family="Times,serif" font-size="14.00">index_app_names</text>
</g>
<!-- download_itunes&#45;&gt;index_app_names -->
<g id="edge6" class="edge"><title>download_itunes-&gt;index_app_names</title>
<path fill="none" stroke="black" d="M253,-143.697C253,-135.983 253,-126.712 253,-118.112"></path>
<polygon fill="black" stroke="black" points="256.5,-118.104 253,-108.104 249.5,-118.104 256.5,-118.104"></polygon>
<g id="edge8" class="edge"><title>download_itunes-&gt;index_app_names</title>
<path fill="none" stroke="black" d="M256,-215.697C256,-207.983 256,-198.712 256,-190.112"></path>
<polygon fill="black" stroke="black" points="259.5,-190.104 256,-180.104 252.5,-190.104 259.5,-190.104"></polygon>
</g>
<!-- index_categories -->
<g id="node9" class="node"><title>index_categories</title>
<ellipse fill="none" stroke="black" cx="76" cy="-90" rx="76.1936" ry="18"></ellipse>
<text text-anchor="middle" x="76" y="-85.8" font-family="Times,serif" font-size="14.00">index_categories</text>
<g id="node11" class="node"><title>index_categories</title>
<ellipse fill="none" stroke="black" cx="81" cy="-162" rx="76.1936" ry="18"></ellipse>
<text text-anchor="middle" x="81" y="-157.8" font-family="Times,serif" font-size="14.00">index_categories</text>
</g>
<!-- download_itunes&#45;&gt;index_categories -->
<g id="edge8" class="edge"><title>download_itunes-&gt;index_categories</title>
<path fill="none" stroke="black" d="M215.463,-146.155C188.476,-135.482 151.899,-121.016 122.974,-109.577"></path>
<polygon fill="black" stroke="black" points="124.069,-106.247 113.483,-105.824 121.495,-112.756 124.069,-106.247"></polygon>
<g id="edge10" class="edge"><title>download_itunes-&gt;index_categories</title>
<path fill="none" stroke="black" d="M218.887,-218.155C192.205,-207.482 156.041,-193.016 127.443,-181.577"></path>
<polygon fill="black" stroke="black" points="128.644,-178.288 118.06,-177.824 126.044,-184.787 128.644,-178.288"></polygon>
</g>
<!-- html_categories -->
<g id="node11" class="node"><title>html_categories</title>
<ellipse fill="none" stroke="black" cx="76" cy="-18" rx="72.5712" ry="18"></ellipse>
<text text-anchor="middle" x="76" y="-13.8" font-family="Times,serif" font-size="14.00">html_categories</text>
<!-- index_rank -->
<g id="node13" class="node"><title>index_rank</title>
<ellipse fill="none" stroke="black" cx="404" cy="-90" rx="54.219" ry="18"></ellipse>
<text text-anchor="middle" x="404" y="-85.8" font-family="Times,serif" font-size="14.00">index_rank</text>
</g>
<!-- index_app_names&#45;&gt;html_categories -->
<g id="edge18" class="edge"><title>index_app_names-&gt;html_categories</title>
<path fill="none" stroke="black" d="M215.029,-73.9832C187.924,-63.2638 151.309,-48.7834 122.459,-37.3738"></path>
<polygon fill="black" stroke="black" points="123.584,-34.055 112.998,-33.632 121.01,-40.5644 123.584,-34.055"></polygon>
</g>
<!-- html_bundle -->
<g id="node13" class="node"><title>html_bundle</title>
<ellipse fill="none" stroke="black" cx="399" cy="-18" rx="59.2871" ry="18"></ellipse>
<text text-anchor="middle" x="399" y="-13.8" font-family="Times,serif" font-size="14.00">html_bundle</text>
</g>
<!-- index_app_names&#45;&gt;html_bundle -->
<g id="edge12" class="edge"><title>index_app_names-&gt;html_bundle</title>
<path fill="none" stroke="black" d="M285.402,-73.4647C307.258,-62.986 336.232,-49.0943 359.448,-37.9633"></path>
<polygon fill="black" stroke="black" points="361.178,-41.0153 368.682,-33.5359 358.152,-34.7033 361.178,-41.0153"></polygon>
</g>
<!-- html_index_apps -->
<g id="node15" class="node"><title>html_index_apps</title>
<ellipse fill="none" stroke="black" cx="244" cy="-18" rx="77.3345" ry="18"></ellipse>
<text text-anchor="middle" x="244" y="-13.8" font-family="Times,serif" font-size="14.00">html_index_apps</text>
</g>
<!-- index_app_names&#45;&gt;html_index_apps -->
<g id="edge14" class="edge"><title>index_app_names-&gt;html_index_apps</title>
<path fill="none" stroke="black" d="M250.775,-71.6966C249.783,-63.9827 248.592,-54.7125 247.486,-46.1124"></path>
<polygon fill="black" stroke="black" points="250.946,-45.5763 246.199,-36.1043 244.003,-46.469 250.946,-45.5763"></polygon>
</g>
<!-- html_index_domains -->
<g id="node17" class="node"><title>html_index_domains</title>
<ellipse fill="none" stroke="black" cx="569" cy="-18" rx="92.3709" ry="18"></ellipse>
<text text-anchor="middle" x="569" y="-13.8" font-family="Times,serif" font-size="14.00">html_index_domains</text>
</g>
<!-- index_app_names&#45;&gt;html_index_domains -->
<g id="edge16" class="edge"><title>index_app_names-&gt;html_index_domains</title>
<path fill="none" stroke="black" d="M308.043,-76.8069C361.506,-64.9638 442.827,-46.9497 500.461,-34.1828"></path>
<polygon fill="black" stroke="black" points="501.568,-37.5224 510.575,-31.9424 500.054,-30.688 501.568,-37.5224"></polygon>
</g>
<!-- index_categories&#45;&gt;html_categories -->
<g id="edge10" class="edge"><title>index_categories-&gt;html_categories</title>
<path fill="none" stroke="black" d="M76,-71.6966C76,-63.9827 76,-54.7125 76,-46.1124"></path>
<polygon fill="black" stroke="black" points="79.5001,-46.1043 76,-36.1043 72.5001,-46.1044 79.5001,-46.1043"></polygon>
</g>
<!-- index_meta -->
<g id="node19" class="node"><title>index_meta</title>
<ellipse fill="none" stroke="black" cx="423" cy="-90" rx="56.1351" ry="18"></ellipse>
<text text-anchor="middle" x="423" y="-85.8" font-family="Times,serif" font-size="14.00">index_meta</text>
</g>
<!-- index_meta&#45;&gt;html_bundle -->
<g id="edge26" class="edge"><title>index_meta-&gt;html_bundle</title>
<path fill="none" stroke="black" d="M417.19,-72.055C414.489,-64.1762 411.211,-54.6165 408.187,-45.794"></path>
<polygon fill="black" stroke="black" points="411.477,-44.5996 404.923,-36.2753 404.855,-46.8699 411.477,-44.5996"></polygon>
</g>
<!-- index_meta&#45;&gt;html_index_domains -->
<g id="edge20" class="edge"><title>index_meta-&gt;html_index_domains</title>
<path fill="none" stroke="black" d="M452.898,-74.6655C474.182,-64.4605 503.084,-50.6037 526.726,-39.2685"></path>
<polygon fill="black" stroke="black" points="528.329,-42.3816 535.833,-34.9022 525.302,-36.0695 528.329,-42.3816"></polygon>
</g>
<!-- bundle_combine&#45;&gt;index_meta -->
<g id="edge24" class="edge"><title>bundle_combine-&gt;index_meta</title>
<path fill="none" stroke="black" d="M423,-143.697C423,-135.983 423,-126.712 423,-118.112"></path>
<polygon fill="black" stroke="black" points="426.5,-118.104 423,-108.104 419.5,-118.104 426.5,-118.104"></polygon>
<!-- bundle_combine&#45;&gt;index_rank -->
<g id="edge12" class="edge"><title>bundle_combine-&gt;index_rank</title>
<path fill="none" stroke="black" d="M423.706,-144.055C420.78,-136.176 417.229,-126.617 413.952,-117.794"></path>
<polygon fill="black" stroke="black" points="417.179,-116.431 410.417,-108.275 410.617,-118.868 417.179,-116.431"></polygon>
</g>
<!-- index_domains -->
<g id="node26" class="node"><title>index_domains</title>
<ellipse fill="none" stroke="black" cx="568" cy="-90" rx="70.0665" ry="18"></ellipse>
<text text-anchor="middle" x="568" y="-85.8" font-family="Times,serif" font-size="14.00">index_domains</text>
<g id="node15" class="node"><title>index_domains</title>
<ellipse fill="none" stroke="black" cx="556" cy="-90" rx="70.0665" ry="18"></ellipse>
<text text-anchor="middle" x="556" y="-85.8" font-family="Times,serif" font-size="14.00">index_domains</text>
</g>
<!-- bundle_combine&#45;&gt;index_domains -->
<g id="edge28" class="edge"><title>bundle_combine-&gt;index_domains</title>
<path fill="none" stroke="black" d="M454.821,-145.638C476.069,-135.38 504.21,-121.795 527.116,-110.737"></path>
<polygon fill="black" stroke="black" points="528.769,-113.825 536.253,-106.326 525.726,-107.522 528.769,-113.825"></polygon>
<g id="edge14" class="edge"><title>bundle_combine-&gt;index_domains</title>
<path fill="none" stroke="black" d="M458.277,-145.291C476.191,-135.338 499.552,-122.36 518.942,-111.588"></path>
<polygon fill="black" stroke="black" points="520.776,-114.573 527.818,-106.657 517.376,-108.454 520.776,-114.573"></polygon>
</g>
<!-- index_app_names&#45;&gt;index_rank -->
<g id="edge24" class="edge"><title>index_app_names-&gt;index_rank</title>
<path fill="none" stroke="black" d="M288.846,-145.465C311.4,-134.797 341.433,-120.593 365.172,-109.364"></path>
<polygon fill="black" stroke="black" points="366.718,-112.505 374.261,-105.066 363.725,-106.177 366.718,-112.505"></polygon>
</g>
<!-- html_categories -->
<g id="node17" class="node"><title>html_categories</title>
<ellipse fill="none" stroke="black" cx="84" cy="-90" rx="72.5712" ry="18"></ellipse>
<text text-anchor="middle" x="84" y="-85.8" font-family="Times,serif" font-size="14.00">html_categories</text>
</g>
<!-- index_app_names&#45;&gt;html_categories -->
<g id="edge20" class="edge"><title>index_app_names-&gt;html_categories</title>
<path fill="none" stroke="black" d="M219.102,-145.983C192.877,-135.31 157.491,-120.909 129.513,-109.523"></path>
<polygon fill="black" stroke="black" points="130.535,-106.16 119.953,-105.632 127.896,-112.643 130.535,-106.16"></polygon>
</g>
<!-- html_index_apps -->
<g id="node19" class="node"><title>html_index_apps</title>
<ellipse fill="none" stroke="black" cx="254" cy="-90" rx="77.3345" ry="18"></ellipse>
<text text-anchor="middle" x="254" y="-85.8" font-family="Times,serif" font-size="14.00">html_index_apps</text>
</g>
<!-- index_app_names&#45;&gt;html_index_apps -->
<g id="edge18" class="edge"><title>index_app_names-&gt;html_index_apps</title>
<path fill="none" stroke="black" d="M255.506,-143.697C255.285,-135.983 255.02,-126.712 254.775,-118.112"></path>
<polygon fill="black" stroke="black" points="258.273,-118 254.489,-108.104 251.276,-118.2 258.273,-118"></polygon>
</g>
<!-- index_categories&#45;&gt;html_categories -->
<g id="edge16" class="edge"><title>index_categories-&gt;html_categories</title>
<path fill="none" stroke="black" d="M81.7416,-143.697C82.0722,-135.983 82.4695,-126.712 82.838,-118.112"></path>
<polygon fill="black" stroke="black" points="86.3355,-118.245 83.267,-108.104 79.3419,-117.945 86.3355,-118.245"></polygon>
</g>
<!-- html_index_domains -->
<g id="node22" class="node"><title>html_index_domains</title>
<ellipse fill="none" stroke="black" cx="561" cy="-18" rx="92.3709" ry="18"></ellipse>
<text text-anchor="middle" x="561" y="-13.8" font-family="Times,serif" font-size="14.00">html_index_domains</text>
</g>
<!-- index_rank&#45;&gt;html_index_domains -->
<g id="edge22" class="edge"><title>index_rank-&gt;html_index_domains</title>
<path fill="none" stroke="black" d="M435.018,-75.1703C458.258,-64.8083 490.393,-50.481 516.39,-38.8899"></path>
<polygon fill="black" stroke="black" points="517.954,-42.0246 525.662,-34.7557 515.104,-35.6313 517.954,-42.0246"></polygon>
</g>
<!-- html_bundle -->
<g id="node25" class="node"><title>html_bundle</title>
<ellipse fill="none" stroke="black" cx="271" cy="-18" rx="59.2871" ry="18"></ellipse>
<text text-anchor="middle" x="271" y="-13.8" font-family="Times,serif" font-size="14.00">html_bundle</text>
</g>
<!-- index_rank&#45;&gt;html_bundle -->
<g id="edge26" class="edge"><title>index_rank-&gt;html_bundle</title>
<path fill="none" stroke="black" d="M376.442,-74.496C356.736,-64.1241 329.985,-50.0446 308.359,-38.6626"></path>
<polygon fill="black" stroke="black" points="309.919,-35.5287 299.44,-33.9684 306.659,-41.7232 309.919,-35.5287"></polygon>
</g>
<!-- html_rank -->
<g id="node28" class="node"><title>html_rank</title>
<ellipse fill="none" stroke="black" cx="400" cy="-18" rx="50.5965" ry="18"></ellipse>
<text text-anchor="middle" x="400" y="-13.8" font-family="Times,serif" font-size="14.00">html_rank</text>
</g>
<!-- index_rank&#45;&gt;html_rank -->
<g id="edge30" class="edge"><title>index_rank-&gt;html_rank</title>
<path fill="none" stroke="black" d="M403.011,-71.6966C402.57,-63.9827 402.041,-54.7125 401.549,-46.1124"></path>
<polygon fill="black" stroke="black" points="405.042,-45.8883 400.977,-36.1043 398.054,-46.2878 405.042,-45.8883"></polygon>
</g>
<!-- index_domains&#45;&gt;html_index_domains -->
<g id="edge30" class="edge"><title>index_domains-&gt;html_index_domains</title>
<path fill="none" stroke="black" d="M568.247,-71.6966C568.357,-63.9827 568.49,-54.7125 568.613,-46.1124"></path>
<polygon fill="black" stroke="black" points="572.112,-46.1533 568.756,-36.1043 565.113,-46.0533 572.112,-46.1533"></polygon>
<g id="edge28" class="edge"><title>index_domains-&gt;html_index_domains</title>
<path fill="none" stroke="black" d="M557.236,-71.6966C557.787,-63.9827 558.449,-54.7125 559.063,-46.1124"></path>
<polygon fill="black" stroke="black" points="562.557,-46.3283 559.778,-36.1043 555.575,-45.8295 562.557,-46.3283"></polygon>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 10 KiB