|
|
<!DOCTYPE html><html lang="en"> |
|
|
<head><meta charset="utf-8"> |
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>STimage-1K4M Dataset - STimage-1K4M</title> |
|
|
|
|
|
<meta name="description" content="Your Site Description |
|
|
"> |
|
|
<link rel="canonical" href="https://jiawenchenn.github.io/STimage-1K4M/docs/00_about.html"><link rel="alternate" type="application/rss+xml" title="STimage-1K4M" href="/STimage-1K4M/feed.xml"><link rel="apple-touch-icon" sizes="180x180" href="/STimage-1K4M/assets/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/STimage-1K4M/assets/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/STimage-1K4M/assets/favicon-16x16.png"><link rel="manifest" href="/STimage-1K4M/assets/site.webmanifest"><link rel="mask-icon" href="/STimage-1K4M/assets/safari-pinned-tab.svg" color="#fc4d50"><link rel="shortcut icon" href="/STimage-1K4M/assets/favicon.ico"> |
|
|
|
|
|
<meta name="msapplication-TileColor" content="#ffc40d"><meta name="msapplication-config" content="/STimage-1K4M/assets/browserconfig.xml"> |
|
|
|
|
|
<meta name="theme-color" content="#ffffff"> |
|
|
<link rel="stylesheet" href="../assets/css/main.css"><link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/font-awesome/5.15.1/css/all.css" > |
|
|
|
|
|
|
|
|
<script>(function() { |
|
|
window.isArray = function(val) { |
|
|
return Object.prototype.toString.call(val) === '[object Array]'; |
|
|
}; |
|
|
window.isString = function(val) { |
|
|
return typeof val === 'string'; |
|
|
}; |
|
|
|
|
|
window.hasEvent = function(event) { |
|
|
return 'on'.concat(event) in window.document; |
|
|
}; |
|
|
|
|
|
window.isOverallScroller = function(node) { |
|
|
return node === document.documentElement || node === document.body || node === window; |
|
|
}; |
|
|
|
|
|
window.isFormElement = function(node) { |
|
|
var tagName = node.tagName; |
|
|
return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA'; |
|
|
}; |
|
|
|
|
|
window.pageLoad = (function () { |
|
|
var loaded = false, cbs = []; |
|
|
window.addEventListener('load', function () { |
|
|
var i; |
|
|
loaded = true; |
|
|
if (cbs.length > 0) { |
|
|
for (i = 0; i < cbs.length; i++) { |
|
|
cbs[i](); |
|
|
} |
|
|
} |
|
|
}); |
|
|
return { |
|
|
then: function(cb) { |
|
|
cb && (loaded ? cb() : (cbs.push(cb))); |
|
|
} |
|
|
}; |
|
|
})(); |
|
|
})(); |
|
|
(function() { |
|
|
window.throttle = function(func, wait) { |
|
|
var args, result, thisArg, timeoutId, lastCalled = 0; |
|
|
|
|
|
function trailingCall() { |
|
|
lastCalled = new Date; |
|
|
timeoutId = null; |
|
|
result = func.apply(thisArg, args); |
|
|
} |
|
|
return function() { |
|
|
var now = new Date, |
|
|
remaining = wait - (now - lastCalled); |
|
|
|
|
|
args = arguments; |
|
|
thisArg = this; |
|
|
|
|
|
if (remaining <= 0) { |
|
|
clearTimeout(timeoutId); |
|
|
timeoutId = null; |
|
|
lastCalled = now; |
|
|
result = func.apply(thisArg, args); |
|
|
} else if (!timeoutId) { |
|
|
timeoutId = setTimeout(trailingCall, remaining); |
|
|
} |
|
|
return result; |
|
|
}; |
|
|
}; |
|
|
})(); |
|
|
(function() { |
|
|
var Set = (function() { |
|
|
var add = function(item) { |
|
|
var i, data = this._data; |
|
|
for (i = 0; i < data.length; i++) { |
|
|
if (data[i] === item) { |
|
|
return; |
|
|
} |
|
|
} |
|
|
this.size ++; |
|
|
data.push(item); |
|
|
return data; |
|
|
}; |
|
|
|
|
|
var Set = function(data) { |
|
|
this.size = 0; |
|
|
this._data = []; |
|
|
var i; |
|
|
if (data.length > 0) { |
|
|
for (i = 0; i < data.length; i++) { |
|
|
add.call(this, data[i]); |
|
|
} |
|
|
} |
|
|
}; |
|
|
Set.prototype.add = add; |
|
|
Set.prototype.get = function(index) { return this._data[index]; }; |
|
|
Set.prototype.has = function(item) { |
|
|
var i, data = this._data; |
|
|
for (i = 0; i < data.length; i++) { |
|
|
if (this.get(i) === item) { |
|
|
return true; |
|
|
} |
|
|
} |
|
|
return false; |
|
|
}; |
|
|
Set.prototype.is = function(map) { |
|
|
if (map._data.length !== this._data.length) { return false; } |
|
|
var i, j, flag, tData = this._data, mData = map._data; |
|
|
for (i = 0; i < tData.length; i++) { |
|
|
for (flag = false, j = 0; j < mData.length; j++) { |
|
|
if (tData[i] === mData[j]) { |
|
|
flag = true; |
|
|
break; |
|
|
} |
|
|
} |
|
|
if (!flag) { return false; } |
|
|
} |
|
|
return true; |
|
|
}; |
|
|
Set.prototype.values = function() { |
|
|
return this._data; |
|
|
}; |
|
|
return Set; |
|
|
})(); |
|
|
|
|
|
window.Lazyload = (function(doc) { |
|
|
var queue = {js: [], css: []}, sources = {js: {}, css: {}}, context = this; |
|
|
var createNode = function(name, attrs) { |
|
|
var node = doc.createElement(name), attr; |
|
|
for (attr in attrs) { |
|
|
if (attrs.hasOwnProperty(attr)) { |
|
|
node.setAttribute(attr, attrs[attr]); |
|
|
} |
|
|
} |
|
|
return node; |
|
|
}; |
|
|
var end = function(type, url) { |
|
|
var s, q, qi, cbs, i, j, cur, val, flag; |
|
|
if (type === 'js' || type ==='css') { |
|
|
s = sources[type], q = queue[type]; |
|
|
s[url] = true; |
|
|
for (i = 0; i < q.length; i++) { |
|
|
cur = q[i]; |
|
|
if (cur.urls.has(url)) { |
|
|
qi = cur, val = qi.urls.values(); |
|
|
qi && (cbs = qi.callbacks); |
|
|
for (flag = true, j = 0; j < val.length; j++) { |
|
|
cur = val[j]; |
|
|
if (!s[cur]) { |
|
|
flag = false; |
|
|
} |
|
|
} |
|
|
if (flag && cbs && cbs.length > 0) { |
|
|
for (j = 0; j < cbs.length; j++) { |
|
|
cbs[j].call(context); |
|
|
} |
|
|
qi.load = true; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
var load = function(type, urls, callback) { |
|
|
var s, q, qi, node, i, cur, |
|
|
_urls = typeof urls === 'string' ? new Set([urls]) : new Set(urls), val, url; |
|
|
if (type === 'js' || type ==='css') { |
|
|
s = sources[type], q = queue[type]; |
|
|
for (i = 0; i < q.length; i++) { |
|
|
cur = q[i]; |
|
|
if (_urls.is(cur.urls)) { |
|
|
qi = cur; |
|
|
break; |
|
|
} |
|
|
} |
|
|
val = _urls.values(); |
|
|
if (qi) { |
|
|
callback && (qi.load || qi.callbacks.push(callback)); |
|
|
callback && (qi.load && callback()); |
|
|
} else { |
|
|
q.push({ |
|
|
urls: _urls, |
|
|
callbacks: callback ? [callback] : [], |
|
|
load: false |
|
|
}); |
|
|
for (i = 0; i < val.length; i++) { |
|
|
node = null, url = val[i]; |
|
|
if (s[url] === undefined) { |
|
|
(type === 'js' ) && (node = createNode('script', { src: url })); |
|
|
(type === 'css') && (node = createNode('link', { rel: 'stylesheet', href: url })); |
|
|
if (node) { |
|
|
node.onload = (function(type, url) { |
|
|
return function() { |
|
|
end(type, url); |
|
|
}; |
|
|
})(type, url); |
|
|
(doc.head || doc.body).appendChild(node); |
|
|
s[url] = false; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
return { |
|
|
js: function(url, callback) { |
|
|
load('js', url, callback); |
|
|
}, |
|
|
css: function(url, callback) { |
|
|
load('css', url, callback); |
|
|
} |
|
|
}; |
|
|
})(this.document); |
|
|
})(); |
|
|
</script><script> |
|
|
(function() { |
|
|
var TEXT_VARIABLES = { |
|
|
version: '2.2.6', |
|
|
sources: { |
|
|
font_awesome: 'https://cdn.bootcdn.net/ajax/libs/font-awesome/5.15.1/css/all.css', |
|
|
jquery: 'https://cdn.bootcss.com/jquery/3.1.1/jquery.min.js', |
|
|
leancloud_js_sdk: '//cdn.jsdelivr.net/npm/leancloud-storage@3.13.2/dist/av-min.js', |
|
|
chart: 'https://cdn.bootcss.com/Chart.js/2.7.2/Chart.bundle.min.js', |
|
|
gitalk: { |
|
|
js: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.js', |
|
|
css: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.css' |
|
|
}, |
|
|
valine: 'https://unpkg.com/valine/dist/Valine.min.js', |
|
|
mathjax: 'https://cdn.bootcss.com/mathjax/2.7.4/MathJax.js?config=TeX-MML-AM_CHTML', |
|
|
mermaid: 'https://cdn.bootcss.com/mermaid/8.0.0-rc.8/mermaid.min.js' |
|
|
}, |
|
|
site: { |
|
|
toc: { |
|
|
selectors: 'h1,h2,h3' |
|
|
} |
|
|
}, |
|
|
paths: { |
|
|
search_js: '/STimage-1K4M/assets/search.js' |
|
|
} |
|
|
}; |
|
|
window.TEXT_VARIABLES = TEXT_VARIABLES; |
|
|
})(); |
|
|
</script> |
|
|
</head> |
|
|
<body> |
|
|
<div class="root" data-is-touch="false"> |
|
|
<div class="layout--page js-page-root"><div class="page__main js-page-main page__viewport cell cell--auto"> |
|
|
|
|
|
<div class="page__main-inner"><div class="page__header d-print-none"><header class="header"><div class="main"> |
|
|
<div class="header__title"> |
|
|
<div class="header__brand"><a title="Your Site Description |
|
|
" href="/STimage-1K4M/docs/00_about.html">STimage-1K4M</a></div><button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button></div><nav class="navigation"> |
|
|
<ul><li class="navigation__item"><a href="/STimage-1K4M/docs/00_about">About</a></li><li class="navigation__item"><a href="/STimage-1K4M/docs/01-make-meta">Document</a></li></ul> |
|
|
</nav></div> |
|
|
</header> |
|
|
</div><div class="page__content"><div class ="main"><div class="grid grid--reverse"> |
|
|
|
|
|
<div class="col-aside d-print-none js-col-aside"></div> |
|
|
|
|
|
<div class="col-main cell cell--auto"> |
|
|
|
|
|
|
|
|
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1>STimage-1K4M Dataset</h1></header></div><meta itemprop="headline" content="STimage-1K4M Dataset"><meta itemprop="author" content="Jiawen CHen"/><div class="js-article-content"><div class="layout--article"> |
|
|
|
|
|
|
|
|
<div class="article__content" itemprop="articleBody"><p>Welcome to the STimage-1K4M Dataset repository. This dataset is designed to foster research in the field of spatial transcriptomics, combining high-resolution histopathology images with detailed gene expression data.</p> |
|
|
|
|
|
<p><img src="doc_data/f1.png" alt="teaser" title="teaser" /></p> |
|
|
|
|
|
<h2 id="dataset-description">Dataset Description</h2> |
|
|
|
|
|
<p>STimage-1K4M consists of 1,149 spatial transcriptomics slides, totaling over 4 million spots with paired gene expression data. This dataset includes:</p> |
|
|
|
|
|
<ul> |
|
|
<li>Images.</li> |
|
|
<li>Gene expression profiles matched with high-resolution histopathology images.</li> |
|
|
<li>Spatial coordinates for each spot.</li> |
|
|
</ul> |
|
|
|
|
|
<p>See example folder for an example slide from Andersson et al. (pmid: 34650042).</p> |
|
|
|
|
|
<h2 id="getting-started">Getting Started</h2> |
|
|
|
|
|
<p>To use the STimage-1K4M dataset in your research, please access the dataset via <a href="https://huggingface.co/datasets/jiawennnn/STimage-1K4M">Hugging Face</a>.</p> |
|
|
|
|
|
<h2 id="data-structure">Data structure</h2> |
|
|
<p>The data structure is organized as follows:</p> |
|
|
|
|
|
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>├── annotation <span class="c"># Pathologist annotation</span> |
|
|
├── meta <span class="c"># Test files (alternatively `spec` or `tests`)</span> |
|
|
│ ├── bib.txt <span class="c"># the bibtex for all studies with pmid included in the dataset</span> |
|
|
│ ├── meta_all_gene.csv <span class="c"># The meta information</span> |
|
|
├── ST <span class="c"># Include all data for tech: Spatial Transcriptomics</span> |
|
|
│ ├── coord <span class="c"># Include the spot coordinates & spot radius of each slide</span> |
|
|
│ ├── gene_exp <span class="c"># Include the gene expression of each slide</span> |
|
|
│ └── image <span class="c"># Include the image each slide</span> |
|
|
├── Visium <span class="c"># Include all data for tech: Visium, same structure as ST</span> |
|
|
├── VisiumHD <span class="c"># Include all data for tech: VisiumHD, same structure as ST</span> |
|
|
</code></pre></div></div> |
|
|
<h2 id="document">Document</h2> |
|
|
<p>The code for data processing and reproducing evaluation result in the paper are in <a href="01-make-meta">Document</a>.</p> |
|
|
|
|
|
<h2 id="acknowledgement">Acknowledgement</h2> |
|
|
<p>The fine-tuning and evaluation codes borrows heavily from <a href="https://github.com/openai/CLIP/issues/83">CLIP</a> and <a href="https://github.com/PathologyFoundation/plip/">PLIP</a>.</p> |
|
|
|
|
|
<h2 id="citation">Citation</h2> |
|
|
|
|
|
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>@misc{chen2024stimage1k4m, |
|
|
title={STimage-1K4M: A histopathology image-gene expression dataset for spatial transcriptomics}, |
|
|
author={Jiawen Chen and Muqing Zhou and Wenrong Wu and Jinwei Zhang and Yun Li and Didong Li}, |
|
|
year={2024}, |
|
|
eprint={2406.06393}, |
|
|
archivePrefix={arXiv}, |
|
|
primaryClass={cs.CV} |
|
|
} |
|
|
</code></pre></div></div> |
|
|
|
|
|
<h2 id="license">License</h2> |
|
|
|
|
|
<p>All code is licensed under the MIT License - see the LICENSE.md file for details.</p> |
|
|
|
|
|
<p align="center"> |
|
|
<br /> |
|
|
<br /> |
|
|
<img src="doc_data/UNC_logo_RGB.png" width="200" /> |
|
|
<br /> |
|
|
<br /> |
|
|
</p> |
|
|
</div><div class="d-print-none"><footer class="article__footer"> |
|
|
|
|
|
|
|
|
</footer> |
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
<script>(function() { |
|
|
var SOURCES = window.TEXT_VARIABLES.sources; |
|
|
window.Lazyload.js(SOURCES.jquery, function() { |
|
|
$(function() { |
|
|
var $this ,$scroll; |
|
|
var $articleContent = $('.js-article-content'); |
|
|
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar'); |
|
|
var scroll = hasSidebar ? '.js-page-main' : 'html, body'; |
|
|
$scroll = $(scroll); |
|
|
|
|
|
$articleContent.find('.highlight').each(function() { |
|
|
$this = $(this); |
|
|
$this.attr('data-lang', $this.find('code').attr('data-lang')); |
|
|
}); |
|
|
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() { |
|
|
$this = $(this); |
|
|
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>')); |
|
|
}); |
|
|
$articleContent.on('click', '.anchor', function() { |
|
|
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
})(); |
|
|
</script> |
|
|
</div><section class="page__comments d-print-none"></section></article> |
|
|
|
|
|
|
|
|
</div> |
|
|
</div></div></div><div class="page__footer d-print-none"> |
|
|
<footer class="footer py-4 js-page-footer"> |
|
|
<div class="main"><div itemscope itemtype="http://schema.org/Person"> |
|
|
<meta itemprop="name" content="Jiawen CHen"><meta itemprop="url" content="/STimage-1K4M/"><div class="footer__author-links"><div class="author-links"> |
|
|
<ul class="menu menu--nowrap menu--inline"><li title="Send me an Email."> |
|
|
<a class="button button--circle mail-button" itemprop="email" href="mailto:jiawenn@email.unc.edu" target="_blank"> |
|
|
<i class="fas fa-envelope"></i> |
|
|
</a><li title="Follow me on Github."> |
|
|
<a class="button button--circle github-button" itemprop="sameAs" href="https://github.com/JiawenChenn" target="_blank"> |
|
|
<div class="icon"><svg fill="#000000" width="24px" height="24px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"> |
|
|
<path class="svgpath" data-index="path_0" fill="#272636" d="M0 525.2c0 223.6 143.3 413.7 343 483.5 26.9 6.8 22.8-12.4 22.8-25.4l0-88.7c-155.3 18.2-161.5-84.6-172-101.7-21.1-36-70.8-45.2-56-62.3 35.4-18.2 71.4 4.6 113.1 66.3 30.2 44.7 89.1 37.2 119 29.7 6.5-26.9 20.5-50.9 39.7-69.6C248.8 728.2 181.7 630 181.7 513.2c0-56.6 18.7-108.7 55.3-150.7-23.3-69.3 2.2-128.5 5.6-137.3 66.5-6 135.5 47.6 140.9 51.8 37.8-10.2 80.9-15.6 129.1-15.6 48.5 0 91.8 5.6 129.8 15.9 12.9-9.8 77-55.8 138.8-50.2 3.3 8.8 28.2 66.7 6.3 135 37.1 42.1 56 94.6 56 151.4 0 117-67.5 215.3-228.8 243.7 26.9 26.6 43.6 63.4 43.6 104.2l0 128.8c0.9 10.3 0 20.5 17.2 20.5C878.1 942.4 1024 750.9 1024 525.3c0-282.9-229.3-512-512-512C229.1 13.2 0 242.3 0 525.2L0 525.2z" /> |
|
|
</svg> |
|
|
</div> |
|
|
</a> |
|
|
</li></ul> |
|
|
</div> |
|
|
</div> |
|
|
</div><div class="site-info mt-2"> |
|
|
<div> |
|
|
|
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</footer> |
|
|
</div></div> |
|
|
</div><script>(function() { |
|
|
var SOURCES = window.TEXT_VARIABLES.sources; |
|
|
window.Lazyload.js(SOURCES.jquery, function() { |
|
|
var $body = $('body'), $window = $(window); |
|
|
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main'); |
|
|
var activeCount = 0; |
|
|
function modal(options) { |
|
|
var $root = this, visible, onChange, hideWhenWindowScroll = false; |
|
|
var scrollTop; |
|
|
function setOptions(options) { |
|
|
var _options = options || {}; |
|
|
visible = _options.initialVisible === undefined ? false : show; |
|
|
onChange = _options.onChange; |
|
|
hideWhenWindowScroll = _options.hideWhenWindowScroll; |
|
|
} |
|
|
function init() { |
|
|
setState(visible); |
|
|
} |
|
|
function setState(isShow) { |
|
|
if (isShow === visible) { |
|
|
return; |
|
|
} |
|
|
visible = isShow; |
|
|
if (visible) { |
|
|
activeCount++; |
|
|
scrollTop = $(window).scrollTop() || $pageMain.scrollTop(); |
|
|
$root.addClass('modal--show'); |
|
|
$pageMain.scrollTop(scrollTop); |
|
|
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden')); |
|
|
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide); |
|
|
$window.on('keyup', handleKeyup); |
|
|
} else { |
|
|
activeCount > 0 && activeCount--; |
|
|
$root.removeClass('modal--show'); |
|
|
$window.scrollTop(scrollTop); |
|
|
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden')); |
|
|
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide); |
|
|
$window.off('keyup', handleKeyup); |
|
|
} |
|
|
onChange && onChange(visible); |
|
|
} |
|
|
function show() { |
|
|
setState(true); |
|
|
} |
|
|
function hide() { |
|
|
setState(false); |
|
|
} |
|
|
function handleKeyup(e) { |
|
|
|
|
|
if (e.which === 27) { |
|
|
hide(); |
|
|
} |
|
|
} |
|
|
setOptions(options); |
|
|
init(); |
|
|
return { |
|
|
show: show, |
|
|
hide: hide, |
|
|
$el: $root |
|
|
}; |
|
|
} |
|
|
$.fn.modal = modal; |
|
|
}); |
|
|
})(); |
|
|
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal"><script> |
|
|
(function () { |
|
|
var SOURCES = window.TEXT_VARIABLES.sources; |
|
|
window.Lazyload.js(SOURCES.jquery, function() { |
|
|
|
|
|
var search = (window.search || (window.search = {})); |
|
|
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ? |
|
|
true : window.useDefaultSearchBox ; |
|
|
|
|
|
var $searchModal = $('.js-page-search-modal'); |
|
|
var $searchToggle = $('.js-search-toggle'); |
|
|
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true }); |
|
|
var modalVisible = false; |
|
|
search.searchModal = searchModal; |
|
|
|
|
|
var $searchBox = null; |
|
|
var $searchInput = null; |
|
|
var $searchClear = null; |
|
|
|
|
|
function getModalVisible() { |
|
|
return modalVisible; |
|
|
} |
|
|
search.getModalVisible = getModalVisible; |
|
|
|
|
|
function handleModalChange(visible) { |
|
|
modalVisible = visible; |
|
|
if (visible) { |
|
|
search.onShow && search.onShow(); |
|
|
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus(); |
|
|
} else { |
|
|
search.onShow && search.onHide(); |
|
|
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur(); |
|
|
setTimeout(function() { |
|
|
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty')); |
|
|
search.clear && search.clear(); |
|
|
window.pageAsideAffix && window.pageAsideAffix.refresh(); |
|
|
}, 400); |
|
|
} |
|
|
} |
|
|
|
|
|
$searchToggle.on('click', function() { |
|
|
modalVisible ? searchModal.hide() : searchModal.show(); |
|
|
}); |
|
|
|
|
|
$(window).on('keyup', function(e) { |
|
|
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) { |
|
|
modalVisible || searchModal.show(); |
|
|
} |
|
|
}); |
|
|
|
|
|
if (useDefaultSearchBox) { |
|
|
$searchBox = $('.js-search-box'); |
|
|
$searchInput = $searchBox.children('input'); |
|
|
$searchClear = $searchBox.children('.js-icon-clear'); |
|
|
search.getSearchInput = function() { |
|
|
return $searchInput.get(0); |
|
|
}; |
|
|
search.getVal = function() { |
|
|
return $searchInput.val(); |
|
|
}; |
|
|
search.setVal = function(val) { |
|
|
$searchInput.val(val); |
|
|
}; |
|
|
|
|
|
$searchInput.on('focus', function() { |
|
|
$(this).addClass('focus'); |
|
|
}); |
|
|
$searchInput.on('blur', function() { |
|
|
$(this).removeClass('focus'); |
|
|
}); |
|
|
$searchInput.on('input', window.throttle(function() { |
|
|
var val = $(this).val(); |
|
|
if (val === '' || typeof val !== 'string') { |
|
|
search.clear && search.clear(); |
|
|
} else { |
|
|
$searchBox.addClass('not-empty'); |
|
|
search.onInputNotEmpty && search.onInputNotEmpty(val); |
|
|
} |
|
|
}, 400)); |
|
|
$searchClear.on('click', function() { |
|
|
$searchInput.val(''); $searchBox.removeClass('not-empty'); |
|
|
search.clear && search.clear(); |
|
|
}); |
|
|
} |
|
|
}); |
|
|
})(); |
|
|
</script><div class="search search--dark"> |
|
|
<div class="main"> |
|
|
<div class="search__header">Search</div> |
|
|
<div class="search-bar"> |
|
|
<div class="search-box js-search-box"> |
|
|
<div class="search-box__icon-search"><i class="fas fa-search"></i></div> |
|
|
<input type="text" /> |
|
|
<div class="search-box__icon-clear js-icon-clear"> |
|
|
<a><i class="fas fa-times"></i></a> |
|
|
</div> |
|
|
</div> |
|
|
<button class="button button--theme-dark button--pill search__cancel js-search-toggle"> |
|
|
Cancel</button> |
|
|
</div> |
|
|
<div class="search-result js-search-result"></div> |
|
|
</div> |
|
|
</div> |
|
|
<script>var SOURCES = window.TEXT_VARIABLES.sources; |
|
|
var PAHTS = window.TEXT_VARIABLES.paths; |
|
|
window.Lazyload.js([SOURCES.jquery, PAHTS.search_js], function() { |
|
|
var search = (window.search || (window.search = {})); |
|
|
var searchData = window.TEXT_SEARCH_DATA || {}; |
|
|
|
|
|
function memorize(f) { |
|
|
var cache = {}; |
|
|
return function () { |
|
|
var key = Array.prototype.join.call(arguments, ','); |
|
|
if (key in cache) return cache[key]; |
|
|
else return cache[key] = f.apply(this, arguments); |
|
|
}; |
|
|
} |
|
|
|
|
|
|
|
|
function searchByQuery(query) { |
|
|
var i, j, key, keys, cur, _title, result = {}; |
|
|
keys = Object.keys(searchData); |
|
|
for (i = 0; i < keys.length; i++) { |
|
|
key = keys[i]; |
|
|
for (j = 0; j < searchData[key].length; j++) { |
|
|
cur = searchData[key][j], _title = cur.title; |
|
|
if ((result[key] === undefined || result[key] && result[key].length < 4 ) |
|
|
&& _title.toLowerCase().indexOf(query.toLowerCase()) >= 0) { |
|
|
if (result[key] === undefined) { |
|
|
result[key] = []; |
|
|
} |
|
|
result[key].push(cur); |
|
|
} |
|
|
} |
|
|
} |
|
|
return result; |
|
|
} |
|
|
|
|
|
var renderHeader = memorize(function(header) { |
|
|
return $('<p class="search-result__header">' + header + '</p>'); |
|
|
}); |
|
|
|
|
|
var renderItem = function(index, title, url) { |
|
|
return $('<li class="search-result__item" data-index="' + index + '"><a class="button" href="' + url + '">' + title + '</a></li>'); |
|
|
}; |
|
|
|
|
|
function render(data) { |
|
|
if (!data) { return null; } |
|
|
var $root = $('<ul></ul>'), i, j, key, keys, cur, itemIndex = 0; |
|
|
keys = Object.keys(data); |
|
|
for (i = 0; i < keys.length; i++) { |
|
|
key = keys[i]; |
|
|
$root.append(renderHeader(key)); |
|
|
for (j = 0; j < data[key].length; j++) { |
|
|
cur = data[key][j]; |
|
|
$root.append(renderItem(itemIndex++, cur.title, cur.url)); |
|
|
} |
|
|
} |
|
|
return $root; |
|
|
} |
|
|
|
|
|
|
|
|
var $result = $('.js-search-result'), $resultItems; |
|
|
var lastActiveIndex, activeIndex; |
|
|
|
|
|
function clear() { |
|
|
$result.html(null); |
|
|
$resultItems = $('.search-result__item'); activeIndex = 0; |
|
|
} |
|
|
function onInputNotEmpty(val) { |
|
|
$result.html(render(searchByQuery(val))); |
|
|
$resultItems = $('.search-result__item'); activeIndex = 0; |
|
|
$resultItems.eq(0).addClass('active'); |
|
|
} |
|
|
|
|
|
search.clear = clear; |
|
|
search.onInputNotEmpty = onInputNotEmpty; |
|
|
|
|
|
function updateResultItems() { |
|
|
lastActiveIndex >= 0 && $resultItems.eq(lastActiveIndex).removeClass('active'); |
|
|
activeIndex >= 0 && $resultItems.eq(activeIndex).addClass('active'); |
|
|
} |
|
|
|
|
|
function moveActiveIndex(direction) { |
|
|
var itemsCount = $resultItems ? $resultItems.length : 0; |
|
|
if (itemsCount > 1) { |
|
|
lastActiveIndex = activeIndex; |
|
|
if (direction === 'up') { |
|
|
activeIndex = (activeIndex - 1 + itemsCount) % itemsCount; |
|
|
} else if (direction === 'down') { |
|
|
activeIndex = (activeIndex + 1 + itemsCount) % itemsCount; |
|
|
} |
|
|
updateResultItems(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
$(window).on('keyup', function(e) { |
|
|
var modalVisible = search.getModalVisible && search.getModalVisible(); |
|
|
if (modalVisible) { |
|
|
if (e.which === 38) { |
|
|
modalVisible && moveActiveIndex('up'); |
|
|
} else if (e.which === 40) { |
|
|
modalVisible && moveActiveIndex('down'); |
|
|
} else if (e.which === 13) { |
|
|
modalVisible && $resultItems && activeIndex >= 0 && $resultItems.eq(activeIndex).children('a')[0].click(); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
$result.on('mouseover', '.search-result__item > a', function() { |
|
|
var itemIndex = $(this).parent().data('index'); |
|
|
itemIndex >= 0 && (lastActiveIndex = activeIndex, activeIndex = itemIndex, updateResultItems()); |
|
|
}); |
|
|
}); |
|
|
</script> |
|
|
</div></div> |
|
|
|
|
|
|
|
|
<script>(function() { |
|
|
var SOURCES = window.TEXT_VARIABLES.sources; |
|
|
window.Lazyload.js(SOURCES.jquery, function() { |
|
|
function scrollToAnchor(anchor, duration, callback) { |
|
|
var $root = this; |
|
|
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() { |
|
|
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor); |
|
|
callback && callback(); |
|
|
}); |
|
|
} |
|
|
$.fn.scrollToAnchor = scrollToAnchor; |
|
|
}); |
|
|
})(); |
|
|
(function() { |
|
|
var SOURCES = window.TEXT_VARIABLES.sources; |
|
|
window.Lazyload.js(SOURCES.jquery, function() { |
|
|
function affix(options) { |
|
|
var $root = this, $window = $(window), $scrollTarget, $scroll, |
|
|
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true, |
|
|
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop, |
|
|
hasInit = false, curState; |
|
|
|
|
|
function setOptions(options) { |
|
|
var _options = options || {}; |
|
|
_options.offsetBottom && (offsetBottom = _options.offsetBottom); |
|
|
_options.scrollTarget && (scrollTarget = _options.scrollTarget); |
|
|
_options.scroll && (scroll = _options.scroll); |
|
|
_options.disabled !== undefined && (disabled = _options.disabled); |
|
|
$scrollTarget = $(scrollTarget); |
|
|
isOverallScroller = window.isOverallScroller($scrollTarget[0]); |
|
|
$scroll = $(scroll); |
|
|
} |
|
|
function preCalc() { |
|
|
top(); |
|
|
rootHeight = $root.outerHeight(); |
|
|
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop()); |
|
|
rootLeft = $root.offset().left; |
|
|
} |
|
|
function calc(needPreCalc) { |
|
|
needPreCalc && preCalc(); |
|
|
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight; |
|
|
rootBottomTop = scrollBottom - rootTop; |
|
|
} |
|
|
function top() { |
|
|
if (curState !== 'top') { |
|
|
$root.removeClass('fixed').css({ |
|
|
left: 0, |
|
|
top: 0 |
|
|
}); |
|
|
curState = 'top'; |
|
|
} |
|
|
} |
|
|
function fixed() { |
|
|
if (curState !== 'fixed') { |
|
|
$root.addClass('fixed').css({ |
|
|
left: rootLeft + 'px', |
|
|
top: 0 |
|
|
}); |
|
|
curState = 'fixed'; |
|
|
} |
|
|
} |
|
|
function bottom() { |
|
|
if (curState !== 'bottom') { |
|
|
$root.removeClass('fixed').css({ |
|
|
left: 0, |
|
|
top: rootBottomTop + 'px' |
|
|
}); |
|
|
curState = 'bottom'; |
|
|
} |
|
|
} |
|
|
function setState() { |
|
|
var scrollTop = $scrollTarget.scrollTop(); |
|
|
if (scrollTop >= rootTop && scrollTop <= scrollBottom) { |
|
|
fixed(); |
|
|
} else if (scrollTop < rootTop) { |
|
|
top(); |
|
|
} else { |
|
|
bottom(); |
|
|
} |
|
|
} |
|
|
function init() { |
|
|
if(!hasInit) { |
|
|
var interval, timeout; |
|
|
calc(true); setState(); |
|
|
|
|
|
interval = setInterval(function() { |
|
|
calc(); |
|
|
}, 100); |
|
|
timeout = setTimeout(function() { |
|
|
clearInterval(interval); |
|
|
}, 45000); |
|
|
window.pageLoad.then(function() { |
|
|
setTimeout(function() { |
|
|
clearInterval(interval); |
|
|
clearTimeout(timeout); |
|
|
}, 3000); |
|
|
}); |
|
|
$scrollTarget.on('scroll', function() { |
|
|
disabled || setState(); |
|
|
}); |
|
|
$window.on('resize', function() { |
|
|
disabled || (calc(true), setState()); |
|
|
}); |
|
|
hasInit = true; |
|
|
} |
|
|
} |
|
|
|
|
|
setOptions(options); |
|
|
if (!disabled) { |
|
|
init(); |
|
|
} |
|
|
$window.on('resize', window.throttle(function() { |
|
|
init(); |
|
|
}, 200)); |
|
|
return { |
|
|
setOptions: setOptions, |
|
|
refresh: function() { |
|
|
calc(true, { animation: false }); setState(); |
|
|
} |
|
|
}; |
|
|
} |
|
|
$.fn.affix = affix; |
|
|
}); |
|
|
})(); |
|
|
(function() { |
|
|
var SOURCES = window.TEXT_VARIABLES.sources; |
|
|
window.Lazyload.js(SOURCES.jquery, function() { |
|
|
function toc(options) { |
|
|
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur, |
|
|
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false, |
|
|
headingsPos, scrolling = false, hasRendered = false, hasInit = false; |
|
|
|
|
|
function setOptions(options) { |
|
|
var _options = options || {}; |
|
|
_options.selectors && (selectors = _options.selectors); |
|
|
_options.container && (container = _options.container); |
|
|
_options.scrollTarget && (scrollTarget = _options.scrollTarget); |
|
|
_options.scroller && (scroller = _options.scroller); |
|
|
_options.disabled !== undefined && (disabled = _options.disabled); |
|
|
$headings = $(container).find(selectors).filter('[id]'); |
|
|
$scrollTarget = $(scrollTarget); |
|
|
$scroller = $(scroller); |
|
|
} |
|
|
function calc() { |
|
|
headingsPos = []; |
|
|
$headings.each(function() { |
|
|
headingsPos.push(Math.floor($(this).position().top)); |
|
|
}); |
|
|
} |
|
|
function setState(element, disabled) { |
|
|
var scrollTop = $scrollTarget.scrollTop(), i; |
|
|
if (disabled || !headingsPos || headingsPos.length < 1) { return; } |
|
|
if (element) { |
|
|
$activeCur = element; |
|
|
} else { |
|
|
for (i = 0; i < headingsPos.length; i++) { |
|
|
if (scrollTop >= headingsPos[i]) { |
|
|
$activeCur = $tocLi.eq(i); |
|
|
} else { |
|
|
$activeCur || ($activeCur = $tocLi.eq(i)); |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
$activeLast && $activeLast.removeClass('active'); |
|
|
($activeLast = $activeCur).addClass('active'); |
|
|
} |
|
|
function render() { |
|
|
if(!hasRendered) { |
|
|
$root.append($tocUl); |
|
|
$headings.each(function() { |
|
|
var $this = $(this); |
|
|
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase()) |
|
|
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id')))); |
|
|
}); |
|
|
$tocLi = $tocUl.children('li'); |
|
|
$tocUl.on('click', 'a', function(e) { |
|
|
e.preventDefault(); |
|
|
var $this = $(this); |
|
|
scrolling = true; |
|
|
setState($this.parent()); |
|
|
$scroller.scrollToAnchor($this.attr('href'), 400, function() { |
|
|
scrolling = false; |
|
|
}); |
|
|
}); |
|
|
} |
|
|
hasRendered = true; |
|
|
} |
|
|
function init() { |
|
|
var interval, timeout; |
|
|
if(!hasInit) { |
|
|
render(); calc(); setState(null, scrolling); |
|
|
|
|
|
interval = setInterval(function() { |
|
|
calc(); |
|
|
}, 100); |
|
|
timeout = setTimeout(function() { |
|
|
clearInterval(interval); |
|
|
}, 45000); |
|
|
window.pageLoad.then(function() { |
|
|
setTimeout(function() { |
|
|
clearInterval(interval); |
|
|
clearTimeout(timeout); |
|
|
}, 3000); |
|
|
}); |
|
|
$scrollTarget.on('scroll', function() { |
|
|
disabled || setState(null, scrolling); |
|
|
}); |
|
|
$window.on('resize', window.throttle(function() { |
|
|
if (!disabled) { |
|
|
render(); calc(); setState(null, scrolling); |
|
|
} |
|
|
}, 100)); |
|
|
} |
|
|
hasInit = true; |
|
|
} |
|
|
|
|
|
setOptions(options); |
|
|
if (!disabled) { |
|
|
init(); |
|
|
} |
|
|
$window.on('resize', window.throttle(function() { |
|
|
init(); |
|
|
}, 200)); |
|
|
return { |
|
|
setOptions: setOptions |
|
|
}; |
|
|
} |
|
|
$.fn.toc = toc; |
|
|
}); |
|
|
})(); |
|
|
|
|
|
|
|
|
|
|
|
</script><script type="text/x-mathjax-config"> |
|
|
var _config = { tex2jax: { |
|
|
inlineMath: [['$','$'], ['\\(','\\)']] |
|
|
}};MathJax.Hub.Config(_config); |
|
|
</script> |
|
|
<script type="text/javascript" src="https://cdn.bootcss.com/mathjax/2.7.4/MathJax.js?config=TeX-MML-AM_CHTML" async></script> |
|
|
|
|
|
</div> |
|
|
<script>(function () { |
|
|
var $root = document.getElementsByClassName('root')[0]; |
|
|
if (window.hasEvent('touchstart')) { |
|
|
$root.dataset.isTouch = true; |
|
|
document.addEventListener('touchstart', function(){}, false); |
|
|
} |
|
|
})(); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|
|
|
|