added cactus theme files
21
hugo-content/themes/cactus/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 ZERAN WU
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
299
hugo-content/themes/cactus/README.md
Normal file
@@ -0,0 +1,299 @@
|
||||
## Cactus
|
||||
|
||||
A hugo theme for personal blog. Fork from hexo theme [cactus](https://github.com/probberechts/hexo-theme-cactus) created by @probberechts.
|
||||
|
||||
[Live demo on github pages](https://www.takuzen.me/hugo-theme-cactus/).
|
||||
|
||||
Some works are still in progress. See [TODOS](#todos) below.
|
||||
|
||||
## Install
|
||||
|
||||
1. clone cactus to your hugo site's `themes` folder.
|
||||
```
|
||||
git clone https://github.com/monkeyWzr/hugo-theme-cactus.git themes/cactus
|
||||
```
|
||||
|
||||
2. change your theme to cactus in your site config
|
||||
```toml
|
||||
# config.toml
|
||||
|
||||
theme = "cactus"
|
||||
```
|
||||
|
||||
3. config your site. See [Config] or a [complete config sample](exampleSite/config.toml)
|
||||
4. test your site
|
||||
```
|
||||
hugo server
|
||||
```
|
||||
|
||||
5. publish your site in your prefered way. See hugo's doc: [Hosting & Deployment](https://gohugo.io/hosting-and-deployment/)
|
||||
|
||||
## Config
|
||||
|
||||
### Color themes
|
||||
|
||||
```toml
|
||||
[params]
|
||||
|
||||
colortheme = "white" # dark, light, white, or classic
|
||||
```
|
||||
|
||||
### Custom CSS
|
||||
|
||||
```toml
|
||||
[params]
|
||||
css = ["css/custom.css"]
|
||||
```
|
||||
|
||||
You can add multiple custom stylesheets which will be loaded after the main theme css.
|
||||
For example, the above line will load the CSS-file placed at `/static/css/custom.css`.
|
||||
|
||||
### Navigation
|
||||
|
||||
```toml
|
||||
# Main menu which appears below site header.
|
||||
[[menu.main]]
|
||||
name = "Home"
|
||||
url = "/"
|
||||
weight = 1
|
||||
|
||||
[[menu.main]]
|
||||
name = "All posts"
|
||||
url = "/posts"
|
||||
weight = 2
|
||||
|
||||
[[menu.main]]
|
||||
name = "Tags"
|
||||
url = "/tags"
|
||||
weight = 3
|
||||
|
||||
[[menu.main]]
|
||||
name = "About"
|
||||
url = "/about"
|
||||
weight = 4
|
||||
```
|
||||
|
||||
### Homepage settings
|
||||
|
||||
* description: description will be displayed in the homepage. Markdown syntax is supported in the description string.
|
||||
```toml
|
||||
[params]
|
||||
|
||||
description = "Hugo is a general-purpose website framework. Technically speaking, Hugo is a static site generator. Unlike systems that dynamically build a page with each visitor request, Hugo builds pages when you create or update your content. Since websites are viewed far more often than they are edited, Hugo is designed to provide an optimal viewing experience for your website’s end users and an ideal writing experience for website authors."
|
||||
```
|
||||
|
||||
* set your main section (used as the link for the "writings" title on the homepage)
|
||||
|
||||
```toml
|
||||
[params]
|
||||
mainSection = "posts"
|
||||
```
|
||||
|
||||
* change the default main section title from Writings, to something else:
|
||||
|
||||
```toml
|
||||
[params]
|
||||
mainSectionTitle = "Blog"
|
||||
```
|
||||
|
||||
* Show only the 5 most recent posts (default)
|
||||
|
||||
```toml
|
||||
[params]
|
||||
showAllPostsOnHomePage = false
|
||||
postsOnHomePage = 5
|
||||
```
|
||||
* show all posts
|
||||
|
||||
```toml
|
||||
[params]
|
||||
showAllPostsOnHomePage = true
|
||||
postsOnHomePage = 5 # this option will be ignored
|
||||
```
|
||||
|
||||
* show tagsoverview (default) or not
|
||||
*
|
||||
```toml
|
||||
[params]
|
||||
tagsOverview = true
|
||||
```
|
||||
|
||||
* display the table of contents inline on blog posts, rather than as part of the navigation menu:
|
||||
|
||||
```toml
|
||||
[params]
|
||||
tocInline = true
|
||||
```
|
||||
|
||||
* show projects list (default) or not.
|
||||
|
||||
```toml
|
||||
[params]
|
||||
showProjectsList = true
|
||||
projectsUrl = "https://github.com/monkeyWzr"
|
||||
```
|
||||
|
||||
Projects section will not be shown if no data file is detected. See [Projects list](#projects-list) below.
|
||||
|
||||
### Projects list
|
||||
|
||||
Create your projects data file `data/projects.yaml|toml|json`. Hugo support yaml, toml and json formats.
|
||||
for former hexo cactus users: please assign your json array to a `list` key.
|
||||
|
||||
for example, `data/projects.json`:
|
||||
```json
|
||||
{
|
||||
"list": [
|
||||
{
|
||||
"name":"Hexo",
|
||||
"url":"https://hexo.io/",
|
||||
"desc":"A fast, simple & powerful blog framework"
|
||||
},
|
||||
{
|
||||
"name":"Font Awesome",
|
||||
"url":"http://fontawesome.io/",
|
||||
"desc":"The iconic font and CSS toolkit"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Social media links
|
||||
|
||||
```toml
|
||||
[[params.social]]
|
||||
name = "github"
|
||||
link = "https://github.com/monkeyWzr"
|
||||
|
||||
[[params.social]]
|
||||
name = "email"
|
||||
link = "monkeywzr@gmail.com" # no need for "mailto:" at the start
|
||||
|
||||
[[params.social]]
|
||||
name = "linkedin"
|
||||
link = "https://www.linkedin.com/in/monkeywzr/"
|
||||
```
|
||||
|
||||
The `name` key expects the name of a [Font Awesome icon](https://fontawesome.com/icons?d=gallery&s=brands).
|
||||
|
||||
### Copyright
|
||||
|
||||
Assign your copy right to `.Site.Copyright`. Cactus will append current year to the head.
|
||||
|
||||
TODO: Customizable copyright year
|
||||
|
||||
```toml
|
||||
copyright = "Zeran Wu" # cactus theme will use site title if copyright is not set
|
||||
```
|
||||
|
||||
### Comments
|
||||
|
||||
Comments is disabled by default. Enable comments in your `.Site.Params`.
|
||||
```toml
|
||||
[params]
|
||||
[params.comments]
|
||||
enabled = true
|
||||
# engine = "disqus" # in progress
|
||||
```
|
||||
|
||||
You can also enable/disable comments per post. in your posts' front matter, add:
|
||||
```yaml
|
||||
comments: true
|
||||
```
|
||||
|
||||
The site config is ignored when `comments` option exists in front matter.
|
||||
|
||||
The default engine is disqus. **By now only disqus is supported in cactus.** I will add more options sooner or later. See [Comments Alternatives](https://gohugo.io/content-management/comments/#comments-alternatives)
|
||||
|
||||
Before using disqus, you need to register and get your [disqus shortname](https://help.disqus.com/en/articles/1717111-what-s-a-shortname). Assign your shortname in `.Site.disqusShortname`, or cactus will use `.Site.Title` by default.
|
||||
|
||||
```
|
||||
disqusShortname = "wzr" # cactus will use site title if not set
|
||||
```
|
||||
|
||||
### highlight
|
||||
|
||||
Use hugo's built-in [syntax highlighting](https://gohugo.io/getting-started/configuration-markup#highlight).
|
||||
|
||||
default config:
|
||||
|
||||
```toml
|
||||
[markup]
|
||||
[markup.highlight]
|
||||
codeFences = true
|
||||
guessSyntax = false
|
||||
hl_Lines = ""
|
||||
lineNoStart = 1
|
||||
lineNos = false
|
||||
lineNumbersInTable = true
|
||||
noClasses = true
|
||||
style = "monokai"
|
||||
tabWidth = 4
|
||||
```
|
||||
|
||||
### Analytics
|
||||
|
||||
Cactus uses hugo's bulit in analytics templates. Check [hugo's documents](https://gohugo.io/templates/internal#google-analytics) for details.
|
||||
|
||||
Set you tracking id in your site config.
|
||||
```toml
|
||||
googleAnalytics = "UA-XXXXXXXX-XX" # or G-XXXXXXXX if you are using Google Analytics v4 (gtag.js)
|
||||
```
|
||||
|
||||
If you are using Google Analytics v3 (analytics.js), you can switch to asynchronous tracking by set `params.googleAnalyticsAsync` to `true`.
|
||||
```toml
|
||||
[params]
|
||||
googleAnalyticsAsync = true # not required
|
||||
```
|
||||
|
||||
### RSS
|
||||
|
||||
The rss feed is not generated by default. you can enable it in your site config:
|
||||
|
||||
```toml
|
||||
[params]
|
||||
rss = true
|
||||
```
|
||||
|
||||
The rss link will be `https://example.com/index.xml` assuming your `baseURL` is set to `https://example.com/`
|
||||
|
||||
Please also check [Configure RSS](https://gohugo.io/templates/rss/#configure-rss)
|
||||
|
||||
### Mathjax
|
||||
|
||||
Cactus supports mathjax. Just add `mathjax` option in your site config:
|
||||
```toml
|
||||
[params]
|
||||
mathjax = true # not required
|
||||
```
|
||||
|
||||
You can also enable/disable mathjax per post. In your posts' front matter, add:
|
||||
```yaml
|
||||
mathjax: true # or false
|
||||
```
|
||||
|
||||
The site config will be ignored when `mathjax` option exists in front matter.
|
||||
|
||||
### Archive
|
||||
Pagination on posts archive can be disabled to show all posts in chronological order
|
||||
|
||||
```toml
|
||||
[params]
|
||||
showAllPostsArchive = true # or false (default)
|
||||
```
|
||||
|
||||
## TODOS
|
||||
|
||||
- [ ] More comments engines
|
||||
- [x] RSS
|
||||
- [ ] I18n
|
||||
- [x] Analytics
|
||||
- [ ] Local Search
|
||||
- [ ] toc template
|
||||
- [ ] Customizable copyright year
|
||||
- [ ] gallery
|
||||
- [ ] expose [mathjax configuration](https://docs.mathjax.org/en/latest/web/configuration.html#web-configuration)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
119
hugo-content/themes/cactus/assets/scss/_extend.scss
Normal file
@@ -0,0 +1,119 @@
|
||||
// $base-style
|
||||
h1,
|
||||
.h1 {
|
||||
display: block;
|
||||
margin-top: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
color: $color-accent-1;
|
||||
letter-spacing: .01em;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-size: 1.5em;
|
||||
|
||||
@include antialias();
|
||||
}
|
||||
h2,
|
||||
.h2 {
|
||||
position: relative;
|
||||
display: block;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: .5rem;
|
||||
color: $color-accent-2;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
font-weight: bold;
|
||||
font-size: 1rem;
|
||||
}
|
||||
h3 {
|
||||
color: $color-accent-2;
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
font-size: .9rem;
|
||||
}
|
||||
h4
|
||||
h5
|
||||
h6 {
|
||||
display: inline;
|
||||
text-decoration: none;
|
||||
color: $color-accent-3;
|
||||
font-weight: bold;
|
||||
font-size: .9rem;
|
||||
}
|
||||
h3
|
||||
h4
|
||||
h5
|
||||
h6 {
|
||||
margin-top: .9rem;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
hr {
|
||||
border: .5px dashed $color-accent-3;
|
||||
opacity: .5;
|
||||
margin: 0;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
em
|
||||
cite {
|
||||
font-style: italic;
|
||||
}
|
||||
sup
|
||||
sub {
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
font-size: .75em;
|
||||
line-height: 0;
|
||||
}
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
sub {
|
||||
bottom: -.2em;
|
||||
}
|
||||
small {
|
||||
font-size: .85em;
|
||||
}
|
||||
acronym
|
||||
abbr {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
ul
|
||||
ol
|
||||
dl {
|
||||
line-height: $line-height;
|
||||
}
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol,
|
||||
ol ol {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
ol {
|
||||
list-style: decimal;
|
||||
}
|
||||
dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
text-align: left;
|
||||
font-size: $font-size - 2px;
|
||||
overflow: auto;
|
||||
display: block;
|
||||
}
|
||||
th {
|
||||
padding: 8px;
|
||||
border-bottom: 1px dashed $color-border;
|
||||
color: $color-accent-2;
|
||||
font-weight: bold;
|
||||
font-size: $font-size - 1px;
|
||||
}
|
||||
td {
|
||||
padding: 0 8px;
|
||||
border-bottom: none;
|
||||
}
|
||||
6
hugo-content/themes/cactus/assets/scss/_fonts.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
@font-face {
|
||||
font-style: normal;
|
||||
font-family: "JetBrains Mono";
|
||||
font-display: swap;
|
||||
src: local("JetBrains Mono"), local("JetBrains-Mono"), url("../lib/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff2") format("woff2"), url("../lib/JetBrainsMono/web/woff/JetBrainsMono-Regular.woff") format("woff"), url("../lib/JetBrainsMono/web/eot/JetBrainsMono-Regular.eot") format("embedded-opentype"), url("../lib/JetBrainsMono/ttf/JetBrainsMono-Regular.ttf") format("truetype");
|
||||
};
|
||||
20
hugo-content/themes/cactus/assets/scss/_mixins.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
@mixin antialias() {
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
@mixin hyphens($value) {
|
||||
hyphens: $value;
|
||||
-moz-hyphens: $value;
|
||||
-ms-hyphens: $value;
|
||||
-webkit-hyphens: $value;
|
||||
}
|
||||
@mixin underline($size, $color) {
|
||||
background-image: linear-gradient(transparent, transparent $size, $color $size, $color);
|
||||
background-position: bottom;
|
||||
background-size: 100% 6px;
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
@mixin no-select() {
|
||||
user-select: none;
|
||||
-khtml-user-select: none;
|
||||
}
|
||||
319
hugo-content/themes/cactus/assets/scss/_util.scss
Normal file
@@ -0,0 +1,319 @@
|
||||
/* Basscss */
|
||||
.inline {
|
||||
display: inline;
|
||||
}
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
}
|
||||
.table {
|
||||
display: table;
|
||||
}
|
||||
.table-cell {
|
||||
display: table-cell;
|
||||
}
|
||||
.overflow-hidden {
|
||||
overflow: hidden;
|
||||
}
|
||||
.overflow-scroll {
|
||||
overflow: scroll;
|
||||
}
|
||||
.overflow-auto {
|
||||
overflow: auto;
|
||||
}
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
display: table;
|
||||
content: " ";
|
||||
}
|
||||
.clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
.left {
|
||||
float: left;
|
||||
}
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
.fit {
|
||||
max-width: 100%;
|
||||
}
|
||||
.truncate {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.max-width-1 {
|
||||
max-width: 24rem;
|
||||
}
|
||||
.max-width-2 {
|
||||
max-width: 32rem;
|
||||
}
|
||||
.max-width-3 {
|
||||
max-width: 48rem;
|
||||
}
|
||||
.max-width-4 {
|
||||
max-width: 64rem;
|
||||
}
|
||||
.border-box {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.m0 {
|
||||
margin: 0;
|
||||
}
|
||||
.mt0 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.mr0 {
|
||||
margin-right: 0;
|
||||
}
|
||||
.mb0 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.ml0 {
|
||||
margin-left: 0;
|
||||
}
|
||||
.mx0 {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
.my0 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.m1 {
|
||||
margin: .5rem;
|
||||
}
|
||||
.mt1 {
|
||||
margin-top: .5rem;
|
||||
}
|
||||
.mr1 {
|
||||
margin-right: .5rem;
|
||||
}
|
||||
.mb1 {
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
.ml1 {
|
||||
margin-left: .5rem;
|
||||
}
|
||||
.mx1 {
|
||||
margin-right: .5rem;
|
||||
margin-left: .5rem;
|
||||
}
|
||||
.my1 {
|
||||
margin-top: .5rem;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
.m2 {
|
||||
margin: 1rem;
|
||||
}
|
||||
.mt2 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.mr2 {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
.mb2 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.ml2 {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
.mx2 {
|
||||
margin-right: 1rem;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
.my2 {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.m3 {
|
||||
margin: 2rem;
|
||||
}
|
||||
.mt3 {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.mr3 {
|
||||
margin-right: 2rem;
|
||||
}
|
||||
.mb3 {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.ml3 {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
.mx3 {
|
||||
margin-right: 2rem;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
.my3 {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.m4 {
|
||||
margin: 4rem;
|
||||
}
|
||||
.mt4 {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
.mr4 {
|
||||
margin-right: 4rem;
|
||||
}
|
||||
.mb4 {
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
.ml4 {
|
||||
margin-left: 4rem;
|
||||
}
|
||||
.mx4 {
|
||||
margin-right: 4rem;
|
||||
margin-left: 4rem;
|
||||
}
|
||||
.my4 {
|
||||
margin-top: 4rem;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
.mxn1 {
|
||||
margin-right: -.5rem;
|
||||
margin-left: -.5rem;
|
||||
}
|
||||
.mxn2 {
|
||||
margin-right: -1rem;
|
||||
margin-left: -1rem;
|
||||
}
|
||||
.mxn3 {
|
||||
margin-right: -2rem;
|
||||
margin-left: -2rem;
|
||||
}
|
||||
.mxn4 {
|
||||
margin-right: -4rem;
|
||||
margin-left: -4rem;
|
||||
}
|
||||
.ml-auto {
|
||||
margin-left: auto;
|
||||
}
|
||||
.mr-auto {
|
||||
margin-right: auto;
|
||||
}
|
||||
.mx-auto {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
.p0 {
|
||||
padding: 0;
|
||||
}
|
||||
.pt0 {
|
||||
padding-top: 0;
|
||||
}
|
||||
.pr0 {
|
||||
padding-right: 0;
|
||||
}
|
||||
.pb0 {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.pl0 {
|
||||
padding-left: 0;
|
||||
}
|
||||
.px0 {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
.py0 {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.p1 {
|
||||
padding: .5rem;
|
||||
}
|
||||
.pt1 {
|
||||
padding-top: .5rem;
|
||||
}
|
||||
.pr1 {
|
||||
padding-right: .5rem;
|
||||
}
|
||||
.pb1 {
|
||||
padding-bottom: .5rem;
|
||||
}
|
||||
.pl1 {
|
||||
padding-left: .5rem;
|
||||
}
|
||||
.py1 {
|
||||
padding-top: .5rem;
|
||||
padding-bottom: .5rem;
|
||||
}
|
||||
.px1 {
|
||||
padding-right: .5rem;
|
||||
padding-left: .5rem;
|
||||
}
|
||||
.p2 {
|
||||
padding: 1rem;
|
||||
}
|
||||
.pt2 {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
.pr2 {
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.pb2 {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
.pl2 {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.py2 {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
.px2 {
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.p3 {
|
||||
padding: 2rem;
|
||||
}
|
||||
.pt3 {
|
||||
padding-top: 2rem;
|
||||
}
|
||||
.pr3 {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
.pb3 {
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
.pl3 {
|
||||
padding-left: 2rem;
|
||||
}
|
||||
.py3 {
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
}
|
||||
.px3 {
|
||||
padding-right: 2rem;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
.p4 {
|
||||
padding: 4rem;
|
||||
}
|
||||
.pt4 {
|
||||
padding-top: 4rem;
|
||||
}
|
||||
.pr4 {
|
||||
padding-right: 4rem;
|
||||
}
|
||||
.pb4 {
|
||||
padding-bottom: 4rem;
|
||||
}
|
||||
.pl4 {
|
||||
padding-left: 4rem;
|
||||
}
|
||||
.py4 {
|
||||
padding-top: 4rem;
|
||||
padding-bottom: 4rem;
|
||||
}
|
||||
.px4 {
|
||||
padding-right: 4rem;
|
||||
padding-left: 4rem;
|
||||
}
|
||||
13
hugo-content/themes/cactus/assets/scss/_variables.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
// Fonts
|
||||
$font-family-body: "JetBrains Mono", monospace;
|
||||
$font-family-mono: "JetBrains Mono", monospace;
|
||||
$font-family-tt: "Inconsolata", monospace;
|
||||
$font-size: 14px;
|
||||
$line-height: 1.725;
|
||||
$page-width: 48rem;
|
||||
// Logo
|
||||
$logo-width: 50px;
|
||||
$logo-height: 50px;
|
||||
$logo-grayout: true;
|
||||
// Colors
|
||||
$colors: "dark" // white dark light classic
|
||||
13
hugo-content/themes/cactus/assets/scss/colors/classic.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
$color-background: #fafafa;
|
||||
$color-footer-mobile-1: darken($color-background, 2%);
|
||||
$color-footer-mobile-2: darken($color-background, 10%);
|
||||
$color-background-code: darken($color-background, 2%);
|
||||
$color-border: #666;
|
||||
$color-meta: #666;
|
||||
$color-meta-code: lighten($color-meta, 10%);
|
||||
$color-link: rgba(86, 124, 119, .4);
|
||||
$color-text: #22272a;
|
||||
$color-accent-1: #cc2a41;
|
||||
$color-accent-2: rgba(86, 124, 119, .8);
|
||||
$color-accent-3: #666;
|
||||
$color-quote: #cc2a41;
|
||||
13
hugo-content/themes/cactus/assets/scss/colors/dark.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
$color-background: #1d1f21;
|
||||
$color-footer-mobile-1: lighten($color-background, 2%);
|
||||
$color-footer-mobile-2: lighten($color-background, 10%);
|
||||
$color-background-code: lighten($color-background, 2%);
|
||||
$color-border: #666;
|
||||
$color-meta: #666;
|
||||
$color-meta-code: #666;
|
||||
$color-link: rgba(212, 128, 170, 1);
|
||||
$color-text: #c9cacc;
|
||||
$color-accent-3: #cccccc;
|
||||
$color-accent-2: #eeeeee;
|
||||
$color-accent-1: #2bbc8a;
|
||||
$color-quote: #ccffb6;
|
||||
14
hugo-content/themes/cactus/assets/scss/colors/light.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
// by @GabiThume (https://github.com/gabithume)
|
||||
$color-background: #e2e0de;
|
||||
$color-footer-mobile-1: darken($color-background, 2%);
|
||||
$color-footer-mobile-2: darken($color-background, 10%);
|
||||
$color-background-code: darken($color-background, 2%);
|
||||
$color-border: #666;
|
||||
$color-meta: #666;
|
||||
$color-meta-code: lighten($color-meta, 10%);
|
||||
$color-link: rgba(43, 188, 138, 1);
|
||||
$color-text: #363533;
|
||||
$color-accent-3: #666666;
|
||||
$color-accent-2: #111111;
|
||||
$color-accent-1: #d44375;
|
||||
$color-quote: #ab2251;
|
||||
14
hugo-content/themes/cactus/assets/scss/colors/white.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
// by @sergodeeva (https://github.com/sergodeeva)
|
||||
$color-background: #FFFFFF;
|
||||
$color-footer-mobile-1: darken($color-background, 2%);
|
||||
$color-footer-mobile-2: darken($color-background, 10%);
|
||||
$color-background-code: darken($color-background, 2%);
|
||||
$color-border: #666;
|
||||
$color-meta: #666;
|
||||
$color-meta-code: lighten($color-meta, 10%);
|
||||
$color-link: rgba(212, 128, 170, 1);
|
||||
$color-text: #383838;
|
||||
$color-accent-3: #8c8c8c;
|
||||
$color-accent-2: #383838;
|
||||
$color-accent-1: #2bbc8a;
|
||||
$color-quote: #2bbc8a;
|
||||
32
hugo-content/themes/cactus/assets/scss/partial/archive.scss
Normal file
@@ -0,0 +1,32 @@
|
||||
#archive {
|
||||
.post-list {
|
||||
padding: 0;
|
||||
|
||||
.post-item {
|
||||
margin-bottom: 1rem;
|
||||
margin-left: 0;
|
||||
list-style-type: none;
|
||||
|
||||
.meta {
|
||||
display: block;
|
||||
margin-right: 16px;
|
||||
min-width: 100px;
|
||||
color: $color-meta;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (min-width: 480px) {
|
||||
.post-list {
|
||||
.post-item {
|
||||
display: flex;
|
||||
margin-bottom: 5px;
|
||||
margin-left: 1rem;
|
||||
|
||||
.meta {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
158
hugo-content/themes/cactus/assets/scss/partial/article.scss
Normal file
@@ -0,0 +1,158 @@
|
||||
article {
|
||||
header {
|
||||
.posttitle {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
text-transform: none;
|
||||
font-size: 1.5em;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.meta {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.meta * {
|
||||
color: $color-accent-3;
|
||||
font-size: .85rem;
|
||||
}
|
||||
.author {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .01em;
|
||||
font-weight: 700;
|
||||
}
|
||||
.postdate {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
h2 {
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: -4px;
|
||||
left: -1rem;
|
||||
color: $color-accent-1;
|
||||
content: "#";
|
||||
font-weight: bold;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.content img,
|
||||
.content video {
|
||||
display: block;
|
||||
margin: auto;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
|
||||
/* http://webdesignerwall.com/tutorials/css-elastic-videos */
|
||||
.video-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding-top: 56.25% e;
|
||||
// (9/16 * 100)% // 16:9 ratio
|
||||
height: 0;
|
||||
|
||||
iframe,
|
||||
object,
|
||||
embed {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin-top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
blockquote {
|
||||
margin: 1rem 10px;
|
||||
padding: .5em 10px;
|
||||
background: inherit;
|
||||
color: $color-quote;
|
||||
quotes: "\201C" "\201D" "\2018" "\2019";
|
||||
font-weight: bold;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
&:before {
|
||||
margin-right: .25em;
|
||||
color: $color-quote;
|
||||
content: "\201C";
|
||||
vertical-align: -.4em;
|
||||
font-size: 2em;
|
||||
line-height: .1em;
|
||||
}
|
||||
footer {
|
||||
margin: line-height 0;
|
||||
color: $color-meta;
|
||||
font-size: 11px;
|
||||
|
||||
a {
|
||||
background-image: linear-gradient(transparent, transparent 5px, $color-meta 5px, $color-meta);
|
||||
color: $color-meta;
|
||||
}
|
||||
a:hover {
|
||||
background-image: linear-gradient(transparent, transparent 4px, lighten($color-meta, 20%) 4px, lighten($color-meta, 20%));
|
||||
color: lighten($color-meta, 20%);
|
||||
}
|
||||
cite {
|
||||
&:before {
|
||||
padding: 0 .5em;
|
||||
content: "—";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.pullquote {
|
||||
margin: 0;
|
||||
width: 45%;
|
||||
text-align: left;
|
||||
|
||||
&.left {
|
||||
margin-right: 1em;
|
||||
margin-left: .5em;
|
||||
}
|
||||
&.right {
|
||||
margin-right: .5em;
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
.caption {
|
||||
position: relative;
|
||||
display: block;
|
||||
margin-top: .5em;
|
||||
color: $color-meta;
|
||||
text-align: center;
|
||||
font-size: .9em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.posttitle {
|
||||
text-transform: none;
|
||||
font-size: 1.5em;
|
||||
line-height: 1.25;
|
||||
}
|
||||
.article-tag {
|
||||
.tag-link {
|
||||
&:before {
|
||||
content: "#";
|
||||
@include underline(10px, $color-link);
|
||||
}
|
||||
}
|
||||
}
|
||||
.article-category {
|
||||
.category-link {
|
||||
@include underline(10px, $color-link);
|
||||
}
|
||||
}
|
||||
@media (min-width: 480px) {
|
||||
.article-read-time,
|
||||
.article-tag,
|
||||
.article-category {
|
||||
display: inline;
|
||||
|
||||
&:before {
|
||||
content: "|";
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
#categories {
|
||||
.category-list-title {
|
||||
color: $color-meta;
|
||||
}
|
||||
.category-list {
|
||||
.category-list-item {
|
||||
.category-list-count {
|
||||
color: $color-meta;
|
||||
}
|
||||
.category-list-count:before {
|
||||
content: " (";
|
||||
}
|
||||
.category-list-count:after {
|
||||
content: ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.blog-post-comments {
|
||||
margin-top: 4rem;
|
||||
}
|
||||
66
hugo-content/themes/cactus/assets/scss/partial/footer.scss
Normal file
@@ -0,0 +1,66 @@
|
||||
#footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
color: $color-meta;
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
border-right: 1px solid;
|
||||
border-color: $color-border;
|
||||
vertical-align: middle;
|
||||
|
||||
a {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
li:last-child {
|
||||
margin-right: 0;
|
||||
border-right: 0;
|
||||
|
||||
a {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
a {
|
||||
color: $color-meta;
|
||||
text-decoration: underline;
|
||||
background-image: none;
|
||||
}
|
||||
a:hover {
|
||||
color: lighten($color-meta, 20%);
|
||||
}
|
||||
.footer-left {
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
@media (min-width: 39rem) {
|
||||
#footer {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.footer-left {
|
||||
align-self: flex-start;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.footer-right {
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
};
|
||||
123
hugo-content/themes/cactus/assets/scss/partial/header.scss
Normal file
@@ -0,0 +1,123 @@
|
||||
#header {
|
||||
margin: 0 auto 2rem;
|
||||
width: 100%;
|
||||
|
||||
h1,
|
||||
.h1 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
color: $color-text;
|
||||
letter-spacing: .01em;
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
|
||||
@include antialias();
|
||||
}
|
||||
a {
|
||||
background: none;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
#logo {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
width: $logo-width;
|
||||
height: $logo-height;
|
||||
border-radius: 5px;
|
||||
background-size: $logo-width $logo-height;
|
||||
background-repeat: no-repeat;
|
||||
@if $logo-grayout {
|
||||
filter: grayscale(100%);
|
||||
-webkit-filter: grayscale(100%);
|
||||
}
|
||||
}
|
||||
#nav {
|
||||
color: $color-accent-1;
|
||||
letter-spacing: .01em;
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
font-size: .8rem;
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
line-height: 15px;
|
||||
|
||||
a {
|
||||
margin-right: 15px;
|
||||
color: $color-accent-1;
|
||||
}
|
||||
a:hover {
|
||||
@include underline(5px, $color-accent-1);
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
border-right: 1px dotted;
|
||||
border-color: $color-accent-1;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.icon {
|
||||
display: none;
|
||||
}
|
||||
li:last-child {
|
||||
margin-right: 0;
|
||||
border-right: 0;
|
||||
|
||||
a {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@if $logo-grayout {
|
||||
#header:hover {
|
||||
#logo {
|
||||
filter: none;
|
||||
-webkit-filter: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 480px) {
|
||||
#header #title {
|
||||
display: table;
|
||||
margin-right: 5rem;
|
||||
min-height: $logo-height;
|
||||
h1 {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
#header #nav {
|
||||
ul {
|
||||
a:hover {
|
||||
background: none;
|
||||
}
|
||||
li {
|
||||
display: none;
|
||||
border-right: 0;
|
||||
}
|
||||
li.icon {
|
||||
position: absolute;
|
||||
top: 77px;
|
||||
right: 1rem;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
ul.responsive {
|
||||
li {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
li:not(:first-child) {
|
||||
padding-top: 1rem;
|
||||
padding-left: $logo-width + 20px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
};
|
||||
40
hugo-content/themes/cactus/assets/scss/partial/index.scss
Normal file
@@ -0,0 +1,40 @@
|
||||
.post-list {
|
||||
padding: 0;
|
||||
|
||||
.post-item {
|
||||
margin-bottom: 1rem;
|
||||
margin-left: 0;
|
||||
list-style-type: none;
|
||||
|
||||
.meta {
|
||||
display: block;
|
||||
margin-right: 16px;
|
||||
min-width: 100px;
|
||||
color: $color-meta;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (min-width: 480px) {
|
||||
.post-list {
|
||||
.post-item {
|
||||
display: flex;
|
||||
margin-bottom: 5px;
|
||||
|
||||
.meta {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.project-list {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
|
||||
.project-item {
|
||||
margin-bottom: 5px;
|
||||
p {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
.pagination {
|
||||
display: inline-block;
|
||||
margin-top: 2rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
.page-number {
|
||||
color: $color-text;
|
||||
font-size: .8rem;
|
||||
}
|
||||
a {
|
||||
padding: 4px 6px;
|
||||
border-radius: 5px;
|
||||
// background-color: $color-accent-1
|
||||
background-image: none;
|
||||
color: $color-text;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
background-image: none;
|
||||
}
|
||||
a:hover:not(.active) {
|
||||
color: $color-accent-2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
#header-post {
|
||||
position: fixed;
|
||||
top: 2rem;
|
||||
right: 0;
|
||||
display: inline-block;
|
||||
float: right;
|
||||
z-index: 100;
|
||||
|
||||
a {
|
||||
background: none;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
a.icon {
|
||||
background: none;
|
||||
|
||||
&:hover {
|
||||
color: $color-link;
|
||||
}
|
||||
}
|
||||
nav {
|
||||
ul {
|
||||
display: block;
|
||||
|
||||
list-style-image: none;
|
||||
|
||||
list-style-position: outside;
|
||||
|
||||
list-style-type: none;
|
||||
|
||||
padding-inline-start: 40px;
|
||||
|
||||
li {
|
||||
display: list-item;
|
||||
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
nav > ul {
|
||||
margin-block-end: 1em;
|
||||
|
||||
margin-block-start: 1em;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
li:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
#menu-icon {
|
||||
float: right;
|
||||
margin-right: 2rem;
|
||||
margin-left: 15px;
|
||||
|
||||
&:hover {
|
||||
color: $color-accent-1;
|
||||
}
|
||||
}
|
||||
#menu-icon-tablet {
|
||||
float: right;
|
||||
margin-right: 2rem;
|
||||
margin-left: 15px;
|
||||
|
||||
&:hover {
|
||||
color: $color-accent-1;
|
||||
}
|
||||
}
|
||||
#top-icon-tablet {
|
||||
position: fixed;
|
||||
right: 2rem;
|
||||
bottom: 2rem;
|
||||
margin-right: 2rem;
|
||||
margin-left: 15px;
|
||||
|
||||
&:hover {
|
||||
color: $color-accent-1;
|
||||
}
|
||||
}
|
||||
.active {
|
||||
color: $color-accent-1;
|
||||
}
|
||||
#menu {
|
||||
visibility: hidden;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
#nav {
|
||||
color: $color-accent-1;
|
||||
letter-spacing: .01em;
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
font-size: .8rem;
|
||||
|
||||
ul {
|
||||
line-height: 15px;
|
||||
|
||||
a {
|
||||
margin-right: 15px;
|
||||
color: $color-accent-1;
|
||||
}
|
||||
a:hover {
|
||||
@include underline(5px, $color-accent-1);
|
||||
}
|
||||
li {
|
||||
border-right: 1px dotted $color-accent-1;
|
||||
}
|
||||
li:last-child {
|
||||
margin-right: 0;
|
||||
border-right: 0;
|
||||
|
||||
a {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#actions {
|
||||
float: right;
|
||||
margin-top: 2rem;
|
||||
margin-right: 2rem;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
|
||||
ul {
|
||||
display: block;
|
||||
}
|
||||
.info {
|
||||
display: block;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
#share {
|
||||
clear: both;
|
||||
padding-top: 1rem;
|
||||
padding-right: 2rem;
|
||||
text-align: right;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
#toc {
|
||||
float: right;
|
||||
clear: both;
|
||||
overflow: auto;
|
||||
margin-top: 1rem;
|
||||
padding-right: 2rem;
|
||||
max-width: 20em;
|
||||
max-height: calc(95vh - 7rem);
|
||||
text-align: right;
|
||||
|
||||
a:hover {
|
||||
color: $color-link;
|
||||
}
|
||||
// .toc-level-1 > .toc-link
|
||||
// display: none
|
||||
|
||||
nav > ul > li {
|
||||
color: $color-text;
|
||||
font-size: .8rem;
|
||||
|
||||
&:before {
|
||||
color: $color-accent-1;
|
||||
content: "#";
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
nav > ul > li > ul > li {
|
||||
color: $color-meta;
|
||||
font-size: .7rem;
|
||||
|
||||
&:before {
|
||||
color: $color-accent-1;
|
||||
content: "·";
|
||||
font-weight: bold;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
nav > ul > li > ul > li > ul > li {
|
||||
color: darken($color-meta, 20%);
|
||||
font-size: .4rem;
|
||||
}
|
||||
.toc-level-5 {
|
||||
display: none;
|
||||
}
|
||||
.toc-level-6 {
|
||||
display: none;
|
||||
}
|
||||
.toc-number {
|
||||
display: none;
|
||||
}
|
||||
// smartphone + phapblet
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 500px) {
|
||||
#header-post {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
#header-post {
|
||||
#menu-icon {
|
||||
display: none;
|
||||
}
|
||||
#actions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 1199px) {
|
||||
#header-post {
|
||||
#toc {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 900px) {
|
||||
#header-post {
|
||||
#menu-icon-tablet {
|
||||
display: none !important;
|
||||
}
|
||||
#top-icon-tablet {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 1199px) {
|
||||
#header-post {
|
||||
#actions {
|
||||
width: auto;
|
||||
|
||||
ul {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
}
|
||||
.info {
|
||||
display: inline;
|
||||
float: left;
|
||||
margin-right: 2rem;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,154 @@
|
||||
#footer-post {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 5000000;
|
||||
width: 100%;
|
||||
border-top: 1px solid $color-border;
|
||||
background: $color-footer-mobile-1;
|
||||
transition: opacity .2s;
|
||||
|
||||
a {
|
||||
background: none;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
a.icon {
|
||||
background: none;
|
||||
|
||||
&:hover {
|
||||
color: $color-link;
|
||||
}
|
||||
}
|
||||
#nav-footer {
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
background: $color-footer-mobile-2;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
color: $color-accent-1;
|
||||
font-size: 1em;
|
||||
}
|
||||
a:hover {
|
||||
@include underline(5px, $color-accent-1);
|
||||
}
|
||||
ul {
|
||||
display: table;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
list-style-type: none;
|
||||
|
||||
li {
|
||||
display: inline-table;
|
||||
padding: 10px;
|
||||
width: 20%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
#actions-footer {
|
||||
overflow: auto;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
padding-left: 1rem;
|
||||
color: $color-accent-1;
|
||||
}
|
||||
}
|
||||
#share-footer {
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
background: $color-footer-mobile-2;
|
||||
text-align: center;
|
||||
|
||||
ul {
|
||||
display: table;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
list-style-type: none;
|
||||
|
||||
li {
|
||||
display: inline-table;
|
||||
padding: 10px;
|
||||
width: 20%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
#toc-footer {
|
||||
clear: both;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
background: $color-footer-mobile-2;
|
||||
text-align: left;
|
||||
|
||||
#TableOfContents {
|
||||
ul {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
list-style-type: none;
|
||||
|
||||
li {
|
||||
line-height: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
a:hover {
|
||||
color: $color-link;
|
||||
}
|
||||
// .toc-level-1 > .toc-link
|
||||
// display: none
|
||||
|
||||
#TableOfContents > ul > li {
|
||||
color: $color-text;
|
||||
font-size: .8rem;
|
||||
|
||||
&:before {
|
||||
color: $color-accent-1;
|
||||
content: "#";
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
#TableOfContents > ul > li > ul > li {
|
||||
color: $color-meta;
|
||||
font-size: .7rem;
|
||||
line-height: 15px;
|
||||
|
||||
&:before {
|
||||
color: $color-accent-1;
|
||||
content: "·";
|
||||
|
||||
font-weight: bold;
|
||||
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
#TableOfContents > ul > li > ul > li > ul > li {
|
||||
display: none;
|
||||
}
|
||||
// .toc-level-5
|
||||
// display: none
|
||||
|
||||
// .toc-level-6
|
||||
// display: none
|
||||
|
||||
// .toc-number
|
||||
// display: none
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 500px) {
|
||||
#footer-post-container {
|
||||
display: none;
|
||||
}
|
||||
};
|
||||
49
hugo-content/themes/cactus/assets/scss/partial/search.scss
Normal file
@@ -0,0 +1,49 @@
|
||||
.search-input {
|
||||
padding: 4px 7px;
|
||||
width: 100%;
|
||||
outline: none;
|
||||
border: solid 1px $color-accent-3;
|
||||
border-radius: 5px;
|
||||
background-color: $color-background;
|
||||
color: $color-text;
|
||||
font-size: 1.2rem;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
|
||||
&:focus {
|
||||
border: solid 1px $color-accent-1;
|
||||
}
|
||||
}
|
||||
#search-result {
|
||||
ul.search-result-list {
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
li {
|
||||
margin: 2em auto;
|
||||
}
|
||||
a.search-result-title {
|
||||
background-image: none;
|
||||
color: $color-text;
|
||||
text-transform: capitalize;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
}
|
||||
p.search-result {
|
||||
overflow: hidden;
|
||||
margin: .4em auto;
|
||||
max-height: 13em;
|
||||
text-align: justify;
|
||||
font-size: .8em;
|
||||
}
|
||||
em.search-keyword {
|
||||
border-bottom: 1px dashed $color-link;
|
||||
color: $color-link;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.search-no-result {
|
||||
display: none;
|
||||
padding-bottom: .5em;
|
||||
color: $color-text;
|
||||
}
|
||||
13
hugo-content/themes/cactus/assets/scss/partial/tags.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
#tag-cloud {
|
||||
.tag-cloud-title {
|
||||
color: $color-meta;
|
||||
}
|
||||
.tag-cloud-tags {
|
||||
clear: both;
|
||||
text-align: center;
|
||||
a {
|
||||
display: inline-block;
|
||||
margin: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
86
hugo-content/themes/cactus/assets/scss/partial/tooltip.scss
Normal file
@@ -0,0 +1,86 @@
|
||||
// ref: https://github.com/primer/primer/blob/master/modules/primer-tooltips/lib/tooltips.scss
|
||||
.tooltipped {
|
||||
position: relative;
|
||||
}
|
||||
// This is the tooltip bubble
|
||||
.tooltipped::after {
|
||||
position: absolute;
|
||||
z-index: 1000000;
|
||||
display: none;
|
||||
padding: .2em .5em;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
color: $color-background;
|
||||
font-display: swap;
|
||||
font-weight: 400;
|
||||
font-size: $font-size * 0.8;
|
||||
font-family: $font-family-body;
|
||||
line-height: $line-height;
|
||||
text-rendering: geometricPrecision;
|
||||
text-align: center;
|
||||
word-wrap: break-word;
|
||||
white-space: pre;
|
||||
content: attr(aria-label);
|
||||
background: $color-text;
|
||||
border-radius: 3px;
|
||||
opacity: 0;
|
||||
}
|
||||
// This is the tooltip arrow
|
||||
.tooltipped::before {
|
||||
position: absolute;
|
||||
z-index: 1000001;
|
||||
display: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
color: $color-text;
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
border: 6px solid transparent;
|
||||
opacity: 0;
|
||||
}
|
||||
// delay animation for tooltip
|
||||
@keyframes tooltip-appear {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
};
|
||||
|
||||
// This will indicate when we'll activate the tooltip
|
||||
.tooltipped:hover,
|
||||
.tooltipped:active,
|
||||
.tooltipped:focus {
|
||||
&::before,
|
||||
&::after {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
animation-name: tooltip-appear;
|
||||
animation-duration: 0.1s;
|
||||
animation-fill-mode: forwards;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
// Tooltipped south
|
||||
}
|
||||
.tooltipped-s,
|
||||
.tooltipped-sw {
|
||||
&::after {
|
||||
top: 100%;
|
||||
right: 50%;
|
||||
margin-top: 6px;
|
||||
}
|
||||
&::before {
|
||||
top: auto;
|
||||
right: 50%;
|
||||
bottom: -7px;
|
||||
margin-right: -6px;
|
||||
border-bottom-color: $color-text;
|
||||
}
|
||||
}
|
||||
.tooltipped-sw::after {
|
||||
margin-right: -16px;
|
||||
}
|
||||
// Move the tooltip body to the center of the object.
|
||||
.tooltipped-s::after {
|
||||
transform: translateX(50%);
|
||||
}
|
||||
105
hugo-content/themes/cactus/assets/scss/rtl.scss
Normal file
@@ -0,0 +1,105 @@
|
||||
@font-face {
|
||||
font-family: Vazir;
|
||||
src: url('../lib/vazir-font/Vazir.eot');
|
||||
src: url("../lib/vazir-font/Vazir.eot?#iefix") format('embedded-opentype'), url("../lib/vazir-font/Vazir.woff2") format('woff2'), url("../lib/vazir-font/Vazir.woff") format('woff'), url("../lib/vazir-font/Vazir.ttf") format('truetype');
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Vazir;
|
||||
src: url('../lib/vazir-font/Vazir-Bold.eot');
|
||||
src: url("../lib/vazir-font/Vazir-Bold.eot?#iefix") format('embedded-opentype'), url("../lib/vazir-font/Vazir-Bold.woff2") format('woff2'), url("../lib/vazir-font/Vazir-Bold.woff") format('woff'), url("../lib/vazir-font/Vazir-Bold.ttf") format('truetype');
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: Vazir;
|
||||
src: url('../lib/vazir-font/Vazir-Light.eot');
|
||||
src: url("../lib/vazir-font/Vazir-Light.eot?#iefix") format('embedded-opentype'), url("../lib/vazir-font/Vazir-Light.woff2") format('woff2'), url("../lib/vazir-font/Vazir-Light.woff") format('woff'), url("../lib/vazir-font/Vazir-Light.ttf") format('truetype');
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.rtl {
|
||||
font-family: Vazir, sans-serif;
|
||||
direction: rtl;
|
||||
|
||||
#nav {
|
||||
li {
|
||||
margin-right: 0px !important;
|
||||
padding-left: 15px;
|
||||
border-right: 0px !important;
|
||||
border-left: 1px dotted;
|
||||
}
|
||||
li:last-child {
|
||||
margin-right: 15px !important;
|
||||
border-left: 0 !important;
|
||||
}
|
||||
}
|
||||
#header {
|
||||
#logo {
|
||||
float: right;
|
||||
margin-right: 0;
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
#footer {
|
||||
li {
|
||||
margin-right: 0px;
|
||||
padding-left: 15px;
|
||||
border-right: 0px;
|
||||
border-left: 1px dotted;
|
||||
}
|
||||
li:last-child {
|
||||
margin-right: 15px !important;
|
||||
border-left: 0 !important;
|
||||
}
|
||||
#logo {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
article {
|
||||
.content {
|
||||
h2:before {
|
||||
right: -1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.post-list {
|
||||
.post-item {
|
||||
.meta {
|
||||
margin-left: 16px;
|
||||
margin-right: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: 480px) {
|
||||
.rtl {
|
||||
.post-list {
|
||||
.post-item {
|
||||
.meta {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 480px) {
|
||||
.rtl {
|
||||
#header {
|
||||
#title {
|
||||
margin-left: 5rem;
|
||||
margin-right: 0;
|
||||
}
|
||||
#nav {
|
||||
ul {
|
||||
li {
|
||||
left: 1rem;
|
||||
right: auto;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
231
hugo-content/themes/cactus/assets/scss/style.scss
Normal file
@@ -0,0 +1,231 @@
|
||||
@import "variables";
|
||||
@import "colors/{{ site.Params.colortheme | default "white" }}";
|
||||
@import "util";
|
||||
@import "mixins";
|
||||
@import "extend";
|
||||
@import "fonts";
|
||||
|
||||
// global-reset()
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
border-top: 2px solid $color-text;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
background-color: $color-background;
|
||||
color: $color-text;
|
||||
font-display: swap;
|
||||
font-weight: 400;
|
||||
font-size: $font-size;
|
||||
font-family: $font-family-body;
|
||||
line-height: $line-height;
|
||||
text-rendering: geometricPrecision;
|
||||
flex: 1;
|
||||
|
||||
@include antialias();
|
||||
|
||||
@extend $base-style !optional;
|
||||
}
|
||||
.content {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
overflow-wrap: break-word;
|
||||
|
||||
p {
|
||||
@include hyphens(auto);
|
||||
}
|
||||
code {
|
||||
@include hyphens(manual);
|
||||
}
|
||||
a {
|
||||
color: $color-text;
|
||||
text-decoration: none;
|
||||
|
||||
@include underline(5px, $color-text);
|
||||
|
||||
&:hover {
|
||||
background-image: linear-gradient(transparent, transparent 4px, $color-link 4px, $color-link);
|
||||
}
|
||||
}
|
||||
a.icon {
|
||||
background: none;
|
||||
|
||||
&:hover {
|
||||
color: $color-link;
|
||||
}
|
||||
}
|
||||
h1 a,
|
||||
.h1 a,
|
||||
h2 a,
|
||||
h3 a,
|
||||
h4 a,
|
||||
h5 a,
|
||||
h6 a {
|
||||
background: none;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
h1 a:hover,
|
||||
.h1 a:hover,
|
||||
h2 a:hover,
|
||||
h3 a:hover,
|
||||
h4 a:hover,
|
||||
h5 a:hover,
|
||||
h6 a:hover {
|
||||
@include underline(6px, $color-link);
|
||||
}
|
||||
h6 {
|
||||
a {
|
||||
background: none;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
h6 {
|
||||
a:hover {
|
||||
@include underline(6px, $color-link);
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (min-width: 540px) {
|
||||
.image-wrap {
|
||||
flex-direction: row;
|
||||
margin-bottom: 2rem;
|
||||
|
||||
.image-block {
|
||||
flex: 1 0 35%;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
p {
|
||||
flex: 1 0 65%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.max-width {
|
||||
max-width: $page-width;
|
||||
}
|
||||
@media (max-width: 480px) { // smaller margins at smaller screen widths
|
||||
.px3 {
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.my4 {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 480px) {
|
||||
p {
|
||||
text-align: justify;
|
||||
}
|
||||
}
|
||||
|
||||
@import "partial/header";
|
||||
@import "partial/post/actions_desktop";
|
||||
@import "partial/post/actions_mobile";
|
||||
@import "partial/index";
|
||||
@import "partial/article";
|
||||
@import "partial/archive";
|
||||
@import "partial/comments";
|
||||
@import "partial/footer";
|
||||
@import "partial/pagination";
|
||||
@import "partial/search";
|
||||
@import "partial/tags";
|
||||
@import "partial/tooltip";
|
||||
@import "partial/categories";
|
||||
|
||||
pre {
|
||||
overflow-x: auto;
|
||||
padding: 15px 15px 10px 15px;
|
||||
border: 1px dotted $color-border;
|
||||
border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
font-size: 13px;
|
||||
font-family: $font-family-mono;
|
||||
line-height: 22px;
|
||||
position: relative;
|
||||
|
||||
.code-copy-btn {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
border: 0;
|
||||
border-radius: 0 2px;
|
||||
padding: 0;
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
font-weight: 800;
|
||||
font-size: 0.9em;
|
||||
line-height: 1.7;
|
||||
color: #fff;
|
||||
background-color: #8c8c8c;
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
letter-spacing: 0em;
|
||||
}
|
||||
|
||||
.code-copy-btn:hover {
|
||||
background-color: #666;
|
||||
color: #2bbc8a;
|
||||
}
|
||||
|
||||
code {
|
||||
display: block;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: $font-family-mono;
|
||||
padding: 0 5px;
|
||||
border: 1px dotted $color-border;
|
||||
border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
|
||||
& > div {
|
||||
border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
}
|
||||
|
||||
pre {
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
table {
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
pre {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
pre {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
hugo-content/themes/cactus/exampleSite/.travis.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
language: minimal
|
||||
dist: trusty
|
||||
env:
|
||||
- HUGO_VERSION=0.74.2
|
||||
install:
|
||||
- curl -LO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_Linux-64bit.deb
|
||||
- sudo dpkg -i hugo_${HUGO_VERSION}_Linux-64bit.deb
|
||||
script:
|
||||
- chmod +x ./deploy.sh
|
||||
- ./deploy.sh
|
||||
branches:
|
||||
only:
|
||||
- src
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
||||
94
hugo-content/themes/cactus/exampleSite/config.toml
Normal file
@@ -0,0 +1,94 @@
|
||||
baseURL = "https://example.com"
|
||||
languageCode = "en-us"
|
||||
title = "Cactus theme example"
|
||||
theme = "cactus"
|
||||
copyright = "You" # cactus will use title if copyright is not set
|
||||
disqusShortname = "example" # Used when comments is enabled. Cactus will use site title if not set
|
||||
# googleAnalytics = "UA-1234-5"
|
||||
|
||||
|
||||
# summaryLength = 2
|
||||
|
||||
# Main menu which appears below site header.
|
||||
[[menu.main]]
|
||||
name = "Home"
|
||||
url = "/"
|
||||
weight = 1
|
||||
|
||||
[[menu.main]]
|
||||
name = "Writings"
|
||||
url = "/posts"
|
||||
weight = 2
|
||||
|
||||
[[menu.main]]
|
||||
name = "Tags"
|
||||
url = "/tags"
|
||||
weight = 3
|
||||
|
||||
[[menu.main]]
|
||||
name = "About"
|
||||
url = "/about"
|
||||
weight = 4
|
||||
|
||||
[markup]
|
||||
[markup.tableOfContents]
|
||||
endLevel = 4
|
||||
ordered = false
|
||||
startLevel = 2
|
||||
[markup.highlight]
|
||||
codeFences = true
|
||||
guessSyntax = false
|
||||
hl_Lines = ""
|
||||
lineNoStart = 1
|
||||
lineNos = true
|
||||
lineNumbersInTable = false
|
||||
noClasses = true
|
||||
style = "dracula"
|
||||
tabWidth = 4
|
||||
|
||||
[params]
|
||||
|
||||
colortheme = "white" # dark, light, white, or classic
|
||||
rss = true # generate rss feed. default value is false
|
||||
googleAnalyticsAsync = true # use asynchronous tracking. Synchronous tracking by default
|
||||
showAllPostsArchive = false # default
|
||||
|
||||
# Home page settings
|
||||
description = "Hugo is a general-purpose website framework. Technically speaking, Hugo is a static site generator. Unlike systems that dynamically build a page with each visitor request, Hugo builds pages when you create or update your content. Since websites are viewed far more often than they are edited, Hugo is designed to provide an optimal viewing experience for your website’s end users and an ideal writing experience for website authors."
|
||||
mainSection = "posts" # your main section
|
||||
showAllPostsOnHomePage = false # default
|
||||
postsOnHomePage = 5 # this option will be ignored if showAllPostsOnHomePage is set to true
|
||||
tagsOverview = true # show tags overview by default.
|
||||
showProjectsList = true # show projects list by default (if projects data file exists).
|
||||
projectsUrl = "https://github.com/gohugoio" # title link for projects list
|
||||
|
||||
# https://gohugo.io/functions/format/#hugo-date-and-time-templating-reference
|
||||
dateFormat = "2006-01-02" # default
|
||||
|
||||
# Post page settings
|
||||
show_updated = true # default
|
||||
showReadTime = true # default
|
||||
|
||||
[params.comments]
|
||||
enabled = true # default
|
||||
engine = "cactus_comments" # only disqus, utterances, and cactus_comments is supported
|
||||
[params.comments.utterances]
|
||||
repo = "<github_username>/<github_reponame>"
|
||||
label = "hugo-site-name" # you can use however you want to label your name in your repo's issues
|
||||
theme = "github-light"
|
||||
[params.comments.cactuscomments]
|
||||
siteName = "your_cactus_comments_sitename" # see https://cactus.chat/ on how to register your site name
|
||||
#serverUrl = "" # Defaults to https://matrix.cactus.chat:8448 (Cactus Chat public server)
|
||||
#serverName = "" # Defaults to cactus.chat
|
||||
|
||||
# the value of name should be an valid font awesome icon name (brands type)
|
||||
# https://fontawesome.com/icons?d=gallery&s=brands
|
||||
[[params.social]]
|
||||
name = "github"
|
||||
link = "https://github.com/gohugoio"
|
||||
[[params.social]]
|
||||
name = "linkedin"
|
||||
link = "https://www.linkedin.com/company/github/"
|
||||
[[params.social]]
|
||||
name = "email"
|
||||
link = "example@example.com" # no need for "mailto:" in the head
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: About me
|
||||
date: 2016-08-24 17:51:42
|
||||
---
|
||||
|
||||
Github: [monkeyWzr](https://github.com/monkeyWzr)
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
---
|
||||
title: 幸せ
|
||||
date: 2016-10-22 16:56:54
|
||||
categories: life
|
||||
keywords:
|
||||
---
|
||||
|
||||
:-)
|
||||
|
||||
Look at the stars
|
||||
|
||||
抬头仰望满天繁星
|
||||
|
||||
Look how they shine for you
|
||||
|
||||
看它们为你绽放着 闪烁不息
|
||||
|
||||
And everything you do
|
||||
|
||||
而你的一颦一举
|
||||
|
||||
Yeah' they were all Yellow
|
||||
|
||||
却满含胆怯和羞意
|
||||
|
||||
I came along
|
||||
|
||||
我追随着你的气息
|
||||
|
||||
I wrote a song for you
|
||||
|
||||
为你写下一首歌曲
|
||||
|
||||
And all the things you do
|
||||
|
||||
回想着你的所有举动和笑意
|
||||
|
||||
it was called Yellow
|
||||
|
||||
并用Yellow为这首歌命名
|
||||
|
||||
So then I took my turn
|
||||
|
||||
我耗尽心力
|
||||
|
||||
Oh what a thing to have done
|
||||
|
||||
用行动表达我的爱意
|
||||
|
||||
And it was all Yellow
|
||||
|
||||
噢这过程充满不安羞怯和点滴暖意
|
||||
|
||||
Your skin
|
||||
|
||||
你的每寸肌肤
|
||||
|
||||
Oh yeah' your skin and bones
|
||||
|
||||
你的冰肌玉骨
|
||||
|
||||
Turn into something beautiful
|
||||
|
||||
是那般美好 在我心永驻
|
||||
|
||||
Do you know? you know I love you so
|
||||
|
||||
你可知道 我已深深爱上了你
|
||||
|
||||
You know I love you so
|
||||
|
||||
你知道我已深深为你着迷
|
||||
|
||||
I swam across
|
||||
|
||||
我曾为你横越海洋
|
||||
|
||||
I jumped across for you
|
||||
|
||||
也曾为你翻越高山
|
||||
|
||||
Oh what a thing to do
|
||||
|
||||
而这一切行动的意义
|
||||
|
||||
Cos you were all Yellow
|
||||
|
||||
只为你满含羞怯的笑意
|
||||
|
||||
I drew a line
|
||||
|
||||
我拿起笔 小心翼翼
|
||||
|
||||
I drew a line for you
|
||||
|
||||
画着我脑海中美好的你
|
||||
|
||||
Oh what a thing to do
|
||||
|
||||
为你做这样一件事情
|
||||
|
||||
And it was all Yellow
|
||||
|
||||
心中却也充满暖意
|
||||
|
||||
Your skin
|
||||
|
||||
你的每寸肌肤
|
||||
|
||||
Oh yeah your skin and bones
|
||||
|
||||
你的冰肌玉骨
|
||||
|
||||
Turn into something beautiful
|
||||
|
||||
是那般美好 在我心永驻
|
||||
|
||||
Do you know?
|
||||
|
||||
你可知道
|
||||
|
||||
For you I'd bleed myself dry
|
||||
|
||||
为你我愿意付出一切
|
||||
|
||||
For you I'd bleed myself dry
|
||||
|
||||
直到生命燃尽 也甘心如饴
|
||||
|
||||
It's true
|
||||
|
||||
这就是我的真心
|
||||
|
||||
Look how they shine for you
|
||||
|
||||
看那漫天繁星正为你闪耀不已
|
||||
|
||||
Look how they shine for you
|
||||
|
||||
仿佛亦惊叹于你的美丽
|
||||
|
||||
Look how they shine for
|
||||
|
||||
看那漫天繁星正为你闪耀不已
|
||||
|
||||
Look how they shine for you
|
||||
|
||||
仿佛亦能读懂我此刻心情
|
||||
|
||||
Look how they shine for you
|
||||
|
||||
看那漫天繁星正为你闪耀不已
|
||||
|
||||
Look how they shine
|
||||
|
||||
那漫天的璀璨 皆是为你
|
||||
|
||||
Look at the stars
|
||||
|
||||
抬头仰望那满天繁星
|
||||
|
||||
Look how they shine for you
|
||||
|
||||
看它们正为你绽放着 闪烁不息
|
||||
|
||||
And all the things that you do
|
||||
|
||||
皆是因为你的一颦一举
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
title: composer中的autoload
|
||||
date: 2016-11-05 02:42:06
|
||||
category: tech
|
||||
tags:
|
||||
- php
|
||||
keywords:
|
||||
- composer
|
||||
- autoload
|
||||
- psr-4
|
||||
---
|
||||
|
||||
composer的autoload可以轻松的实现php的自动加载。在`composer.json`中添加`autoload`字段即可。当前支持 `PSR-0` `PSR-4` `classmap`解析和`files`包含。官方推荐PSR-4标准(添加类时不需要重新生成加载器)。
|
||||
|
||||
### PSR-4
|
||||
|
||||
Under the `psr-4` key you define a mapping from namespaces to paths, relative to the package root. When autoloading a class like `Foo\\Bar\\Baz` a namespace prefix `Foo\\` pointing to a directory `src/` means that the autoloader will look for a file named `src/Bar/Baz.php` and include it if present. Note that as opposed to the older PSR-0 style, the prefix (`Foo\\`) is not present in the file path.
|
||||
|
||||
<!-- more -->
|
||||
|
||||
Namespace prefixes must end in `\\` to avoid conflicts between similar prefixes. For example Foo would match classes in the FooBar namespace so the trailing backslashes solve the problem: `Foo\\` and `FooBar\\` are distinct.
|
||||
|
||||
The PSR-4 references are all combined, during install/update, into a single key => value array which may be found in the generated file `vendor/composer/autoload_psr4.php`.
|
||||
|
||||
实例:
|
||||
```
|
||||
{
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Monolog\\": "src/",
|
||||
"Vendor\\Namespace\\": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
如果需要在多个目录下搜索相同前缀,可以以数组的形式指定。
|
||||
```
|
||||
{
|
||||
"autoload": {
|
||||
"psr-4": { "Monolog\\": ["src/", "lib/"] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
也可为所有命名空间指定默认文件夹:
|
||||
```
|
||||
{
|
||||
"autoload": {
|
||||
"psr-4": { "": "src/" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### classmap
|
||||
|
||||
`classmap` 引用的所有组合,都会在 `install/update` 过程中生成,并存储到 `vendor/composer/autoload_classmap.php` 文件中。这个 `map` 是经过扫描指定目录(同样支持直接精确到文件)中所有的 `.php` 和 `.inc` 文件里内置的类而得到的。
|
||||
|
||||
你可以用 `classmap` 生成支持支持自定义加载的不遵循 `PSR-0/4` 规范的类库。要配置它指向需要的目录,以便能够准确搜索到类文件。
|
||||
|
||||
实例:
|
||||
```
|
||||
{
|
||||
"autoload": {
|
||||
"classmap": ["src/", "lib/", "Something.php"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
__相关链接__
|
||||
|
||||
[Example Implementations of PSR-4](http://www.php-fig.org/psr/psr-4/examples/)
|
||||
|
||||
[The composer.json Schema](https://getcomposer.org/doc/04-schema.md#autoload)
|
||||
|
||||
[如何使用composer的autoload来自动加载自己编写的函数库与类库](http://drops.leavesongs.com/php/composer-autoload-class-and-function-written-myself.html)
|
||||
|
||||
[使用composer中的autoload](http://gywbd.github.io/posts/2014/12/composer-autoload.html)
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
title: netfilter/iptables 笔记
|
||||
date: 2016-11-29 21:08:52
|
||||
category: notes
|
||||
tags:
|
||||
- Linux
|
||||
keywords:
|
||||
- iptables
|
||||
- netfilter
|
||||
- linux网络安全
|
||||
- 运维
|
||||
---
|
||||
|
||||
## netfilter 与 iptables
|
||||
|
||||
`netfilter`是linux默认的防火墙,在2.4之后的版本正式进入内核。`netfilter` 使用四个表(Table)来存放控制信息包过滤处理的规则集。每张表由链(Chain)组成,每条链又包含了多条规则(rule)。
|
||||
|
||||
`iptables`是用来编辑操作这些表的一个工具。`iptables`包中也包含了针对IPv6的工具`ip6tables`。
|
||||
|
||||
四个表及其包含的链:
|
||||
|
||||
<!-- more -->
|
||||
|
||||
* filter
|
||||
- INPUT
|
||||
- FORWARD
|
||||
- OUTPUT
|
||||
* nat
|
||||
- PREROUTING
|
||||
- POSTROUTING
|
||||
- OUTPUT
|
||||
* mangle
|
||||
- PREROUTING
|
||||
- INPUT
|
||||
- FORWARD
|
||||
- OUTPUT
|
||||
- POSTROUTING
|
||||
* raw
|
||||
- PREROUTING
|
||||
- OUTPUT
|
||||
|
||||

|
||||
|
||||
### filter机制
|
||||
|
||||
`filter`是`netfilter`中最重要的机制,其任务是执行数据包的过滤操作。具有三种内建链:
|
||||
|
||||
* INPUT - 来自外部的数据包(访问本机)
|
||||
* OUTPUT - 发往外部的数据包(本机访问外部)
|
||||
* FORWORD - “路过”本机的数据包,转发到其他设备
|
||||
|
||||
链中规则的匹配方式遵循`first match`。`filter`会根据数据包特征从相应链中的第一条规则开始逐一进行匹配。只要遇到满足特征的规则后便不再继续。
|
||||
每条链在最底端都定义了默认规则。默认规则只会有一种状态:`ACCEPT`或者`DROP`。默认为`ACCEPT`。
|
||||
|
||||
## iptables命令参数
|
||||
|
||||
格式:
|
||||
```
|
||||
iptables -操作方式 [链名] [条件匹配] [选项]
|
||||
|
||||
iptables -[ACD] chain rule-specification [options]
|
||||
iptables -I chain [rulenum] rule-specification [options]
|
||||
iptables -R chain rulenum rule-specification [options]
|
||||
iptables -D chain rulenum [options]
|
||||
iptables -[LS] [chain [rulenum]] [options]
|
||||
iptables -[FZ] [chain] [options]
|
||||
iptables -[NX] chain
|
||||
iptables -E old-chain-name new-chain-name
|
||||
iptables -P chain target [options]
|
||||
iptables -h (print this help information)
|
||||
|
||||
```
|
||||
|
||||
常用操作方式:
|
||||
|
||||
* `-L(--list)` *[chain]* 列出所有规则或指定链的规则
|
||||
* `-A(--append)` *chain* 在指定链中添加新规则
|
||||
* `-C(--check)` *chain* 检查规则是否存在
|
||||
* `-D(--delete)` *chain rule_num* 删除链中匹配的规则
|
||||
* `-F(--flush)` *[chain]* 清除指定链或者全部链中的规则
|
||||
* `-P(--policy)` *chain* 设置指定链的默认策略
|
||||
* `-R(--replace)` *chain rule_num* 替换指定链中特定行的规则,第一行行数为1
|
||||
|
||||
|
||||
常用选项:
|
||||
|
||||
* `-p(--protocol)` *proto* 指定协议,如`tcp` `udp` `icmp`
|
||||
* `-j(--jump)` *target* 规则的目标(??),如`ACCEPT` `DROP` `REJECT`
|
||||
* `-s(--source)` *address[/mask]* 数据包源IP,可为单IP或CIDR网段或域名
|
||||
* `-d(--destination)` *address[/mask]* 数据包目的IP,可为单IP或CIDR网段或域名
|
||||
* `--dport` *port* 目的端口,必须指明`-p`
|
||||
* `--sport` *port* 来源端口,必须指明`-p`
|
||||
* `--line-numbers` 显示行号
|
||||
|
||||
>关于`-p`配置的上层协议,可参考`/etc/protocols`
|
||||
|
||||
## state模块
|
||||
|
||||
`state`模块实现了“连接跟踪”功能,用来解决某些情况下防火墙内主机对外建立链接的问题。
|
||||
`state`模块定义了四种数据包链接状态,分别为`ESTABLISHED` `NEW` `RELATED` `INVALID` 四种。在TCP/IP标准的定义中,UDP和ICMP数据包是没有链接状态的,但是在state模块的定义中,任何数据包都有连接状态。
|
||||
|
||||
### ESTABLISHED状态
|
||||
|
||||
只要数据包能够成功穿过防火墙,则之后的所有数据包(包括响应数据包)都会被标记为是`ESTABLISHED`状态。
|
||||
|
||||
当我们设置防火墙INPUT链的默认策略为`DROP`时,防火墙内主机很多服务,如ssh客户端基本上就无法与外面的ssh服务端建立连接了。原因很简单,ssh客户端使用的端口是随机的,防火墙无法预知客户端会使用哪一个端口发起链接。因此即使客户端发出了请求,ssh服务端返回的相应数据包也会被防火墙的默认策略拦截。
|
||||
|
||||
ESTABLISHED状态可以很轻易的解决此问题,见[#解决应用程序无法从防火墙主机上对外建立新连接的问题](#解决应用程序无法从防火墙主机上对外建立新连接的问题)
|
||||
|
||||
### NEW状态
|
||||
|
||||
每一条链接中的地一个数据包的状态定义为`NEW`。
|
||||
|
||||
### RELATED状态
|
||||
|
||||
`RELATED`状态的数据包其含义是指,被动产成的应答数据包,且此数据包不属于当前任何链接。换一种说法就是,只要应答的数据包是因为本机发起的连接送出vhu一个数据包,导致了另一条连接的产生,那么这个新连接的所有数据包都属于`RELATED`状态。
|
||||
|
||||
以ubuntu上上的tracepath工具为例,在检测本机与目的主机间跳数时,tracepath是通过发送TTL值从1递增的`tcp`数据包来检测每一跳。路径中的路由器因TTL减为0而回送了一个`ICMP`数据包(ICMP Type 11),该数据包就属于RELATED状态。
|
||||
|
||||
### INVALID状态
|
||||
|
||||
`INVALID`状态指的是状态不明的数据包,即不属于`ESTABLISHED` `NEW` `RELATED`三种类型的数据包。所有的`INVALID`数据包都应该视为恶意数据包。
|
||||
|
||||
|
||||
## 实例
|
||||
|
||||
### 丢弃icmp协议包(禁止ping)
|
||||
|
||||
通过此规则实现禁止ping本机的效果
|
||||
```
|
||||
iptables -A INPUT -p icmp -j DROP
|
||||
```
|
||||
|
||||
### 解决应用程序无法从防火墙主机上对外建立新连接的问题
|
||||
|
||||
```
|
||||
iptables -A INPUT -p tcp -m state ESTABLISHED -j ACCEPT
|
||||
```
|
||||
@@ -0,0 +1,274 @@
|
||||
---
|
||||
title: ruby学习笔记
|
||||
date: 2016-12-08 22:54:49
|
||||
category: notes
|
||||
tags:
|
||||
- ruby
|
||||
keywords:
|
||||
- ruby
|
||||
---
|
||||
|
||||
## regular expressions
|
||||
|
||||
`=~`是用于正则表达式的匹配操作符。返回匹配到的字符串位置或nil。
|
||||
|
||||
```ruby
|
||||
"abcdef" =~ /d/ # return 3
|
||||
"aaaaaa" =~ /d/ # return nil
|
||||
```
|
||||
|
||||
<!-- more -->
|
||||
|
||||
## !和?
|
||||
|
||||
The exclamation point (!, sometimes pronounced aloud as "bang!") indicates something potentially destructive, that is to say, something that can change the value of what it touches.
|
||||
```
|
||||
ruby> s1 = "forth"
|
||||
"forth"
|
||||
ruby> s1.chop! # This changes s1.
|
||||
"fort"
|
||||
ruby> s2 = s1.chop # This puts a changed copy in s2,
|
||||
"for"
|
||||
ruby> s1 # ... without disturbing s1.
|
||||
"fort"
|
||||
```
|
||||
|
||||
You'll also sometimes see chomp and chomp! used. These are more selective: the end of a string gets bit off only if it happens to be a newline.
|
||||
|
||||
The other method naming convention is the question mark (?, sometimes pronounced aloud as "huh?") indicates a "predicate" method, one that can return either true or false.
|
||||
|
||||
## 四种内部中断循环的方式
|
||||
|
||||
* `break`
|
||||
* `next` 等同与continue
|
||||
* `redo` restarts the current iteration
|
||||
* `return`
|
||||
|
||||
## 迭代器 iterator
|
||||
|
||||
Ruby's String type has some useful iterators. `each_byte` is an iterator for each character in the string.
|
||||
|
||||
```shell
|
||||
irb(main):001:0> "abc".each_byte{|c| printf "<%c>", c}; print "\n"
|
||||
<a><b><c>
|
||||
=> nil
|
||||
```
|
||||
|
||||
Another iterator of String is `each_line`.
|
||||
|
||||
```shell
|
||||
irb(main):002:0> "a\nb\nc\n".each_line{|l| print l}
|
||||
a
|
||||
b
|
||||
c
|
||||
=> "a\nb\nc\n"
|
||||
```
|
||||
|
||||
ruby的 `for in` 也是一种迭代。We can use a control structure `retry` in conjunction with an iterated loop, and it will retry the loop from the beginning.
|
||||
|
||||
### yield
|
||||
|
||||
`yield` occurs sometimes in a definition of an iterator. `yield` moves control to the block of code that is passed to the iterator (this will be explored in more detail in the chapter about procedure objects). The following example defines an iterator repeat, which repeats a block of code the number of times specified in an argument.
|
||||
|
||||
```ruby
|
||||
irb(main):003:0> def repeat(num)
|
||||
irb(main):004:1> while num > 0
|
||||
irb(main):005:2> yield
|
||||
irb(main):006:2> num -= 1
|
||||
irb(main):007:2> end
|
||||
irb(main):008:1> end
|
||||
=> :repeat
|
||||
irb(main):009:0> repeat(3) { puts "foo" }
|
||||
foo
|
||||
foo
|
||||
foo
|
||||
=> nil
|
||||
```
|
||||
|
||||
## class
|
||||
|
||||
### 继承
|
||||
|
||||
继承的格式为:
|
||||
```ruby
|
||||
class Superclass
|
||||
def breathe
|
||||
puts "inhale and exhale"
|
||||
end
|
||||
def identify
|
||||
puts "I'm super"
|
||||
end
|
||||
def speak(word)
|
||||
puts word
|
||||
end
|
||||
end
|
||||
|
||||
class Subclass<Superclass
|
||||
# code...
|
||||
end
|
||||
```
|
||||
|
||||
可在在子类中重新声明基类方法,也可以使用`super`关键字来扩展基类方法。`super`也允许我们传递参数给基类方法。
|
||||
```ruby
|
||||
class Subclass<Superclass
|
||||
def identify
|
||||
super
|
||||
puts "I'm sub too"
|
||||
end
|
||||
def speak(word)
|
||||
super("this is from Superclass")
|
||||
puts "now it's from Subclass: #{word}"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### 初始化(构造函数)
|
||||
|
||||
`ruby`使用`initialize`关键字来实现构造函数的功能。
|
||||
|
||||
```ruby
|
||||
class Fruit
|
||||
def initialize( k="apple")
|
||||
@kind = k
|
||||
@condition = "ripe"
|
||||
end
|
||||
end
|
||||
|
||||
apple = Fruit.new "apple"
|
||||
```
|
||||
|
||||
## variables
|
||||
|
||||
* `[a-z] or _` 本地变量
|
||||
* `$` 全局变量
|
||||
* `@` 实例变量 instance variable
|
||||
* `[A-Z]` 常量
|
||||
|
||||
### 全局变量
|
||||
|
||||
全局变量以 `$` 开头。初始化之前,全局变量的值为`nil`。可定义一段procedure来追踪全局变量。
|
||||
```ruby
|
||||
$x #return nil
|
||||
trace_var :$x, proc{puts "$x is now #{$x}"}
|
||||
$x = 5 #return $x is now 5
|
||||
```
|
||||
|
||||
一些特殊的变量(不一定是全局作用域):
|
||||
|
||||
* `$!` latest error message
|
||||
* `$@` location of error
|
||||
* `$_` 上一次由gets读入的字符串
|
||||
* `$.` line number last read by interpreter
|
||||
* `$&` string last matched by regexp
|
||||
* `$~` the last regexp match, as an array of subexpressions
|
||||
* `$n` the nth subexpression in the last match (same as $~[n])
|
||||
* `$=` case-insensitivity flag
|
||||
* `$/` input record separator
|
||||
* `$\` output record separator
|
||||
* `$0` the name of the ruby script file
|
||||
* `$*` 命令行参数
|
||||
* `$$` 当前解释器的进程id
|
||||
* `$?` 上一次子进程的退出状态码
|
||||
|
||||
### 实例变量
|
||||
|
||||
An instance variable has a name beginning with `@`, and its scope is confined to whatever object __self__ refers to. Two different objects, even if they belong to the same class, are allowed to have different values for their instance variables. From outside the object, instance variables __cannot be altered or even observed__ (i.e., ruby's instance variables are never public) except by whatever methods are explicitly provided by the programmer. As with globals, instance variables have the nil value until they are initialized.
|
||||
|
||||
Instance variables do not need to be declared. This indicates a flexible object structure; in fact, each instance variable is dynamically appended to an object when it is first assigned.
|
||||
|
||||
### 常量
|
||||
|
||||
常量名以大写字母开头。给常量重新赋值会得到警告。
|
||||
```shell
|
||||
irb(main):009:0> Wzr=1222
|
||||
=> 1222
|
||||
irb(main):010:0> Wzr=1223
|
||||
(irb):10: warning: already initialized constant Wzr
|
||||
(irb):9: warning: previous definition of Wzr was here
|
||||
=> 1223
|
||||
```
|
||||
|
||||
常量可以在类和模块中定义,并允许外部访问。
|
||||
```ruby
|
||||
class ConstClass
|
||||
C1=120
|
||||
end
|
||||
|
||||
ConstClass::C1 # return 120
|
||||
```
|
||||
|
||||
## 访问器(accessor)
|
||||
|
||||
|
||||
实例属性需要通过属性访问器访问。常规访问器有简化写法:
|
||||
```ruby
|
||||
class Fruit
|
||||
def kind=(k)
|
||||
@kind = k
|
||||
end
|
||||
def kind
|
||||
@kind
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
### inspect方法
|
||||
|
||||
当创建一个对象时解释器会返回一些信息:
|
||||
```ruby
|
||||
irb(main):009:0> apple = Fruit.new
|
||||
=> #<Fruit:0x00000000a34f58>
|
||||
irb(main):010:0>
|
||||
```
|
||||
|
||||
可以通过`inspect`关键字来改变这种默认行为。
|
||||
```ruby
|
||||
class Fruit
|
||||
def inspect
|
||||
"a fruit is created"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
`inspect`方法常用来调试,可通过下面两种方式显示调用:
|
||||
```ruby
|
||||
p anObject
|
||||
puts anObject.inspect
|
||||
```
|
||||
|
||||
### shortcuts
|
||||
|
||||
`Ruby`提供了访问器的一些简写形式:
|
||||
|
||||
|缩写|效果|
|
||||
| :------: | :------: |
|
||||
|attr_reader :v| def v; @v; end|
|
||||
|attr_writer :v| def v=(value); @v=value; end|
|
||||
|attr_accessor :v| attr_reader :v; attr_writer :v|
|
||||
|attr_accessor :v, :w| attr_accessor :v; attr_accessor :w|
|
||||
|
||||
## 注释
|
||||
|
||||
单行注释以`#`开头。
|
||||
块注释可使用`=begin` `=end`来标记。
|
||||
```ruby
|
||||
=begin
|
||||
这是一段注释块。This is a comment block.
|
||||
Egg, I dreamed I was old.
|
||||
=end
|
||||
```
|
||||
|
||||
## Dynamic Dispatch
|
||||
|
||||
## Mixins
|
||||
|
||||
Ruby has no multiple inheritance. But it has mixins.
|
||||
|
||||
### Lookup rulres
|
||||
|
||||
When looking for receiver obj's method m,
|
||||
* look in obj's class
|
||||
* look in mixins the class includes(later includes shadow)
|
||||
* look in obj's superclass
|
||||
* look in mixins the superclass inculdes
|
||||
* ...
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
title: php的闭包特性
|
||||
date: 2017-01-11 18:39:17
|
||||
category: notes
|
||||
tags:
|
||||
- php
|
||||
keywords:
|
||||
- php
|
||||
- 闭包
|
||||
- lambada
|
||||
- 匿名函数
|
||||
---
|
||||
|
||||
闭包和匿名函数在`PHP 5.3.0`引入,并且PHP将两者视为相同的概念。闭包其实是伪装成函数的对象,它的实质其实是`Closure`实例。
|
||||
|
||||
创建闭包非常简单:
|
||||
|
||||
```php
|
||||
$c = function($name) {
|
||||
return sprintf("Hello World! Hello %s!", $name);
|
||||
};
|
||||
|
||||
echo $c('PHP');
|
||||
```
|
||||
|
||||
使用`use`对闭包附加状态,多个参数使用`,`分隔:
|
||||
|
||||
```php
|
||||
function callPerson($name) {
|
||||
return function($about) use ($name) {
|
||||
return sprintf("%s, %s", $name, $about);
|
||||
}
|
||||
}
|
||||
|
||||
$triver = callPerson('Triver');
|
||||
echo $triver("slow down, please!!");
|
||||
|
||||
```
|
||||
|
||||
附加的变量会被封装到闭包内,即使返回的闭包队形已经跳出了`callPerson()`的作用域也仍然会记住`$name`的值。
|
||||
|
||||
闭包有一个有趣的`bindTo()`方法,可以将闭包的内部状态绑定到其他对象上,第二个参数指定了绑定闭包的对象所属的类,从而实现在闭包中访问绑定对象的私有方法和属性。
|
||||
|
||||
```php
|
||||
class Bind {
|
||||
protected $name = 'no name';
|
||||
public $change;
|
||||
|
||||
public function addAction($action) {
|
||||
$this->change = $action->bindTo($this, __CLASS__);
|
||||
}
|
||||
}
|
||||
|
||||
$bind = new Bind();
|
||||
$bind->addAction(function() {
|
||||
$this->name = "php";
|
||||
return $this->name;
|
||||
});
|
||||
|
||||
$change = $bind->change;
|
||||
echo $change();
|
||||
```
|
||||
|
||||
使用这个特性可以方便的为类添加方法并绑定:
|
||||
|
||||
```php
|
||||
trait MetaTrait
|
||||
{
|
||||
//定义$methods数组,用于保存方法(函数)的名字和地址。
|
||||
private $methods = array();
|
||||
//定义addMethod方法,使用闭包类绑定匿名函数。
|
||||
public function addMethod($methodName, $methodCallable)
|
||||
{
|
||||
if (!is_callable($methodCallable)) {
|
||||
throw new InvalidArgumentException('Second param must be callable');
|
||||
}
|
||||
$this->methods[$methodName] = Closure::bind($methodCallable, $this, get_class());
|
||||
}
|
||||
//方法重载。为了避免当调用的方法不存在时产生错误,
|
||||
//可以使用 __call() 方法来避免。
|
||||
public function __call($methodName, array $args)
|
||||
{
|
||||
if (isset($this->methods[$methodName])) {
|
||||
return call_user_func_array($this->methods[$methodName], $args);
|
||||
}
|
||||
|
||||
throw RunTimeException('There is no method with the given name to call');
|
||||
}
|
||||
}
|
||||
|
||||
class HackThursday {
|
||||
use MetaTrait;
|
||||
|
||||
private $dayOfWeek = 'Thursday';
|
||||
|
||||
}
|
||||
|
||||
$test = new HackThursday();
|
||||
$test->addMethod('when', function () {
|
||||
return $this->dayOfWeek;
|
||||
});
|
||||
|
||||
echo $test->when();
|
||||
```
|
||||
|
||||
php7 中增加了 `Closure::call()` 方法,可以更高效的绑定对象作用域并调用。
|
||||
|
||||
```php
|
||||
class A {private $x = 1;}
|
||||
|
||||
// Pre PHP 7 code
|
||||
$getXCB = function() {return $this->x;};
|
||||
$getX = $getXCB->bindTo(new A, 'A'); // intermediate closure
|
||||
echo $getX();
|
||||
|
||||
// PHP 7+ code
|
||||
$getX = function() {return $this->x;};
|
||||
echo $getX->call(new A);
|
||||
```
|
||||
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: java散列知识点总结
|
||||
date: 2017-02-18 19:19:01
|
||||
category: notes
|
||||
tags:
|
||||
- Algorithms
|
||||
keywords:
|
||||
- hash
|
||||
- 散列
|
||||
- 哈希
|
||||
- java
|
||||
---
|
||||
|
||||
java 的根类 `Object` 具有 `hashcode` 方法。当 `equal` 方法被重写时也应当重写 `hashcode` 方法。
|
||||
|
||||
## 基本数据类型的散列码
|
||||
|
||||
* `byte` `short` `int` `char` 类型的搜索键将会转换为 `int`。
|
||||
* `float` 类型的搜索键使用 `Float.floatToIntBits(key)` 作为散列码。
|
||||
* `long` 类型的搜索键会进行折叠操作,如下:
|
||||
|
||||
```java
|
||||
iny hashCode = (int) (key ^ (key >> 32));
|
||||
```
|
||||
|
||||
* `double` 类型的搜索键会使用 `Double.doubleToLongBits(key)` 方法转换为 `long` 类型然后再进行折叠。
|
||||
|
||||
## 字符串类型的散列码
|
||||
|
||||
对于字符串一般使用多项式散列码进行计算,
|
||||
|
||||
~~这里放个公式的图~~
|
||||
|
||||
b的较好取值为31,33,37,39,41。在 java String 类中 `b` 取31。
|
||||
|
||||
```java
|
||||
public static int hash(String key, int tableSize)
|
||||
{
|
||||
int hashVal = 0;
|
||||
|
||||
for (int i = 0; i < key.length(); i++)
|
||||
hashVal = 37*hashVal + key.charAt(i);
|
||||
|
||||
hashVal %= tableSize;
|
||||
if (hashVal < 0)
|
||||
hashVal += tableSize;
|
||||
|
||||
return hashVal;
|
||||
}
|
||||
```
|
||||
|
||||
## 压缩散列码
|
||||
|
||||
由于散列码可能是很大的正数,通常应该对其进行压缩以防止超出索引的范围。若索引范围为 `0 ~ n - 1` ,通常的做法是 `h(hashCode) = hashCode % N` ,选择N为大于2的素数。
|
||||
`java.util.HashMap` 的实现中,将N设置为2的幂值,这样可以使用位运算代替上述的取模:`h(hashCode) = hashCode & (N - 1)` ,两者是完全等价的。
|
||||
|
||||
## 处理冲突
|
||||
|
||||
### 开放地址法
|
||||
|
||||
开放地址法是在冲突发生时,在散列表中找到一个开放位置的过程。
|
||||
|
||||
* 线性探测,存在成簇问题
|
||||
* 二次探测,存在二次成簇问题,并且不能保证一个开放的单元总是可以被找到。
|
||||
* 再哈希法
|
||||
|
||||
### 链地址法
|
||||
|
||||
链地址法是将具有同样索引的条目放在同一位置,每个位置使用一个桶(ArrayList or LinkedList)来放置多个条目。
|
||||
|
||||
## 装填因子
|
||||
|
||||
装填因子衡量一个散列表有多满。`lamda = n / N` 。对于开放地址法,装填因子介于 0 ~ 1,对于链地址法,装填因子可能为任意值。通常开放地址法需要将装填因子维持在0.5以下,而链地址法为0.9以下。`java.util.HashMap` 采用了阈值0.75。
|
||||
@@ -0,0 +1,438 @@
|
||||
---
|
||||
title: ES6について
|
||||
date: 2019-01-18 16:00:00
|
||||
tags:
|
||||
- JavaScript
|
||||
category: tech
|
||||
keywords:
|
||||
- Javascript
|
||||
- ES2015
|
||||
- ES6
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
[https://github.com/lukehoban/es6features#readme](https://github.com/lukehoban/es6features#readme)
|
||||
|
||||
[http://help.wtf/es6](http://help.wtf/es6)
|
||||
|
||||
[http://es6-features.org](http://es6-features.org)
|
||||
|
||||
## String
|
||||
|
||||
### String.x is deprecated; use String.prototype.x instead.
|
||||
|
||||
非推奨の構文:
|
||||
|
||||
```Javascript
|
||||
var num = 15;
|
||||
String.replace(num, /5/, '2');
|
||||
```
|
||||
|
||||
標準の構文:
|
||||
|
||||
```Javascript
|
||||
var num = 15;
|
||||
String(num).replace(/5/, '2');
|
||||
```
|
||||
|
||||
### Template literal
|
||||
|
||||
Nesting templates:
|
||||
|
||||
```Javascript
|
||||
const classes = `header ${ isLargeScreen() ? '' : `icon-${item.isCollapsed ? 'expander' : 'collapser'}` }`;
|
||||
```
|
||||
<!--more-->
|
||||
|
||||
タグ付けされたtemplate
|
||||
|
||||
```Javascript
|
||||
var a = 5;
|
||||
var b = 10;
|
||||
|
||||
function tag(strings, ...values) {
|
||||
console.log(strings[0]); // "Hello "
|
||||
console.log(strings[1]); // " world"
|
||||
console.log(values[0]); // 15
|
||||
console.log(values[1]); // 50
|
||||
|
||||
return "Bazinga!";
|
||||
}
|
||||
|
||||
tag`Hello ${ a + b } world ${ a * b}`;
|
||||
// "Bazinga!"
|
||||
```
|
||||
|
||||
[http://help.wtf/es6#template_literals](http://help.wtf/es6#template_literals)
|
||||
|
||||
```Javascript
|
||||
// Backticks enclose a template literal; ${} interpolates arbitrary expressions
|
||||
let num = 99; // see block scope
|
||||
console.log(`${num} bottles of beer on the wall, ${num} bottles of beer
|
||||
Take one down and pass it around, ${--num} bottles of beer!`);
|
||||
|
||||
// Tagged form: Attach a function that processes string fragments and evaluated
|
||||
// expressions
|
||||
function celsius(strings, ...values) {
|
||||
let rv = '';
|
||||
strings.forEach((string, index) => { // See arrow functions
|
||||
rv += string;
|
||||
if (typeof values[index] !== 'undefined')
|
||||
rv += Math.round((values[index] - 32) / 1.8);
|
||||
});
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Converts all the interpolated numbers to the proper unit
|
||||
console.log(celsius `Today temperatures ranged from ${60} to ${65} degrees.`);
|
||||
```
|
||||
|
||||
## var and let
|
||||
|
||||
[https://eslint.org/docs/rules/no-var](https://eslint.org/docs/rules/no-var)
|
||||
|
||||
>ECMAScript 6 allows programmers to create variables with block scope instead of function scope using the let and const keywords. Block scope is common in many other programming languages and helps programmers avoid mistakes.
|
||||
|
||||
```Javascript
|
||||
if (true) {
|
||||
var i = 1;
|
||||
let j = 2;
|
||||
}
|
||||
|
||||
console.log(i) // 1
|
||||
console.log(j) // ReferenceError: not defined
|
||||
```
|
||||
|
||||
## Default function parameters
|
||||
|
||||
```Javascript
|
||||
function multiply(a, b = 1) {
|
||||
return a * b;
|
||||
}
|
||||
```
|
||||
|
||||
## Iterators and for...of
|
||||
|
||||
```Javascript
|
||||
let fibonacci = {
|
||||
[Symbol.iterator]() {
|
||||
let pre = 0, cur = 1;
|
||||
return {
|
||||
next() {
|
||||
[pre, cur] = [cur, pre + cur];
|
||||
return { done: false, value: cur }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var n of fibonacci) {
|
||||
// truncate the sequence at 1000
|
||||
if (n > 1000)
|
||||
break;
|
||||
console.log(n);
|
||||
}
|
||||
```
|
||||
|
||||
More: [Duck Typing](https://en.wikipedia.org/wiki/Duck_typing)
|
||||
|
||||
## Modules
|
||||
|
||||
### import and export
|
||||
|
||||
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)
|
||||
|
||||
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export)
|
||||
|
||||
### Module Loaders
|
||||
|
||||
Module loaders support:
|
||||
|
||||
* Dynamic loading
|
||||
* State isolation
|
||||
* Global namespace isolation
|
||||
* Compilation hooks
|
||||
* Nested virtualization
|
||||
|
||||
The default module loader can be configured, and new loaders can be constructed to evaluate and load code in isolated or constrained contexts.
|
||||
|
||||
```Javascript
|
||||
// Dynamic loading – ‘System’ is default loader
|
||||
System.import('lib/math').then(function(m) {
|
||||
alert("2π = " + m.sum(m.pi, m.pi));
|
||||
});
|
||||
|
||||
// Create execution sandboxes – new Loaders
|
||||
var loader = new Loader({
|
||||
global: fixup(window) // replace ‘console.log’
|
||||
});
|
||||
loader.eval("console.log('hello world!');");
|
||||
|
||||
// Directly manipulate module cache
|
||||
System.get('jquery');
|
||||
System.set('jquery', Module({$: $})); // WARNING: not yet finalized
|
||||
```
|
||||
|
||||
## Promises
|
||||
|
||||
[JavaScript Promiseの本](https://github.com/azu/promises-book)
|
||||
|
||||
```Javascript
|
||||
function asyncFunction() {
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
setTimeout(function () {
|
||||
resolve('Async Hello world');
|
||||
}, 16);
|
||||
});
|
||||
}
|
||||
|
||||
asyncFunction().then(function (value) {
|
||||
console.log(value); // => 'Async Hello world'
|
||||
}).catch(function (error) {
|
||||
console.error(error);
|
||||
});
|
||||
```
|
||||
|
||||
## Generators
|
||||
|
||||
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols)
|
||||
|
||||
```Javascript
|
||||
// A generator function will return an object that implements the iteration
|
||||
// protocol, i.e., it has a next() method that returns
|
||||
// { value: < some value>, done: <true or false> }
|
||||
function* incRand(max) { // Asterisk defines this as a generator function
|
||||
while (true) {
|
||||
// Pause execution after the yield, resume when next(<something>) is called
|
||||
// and assign <something> to x
|
||||
let x = yield Math.floor(Math.random() * max + 1);
|
||||
max += x;
|
||||
}
|
||||
}
|
||||
var rng = incRand(2); // Now we have a generator object to work with
|
||||
console.log(rng.next()); // { value: <between 1 and 2>, done: false }
|
||||
console.log(rng.next(3)); // as above, but between 1 and 5
|
||||
console.log(rng.next()); // as above, but NaN since 5 + undefined results in NaN
|
||||
console.log(rng.next(20)); // Oops, looks like we broke it! NaN again.
|
||||
rng.throw(new Error('Unrecoverable generator state.')); // Will be thrown from yield
|
||||
```
|
||||
|
||||
## shorthand of Object initializer
|
||||
|
||||
[https://ariya.io/2013/02/es6-and-object-literal-property-value-shorthand](https://ariya.io/2013/02/es6-and-object-literal-property-value-shorthand)
|
||||
|
||||
```Javascript
|
||||
// Shorthand property names (ES2015)
|
||||
var a = 'foo', b = 42, c = {};
|
||||
var o = {a, b, c};
|
||||
|
||||
// Shorthand method names (ES2015)
|
||||
var o = {
|
||||
property(parameters) {}
|
||||
};
|
||||
|
||||
// Computed property names (ES2015)
|
||||
var prop = 'foo';
|
||||
var o = {
|
||||
[prop]: 'hey',
|
||||
['b' + 'ar']: 'there'
|
||||
};
|
||||
```
|
||||
|
||||
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)
|
||||
|
||||
## Destructuring
|
||||
|
||||
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
|
||||
|
||||
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables. Destructuring is **fail-soft**, similar to standard object lookup foo["bar"], producing undefined values when not found.
|
||||
|
||||
```Javascript
|
||||
let [n1, n2, n3, n4, ...r] = [100, 'three', 34, {number: 23}, 694, 'eighteen'];
|
||||
console.log(n1, n2, n3, n4); // "100 'three' 34 { number: 23 }"
|
||||
console.log(r); // "[ 694, 'eighteen' ]"
|
||||
```
|
||||
|
||||
Two variables values can be swapped in one destructuring expression
|
||||
|
||||
```Javascript
|
||||
var a = 1;
|
||||
var b = 3;
|
||||
|
||||
[a, b] = [b, a];
|
||||
console.log(a); // 3
|
||||
console.log(b); // 1
|
||||
```
|
||||
|
||||
Object Destructuring
|
||||
|
||||
```Javascript
|
||||
var o = {p: 42, q: true};
|
||||
var {p, q} = o;
|
||||
|
||||
console.log(p); // 42
|
||||
console.log(q); // true
|
||||
```
|
||||
|
||||
Works for function parameters
|
||||
|
||||
```Javascript
|
||||
var fmt = ({id = 0, name}) => `${id}: ${name}`;
|
||||
console.log(fmt({ id: 1, name: 'joe'}));
|
||||
```
|
||||
|
||||
## symbol
|
||||
|
||||
[https://developer.mozilla.org/en-US/docs/Glossary/Symbol](https://developer.mozilla.org/en-US/docs/Glossary/Symbol)
|
||||
|
||||
`sympol` is a primitive data type. The Symbol() function returns a value of type symbol and every returned value is unique. It **does not** support `new Symbol()`
|
||||
|
||||
```Javascript
|
||||
var sym1 = Symbol();
|
||||
var sym2 = Symbol('foo');
|
||||
var sym3 = Symbol('foo');
|
||||
|
||||
Symbol('foo') === Symbol('foo'); // false
|
||||
var sym = new Symbol(); // TypeError
|
||||
```
|
||||
|
||||
## Map + Set + WeakMap + WeakSet
|
||||
|
||||
Reference: [Why WeakMap?](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap#Why_WeakMap)
|
||||
>Native WeakMaps hold "weak" references to key objects, which means that they do not prevent garbage collection in case there would be no other reference to the key object. This also avoids preventing garbage collection of values in the map.
|
||||
|
||||
```Javascript
|
||||
/ Sets
|
||||
var s = new Set();
|
||||
s.add("hello").add("goodbye").add("hello");
|
||||
s.size === 2;
|
||||
s.has("hello") === true;
|
||||
|
||||
// Maps
|
||||
var m = new Map();
|
||||
m.set("hello", 42);
|
||||
m.set(s, 34);
|
||||
m.get(s) == 34;
|
||||
|
||||
// Weak Maps
|
||||
var wm = new WeakMap();
|
||||
wm.set(s, { extra: 42 });
|
||||
wm.size === undefined
|
||||
|
||||
// Weak Sets
|
||||
var ws = new WeakSet();
|
||||
ws.add({ data: 42 });
|
||||
// Because the added object has no other references, it will not be held in the set
|
||||
```
|
||||
|
||||
## New APIs in core libraries
|
||||
|
||||
```Javascript
|
||||
// Number
|
||||
Number.EPSILON
|
||||
Number.isInteger(Infinity) // false
|
||||
Number.isNaN("NaN") // false
|
||||
|
||||
// Math
|
||||
Math.acosh(3) // 1.762747174039086
|
||||
Math.hypot(3, 4) // 5
|
||||
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
|
||||
|
||||
// String
|
||||
"abcde".includes("cd") // true
|
||||
"abc".repeat(3) // "abcabcabc"
|
||||
|
||||
// Array
|
||||
Array.from(document.querySelectorAll('*')) // Returns a real Array
|
||||
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
|
||||
[0, 0, 0].fill(7, 1) // [0,7,7]
|
||||
[1, 2, 3].find(x => x == 3) // 3
|
||||
[1, 2, 3].findIndex(x => x == 2) // 1
|
||||
[1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2]
|
||||
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
|
||||
["a", "b", "c"].keys() // iterator 0, 1, 2
|
||||
["a", "b", "c"].values() // iterator "a", "b", "c"
|
||||
|
||||
// Object
|
||||
Object.assign(Point, { origin: new Point(0,0) })
|
||||
```
|
||||
|
||||
## Proxies
|
||||
|
||||
The Proxy object is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).
|
||||
|
||||
See more examples at [MDN Proxy doc]([https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy))
|
||||
|
||||
**No-op forwarding proxy**
|
||||
|
||||
``` Javascript
|
||||
var target = {};
|
||||
var p = new Proxy(target, {});
|
||||
|
||||
p.a = 37; // operation forwarded to the target
|
||||
|
||||
console.log(target.a); // 37. The operation has been properly forwarded
|
||||
```
|
||||
|
||||
**Validation**
|
||||
|
||||
```Javascript
|
||||
let validator = {
|
||||
set: function(obj, prop, value) {
|
||||
if (prop === 'age') {
|
||||
if (!Number.isInteger(value)) {
|
||||
throw new TypeError('The age is not an integer');
|
||||
}
|
||||
if (value > 200) {
|
||||
throw new RangeError('The age seems invalid');
|
||||
}
|
||||
}
|
||||
|
||||
// The default behavior to store the value
|
||||
obj[prop] = value;
|
||||
|
||||
// Indicate success
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
let person = new Proxy({}, validator);
|
||||
|
||||
person.age = 100;
|
||||
console.log(person.age); // 100
|
||||
person.age = 'young'; // Throws an exception
|
||||
person.age = 300; // Throws an exception
|
||||
```
|
||||
|
||||
## Binary and Octal Literals
|
||||
|
||||
```
|
||||
0b111110111 === 503 // true
|
||||
0o767 === 503 // true
|
||||
```
|
||||
|
||||
## その他
|
||||
|
||||
### Reflect API
|
||||
|
||||
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect)
|
||||
|
||||
### Tail calls
|
||||
|
||||
```Javascript
|
||||
function factorial(n, acc = 1) {
|
||||
'use strict';
|
||||
if (n <= 1) return acc;
|
||||
return factorial(n - 1, n * acc);
|
||||
}
|
||||
|
||||
// Stack overflow in most implementations today,
|
||||
// but safe on arbitrary inputs in ES6
|
||||
factorial(100000)
|
||||
```
|
||||
|
||||
### Unicode
|
||||
|
||||
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode)
|
||||
@@ -0,0 +1,111 @@
|
||||
---
|
||||
title: VueのNavigation Guards
|
||||
date: 2019-01-25 09:00:00
|
||||
tags:
|
||||
- JavaScript
|
||||
- Vue.js
|
||||
category: tech
|
||||
keywords:
|
||||
- Vue.js
|
||||
- Javascript
|
||||
- ES2015
|
||||
- ES6
|
||||
---
|
||||
|
||||
Navigation guards are provided by `vue-router`.
|
||||
Three ways to hook:
|
||||
* globally
|
||||
* per-route
|
||||
* in-component
|
||||
|
||||
__NOTE:__
|
||||
1. Params or query changes won't trigger enter/leave navigation guards. You can either watch the `$route` object to react to those changes, or use the `beforeRouteUpdate` in-component guard.
|
||||
2. Make sure to always call the next function, otherwise the hook will never be resolved.
|
||||
|
||||
## Global
|
||||
|
||||
```Javascript
|
||||
const router = new VueRouter({ ... })
|
||||
|
||||
// Before Guards
|
||||
router.beforeEach((to, from, next) => {
|
||||
// ...
|
||||
})
|
||||
|
||||
// Resolve Guards
|
||||
// beforeResolve guards will be called right before the navigation is confirmed
|
||||
// after all in-component guards and async route components are resolved
|
||||
router.beforeResolve((to, from, next) => {
|
||||
// ...
|
||||
})
|
||||
|
||||
// After Hooks
|
||||
router.afterEach((to, from) => {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
## Pre-reoute
|
||||
|
||||
```Javascript
|
||||
const router = new VueRouter({
|
||||
routes: [
|
||||
{
|
||||
path: '/foo',
|
||||
component: Foo,
|
||||
beforeEnter: (to, from, next) => {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
## In-component
|
||||
|
||||
```Javascript
|
||||
const Foo = {
|
||||
template: `...`,
|
||||
beforeRouteEnter (to, from, next) {
|
||||
// called before the route that renders this component is confirmed.
|
||||
// does NOT have access to `this` component instance,
|
||||
// because it has not been created yet when this guard is called!
|
||||
// However, you can access the instance by passing a callback to next.
|
||||
// The callback will be called when the navigation is confirmed
|
||||
// and the component instance will be passed to the callback as the argument
|
||||
beforeRouteEnter (to, from, next) {
|
||||
next(vm => {
|
||||
// access to component instance via `vm`
|
||||
})
|
||||
}
|
||||
},
|
||||
beforeRouteUpdate (to, from, next) {
|
||||
// called when the route that renders this component has changed,
|
||||
// but this component is reused in the new route.
|
||||
// For example, for a route with dynamic params `/foo/:id`, when we
|
||||
// navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance
|
||||
// will be reused, and this hook will be called when that happens.
|
||||
// has access to `this` component instance.
|
||||
},
|
||||
beforeRouteLeave (to, from, next) {
|
||||
// called when the route that renders this component is about to
|
||||
// be navigated away from.
|
||||
// has access to `this` component instance.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Resolve flow
|
||||
|
||||
+ Navigation triggered.
|
||||
+ Call leave guards in deactivated components.
|
||||
+ Call global beforeEach guards.
|
||||
+ Call beforeRouteUpdate guards in reused components.
|
||||
+ Call beforeEnter in route configs.
|
||||
+ Resolve async route components.
|
||||
+ Call beforeRouteEnter in activated components.
|
||||
+ Call global beforeResolve guards.
|
||||
+ Navigation confirmed.
|
||||
+ Call global afterEach hooks.
|
||||
+ DOM updates triggered.
|
||||
+ Call callbacks passed to next in beforeRouteEnter guards with instantiated instances.
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: Object.assign() with accessor descriptor
|
||||
date: 2019-03-08 09:00:00
|
||||
tags:
|
||||
- JavaScript
|
||||
category: tech
|
||||
keywords:
|
||||
- Javascript
|
||||
- ES2015
|
||||
- ES6
|
||||
---
|
||||
[MDN docs:](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Copying_accessors)
|
||||
>The Object.assign() method only copies enumerable and own properties from a source object to a target object. It uses [[Get]] on the source and [[Set]] on the target, so it will invoke getters and setters. Therefore it assigns properties versus just copying or defining new properties. This may make it unsuitable for merging new properties into a prototype if the merge sources contain getters.
|
||||
|
||||
For example
|
||||
|
||||
```js
|
||||
class Cat {
|
||||
constructor(name) {
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this._name;
|
||||
}
|
||||
set name(value) {
|
||||
this._name = value;
|
||||
}
|
||||
}
|
||||
|
||||
let nyannko = new Cat("nyannko");
|
||||
let copy = Object.assign({}, nyannko)
|
||||
|
||||
console.log(nyannko.name) // nyannko
|
||||
console.log(copy.name) // undefined
|
||||
```
|
||||
|
||||
The `name` property is lost.
|
||||
|
||||
<!--more-->
|
||||
|
||||
To copy accessors, we can use `Object.getOwnPropertyDescriptor()` and `Object.defineProperty()` as the MDN docs recommend:
|
||||
|
||||
```js
|
||||
var obj = {
|
||||
foo: 1,
|
||||
get bar() {
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
|
||||
var copy = Object.assign({}, obj);
|
||||
console.log(copy);
|
||||
// { foo: 1, bar: 2 }, the value of copy.bar is obj.bar's getter's return value.
|
||||
|
||||
// This is an assign function that copies full descriptors
|
||||
function completeAssign(target, ...sources) {
|
||||
sources.forEach(source => {
|
||||
let descriptors = Object.keys(source).reduce((descriptors, key) => {
|
||||
descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
|
||||
return descriptors;
|
||||
}, {});
|
||||
// by default, Object.assign copies enumerable Symbols too
|
||||
Object.getOwnPropertySymbols(source).forEach(sym => {
|
||||
let descriptor = Object.getOwnPropertyDescriptor(source, sym);
|
||||
if (descriptor.enumerable) {
|
||||
descriptors[sym] = descriptor;
|
||||
}
|
||||
});
|
||||
Object.defineProperties(target, descriptors);
|
||||
});
|
||||
return target;
|
||||
}
|
||||
|
||||
var copy = completeAssign({}, obj);
|
||||
console.log(copy);
|
||||
// { foo:1, get bar() { return 2 } }
|
||||
```
|
||||
|
||||
The other way is `Object.prototype.__proto__` (but **not recommended**):
|
||||
|
||||
```js
|
||||
let completeCopy = Object.assign({__proto__: nyannko.__proto__}, nyannko);
|
||||
console.log(completeCopy.name); // nyannko
|
||||
```
|
||||
|
||||
`Object.prototype.__proto__` is deprecated so be aware that this may cease to work at any time.
|
||||
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto)
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: Bind specific arguments of a function
|
||||
date: 2019-03-08 09:00:00
|
||||
tags:
|
||||
- JavaScript
|
||||
category: tech
|
||||
keywords:
|
||||
- Javascript
|
||||
- ES2015
|
||||
- ES6
|
||||
---
|
||||
|
||||
To bind specific (nth) arguments of a function, we can write a decorator instead of using `Function.bind()`:
|
||||
|
||||
```js
|
||||
function func(p1, p2, p3) {
|
||||
console.log(p1, p2, p3);
|
||||
}
|
||||
// the binding starts after however many are passed in.
|
||||
function decorator(...bound_args) {
|
||||
return function(...args) {
|
||||
return func(...args, ...bound_args);
|
||||
};
|
||||
}
|
||||
|
||||
// bind the last parameter
|
||||
let f = decorator("3");
|
||||
f("a", "b"); // a b 3
|
||||
|
||||
// bind the last two parameter
|
||||
let f2 = decorator("2", "3")
|
||||
f2("a"); // a 2 3
|
||||
```
|
||||
|
||||
Even if we want to bind just the nth argument, we can do as follows:
|
||||
|
||||
```js
|
||||
// bind a specific (nth) argument
|
||||
function decoratorN(n, bound_arg) {
|
||||
return function(...args) {
|
||||
args[n-1] = bound_arg;
|
||||
return func(...args);
|
||||
}
|
||||
}
|
||||
|
||||
let fN = decoratorN(2, "2");
|
||||
fN("a","b","c"); // a 2 c
|
||||
```
|
||||
|
||||
[https://stackoverflow.com/questions/27699493/javascript-partially-applied-function-how-to-bind-only-the-2nd-parameter](https://stackoverflow.com/questions/27699493/javascript-partially-applied-function-how-to-bind-only-the-2nd-parameter)
|
||||
@@ -0,0 +1,153 @@
|
||||
---
|
||||
title: Js tips I can't remember
|
||||
date: 2019-03-22 09:00:00
|
||||
tags:
|
||||
- JavaScript
|
||||
category: tech
|
||||
keywords:
|
||||
- Javascript
|
||||
- ES2015
|
||||
- ES6
|
||||
---
|
||||
|
||||
## `__proto__` VS `prototype`
|
||||
|
||||
>`__proto__` is the actual object that is used in the lookup chain to resolve methods and others. `prototype` is the object that is used to build `__proto__` when creating an object with `new`.
|
||||
|
||||
>The "cool kids" in JavaScript would generally pronounce `__proto__` as "**dunder proto**".
|
||||
|
||||
[https://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript](https://stackoverflow.com/questions/9959727/proto-vs-prototype-in-javascript)
|
||||
|
||||
```javascript
|
||||
( new Foo ).__proto__ === Foo.prototype; // true
|
||||
( new Foo ).prototype === undefined; // true
|
||||
```
|
||||
<!--more-->
|
||||
|
||||
## `{}` VS `Object.create(null)`
|
||||
|
||||
`Object.create(null)` can create a *'pure'* empty object that is without the delegation to `Object.prototype`.
|
||||
```javascript
|
||||
let obj1 = {};
|
||||
let obj2 = Object.create(null);
|
||||
console.log(obj1.__proto__); // {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
|
||||
console.log(obj2.__proto__); // undefined
|
||||
```
|
||||
|
||||
## Computed Property Names
|
||||
|
||||
ES6 adds computed property names, which is helpful when declaring objects using the object-literal syntax.
|
||||
```javascript
|
||||
var prefix = "foo";
|
||||
|
||||
var myObject = {
|
||||
[prefix + "bar"]: "hello",
|
||||
[prefix + "baz"]: "world"
|
||||
};
|
||||
|
||||
myObject["foobar"]; // hello
|
||||
myObject["foobaz"]; // world
|
||||
```
|
||||
|
||||
## Dangerous `Function.prototype.name` comparison
|
||||
|
||||
Sometimes we may spectify a function by comparing the function name:
|
||||
```javascript
|
||||
function Foo() {};
|
||||
let foo = new Foo();
|
||||
|
||||
if (foo.constructor.name === 'Foo') {
|
||||
console.log("'foo' is an instance of 'Foo'");
|
||||
} else {
|
||||
console.log('Oops!');
|
||||
}
|
||||
```
|
||||
|
||||
It may behave unexpectedly after building because most build tools compress the code to forms like:
|
||||
```javascript
|
||||
function a() {};
|
||||
let b = new a();
|
||||
if (b.constructor.name === 'Foo') {
|
||||
console.log("'foo' is an instance of 'Foo'");
|
||||
} else {
|
||||
console.log('Oops!');
|
||||
}
|
||||
```
|
||||
|
||||
## A more robust way of performing object property check
|
||||
|
||||
Normally we use `hasOwnProperty()` checks to see if object has the property or not. But consider objects created by:
|
||||
```javascript
|
||||
// this will not consult the [[Prototype]] chain
|
||||
let obj = Object.create(null)
|
||||
```
|
||||
Such object does not link to `Object.prototype`, thus `obj.hasOwnProperty(...)` would fail and raise error.
|
||||
|
||||
We can use a more robust way to perform property check:
|
||||
```javascript
|
||||
Object.prototype.hasOwnProperty.call(obj,"a") //false
|
||||
```
|
||||
Or:
|
||||
```javascript
|
||||
// this will check the current object or any higher level of the [[Prototype]] chain
|
||||
"a" in obj
|
||||
```
|
||||
|
||||
## `for...in` vs `for...of`
|
||||
|
||||
The `for..in` loop iterates over the list of enumerable properties on an object (including its `[[Prototype]]` chain).The `for...of` iterate over the values directly instead of the array indices (or object properties). It asks built-in or custom `@@iterator` of the thing to be iterated.
|
||||
```javascript
|
||||
let arr = ['a', 'b', 'c']
|
||||
arr.name = 'foo'
|
||||
|
||||
arr // ["a", "b", "c", name: "foo"]
|
||||
|
||||
for (key in arr)
|
||||
console.log(key) // 0 1 2 name
|
||||
|
||||
for (value of arr)
|
||||
console.log(value) // a b c
|
||||
```
|
||||
|
||||
## "(Prototypal) Inheritance"
|
||||
|
||||
When writing "prototype style" code like implementing Parent-Child class inheritance:
|
||||
```javascript
|
||||
function Foo(name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
Foo.prototype.myName = function() {
|
||||
return this.name;
|
||||
};
|
||||
|
||||
function Bar(name,label) {
|
||||
Foo.call( this, name );
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
// Then trying to build a family with Foo and Bar
|
||||
// ...
|
||||
```
|
||||
A common mis-conception/confusion here is that either of the following approaches would also work, but they do not work as you'd expect:
|
||||
```javascript
|
||||
// doesn't work like you want!
|
||||
Bar.prototype = Foo.prototype;
|
||||
|
||||
// works kinda like you want, but with
|
||||
// side-effects you probably don't want :(
|
||||
Bar.prototype = new Foo();
|
||||
```
|
||||
|
||||
the right way is:
|
||||
```javascript
|
||||
// "make a new 'Bar dot prototype' object that's linked to 'Foo dot prototype'."
|
||||
// pre-ES6
|
||||
// throws away default existing `Bar.prototype`
|
||||
Bar.prototype = Object.create( Foo.prototype );
|
||||
|
||||
// ES6+
|
||||
// modifies existing `Bar.prototype`
|
||||
Object.setPrototypeOf( Bar.prototype, Foo.prototype );
|
||||
```
|
||||
**reference**: [You-Dont-Know-JS: "(Prototypal) Inheritance"](https://github.com/getify/You-Dont-Know-JS/blob/master/this%20%26%20object%20prototypes/ch5.md#prototypal-inheritance)
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: JavaScript Comparison operation at a glance
|
||||
date: 2019-03-28 09:00:00
|
||||
tags:
|
||||
- JavaScript
|
||||
category: tech
|
||||
keywords:
|
||||
- Javascript
|
||||
- ES2015
|
||||
- ES6
|
||||
---
|
||||
|
||||
When given a scenario like:
|
||||
|
||||
```javascript
|
||||
console.log(null > -1) //true
|
||||
```
|
||||
|
||||
It produces `true`, which makes me think `null` is treated as `0`. But when I run:
|
||||
```javascript
|
||||
console.log(null == 0) // false
|
||||
console.log(null > 0) // false
|
||||
console.log(null < 0) // false
|
||||
```
|
||||
They all output `false`!
|
||||
|
||||
I googled a lot and finally found answers in [Ecma-262 Specification](http://www.ecma-international.org/ecma-262/8.0/#sec-abstract-equality-comparison).
|
||||
|
||||
The comparison `x == y`, where x and y are values, produces true or false. Such a comparison is performed as follows:
|
||||
<!-- more -->
|
||||
```text
|
||||
1. If Type(x) is the same as Type(y), then return the result of performing Strict Equality Comparison x === y.
|
||||
2. If x is null and y is undefined, return true.
|
||||
3. If x is undefined and y is null, return true.
|
||||
4. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
|
||||
5. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.
|
||||
6. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
|
||||
7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
|
||||
8. If Type(x) is either String, Number, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
|
||||
9. If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.
|
||||
10. Return false.
|
||||
```
|
||||
|
||||
Relational comparison is much more complex so I'm not copying that section. Read at the [spec website](http://www.ecma-international.org/ecma-262/8.0/#sec-abstract-relational-comparison).
|
||||
|
||||
## TL;DR
|
||||
|
||||
Anyway it seems that in `null == 0`, `null` is treated just as is, and **equality** comparison between `null` and `Number` always return **false** (No 10).
|
||||
But when it comes `null > -1`, `null` is conversed to 0 using [ToNumber()](http://www.ecma-international.org/ecma-262/8.0/#sec-tonumber) algorithm.
|
||||
|
||||
Read more:
|
||||
* [https://github.com/getify/You-Dont-Know-JS/issues/1238](https://github.com/getify/You-Dont-Know-JS/issues/1238)
|
||||
* [http://www.ecma-international.org/ecma-262/8.0/#sec-abstract-relational-comparison](http://www.ecma-international.org/ecma-262/8.0/#sec-abstract-relational-comparison)
|
||||
* [http://www.ecma-international.org/ecma-262/8.0/#sec-tonumber](http://www.ecma-international.org/ecma-262/8.0/#sec-tonumber)
|
||||
@@ -0,0 +1,208 @@
|
||||
---
|
||||
title: uipath ノート(一)
|
||||
date: 2019-10-27 09:00:00
|
||||
tags:
|
||||
- RPA
|
||||
categories:
|
||||
- notes
|
||||
keywords:
|
||||
- RPA
|
||||
- uipath
|
||||
---
|
||||
|
||||
## 変数
|
||||
|
||||
* Int32
|
||||
* String
|
||||
* Boolean
|
||||
* GenericValue [参照](https://docs.uipath.com/studio/lang-ja/docs/genericvalue-variables)
|
||||
|
||||
>UiPath Studio には GenericValue 変数の自動変換メカニズムがあり、式を正しく定義することで、目的の結果を得ることができます。式の最初の要素は、Studio から操作するガイドラインとして使用されることを考慮してください。例えば、2 つの GenericValue 変数を追加し、式の最初の変数が String として定義されてた場合、最初に代入された値を元に 2 つの値を結合し、文字列となります。 Integer として定義した場合の結果は、合計の値となります。
|
||||
|
||||
* Array
|
||||
* DataTime
|
||||
* DataTable
|
||||
|
||||
[.Net 変数型を参照して探す方法](https://docs.uipath.com/studio/lang-ja/docs/managing-variables#section-browsing-for-net-variable-types)
|
||||
|
||||
## アクティビティ
|
||||
|
||||
### レイアウトダイアグラム
|
||||
|
||||
* シーエンス: より高度で複雑な自動化に向こう
|
||||
* フローチャート: シンプルな自動化プロジェクトに適している
|
||||
* ステートマシン
|
||||
* Global Exception Handler (グローバル例外ハンドラー)
|
||||
|
||||
>参照:[https://docs.uipath.com/studio/lang-ja/docs/workflow-design](https://docs.uipath.com/studio/lang-ja/docs/workflow-design)
|
||||
|
||||
### 選択肢
|
||||
|
||||
* 条件分岐(If): シーケンス用条件分岐
|
||||
* フロー条件分岐(flow desicion): フローチャート用条件分岐
|
||||
* フロースイッチ (Flow Switch)
|
||||
|
||||
### 繰り返し
|
||||
|
||||
* 繰り返し(前判定)(While)
|
||||
* 繰り返し(後判定)(Do While)
|
||||
* 繰り返し(コレクションの各要素)(For Each)
|
||||
|
||||
### データ操作
|
||||
|
||||
* CSVを読み込み(Read CSV)
|
||||
- オプションにエンコーディングを指定できる(日本語データがある場合、`"SHIFT-JIS"`を指定する)
|
||||
- 列名を含める(IncludeColumnNames)を指定できる
|
||||
- 出力タイプ:`System.Data.DataTable` [Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.data.datatable?view=netframework-4.8)
|
||||
- `Select`メソッドで半角スペースを含む列名で指定する場合、半角スペースが特殊文字ではなく文字列として認識されるため、`[]`で列名を囲う
|
||||
|
||||
```
|
||||
Names.Select("[メンバー ステータス] = 'Yes'")
|
||||
```
|
||||
|
||||
### その他
|
||||
|
||||
* メッセージボックス
|
||||
* 代入(assign)
|
||||
* 待機 (Delay)
|
||||
* 入力ダイアログ(input dialog):
|
||||
* フォルダーを選択(Select Folder)
|
||||
* 一行を書き込み(Write Line): Debug用?
|
||||
* アプリケーションを開く(Open Application)
|
||||
* ブラウザを開く(Open Browser)
|
||||
* ブラウザーにアタッチ(Attach Browser)
|
||||
* 文字を入力(Type Into)
|
||||
* ハイライト(Highlight)
|
||||
* [アンカーベース(Anchor Base)](#アンカーベース-Anchor-Base)
|
||||
* スクリーンショットをと撮る(Take Screenshot)
|
||||
* 要素の有無を検出(Element Exists)
|
||||
* 要素の消滅を待つ(Wait Element Vanish)
|
||||
|
||||
## レコーディング機能
|
||||
|
||||
<span style="color: green">\[○\]レコーディング可</span>
|
||||
* 左クリック
|
||||
- ボタン
|
||||
- チェックボックス
|
||||
- ドロップダウン
|
||||
- ...
|
||||
* 文字入力
|
||||
|
||||
<span style="color: red">\[×\]レコーディング不可</span>
|
||||
* ショットカットキー
|
||||
* 修飾キー(`ctrl + c`など)
|
||||
* 右クリック
|
||||
* マウスホバー
|
||||
|
||||
**ショットカット**
|
||||
* F2 - 時間差で選択
|
||||
* F3 - 領域を選択
|
||||
|
||||
### ベーシック
|
||||
|
||||
* 適する操作内容:複数ウィンドウ上の単一の操作
|
||||
* 生成されるワークフローがシンプル
|
||||
|
||||
### デスクトップ
|
||||
|
||||
* 適する操作内容:同一ウィンドウ上の連続した操作
|
||||
* 生成されるワークフローが複雑
|
||||
* セレクターの保守性が良い
|
||||
|
||||
### ウェブ
|
||||
|
||||
ウェブアプリとブラウザーでレコーディングを行うためのものです。コンテナーを生成し、既定で 入力をシミュレート (Simulate Type)/クリック (Click) の入力メソッドを使用します。
|
||||
|
||||
## 入力/出力方法まとめ
|
||||
|
||||
### 入力
|
||||
|
||||
* デフォルト(Default)
|
||||
- 互換性が高い
|
||||
- キーボード対応
|
||||
* ウィンドウメッセージ(Windows Message)
|
||||
- バックグラウンド処理
|
||||
- キーボード対応
|
||||
* シミュレート(Simulate Type/Click)
|
||||
- 互換性が低い
|
||||
- バックグラウンド処理
|
||||
- フィールド内自動削除
|
||||
|
||||
### 出力
|
||||
|
||||
* フルテキスト(FullText)
|
||||
- スピード:★★★★★
|
||||
- 正確さ:100%
|
||||
- バックグラウンド処理
|
||||
- 非表示の項目を取得
|
||||
* ネイティブ(Native)
|
||||
- スピード:★★★★
|
||||
- 正確さ:100%
|
||||
- 文字情報を取得
|
||||
* OCR
|
||||
- スピード:★★
|
||||
- 正確さ:98%(?)
|
||||
- 文字情報を取得
|
||||
- CITRIX対応
|
||||
|
||||
## セレクター
|
||||
|
||||
* 部分セレクター
|
||||
* 完全セレクター
|
||||
* 動的なセレクター:ワイルドカードを使ってる
|
||||
* アンカーベース(Anchor Base)
|
||||
* 相対的なアンカー(Select Relative Element)
|
||||
|
||||
>セレクターがあまり安定しないと考えられるときには、[Anchor Base(アンカーベース)] アクティビティや、UiPath Explorer の [Select Relative Element(相対的なアンカーを選択してください)] を使用することで、信頼性の高い自動化を構築できる場合があります。
|
||||
|
||||
### アンカーベース(Anchor Base)
|
||||
|
||||
アンカーとして使用できるアクティビティ:
|
||||
* 要素を探す(Find Element)
|
||||
* 画像を探す(Find Image)
|
||||
|
||||
## EXCEL操作
|
||||
|
||||
* EXCELアプリケーションスコープ(Excel Application Scope)
|
||||
* 範囲を追加(Append Range)
|
||||
* データテーブルを並べ替え(Sort Data Table)
|
||||
* データテーブルをフィルタリング(Filter Data Table)
|
||||
* Build Data Table
|
||||
* Generate Data Table
|
||||
* Output Data Table
|
||||
|
||||
## PDF操作
|
||||
|
||||
PDF Activities Pack
|
||||
`UiPath.PDF.Activities`
|
||||
|
||||
* PDFのテキストを読み込み(Read PDf Text)
|
||||
- 範囲(Range):ページ範囲を指定する。ディフォルトは`"ALL"`。例: `1` `3-5`
|
||||
* OCRでPDFを読み込み(Read PDF With OCR)
|
||||
* 画面スクレイピング(Screen Scraping)も適用
|
||||
|
||||
## MAIL操作
|
||||
|
||||
MailMessageの型:
|
||||
```
|
||||
System.Net.Mail.MailMessage
|
||||
System.Web.Mail.MailMessage
|
||||
```
|
||||
|
||||
* SMTP
|
||||
* POP3
|
||||
* IMAP
|
||||
* Outlook
|
||||
* Exchange
|
||||
* IBM Notes
|
||||
|
||||
## Debug
|
||||
|
||||
* トライキャッチ(Try Catch)
|
||||
* メッセージをログ(Log Message)
|
||||
- Critical
|
||||
- Error
|
||||
- Warning
|
||||
- Information
|
||||
- Trace
|
||||
- Verbose: Traceと同じレベルだが、アクティビティ start および end の両方のメッセージと、使用される変数および引数の値を記録する
|
||||
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: JavaScript URI エンコーディング
|
||||
date: 2019-11-10 09:00:00
|
||||
tags:
|
||||
- JavaScript
|
||||
categories:
|
||||
- notes
|
||||
keywords:
|
||||
- Javascript
|
||||
- encodeURIComponent
|
||||
---
|
||||
|
||||
## まとめ
|
||||
|
||||
`encodeURI()`と`encodeURIComponent()`はRFC 2396準拠である。
|
||||
`encodeURI()` は完全な URI を表すのに必要な文字 (Reserved Characters) はエンコードしません。
|
||||
また、予約されていないが "そのまま" URI に使用できる(Unreserved Marks) 文字をエンコードしません。
|
||||
`encodeURIComponent()` は "Unreserved Marks" 文字をエンコードしません。
|
||||
|
||||
```JavaScript
|
||||
var set1 = ";,/?:@&=+$#"; // Reserved Characters
|
||||
var set2 = "-_.!~*'()"; // Unreserved Marks
|
||||
|
||||
console.log(encodeURI(set1)); // ;,/?:@&=+$
|
||||
console.log(encodeURI(set2)); // -_.!~*'()
|
||||
|
||||
console.log(encodeURIComponent(set1)); // %3B%2C%2F%3F%3A%40%26%3D%2B%24
|
||||
console.log(encodeURIComponent(set2)); // -_.!~*'()
|
||||
```
|
||||
|
||||
## rfc2396 appendix-A
|
||||
|
||||
[https://tools.ietf.org/html/rfc2396#appendix-A](https://tools.ietf.org/html/rfc2396#appendix-A)
|
||||
```
|
||||
URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
|
||||
absoluteURI = scheme ":" ( hier_part | opaque_part )
|
||||
relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
|
||||
|
||||
hier_part = ( net_path | abs_path ) [ "?" query ]
|
||||
opaque_part = uric_no_slash *uric
|
||||
|
||||
uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
|
||||
"&" | "=" | "+" | "$" | ","
|
||||
|
||||
net_path = "//" authority [ abs_path ]
|
||||
abs_path = "/" path_segments
|
||||
rel_path = rel_segment [ abs_path ]
|
||||
|
||||
rel_segment = 1*( unreserved | escaped |
|
||||
";" | "@" | "&" | "=" | "+" | "$" | "," )
|
||||
|
||||
scheme = alpha *( alpha | digit | "+" | "-" | "." )
|
||||
|
||||
authority = server | reg_name
|
||||
|
||||
reg_name = 1*( unreserved | escaped | "$" | "," |
|
||||
";" | ":" | "@" | "&" | "=" | "+" )
|
||||
|
||||
server = [ [ userinfo "@" ] hostport ]
|
||||
userinfo = *( unreserved | escaped |
|
||||
";" | ":" | "&" | "=" | "+" | "$" | "," )
|
||||
|
||||
hostport = host [ ":" port ]
|
||||
host = hostname | IPv4address
|
||||
hostname = *( domainlabel "." ) toplabel [ "." ]
|
||||
domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
|
||||
toplabel = alpha | alpha *( alphanum | "-" ) alphanum
|
||||
IPv4address = 1*digit "." 1*digit "." 1*digit "." 1*digit
|
||||
port = *digit
|
||||
|
||||
path = [ abs_path | opaque_part ]
|
||||
path_segments = segment *( "/" segment )
|
||||
segment = *pchar *( ";" param )
|
||||
param = *pchar
|
||||
pchar = unreserved | escaped |
|
||||
":" | "@" | "&" | "=" | "+" | "$" | ","
|
||||
|
||||
query = *uric
|
||||
|
||||
fragment = *uric
|
||||
|
||||
uric = reserved | unreserved | escaped
|
||||
reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
|
||||
"$" | ","
|
||||
unreserved = alphanum | mark
|
||||
mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
|
||||
"(" | ")"
|
||||
|
||||
escaped = "%" hex hex
|
||||
hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
|
||||
"a" | "b" | "c" | "d" | "e" | "f"
|
||||
|
||||
alphanum = alpha | digit
|
||||
alpha = lowalpha | upalpha
|
||||
|
||||
lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
|
||||
"j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
|
||||
"s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
|
||||
upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" |
|
||||
"J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" |
|
||||
"S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
|
||||
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" |
|
||||
"8" | "9"
|
||||
```
|
||||
|
||||
## 参考
|
||||
|
||||
[https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent)
|
||||
|
||||
[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI)
|
||||
|
||||
[https://qiita.com/aosho235/items/0581fc82f8ce2c5ac055](https://qiita.com/aosho235/items/0581fc82f8ce2c5ac055)
|
||||
|
||||
[https://tools.ietf.org/html/rfc2396](https://tools.ietf.org/html/rfc2396)
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: uipath ノート(二)- Best Practice
|
||||
date: 2019-11-18 09:00:00
|
||||
tags:
|
||||
- RPA
|
||||
categories:
|
||||
- notes
|
||||
keywords:
|
||||
- RPA
|
||||
- uipath
|
||||
---
|
||||
|
||||
## 時間をかけて各ワークフローに適したレイアウトを選択しましょう。
|
||||
|
||||
* メイン: フローチャートまたはステートマシン
|
||||
|
||||
* ビジネスロジック: フローチャート
|
||||
|
||||
* UI インタラクション: シーケンス
|
||||
|
||||
* フローチャートを使用することで、入れ子状の IF を回避
|
||||
|
||||
## プロセスを小さなワークフローに分割しましょう。
|
||||
|
||||
* 分割したものを個別に開発、テスト
|
||||
|
||||
* ワークフローの再利用
|
||||
|
||||
* 分割ファイルで作業することによる、より効率的な共同作業
|
||||
|
||||
## 必ず例外を処理しましょう。
|
||||
|
||||
* 例外が発生しやすいワークフローを [Try Catch (トライキャッチ)] ブロックに配置
|
||||
|
||||
* 外部から呼び出されたワークフローでも同様
|
||||
|
||||
* リカバリシーケンスの設定
|
||||
|
||||
## ワークフローを読みやすいものにしましょう。
|
||||
|
||||
* すべてのコンポーネントにわかりやすい名前を選択
|
||||
|
||||
* 注記やコメントの使用
|
||||
|
||||
* リアルタイムの実行状況のログ取得
|
||||
|
||||
* 環境設定を Config ファイルに格納
|
||||
|
||||
## 不要になったアプリケーションを終了させることで、常にクリーンな状態を維持しましょう。
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: uipath ノート(三)- uipath orchestrator
|
||||
date: 2019-11-23 09:00:00
|
||||
tags:
|
||||
- RPA
|
||||
categories:
|
||||
- notes
|
||||
keywords:
|
||||
- RPA
|
||||
- uipath
|
||||
---
|
||||
|
||||
## 利用手順
|
||||
|
||||
Official doc: [https://docs.uipath.com/robot/docs/from-orchestrator-and-the-orchestrator-settings-window](https://docs.uipath.com/robot/docs/from-orchestrator-and-the-orchestrator-settings-window)
|
||||
|
||||
### マシンを追加
|
||||
|
||||
マシン側でマシン名とユーザ名を確認
|
||||
|
||||
```
|
||||
C:\Users\user>hostname
|
||||
DESKTOP-ABCDE5F
|
||||
|
||||
C:\Users\user>whoami
|
||||
desktop-abcde5f\user
|
||||
|
||||
C:\Users\user>
|
||||
```
|
||||
|
||||
追加完了後、マシンキーを取得する。
|
||||
|
||||
### ロボットを登録
|
||||
|
||||
* Type: studio (開発用?)
|
||||
* Domain/Username: 上記のユーザ名
|
||||
|
||||
### ローカルのorchestrator設定
|
||||
|
||||
Uipath Robotを開き⇒orchestratorの設定で、上記のマシンキーを入力する。
|
||||
orchestrator URLに `https://platform.uipath.com/` を入力する.
|
||||
`Invalid machine key`というエラーが出たら、下記のようなURLを試す:
|
||||
```
|
||||
https://platform.uipath.com/<account name>/<service name>
|
||||
```
|
||||
|
||||
参照:[Uipath orchestrator error : invalid machine key](https://forum.uipath.com/t/uipath-orchestrator-error-invalid-machine-key/153438/16)
|
||||
|
||||
### ロボットグループ(Environment)作成
|
||||
|
||||
### プロジェクトをパブリッシュ(Publish)
|
||||
|
||||
### プロセスを追加
|
||||
|
||||
Automations ⇒ Processes
|
||||
|
||||
### ジョブ(Jobs)の実行
|
||||
|
||||
Monitoring ⇒ Jobs
|
||||
|
||||
## その他
|
||||
|
||||
### 再パブリッシュすると、Processが最新バージョンを使うため、変更作業が必要
|
||||
|
||||
Processes ⇒ More Options ⇒ View Process ⇒ 最新のバージョンに切り替える
|
||||
|
||||
### ジョブの停止
|
||||
|
||||
* 停止(Stop):必ずワークフロー内で「停止すべきか確認(Should Stop)」アクティビティを使用する
|
||||
* 強制終了(Kill):処理中の内容に関わらず、ジョブを停止する
|
||||
|
||||
### アクティブなジョブは削除できない
|
||||
|
||||
### パラメーター変更の優先順位
|
||||
|
||||
ジョブ (Jobs) -> プロセス (Processes) -> パッケージ(UiPath Studio)
|
||||
|
||||
### マシンテンプレート
|
||||
|
||||
Machine Templates only work for Active Directory users, Attended Floating Robots and Studio Floating Robots.
|
||||
@@ -0,0 +1,646 @@
|
||||
---
|
||||
title: Standard ML notes
|
||||
date: 2019-12-30 09:00:00
|
||||
tags:
|
||||
- SML
|
||||
categories:
|
||||
- notes
|
||||
keywords:
|
||||
- SML
|
||||
---
|
||||
|
||||
## Basics
|
||||
|
||||
### Comments
|
||||
|
||||
```ML
|
||||
(* SML comment *)
|
||||
```
|
||||
|
||||
### Variable bindings and Expressions
|
||||
|
||||
```ML
|
||||
val x = 34;
|
||||
(* static environment: x : int *)
|
||||
(* dynamic environment: x --> 34 *)
|
||||
val y = x + 1;
|
||||
|
||||
(* Use tilde character instead of minus to reprsent negation *)
|
||||
val z = ~1;
|
||||
|
||||
(* Integer Division *)
|
||||
val w = y div x
|
||||
```
|
||||
|
||||
Strings:
|
||||
|
||||
```ML
|
||||
(* `\n`のようなエスケープシーケンスが利用できる *)
|
||||
val x = "hello\n";
|
||||
(* 文字列の連結には'^'を使う *)
|
||||
val y = "hello " ^ "world";
|
||||
```
|
||||
|
||||
An ML program is a sequence of bindings. Each binding gets **type-checked** and then **evaluated**.
|
||||
What type a binding has depends on a static environment. How a binding is evaluated depends on a dynamic environment.
|
||||
Sometimes we use just `environment` to mean dynamic environment and use `context` as a synonym for static environment.
|
||||
|
||||
* Syntaxs : How to write it.
|
||||
* Semantics: How it type-checks and evaluates
|
||||
* Value: an expression that has no more computation to do
|
||||
|
||||
### Shadowing
|
||||
|
||||
**Bindings are immutable** in SML. Given `val x = 8 + 9;` we produce a dynamic environment where x maps to 17.
|
||||
In this environment x will always map to 17; there is no "assignment statement" in ML for changing what x maps to.
|
||||
You can have another binding later, say `val x = 19;`, but that just creates a differnt environment
|
||||
where the later binding for x **shadows** the earlier one.
|
||||
|
||||
### Function Bindings
|
||||
|
||||
```ML
|
||||
fun pow (x:int, y:int) = (* correct only for y >= 0 *)
|
||||
if y = 0
|
||||
then 1
|
||||
else x * pow(x, y-1);
|
||||
|
||||
fun cube (x : int) =
|
||||
pow(x, 3);
|
||||
|
||||
val ans = cube(4);
|
||||
(* The parentheses are not necessary if there is only one argument
|
||||
val ans = cube 4; *)
|
||||
```
|
||||
|
||||
* Syntax: `fun x0 (x1 : t1, ..., xn : tn) = e`
|
||||
* Type-checking:
|
||||
- `t1 * ... * tn -> t`
|
||||
- The type of a function is "argument types" -> "reslut types"
|
||||
* Evaluation:
|
||||
- A function is a value
|
||||
- The environment we extends arguments with is that “was current” when the function was defined, not the one where it is being called.
|
||||
|
||||
### Pairs and other Tuples
|
||||
|
||||
```ML
|
||||
fun swap (pr : int*bool) =
|
||||
(#2 pr, #1 pr);
|
||||
|
||||
fun sum_two_pairs (pr1 : int * int, pr2 : int * int) =
|
||||
(#1 pr1) + (#2 pr1 ) + (#1 pr2) + (#2 pr2);
|
||||
|
||||
fun div_mod (x : int, y: int) =
|
||||
(x div y, x mod y);
|
||||
|
||||
fun sort_pair(pr : int * int) =
|
||||
|
||||
if (#1 pr) < (#2 pr) then
|
||||
pr
|
||||
else
|
||||
(#2 pr, #1 pr);
|
||||
```
|
||||
|
||||
ML supportstuplesby allowing any number of parts. Pairs and tuples can be nested however you want. For example, a 3-tuple (i.e., a triple) of integers has type int*int*int. An example is (7,9,11) and you retrieve the parts with #1 e, #2 e, and #3 e where e is an expression that evaluates to a triple.
|
||||
|
||||
```ML
|
||||
val a = (7, 9, 11) (* int * int * int *)
|
||||
val x = (3, (4, (5,6))); (* int * (int * (int * int)) *)
|
||||
val y = (#2 x, (#1 x, #2 (#2 x))); (* (int * (int * int)) * (int * (int * int)) *)
|
||||
val ans = (#2 y, 4); (* (int * (int * int)) * int *)
|
||||
```
|
||||
|
||||
### Lists
|
||||
|
||||
```ML
|
||||
val x = [7,8,9];
|
||||
5::x; (* 5 consed onto x *)
|
||||
6::5::x;
|
||||
[6]::[[1,2],[3,4];
|
||||
```
|
||||
|
||||
To append a list t a list, use list-append operator `@`:
|
||||
[Reference:# The Standard ML Basis Library]([http://sml-family.org/Basis/list.html](http://sml-family.org/Basis/list.html))
|
||||
>Interface:
|
||||
> **val** [@](http://sml-family.org/Basis/list.html#SIG:LIST.@:VAL) **:** _'a_ list * _'a_ list **->** _'a_ list
|
||||
|
||||
```
|
||||
val x = [1,2] @ [3,4,5]; (* [1,2,3,4,5] *)
|
||||
```
|
||||
Accessing:
|
||||
```ML
|
||||
val x = [7,8,9];
|
||||
null x; (* False *)
|
||||
null []; (* True *)
|
||||
hd x; (* 7 *)
|
||||
tl x; (* [8, 9] *)
|
||||
```
|
||||
|
||||
### List Functions
|
||||
|
||||
```ML
|
||||
fun sum_list(xs : int list) =
|
||||
if null xs
|
||||
then 0
|
||||
else hd xs + sum_list(tl xs);
|
||||
|
||||
fun list_product(xs : int list) =
|
||||
if null xs
|
||||
then 1
|
||||
else hd xs * list_product(tl xs);
|
||||
|
||||
fun countdown(x : int) =
|
||||
if x = 0
|
||||
then []
|
||||
else x :: countdown(x - 1);
|
||||
|
||||
fun append (xs : int lisst, ys : int list) =
|
||||
if null xs
|
||||
then ys
|
||||
else (hd xs) :: append((tl xs), ys);
|
||||
|
||||
fun sum_pair_list(xs : (int * int) list) =
|
||||
if null xs
|
||||
then 0
|
||||
else #1 (hd xs) + #2 (hd xs) + sum_pair_list(tl xs);
|
||||
|
||||
fun firsts (xs : (int * int) list) =
|
||||
if null xs
|
||||
then []
|
||||
else (#1 (hd xs)) :: firsts(tl xs);
|
||||
|
||||
fun seconds (xs : (int * int) list) =
|
||||
if null xs
|
||||
then []
|
||||
else (#2 (hd xs)) :: seconds(tl xs);
|
||||
|
||||
fun sum_pair_list2 (xs : (int * int) list) =
|
||||
(sum_list(firsts xs)) + (sum_list(seconds xs));
|
||||
|
||||
```
|
||||
|
||||
Functions that make and us lists are almost always recursice becasue a list has an unknown length. To write a recursive function the thought process involves two steps:
|
||||
* think about the _base case_
|
||||
* think about the _recursive case_
|
||||
|
||||
### Let Expressions
|
||||
|
||||
* Syntax: `let b1 b2 ... bn in e end`
|
||||
- Each `bi` is any binding an `e` is any expression
|
||||
|
||||
```ML
|
||||
let val x = 1
|
||||
in
|
||||
(let val x = 2 in x+1 end) + (let val y = x+2 in y+1 end)
|
||||
end
|
||||
|
||||
fun countup_from1 (x:int) =
|
||||
let fun count (from:int) =
|
||||
if from=x
|
||||
then x::[]
|
||||
else from :: count(from+1)
|
||||
in
|
||||
count(1)
|
||||
end
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
An option value has either 0 or 1 thing: `None` is an option value carrying nothing whereas `SOME e` evaluates e to a value v and becomes the option carrying the one value v. The type of `NONE` is `'a option` and the type of `SOME e` is `t option` if e has type t.
|
||||
|
||||
We have:
|
||||
* `isSome` which evaluates to false if its argument is NONE
|
||||
* `valOf` to get the value carried by `SOME`(raising exception for `NONE`)
|
||||
|
||||
```ML
|
||||
fun max1( xs : int list) =
|
||||
if null xs
|
||||
then NONE
|
||||
else
|
||||
let val tl_ans = max1(tl xs)
|
||||
in
|
||||
if isSome tl_ans andalso valOf tl_ans > hd xs
|
||||
then tl_ans
|
||||
else SOME (hd xs)
|
||||
end;
|
||||
```
|
||||
|
||||
## Some More Expressions
|
||||
|
||||
Boolean operations:
|
||||
* `e1 andalso e2`
|
||||
- if result of e1 is false then false else result of e2
|
||||
* `e1 orelse e2`
|
||||
* `not e1`
|
||||
|
||||
**※Syntax `&&` and `||` don't exist in ML and `!` means something different.**
|
||||
|
||||
**※`andalso` and `orelse` are just keywords. `not` is a pre-defined function.**
|
||||
|
||||
Comparisons:
|
||||
* `=` `<>` `>` `<` `>=` `<=`
|
||||
- `=` and `<>` can be used with any "equality type" but not with real
|
||||
|
||||
## Build New Types
|
||||
|
||||
To Create a compound type, there are really only three essential building blocks:
|
||||
|
||||
* **Each-of** : A compound type t describes values that contain each of values of type `t1` `t2` ... `tn`
|
||||
* **One-of**: A compound type t describes values that contain a value of one of the types `t1` `t2` ... `tn`
|
||||
* **Self-refenence**: A compound type t may refer to itself in its definition in order to describe recursive data structures like lists and trees.
|
||||
|
||||
### Records
|
||||
|
||||
Record types are "each-of" types where each component is a named field. The order of fields never matters.
|
||||
```ML
|
||||
val x = {bar = (1+2,true andalso true), foo = 3+4, baz = (false,9) }
|
||||
#bar x (* (3, true) *)
|
||||
```
|
||||
|
||||
Tupels are actually syntactic sugar for records. `#1 e`, `#2 e`, etc. mean: get the contents of the field named 1, 2, etc.
|
||||
```ML
|
||||
- val x = {1="a",2="b"};
|
||||
val x = ("a","b") : string * string
|
||||
- val y = {1="a", 3="b"};
|
||||
val y = {1="a",3="b"} : {1:string, 3:string}
|
||||
```
|
||||
|
||||
|
||||
### Datatype bindings
|
||||
|
||||
```ML
|
||||
datatype mytype = TwoInts of int*int
|
||||
| Str of string
|
||||
| Pizza;
|
||||
val a = Str "hi"; (* Str "hi" : mytype *)
|
||||
val b = Str; (* fn : string -> mytype *)
|
||||
val c = Pizza; (* Pizza : mytype *)
|
||||
val d = TwoInts(1+2, 3+4); (* TwoInts (3,7) : mytype *)
|
||||
val e = a; (* Str "hi" : mytype *)
|
||||
```
|
||||
The example above adds four things to the environment:
|
||||
* A new type mytype that we can now use just like any other types
|
||||
* Three constructors `TwoInts`, `Str`, `Pizza`
|
||||
|
||||
We can also create a type synonmy which is entirely interchangeable with the existing type.
|
||||
```ML
|
||||
type foo = int
|
||||
(* we can write foo wherever we write int and vice-versa *)
|
||||
```
|
||||
|
||||
## Case Expressions
|
||||
|
||||
To access to datatype values, we can use a case expression:
|
||||
```ML
|
||||
fun f (x : mytype) =
|
||||
case x of
|
||||
Pizza => 3
|
||||
| Str s => 8
|
||||
| TwoInts(i1, i2) => i1 + i2;
|
||||
|
||||
f(Str("a")); (* val it = 8 : int *)
|
||||
```
|
||||
We separate the branches with the `|` character. Each branch has the form `p => e` where p is a pattern and e is an expression. Patterns are used to match against the result of evaluating the case's first expression. This is why evaluating a case-expression is called pattern-matching.
|
||||
|
||||
## Lists and Options are Datatypes too
|
||||
|
||||
`SOME` and `NONE` are actually constructors. So you can use them in a case like:
|
||||
```ML
|
||||
fun inc_or_zero intoption =
|
||||
case intoption of
|
||||
NONE => 0
|
||||
| SOME i => i+1;
|
||||
```
|
||||
|
||||
As for list, `[]` and `::` are also constructors. `::` is a little unusual because it is an infix operator so when in patterns:
|
||||
```ML
|
||||
fun sum_list xs =
|
||||
case xs of
|
||||
[] => 0
|
||||
| x::xs' => x + sum_list xs';
|
||||
|
||||
fun append(xs, ys) =
|
||||
case xs of
|
||||
[] => ys
|
||||
| x::xs' => x :: append(xs', ys);
|
||||
```
|
||||
|
||||
## Pattern-matching
|
||||
|
||||
Val-bindings are actually using pattern-matching.
|
||||
```ML
|
||||
val (x, y, z) = (1,2,3);
|
||||
(*
|
||||
val x = 1 : int
|
||||
val y = 2 : int
|
||||
val z = 3 : int
|
||||
*)
|
||||
```
|
||||
|
||||
When defining a function, we can also use pattern-matching
|
||||
```ML
|
||||
fun sum_triple (x, y, z) =
|
||||
x + y + z;
|
||||
```
|
||||
Actually, all functions in ML takes one tripple as an argument. There is no such thing as a mutli-argument function or zero-argument function in ML.
|
||||
The binding `fun () = e` is using the unit-pattern `()` to match against calls that pass the unit value `()`, which is the only value fo a pre-defined datatype `unit`.
|
||||
|
||||
The definition of patterns is recursive. We can use nested patterns instead of nested cae expressions.
|
||||
|
||||
We can use wildcard pattern `_` in patterns.
|
||||
```ML
|
||||
fun len xs =
|
||||
case xs of
|
||||
[] => 0
|
||||
| _::xs' => 1 + len xs';
|
||||
|
||||
```
|
||||
|
||||
### Function Patterns
|
||||
|
||||
In a function binding, we can use a syntactic sugar instead of using case expressions:
|
||||
|
||||
```ML
|
||||
fun f p1 = e1
|
||||
| f p2 = e2
|
||||
...
|
||||
| f pn = en
|
||||
```
|
||||
|
||||
for example
|
||||
```ML
|
||||
fun append ([], ys) = ys
|
||||
| append (x::xs', ys) = x :: append(xs', ys);
|
||||
```
|
||||
|
||||
## Exceptions
|
||||
|
||||
To create new kinds of exceptions we can use exception bindings.
|
||||
```ML
|
||||
exception MyUndesirableCondition;
|
||||
exception MyOtherException of int * int;
|
||||
```
|
||||
|
||||
Use `raise` to raise exceptions. Use `handle` to catch exceptions.
|
||||
```ML
|
||||
fun hd xs =
|
||||
case xs of
|
||||
[] => raise List.Empty
|
||||
| x::_ => x;
|
||||
|
||||
(* The type of maxlist will be int list * exn -> int *)
|
||||
fun maxlist(xs, ex) =
|
||||
case xs of
|
||||
[] => raise ex
|
||||
| x::[] => x
|
||||
| x::xs' => Int.max(x, maxlist(xs', ex));
|
||||
|
||||
(* e1 handle ex => e2 *)
|
||||
val y = maxlist([], MyUndesirableCondition)
|
||||
handle MyUndesirableCondition => 42;
|
||||
```
|
||||
|
||||
## Tail Recursion
|
||||
|
||||
There is a situation in a recursive call called **tail call**:
|
||||
>when f makes a recursive call to f, there is nothing more for the caller to do after the callee returns except return the callee's result.
|
||||
|
||||
Consider a sum function:
|
||||
```ML
|
||||
fun sum1 xs =
|
||||
case xs of
|
||||
[] => 0
|
||||
| i::xs' => i + sum1 xs'
|
||||
```
|
||||
|
||||
When the function runs, it will keep a call-stack for each recursive call . But if we change a little bit using tail call :
|
||||
```ML
|
||||
fun sum2 xs =
|
||||
let fun f (xs,acc) =
|
||||
case xs of
|
||||
[] => acc
|
||||
| i::xs' => f(xs',i+acc)
|
||||
in
|
||||
f(xs,0)
|
||||
end
|
||||
```
|
||||
|
||||
we use a local helper `f` and a accumulator `acc` so that the return value of `f` is just the return value of `sum2` . As a result, there is no need to keep every call in stack, just the current `f` is enough. And that's ML and most of other functional programming languages do.
|
||||
Another example: when reversing a list:
|
||||
```ML
|
||||
fun rev1 lst =
|
||||
case lst of
|
||||
[] => []
|
||||
| x::xs => (rev1 xs) @ [x]
|
||||
|
||||
fun rev2 lst =
|
||||
let fun aux(lst,acc) =
|
||||
case lst of
|
||||
[] => acc
|
||||
| x::xs => aux(xs, x::acc)
|
||||
in
|
||||
aux(lst,[])
|
||||
end
|
||||
```
|
||||
`rev1` is `O(n^2)` but rev2 is almost as simple as `O(n)`.
|
||||
|
||||
To make sure which calls are tail calls, we can use a recursive defination of **tail position** like:
|
||||
* In `fun f(x) = e`, `e` is in tail position.
|
||||
* If an expression is not in tail position, then none of its subexpressions are
|
||||
* If `if e1 then e2 else e3` is in tail position, then `e2` and `e3` are in tail position (but not `e1`). (Case-expressions are similar.)
|
||||
* If `let b1 ... bn in e end` is in tail position, then e is in tail position (but no expressions in the bindings are).
|
||||
* Function-call arguments are not in tail position.
|
||||
|
||||
## First-class Functions
|
||||
|
||||
The most common use of first class functions is passing them as arguments to other functions.
|
||||
|
||||
```ML
|
||||
fun n_times (f, n, x) =
|
||||
if n=0
|
||||
then x
|
||||
else f (n_times(f, n-1,x))
|
||||
```
|
||||
|
||||
The function `n_times` is called higher-order funciton. Its type is:
|
||||
```ML
|
||||
fn : ('a -> 'a) * int * 'a -> 'a
|
||||
```
|
||||
`'a` means they can be any type. This is called _parametric polymorphism_ , or _generic types_ .
|
||||
|
||||
Instead, consider a function that is not polymorphic:
|
||||
```ML
|
||||
(* (int -> int) * int -> int *)
|
||||
fun times_until_zero (f, x) =
|
||||
if x = 0
|
||||
then 0
|
||||
else 1 + times_until_zero(f, f x)
|
||||
```
|
||||
|
||||
### Anonymous Functions
|
||||
|
||||
```ML
|
||||
fun triple_n_times (n, x) =
|
||||
n_times((fn x => 3*x), n, x)
|
||||
```
|
||||
|
||||
Maps:
|
||||
```ML
|
||||
(* ('a -> 'b) * 'a list -> 'b list *)
|
||||
fun map (f, xs) =
|
||||
case xs of
|
||||
[] => []
|
||||
| x::xs' => (f x)::(map(f, xs'));
|
||||
```
|
||||
|
||||
Filters:
|
||||
```ML
|
||||
(* ('a -> bool) * 'a list -> 'a list *)
|
||||
fun filter (f, xs) =
|
||||
case xs of
|
||||
[] => []
|
||||
| x::xs' => if f x
|
||||
then x::(filter (f, xs'))
|
||||
else filter (f, xs');
|
||||
```
|
||||
|
||||
### Lexical scope VS dynamic scope
|
||||
|
||||
### Combining Functions
|
||||
|
||||
```ML
|
||||
fun sqrt_of_abs i = (Math.sqrt o Real.fromInt o abs) i;
|
||||
```
|
||||
|
||||
Use our own infix operator to define a left-to-right syntax.
|
||||
```ML
|
||||
infix |>
|
||||
fun x |> f = f x;
|
||||
fun sqrt_of_abs i = i |> abs |> Real.fromInt |> Math.sqrt;
|
||||
```
|
||||
|
||||
### Currying
|
||||
|
||||
```ML
|
||||
(* fun sorted(x, y z) = z >= y andalso y >= x *)
|
||||
val sorted = fn x => fn y => fn z => z >= y andalso y >= x;
|
||||
|
||||
(* just syntactic sugar for code above *)
|
||||
fun sorted_nicer x y z = z >= y andalso y >= x;
|
||||
```
|
||||
when calling curried the function:
|
||||
```ML
|
||||
(* ((sorted_nicer x) y) z *)
|
||||
(* or just: *)
|
||||
sorted_nicer x y z
|
||||
```
|
||||
|
||||
```ML
|
||||
|
||||
```
|
||||
|
||||
## Type Inference
|
||||
|
||||
Key steps in ML:
|
||||
* Determine types of bindings in order
|
||||
* For each val of fun binding:
|
||||
* Analyze definition for all necessary facts
|
||||
* Type erro if no way for all facts to hold
|
||||
* Use type variables like `'a` for any unconstrained type
|
||||
* Enforce the value restriction
|
||||
|
||||
One example:
|
||||
```ML
|
||||
(*
|
||||
compose : T1 * T2 -> T3
|
||||
f : T1
|
||||
g : T2
|
||||
x : T4
|
||||
body being a function has type T3=T4->T5
|
||||
from g being passed x, T2=T4->T6 for some T6
|
||||
from f being passed the result of g, T1=T6->T7
|
||||
from call to f being body of anonymous function, T7 = T5
|
||||
all together, (T6->T5) * (T4->T6) -> (T4->T5)
|
||||
so ('a->'b) * ('c->'a) -> ('c->'b)
|
||||
*)
|
||||
fun compose (f, g) = fn x => f (g x)
|
||||
```
|
||||
|
||||
### Value restriction
|
||||
|
||||
A variable-binding can have a polymorphic type only if the expression is a variable or value:
|
||||
```ML
|
||||
val r = ref NONE
|
||||
val _ = r := SOME "hi"
|
||||
val i - 1 + valOf (!r)
|
||||
```
|
||||
If there is is no value-restriction, the code above will type check, which shouldn't.
|
||||
With value restriction, ML will give a warning when type-checking:
|
||||
```
|
||||
- val r = ref NONE;
|
||||
stdIn:2.5-2.17 Warning: type vars not generalized because of
|
||||
value restriction are instantiated to dummy types (X1,X2,...)
|
||||
val r = ref NONE : ?.X1 option ref
|
||||
```
|
||||
|
||||
## Mutual Recursion
|
||||
|
||||
Mutual recursion allows `f` to call `g` and `g` to call `f`.
|
||||
In ML, There is an `and` keyword to allow that:
|
||||
```ML
|
||||
fun p1 = e1
|
||||
and p2 = e2
|
||||
and p3 = p3
|
||||
```
|
||||
|
||||
## Modules
|
||||
|
||||
```ML
|
||||
structure MyMathLib =
|
||||
struct
|
||||
fun fact x = x
|
||||
val half_pi = Math.pi / 2.0
|
||||
fun doubler x = x * 2
|
||||
end
|
||||
```
|
||||
|
||||
### Signatures
|
||||
|
||||
A signature is a type for a module.
|
||||
```ML
|
||||
signature SIGNAME =
|
||||
sig types-for-bindings
|
||||
end
|
||||
```
|
||||
|
||||
Ascribing a signature to a module:
|
||||
```ML
|
||||
structure myModule :> SIGNAME =
|
||||
struct bindings end;
|
||||
```
|
||||
|
||||
Anything not in the signature cannot be used outside the module.
|
||||
|
||||
```ML
|
||||
signature MATHLIB =
|
||||
sig
|
||||
val fact : int -> int
|
||||
val half_pi : real
|
||||
(* make doubler unaccessable outside the MyMathLib *)
|
||||
(* val doubler : int -> int *)
|
||||
end
|
||||
|
||||
structure MyMathLib :> MATHLIB =
|
||||
struct
|
||||
fun fact x = x
|
||||
val half_pi = Math.pi / 2.0
|
||||
fun doubler x = x * 2
|
||||
end
|
||||
```
|
||||
|
||||
### Signature matching
|
||||
|
||||
## Equivalence
|
||||
|
||||
* PL Equivalence
|
||||
* Asymptotic equivalence
|
||||
* System equivalence
|
||||
@@ -0,0 +1,245 @@
|
||||
---
|
||||
title: Racket notes
|
||||
date: 2020-02-29 09:00:00
|
||||
tags:
|
||||
- racket
|
||||
categories:
|
||||
- notes
|
||||
keywords:
|
||||
- racket
|
||||
---
|
||||
|
||||
## Basic
|
||||
|
||||
```racket
|
||||
#lang racket
|
||||
(provide (all-defined-out))
|
||||
|
||||
;this is a comment
|
||||
|
||||
(define s "hello")
|
||||
|
||||
(define x 3)
|
||||
(define y (+ x 2))
|
||||
|
||||
(define cube1
|
||||
(lambda (x)
|
||||
(* x (* x x))))
|
||||
|
||||
(define cube2
|
||||
(lambda (x)
|
||||
(* x x x)))
|
||||
|
||||
(define (cube3 x)
|
||||
(* x x x))
|
||||
|
||||
(define (pow1 x y)
|
||||
(if (=y 0)
|
||||
1
|
||||
(* x (pow1 x (- y 1)))))
|
||||
|
||||
; currying
|
||||
(define pow2
|
||||
(lambda (x)
|
||||
(lambda (y)
|
||||
(pow1 x y))))
|
||||
|
||||
```
|
||||
|
||||
### List
|
||||
|
||||
* Empty list: `null`
|
||||
* `()` doesn"t work for `null` but `'()` does
|
||||
* build a list: `(list e1 ... en)`
|
||||
* Constructor: `cons`
|
||||
* Access head of list: `car`
|
||||
* Access tail of list: `cdr`
|
||||
* Check for empty: `null?`
|
||||
|
||||
### Syntax
|
||||
|
||||
A term is either:
|
||||
* An atom like `#t, #f, 34, "hi", null, 4.0, x,...`
|
||||
* A special form like `define, lambda, if`
|
||||
* A sequence of terms in parentheses: `(t1 t2 t3)`
|
||||
* Can use `[` anything you use `(`
|
||||
|
||||
Remember parentheses matters! For example:
|
||||
`(e)` means call e with 0 argument.
|
||||
|
||||
### Dynamic typing
|
||||
|
||||
```racket
|
||||
(define lst (list #t "hi" 1 (list 2 3 4)))
|
||||
```
|
||||
|
||||
### Cond
|
||||
|
||||
```racket
|
||||
(define (sum3 xs)
|
||||
(cond [(null? xs) 0]
|
||||
[(number? (car xs)) (+ (car xs) (sum3 (cdr xs)))]
|
||||
[#t 0]))
|
||||
```
|
||||
|
||||
### What is true?
|
||||
|
||||
Anything that is not `#f` is true `#t`.
|
||||
|
||||
### Local bindings
|
||||
|
||||
#### let/let*/letrec
|
||||
|
||||
```racket
|
||||
(let ([x1 e1]
|
||||
[x2 e2]
|
||||
...
|
||||
[xn en])
|
||||
e)
|
||||
```
|
||||
|
||||
Racket uses the environment **before** the let-expression to evaluate `e1 e2 ... en`, which means if `en` uses `x1`, `x2`, that would mean some outer variables of the same name. Instead, the expressions in `let*` are evaluated in the environment produced from the previous bindings (later ones shadow) .
|
||||
|
||||
The expressions in `letrec` are evaluated in the environment that includes all th bindings. It is needed for mutual recursion.
|
||||
|
||||
### set!
|
||||
|
||||
Racket has assignment statements:
|
||||
```racket
|
||||
(set! x e)
|
||||
```
|
||||
|
||||
Once you have side-effects, sequences are useful:
|
||||
```racket
|
||||
(begin e1 e2 e3)
|
||||
```
|
||||
|
||||
### cons/mcons
|
||||
|
||||
`cons` produces pairs or lists. (Actually lists are just extented pairs)
|
||||
```racket
|
||||
(define pr (cons 1 (cons #t "hi"))) ; is a pair
|
||||
(define lst (cons 1 (cons #t (cons "hi" null)))) ; is a list
|
||||
```
|
||||
|
||||
`mcons` is another way to make pairs which allows you to change the value inside piars:
|
||||
```racket
|
||||
(define mpr (mcons 1 (mcons #t "hi")))
|
||||
(mcar mpr) ; 1
|
||||
(mcdr mpr) ; (mcons (#t "hi"))
|
||||
(set-mcdr! mpr 47) ; mpr becomes (mcons 1 47)
|
||||
```
|
||||
|
||||
Related form:
|
||||
* `mcons`
|
||||
* `mcar`
|
||||
* `mcdr`
|
||||
* `mpair?`
|
||||
* `set-mcar!`
|
||||
* `set-mcdr!`
|
||||
|
||||
## Delayed Evaluation and Thunk
|
||||
|
||||
In most programming languages, given `e1 e2 ... en`, the function arguments `e2, ..., en` are evaluated once before the function body is executed.
|
||||
So if we define a function like:
|
||||
```rkt
|
||||
(define (my-if-bad x y z) (if x y z))
|
||||
|
||||
(define (factorial-wrong x)
|
||||
(my-if-bad (= x 0)
|
||||
1
|
||||
(* x (factorial-wrong (- x 1)))))
|
||||
```
|
||||
if we use `if` instead of `my-if-bad`, `factorial-wrong` acts just like we want. But with `my-if-bad`, the function never stops because the two branches evaluate at the same time.
|
||||
|
||||
Thanks to lambda, we can delay the evaluation, using the fact that function bodies are not evaluated until the function gets called.
|
||||
```rkt
|
||||
(define (my-if x y z) (if x (y) (z)))
|
||||
|
||||
(define (factorial x)
|
||||
(my-if (= x 0)
|
||||
(lambda () 1)
|
||||
(lambda () (* x (factorial (- x 1))))))
|
||||
```
|
||||
The general idiom of using a zero-argument function to delay evaluation is also called a **thunk** (or, thunk the argument).
|
||||
|
||||
By the way,
|
||||
|
||||
## Lazy-evaluation/Call-by-need/Promises
|
||||
|
||||
## Streams
|
||||
|
||||
A stream is an infinite sequence of values.
|
||||
```racket
|
||||
#lang racket
|
||||
; 1 1 1 1 ...
|
||||
(define ones (lambda () (cons 1 ones)))
|
||||
|
||||
; 1 2 3 4 ...
|
||||
(define nats
|
||||
(letrec ([f (lambda (x) (cons x (lambda () (f (+ x 1)))))])
|
||||
(lambda () (f 1))))
|
||||
|
||||
; 2 4 6 8 ...
|
||||
(define power-of-two
|
||||
(letrec ([f (lambda (x) (cons x (lambda () (f (* x 2)))))])
|
||||
(lambda () (f 2))))
|
||||
|
||||
; higher-order maker
|
||||
(define (stream-maker fn arg)
|
||||
(letrec ([f (lambda (x) (cons x (lambda () (f (fn x arg)))))])
|
||||
(lambda () (f arg))))
|
||||
```
|
||||
|
||||
## Memoization (Not Memorization)
|
||||
|
||||
Memoization is another idiom related to lazy evaluation that does not actually use thunks. To implement memoization we do use mutation: Whenever the function is called with an argument we have not seen before, we compute the answer and then add the result to the table.
|
||||
|
||||
```racket
|
||||
(define fibonacci
|
||||
(letrec([memo null]
|
||||
[f (lambda (x)
|
||||
(let ([ans (assoc x memo)])
|
||||
(if ans
|
||||
(cdr ans)
|
||||
(let ([new-ans (if (or (= x 1) (= x 2))
|
||||
1
|
||||
(+ (f (- x 1))
|
||||
(f (- x 2))))])
|
||||
(begin
|
||||
(set! memo (cons (cons x new-ans) memo))
|
||||
new-ans)))))])
|
||||
f))
|
||||
```
|
||||
|
||||
## Macros
|
||||
|
||||
Think about these things about macros and how Racket handles them better than other macro systems(notably C/C++)
|
||||
|
||||
* Tokenization
|
||||
* Parenthesization
|
||||
* Scope
|
||||
|
||||
### Syntax
|
||||
|
||||
```racket
|
||||
(define-syntax myif
|
||||
(syntax-rules (then else)
|
||||
[(my-if e1 then e2 else e3)
|
||||
(if e1 e2 e3)]))
|
||||
|
||||
(define-syntax my-delay
|
||||
(syntax-rules ()
|
||||
[(my-delay e)
|
||||
(mcons #f (lambda () e))]))
|
||||
|
||||
```
|
||||
### Hygiene
|
||||
|
||||
## Recursive Datatypes Via Rackets's `struct`
|
||||
|
||||
[https://docs.racket-lang.org/reference/define-struct.html?q=struct#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._struct%29%29](https://docs.racket-lang.org/reference/define-struct.html?q=struct#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._struct%29%29)
|
||||
|
||||
```racket
|
||||
(struct foo (bar baz quux) #:transparent
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: Programming Language - Subtyping
|
||||
date: 2020-04-29 09:00:00
|
||||
tags:
|
||||
- programming language
|
||||
category: tech
|
||||
keywords:
|
||||
- subtyping
|
||||
---
|
||||
|
||||
## Some Good Subtyping Rules
|
||||
|
||||
* Width subtyping: A supertype can have a subset of fields with the same types, i.e., a subtype can have extra fields.
|
||||
* Permutation subtypings: A supertype can have the same set of fields with the same types in a different order.
|
||||
* Transitivity: if t1 is subtype of t2, and t2 is subtype of t3, then t1 is subtype of t3.
|
||||
* Reflexivity: Every type is a subtype of itself.
|
||||
|
||||
Given the three features of (1) setting a field, (2) letting depth
|
||||
subtyping change the type of a field, and (3) having a sound type system actually prevent field-missing errors, we can have any two of the three, but not all of them.
|
||||
|
||||
## Function Subtyping
|
||||
|
||||
Function subtyping **contravariant** in argument(s) and **covariant** in results.
|
||||
If t3 is subtype of t1, and t2 is a subtype of t4, then `t1 -> t2` is a subtype of `t3 -> t4`.
|
||||
@@ -0,0 +1,622 @@
|
||||
---
|
||||
title: Algorithms - Graphs
|
||||
date: 2020-05-01 09:00:00
|
||||
tags:
|
||||
- Algorithms
|
||||
category: notes
|
||||
keywords:
|
||||
- Algorithms
|
||||
- Graphs
|
||||
mathjax: true
|
||||
---
|
||||
|
||||
## Undirected Graphs
|
||||
|
||||
### Some problems
|
||||
|
||||
* Path
|
||||
* Shortest path
|
||||
* Cycle
|
||||
* Ehler tour: A cycle that uses each edge excatly once.
|
||||
* Hamilton tour: A cycle that uses each vertex exactly once
|
||||
- classical NP-complete problem.
|
||||
* Connectivity
|
||||
* MST:
|
||||
* Biconnectivity: A vertex whose removal disconnects the graph
|
||||
* Planarity
|
||||
* Graph isomorphism: Are two graphs identical?
|
||||
- No one knows so far. A lonstanding open problem
|
||||
|
||||
### Representations
|
||||
|
||||
Real-world graphs tend to be **sparse** (huge number of vertices, small average vertex degree).
|
||||
|
||||
* Set-of-edges representation
|
||||
- unefficient
|
||||
* Adjacency-matrix representation
|
||||
- space cost is prohibitive
|
||||
* Adjacency-list array representation
|
||||
- GOOD
|
||||
|
||||
### Adjacency-list Data structure
|
||||
|
||||
* Space usage proportional to V + E
|
||||
* Constant time to add an edge
|
||||
* Time proportional to the degree of v to iterate through vertices adjacent to v
|
||||
|
||||
### Depth-first Search (DFS)
|
||||
|
||||
Typical applications:
|
||||
* Find all vertices connected to a given source vertex
|
||||
* Find a path between two vertices
|
||||
|
||||
Algorithm:
|
||||
* Use recursion (a function-call stack) or an explicit stack.
|
||||
* Mark each visited vertex (and keep track of edge taken to visit it)
|
||||
* Return (retrace steps) when no unvisited options
|
||||
|
||||
```Java
|
||||
public class DepthFirstPaths{
|
||||
private blloean[] marked;
|
||||
private int[] edgeTO;
|
||||
private int s;
|
||||
public DepthFirstPaths(Graph G, int s)
|
||||
{
|
||||
// ...
|
||||
dfs(G, s);
|
||||
}
|
||||
|
||||
private void dfs(Graph Gm int v)
|
||||
{
|
||||
marked[v] = true;
|
||||
for (int w : G.adj(v))
|
||||
if (!marked[v])
|
||||
{
|
||||
dfs(G, w)
|
||||
edgeTo[w] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Propositions:
|
||||
1. DFS marks all vertices connected to s in time proportional to the sum of their degrees.
|
||||
2. After DFS, can find vertices connected to s in constant time and can find a path to s in time proportional to its length.
|
||||
|
||||
### Breadth-first Search (BFS)
|
||||
|
||||
Typical applications:
|
||||
* shortest path
|
||||
|
||||
Algorithm:
|
||||
* Put s onto a queue, and mark s as visited
|
||||
* Take the next vertex v from the queue and mark it
|
||||
* Put onto the queue all unmarked vertices that are adjacent to v
|
||||
|
||||
```Java
|
||||
public class BreadthFirstPaths
|
||||
{
|
||||
private boolean[] marked;
|
||||
private int[] edgeTo;
|
||||
// ...
|
||||
private void bfs(Graph G, int s)
|
||||
{
|
||||
Queue<Integer> q = new Queue<>();
|
||||
q.enqueue(s);
|
||||
marked[s] = ture;
|
||||
while (!q.isEmpty())
|
||||
{
|
||||
int v = q.dequeue();
|
||||
for (int w: G.adj(v))
|
||||
{
|
||||
if (!marked[w])
|
||||
{
|
||||
q.enqueue(w);
|
||||
marked[w] = true;
|
||||
edgeTo[w] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Proposition:
|
||||
1. BFS computes shortest paths (fewest number of edges) from s to all other vertices in a graph in time proportional to E + V
|
||||
|
||||
### Applications of DFS
|
||||
|
||||
#### Connected components
|
||||
|
||||
The goal is to preprocess graph to answer queries of the form *is v connected to w?* in constant time.
|
||||
|
||||
The relation *is connected to* is an equivalence relation:
|
||||
* Reflexive: v is connected to v
|
||||
* Symmetric: if v is connected to w, then w is connected to v
|
||||
* Transitive: if v connected to w and w connected to x, then v connected to x
|
||||
|
||||
```Java
|
||||
public class CC {
|
||||
private boolean[] marked;
|
||||
private int[] id;
|
||||
private int count;
|
||||
|
||||
public CC(Graph G) {
|
||||
marked = new boolean[G.V()];
|
||||
id = new int[G.V()];
|
||||
for (int v = 0, v < G.V(); v++) {
|
||||
if (!marked[v]) {
|
||||
dfs(G, v);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
private void dfs(Graph G, int v) {
|
||||
marked[v] = true;
|
||||
id[v] = count;
|
||||
for (int w : G.adj(v)) {
|
||||
if (!marked[w]) {
|
||||
dfs(G, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Cycle detection
|
||||
|
||||
Problem: Is a given graph acylic?
|
||||
|
||||
**TODO**
|
||||
|
||||
#### Two-colorability
|
||||
|
||||
Problem: Is the graph bipartite?
|
||||
|
||||
**TODO**
|
||||
|
||||
#### Symbol graphs
|
||||
|
||||
**TODO**
|
||||
|
||||
#### Degrees of separation
|
||||
|
||||
**TODO**
|
||||
|
||||
## Directed Graphs
|
||||
|
||||
>A directed graph (or digraph) is a set of vertices and a collection of directed edges. Each directed edge connects an ordered pair of vertices.
|
||||
|
||||
* *outdegree*: the number of edges going **from** it
|
||||
* *indegree*: the number fo edges going **into** it
|
||||
* *directed path*: a sequence of vertices in which there is a (directed) edge pointing from each vertex in the sequence to its successor in the sequence
|
||||
* *directed cycle*
|
||||
* *simple cycle*: a cycle with no repeated edges or vertices
|
||||
|
||||
|
||||
### Representations
|
||||
|
||||
Again, use [adjacency-lists representation](#Adjacency-list-Data-structure)
|
||||
* Based on iterating over vertices pointing from v
|
||||
* Real-world digraphs tend to be sparse
|
||||
|
||||
```Java
|
||||
public class Digraph {
|
||||
private final int V;
|
||||
private final Bag<Integer>[] adj;
|
||||
|
||||
public Digraph(int V) {
|
||||
this.V = V;
|
||||
adj = (Bag<Integer>[]) new Bag[V];
|
||||
for (int v = 0; v < V; v++) {
|
||||
adj[v] = new Bag<Integer>[];
|
||||
}
|
||||
}
|
||||
|
||||
public void addEdge(int v, int w) {
|
||||
adj[v].add(w);
|
||||
}
|
||||
|
||||
public Iterable<Integer> adj(int v) {
|
||||
return adj[v];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Digraph search
|
||||
|
||||
Reachabiliity problem: Find all vertices reachable from s along a directed path.
|
||||
|
||||
We can use [the same dfs method as for undirected graphs](#Depth-first-Search-(DFS)).
|
||||
* Every undirected graph is a digraph with edges in both directions.
|
||||
* DFS is a digraph algorithm,
|
||||
|
||||
Reachability applications:
|
||||
* program control-flow analysis
|
||||
- Dead-code elimination
|
||||
- infinite-loop detection
|
||||
* mark-sweep garbage collector
|
||||
|
||||
|
||||
Other DFS problems:
|
||||
* Path findind
|
||||
* Topological sort
|
||||
* Directed cycle detection
|
||||
* ...
|
||||
|
||||
BFS problems:
|
||||
* shortest path
|
||||
* multiple-source shortest paths
|
||||
* web crawler application
|
||||
|
||||
### Topological Sort
|
||||
|
||||
>Topological sort: Given a digraph, put the vertices in order such that all its directed edges point from a vertix earlier in the order to a vertex later in the order (or report impossible).
|
||||
|
||||
A digraph has a topological order **if and only if** it is a *directed acyclic graph* (DAG).
|
||||
Topological sort redraws DAG so all edges poitn upwards.
|
||||
|
||||
use **DFS** again. It can be proved that reverse postorder of a DAG is a topological order.
|
||||
(check P578 for the definition of Preorder/Postorder)
|
||||
|
||||
```Java
|
||||
public class DepthFirstOrder {
|
||||
private boolean[] marked;
|
||||
private Stack<Integer> reversePost;
|
||||
|
||||
publiv DepthFirstOrder(Digraph G) {
|
||||
reversePost = new Stack<Integer>();
|
||||
marked = new boolean[G.V()];
|
||||
for (int v = 0; v < G.V(); v++) {
|
||||
if (!marked[v]) dfs(G, v);
|
||||
}
|
||||
}
|
||||
|
||||
private void dfs(Digrapg G, int v) {
|
||||
marked[v] = true;
|
||||
for (int w : G.adj(v)) {
|
||||
if (!marked[w]) dfs(G, w)
|
||||
}
|
||||
reversePost.push(v);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Directed cycle detection
|
||||
|
||||
To find out if a given digraph is a DAG, we can try to find a directec cycle in the digraph.
|
||||
Use DFS and a stack to track the cycle.
|
||||
|
||||
```Java
|
||||
// TODO
|
||||
```
|
||||
|
||||
Some very typical applications of directed cycle detection and topological sort:
|
||||
(A directed cycle means the problem is infeasible)
|
||||
* job schedule
|
||||
* course scuedule
|
||||
* inheritance
|
||||
* spreadsheet
|
||||
- vertex: cell
|
||||
- edge: formula
|
||||
* symbolic links
|
||||
|
||||
### Strong components
|
||||
|
||||
Vertices v and w are **strongly connected** if there is both a directed path from v to w and a directed path from w to v.
|
||||
Strong connectivity is an equvicalence relation.
|
||||
|
||||
#### Kosaraju-Sharir Algorithm
|
||||
|
||||
Kosaraju-Sharir is easy to implement but difficutl to understand. It runs DFS twice:
|
||||
* Given a digraph G, run DFS to compute the topological order of its reverse $G^R$
|
||||
* Run DFS on G in the order given by first DFS
|
||||
|
||||
TODO: ADD Proof
|
||||
|
||||
[https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/KosarajuSharirSCC.java.html](https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/KosarajuSharirSCC.java.html)
|
||||
```Java
|
||||
public class KosarajuSharirSCC {
|
||||
private boolean[] marked; // marked[v] = has vertex v been visited?
|
||||
private int[] id; // id[v] = id of strong component containing v
|
||||
private int count; // number of strongly-connected components
|
||||
|
||||
/**
|
||||
* Computes the strong components of the digraph {@code G}.
|
||||
* @param G the digraph
|
||||
*/
|
||||
public KosarajuSharirSCC(Digraph G) {
|
||||
|
||||
// compute reverse postorder of reverse graph
|
||||
DepthFirstOrder dfs = new DepthFirstOrder(G.reverse());
|
||||
|
||||
// run DFS on G, using reverse postorder to guide calculation
|
||||
marked = new boolean[G.V()];
|
||||
id = new int[G.V()];
|
||||
for (int v : dfs.reversePost()) {
|
||||
if (!marked[v]) {
|
||||
dfs(G, v);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DFS on graph G
|
||||
private void dfs(Digraph G, int v) {
|
||||
marked[v] = true;
|
||||
id[v] = count;
|
||||
for (int w : G.adj(v)) {
|
||||
if (!marked[w]) dfs(G, w);
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Minimum Spanning Trees
|
||||
|
||||
An edge-weighted-graph is a graph where we associate weight or costs with each edge.
|
||||
A spanning tree of an undirected edge-weighted graph G is a subgraph T that is both **a tree (conneted and acyclic)** and **spanning (includes all of the vertices)**.
|
||||
Given an (connected) undirected edge-weighted graph G with V vertices and E edges, the MST of it must have **V - 1** edges.
|
||||
If the graph is not connceted, we compute minimum spanning forest (MST of each component).
|
||||
|
||||
* A *cut* in a graph is a partition of its vertices into two (nonempty) sets
|
||||
* A *crossing edge* connects a vertex in one set with a vertex in the other.
|
||||
* Cut property: Given any cut, the crossing edge of min weight is in the MST.
|
||||
|
||||
### Edge-weight Graph Data Type
|
||||
|
||||
Edge:
|
||||
[https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/Edge.java.html](https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/Edge.java.html)
|
||||
|
||||
EdgeWeigthedGraph:
|
||||
[https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/EdgeWeightedGraph.java.html](https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/EdgeWeightedGraph.java.html)
|
||||
|
||||
### **Greedy MST Algorithm:**
|
||||
* Start with all edges colored gray.
|
||||
* Find cut with no blacked crossing edges; color its min-weight edge black.
|
||||
* Repeat until V-1 edges are colored black.
|
||||
|
||||
### Implementations 1: Kruskal's algorithm
|
||||
|
||||
For edges in ascending order of weight:
|
||||
* Add next edge to Tree unless doing so would create a cycle.
|
||||
|
||||
To efficiently solve this problem, use union-find :
|
||||
1. use a priority queue to maintain all the edges in V
|
||||
2. union-find data structure:
|
||||
- maintain a set for each connected component in T.
|
||||
- if v and w are in saome set, then adding v->w would create a cycle
|
||||
- to add v>w to T, merge sets containing v and w.
|
||||
|
||||
TODO: Add code
|
||||
|
||||
### Implementations 2: Prim's algorithm
|
||||
|
||||
* Start with vertex 0 and greedily grow tree T.
|
||||
* Add To T the min weight edge with exactly oue endpoint in T.
|
||||
* Reapeat unitl V - 1 edges.
|
||||
|
||||
The key to solve this problem is how do we find the crossing edge of minimal weight efficiently.
|
||||
|
||||
A lazy solution (in time proportional to $ElogE$, fair enough):
|
||||
1. Maintain a PQ of edges with (at least) one endpoint in T
|
||||
- Key = edge, priority = weight
|
||||
2. Delete-min to determine next edge e = v->w to add to T
|
||||
3. Disregard if both endpoints v and w are marked (both in T)
|
||||
4. Otherwise, let w be the unmarked vertex (not in T)
|
||||
- add to PQ and edge incident to w (assuming other endpoint not in T)
|
||||
- add e to T and mark w
|
||||
|
||||
TODO: add code
|
||||
|
||||
A eager solution (in time proprotional to $ElogV$, better):
|
||||
1. Maintain a PQ of vertices connected by an edge to T, where priority of v = weight of shortedt edge connecting v to T
|
||||
2. Delete min vertex v and add its associated edge e = v->w to T
|
||||
3. Update PQ by considering all edges e = v->x incident to v
|
||||
- ignore if x is already in T
|
||||
- add x to PQ if not alread on it
|
||||
- decrease priority of x if v->x becomes shortest edge connecting x to T
|
||||
|
||||
This solution uses an [indexed priority queue](https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/IndexMinPQ.java.html) data structure.
|
||||
|
||||
TODO: add code
|
||||
|
||||
## Shortest Paths
|
||||
|
||||
**Some variants:**
|
||||
* Which vertices?
|
||||
- Single source
|
||||
- Source-sink
|
||||
- All pairs
|
||||
* Edge weights
|
||||
- Nonegative weights
|
||||
- Euclidean weights
|
||||
- Arbitrary weights
|
||||
* Cycles?
|
||||
- No directed cycles
|
||||
- No negative cycles
|
||||
|
||||
### Edge-weighted digraph data strcuture
|
||||
|
||||
Weighted directed edge:
|
||||
[https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/DirectedEdge.java.html](https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/DirectedEdge.java.html)
|
||||
|
||||
Edge-weighted digraph:
|
||||
[https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/EdgeWeightedDigraph.java.html](https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/EdgeWeightedDigraph.java.html)
|
||||
|
||||
Use adjacency-lists implementation same as [EdgeWeightedGraph](https://algs4.cs.princeton.edu/code/edu/princeton/cs/algs4/EdgeWeightedGraph.java.html)
|
||||
|
||||
### Generic Single-source Shortest paths
|
||||
|
||||
Our goal is to find the shortest path from s to every other vertex. As a result, what we find will be the **shortest-paths tree (SPT)** for source s.
|
||||
|
||||
#### Relax edge e = v->w
|
||||
|
||||
* distTo[v] is length of shortest known path from s to v
|
||||
* distTo[w] is length of shortest known path from s to w
|
||||
* esgeTo[w] is last edge on shortest known pathh from s to w
|
||||
* if e = v->w gives shorter path to w through v, update both distTo[w] and edgeTo[w]
|
||||
|
||||
```Java
|
||||
private void relax(DirectedEdge e) {
|
||||
int v = e.from(), w = e.to();
|
||||
if (distTo[w] > distTo[v] + e.weight()) {
|
||||
distTo[w] = distTo[v] + e.weight();
|
||||
edgeTo[w] = e;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Optimality conditions
|
||||
|
||||
Given an edge-weighted digraph G, distTo[] are the shortest path distances from s **iff**:
|
||||
* distTo[s] = 0
|
||||
* For each vertex v, distTo[v] is the length of some path from s to v.
|
||||
* For each edge e = v->w, distTo[w] <= distTo[v] + e.weight()
|
||||
|
||||
#### Generic algorithm
|
||||
|
||||
```
|
||||
Generic algorithm (to compute SPT from s) {
|
||||
Initialize distTo[s] = 0 and distTo[v] = $\infty$
|
||||
|
||||
Repeat until optimality conditions are satisfied:
|
||||
- Relax any edge
|
||||
}
|
||||
```
|
||||
|
||||
Efficient implementations:
|
||||
* Nonnegative weights: [Dijkstra's algorithm](#implement-1-dijkstras-algorithm)
|
||||
* No directed cycles (DAGs): [Topological sort algorithm](#implement-2-topological-sort-algorithm)
|
||||
* No negative cycles: [Bellman-Ford](#implement-3-bellman-ford-algorithm)
|
||||
|
||||
### Implement 1: Dijkstra's algorithm
|
||||
When there is no nonnegative weight exists, we can use Dijkstra's algorithm.
|
||||
* Consider vertices in increasing order of distance from s (non-tree vertex with the lowest distTo[] value)
|
||||
* add vertex to tree and relax all edges pointing from that vertex
|
||||
|
||||
```Java
|
||||
public class DijkstraSP{
|
||||
// ...
|
||||
public DijkstraSP(EdgeWeightedDigraph G, int s) {
|
||||
edgeTo = new DirectedEdge[G.V()];
|
||||
distTo = new double[G.V()];
|
||||
pq = new IndexMinPQ<Double>(G.V());
|
||||
|
||||
for (int v = 0; v < G.V(); v++) {
|
||||
distTo[v] = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
distTo[s] = 0;
|
||||
|
||||
pq.insert(s, 0.0);
|
||||
while(!pq.isEmpty()) {
|
||||
int v= pq.delMin();
|
||||
for (DirectedEdge e : G.adj(v)) {
|
||||
relax(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void relax(DirectedEdge e) {
|
||||
int v = e.from(), w = e.to();
|
||||
if (distTo[w] > distTo[v] + e.weight()) {
|
||||
distTo[w] = distTo[v] + e.weight();
|
||||
edgeTo[w] = e;
|
||||
if (pq.contains(w)) pq.decreaseKey(w, distTo[w]);
|
||||
else pq.insert(w, distTo[w]);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Compare to Prim's algorithm:**
|
||||
* Both are computing a graph's spanning tree
|
||||
* Prim's algorithm choose closest vertex to tree as next vertex, while Dijkstra's algorithm choose closest vertex to the source
|
||||
|
||||
### Implement 2: Topological sort algorithm
|
||||
|
||||
When the graph is a DAG, we can consider vertices in topological order and do relaxing.
|
||||
|
||||
```Java
|
||||
// ...
|
||||
Topological topological = new Topological(G);
|
||||
for (int v : topological.order()) {
|
||||
for (DirectedEdge e : G.adj(v)) {
|
||||
relax(e);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**[Seam carving](https://en.wikipedia.org/wiki/Seam_carving)**: Resize an image without distortion.
|
||||
|
||||
**Longest paths**:
|
||||
* Formuate as a shortest paths problem in edge-weighted DAGs
|
||||
- Negate all weights
|
||||
- Find shortest paths
|
||||
- Negate weights in result
|
||||
* Allpication: Parallel job scheduling ([Critical path method, CPM](https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AA%E3%83%86%E3%82%A3%E3%82%AB%E3%83%AB%E3%83%91%E3%82%B9%E6%B3%95)).
|
||||
|
||||
### Implement 3: Bellman-Ford algorithm
|
||||
|
||||
>A SPT exists iff no negative cycles (a directed cycle whose sum of edge weights is negative).
|
||||
|
||||
When we want to find shortest paths with nagative weights, Dijkstra's algorithms doesn't work.
|
||||
We can use Bellman-Ford algorithm as long as there is no negative cycle in the graph.
|
||||
(Bellman-Ford algorithm is a dynamic programming algorithm)
|
||||
|
||||
* Initialize distTo[s] = 0 and distTo[v] = $\infty$
|
||||
* Maintain a queue and repeat until the queue is empty or find a cycle:
|
||||
- Pop vertex v from q
|
||||
- Relax each edge pointing from v to any vertex w:
|
||||
- if distTo[w] can be de decreased, update distTo[w] and add w to the queue
|
||||
|
||||
```Java
|
||||
// ...
|
||||
public BellmanFordSP(EdgeWeightedDigraph G, int s) {
|
||||
distTo = new double[G.V()];
|
||||
edgeTo = new DirectedEdge[G.V()];
|
||||
onQueue = new boolean[G.V()];
|
||||
for (int v = 0; v < G.V(); v++)
|
||||
distTo[v] = Double.POSITIVE_INFINITY;
|
||||
distTo[s] = 0.0;
|
||||
|
||||
// Bellman-Ford algorithm
|
||||
queue = new Queue<Integer>();
|
||||
queue.enqueue(s);
|
||||
onQueue[s] = true;
|
||||
while (!queue.isEmpty() && !hasNegativeCycle()) {
|
||||
int v = queue.dequeue();
|
||||
onQueue[v] = false;
|
||||
relax(G, v);
|
||||
}
|
||||
}
|
||||
|
||||
private void relax(EdgeWeightedDigraph G, int v) {
|
||||
for (DirectedEdge e : G.adj(v)) {
|
||||
int w = e.to();
|
||||
if (distTo[w] > distTo[v] + e.weight()) {
|
||||
distTo[w] = distTo[v] + e.weight();
|
||||
edgeTo[w] = e;
|
||||
if (!onQueue[w]) {
|
||||
queue.enqueue(w);
|
||||
onQueue[w] = true;
|
||||
}
|
||||
}
|
||||
if (++cost % G.V() == 0) {
|
||||
findNegativeCycle();
|
||||
if (hasNegativeCycle()) return; // found a negative cycle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Bellman-Ford algorithm can also be used for finding a negative cycle.
|
||||
|
||||
Negative cycle application: arbitrage detection.
|
||||
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: Changes to String in java (from 1.7.0_06)
|
||||
date: 2020-06-06 09:00:00
|
||||
tags:
|
||||
- Algorithms
|
||||
categories:
|
||||
- notes
|
||||
keywords:
|
||||
- Algorithms
|
||||
- String
|
||||
- radix sort
|
||||
---
|
||||
|
||||
|
||||
Before 1.7.0_06, `String` has 4 non static field:
|
||||
* char[] value
|
||||
* int[] offset
|
||||
* int count
|
||||
* int hash
|
||||
|
||||
`Subing.substring` create a String by sharing the original String's internal `char[] value` and setting offset. This saves memory and makes `String.substring` run in a constant time($O(1)$).
|
||||
Meanwhile, this feature may cause **memory leak**[^1].
|
||||
|
||||
[http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/8deef18bb749/src/share/classes/java/lang/String.java](http://hg.openjdk.java.net/jdk6/jdk6/jdk/file/8deef18bb749/src/share/classes/java/lang/String.java)
|
||||
```Java
|
||||
public final class String
|
||||
implements java.io.Serializable, Comparable<String>, CharSequence
|
||||
{
|
||||
/** The value is used for character storage. */
|
||||
private final char value[];
|
||||
|
||||
/** The offset is the first index of the storage that is used. */
|
||||
private final int offset;
|
||||
|
||||
/** The count is the number of characters in the String. */
|
||||
private final int count;
|
||||
|
||||
/** Cache the hash code for the string */
|
||||
private int hash; // Default to 0
|
||||
|
||||
// ...
|
||||
|
||||
// Package private constructor which shares value array for speed.
|
||||
String(int offset, int count, char value[]) {
|
||||
this.value = value;
|
||||
this.offset = offset;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
/**
|
||||
* Returns a new string that is a substring of this string. The
|
||||
* substring begins at the specified <code>beginIndex</code> and
|
||||
* extends to the character at index <code>endIndex - 1</code>.
|
||||
* Thus the length of the substring is <code>endIndex-beginIndex</code>.
|
||||
* <p>
|
||||
* Examples:
|
||||
* <blockquote><pre>
|
||||
* "hamburger".substring(4, 8) returns "urge"
|
||||
* "smiles".substring(1, 5) returns "mile"
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @return the specified substring.
|
||||
* @exception IndexOutOfBoundsException if the
|
||||
* <code>beginIndex</code> is negative, or
|
||||
* <code>endIndex</code> is larger than the length of
|
||||
* this <code>String</code> object, or
|
||||
* <code>beginIndex</code> is larger than
|
||||
* <code>endIndex</code>.
|
||||
*/
|
||||
public String substring(int beginIndex, int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new StringIndexOutOfBoundsException(beginIndex);
|
||||
}
|
||||
if (endIndex > count) {
|
||||
throw new StringIndexOutOfBoundsException(endIndex);
|
||||
}
|
||||
if (beginIndex > endIndex) {
|
||||
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
|
||||
}
|
||||
return ((beginIndex == 0) && (endIndex == count)) ? this :
|
||||
new String(offset + beginIndex, endIndex - beginIndex, value);
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Since Java 1.7.0_06, `offset` and `count` fields were removed. `String.substring` makes new copies of `value`, which means we can forget about the memory leak but the runtime becomes $O(N)$ at the same time.
|
||||
|
||||
[http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/String.java](http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/String.java)
|
||||
```Java
|
||||
|
||||
public final class String
|
||||
implements java.io.Serializable, Comparable<String>, CharSequence {
|
||||
/** The value is used for character storage. */
|
||||
private final char value[];
|
||||
|
||||
/** Cache the hash code for the string */
|
||||
private int hash; // Default to 0
|
||||
|
||||
// ...
|
||||
|
||||
/**
|
||||
* Allocates a new {@code String} that contains characters from a subarray
|
||||
* of the character array argument. The {@code offset} argument is the
|
||||
* index of the first character of the subarray and the {@code count}
|
||||
* argument specifies the length of the subarray. The contents of the
|
||||
* subarray are copied; subsequent modification of the character array does
|
||||
* not affect the newly created string.
|
||||
*
|
||||
* @param value
|
||||
* Array that is the source of characters
|
||||
*
|
||||
* @param offset
|
||||
* The initial offset
|
||||
*
|
||||
* @param count
|
||||
* The length
|
||||
*
|
||||
* @throws IndexOutOfBoundsException
|
||||
* If the {@code offset} and {@code count} arguments index
|
||||
* characters outside the bounds of the {@code value} array
|
||||
*/
|
||||
public String(char value[], int offset, int count) {
|
||||
if (offset < 0) {
|
||||
throw new StringIndexOutOfBoundsException(offset);
|
||||
}
|
||||
if (count < 0) {
|
||||
throw new StringIndexOutOfBoundsException(count);
|
||||
}
|
||||
// Note: offset or count might be near -1>>>1.
|
||||
if (offset > value.length - count) {
|
||||
throw new StringIndexOutOfBoundsException(offset + count);
|
||||
}
|
||||
this.value = Arrays.copyOfRange(value, offset, offset+count);
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
/**
|
||||
* Returns a string that is a substring of this string. The
|
||||
* substring begins at the specified {@code beginIndex} and
|
||||
* extends to the character at index {@code endIndex - 1}.
|
||||
* Thus the length of the substring is {@code endIndex-beginIndex}.
|
||||
* <p>
|
||||
* Examples:
|
||||
* <blockquote><pre>
|
||||
* "hamburger".substring(4, 8) returns "urge"
|
||||
* "smiles".substring(1, 5) returns "mile"
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @return the specified substring.
|
||||
* @exception IndexOutOfBoundsException if the
|
||||
* {@code beginIndex} is negative, or
|
||||
* {@code endIndex} is larger than the length of
|
||||
* this {@code String} object, or
|
||||
* {@code beginIndex} is larger than
|
||||
* {@code endIndex}.
|
||||
*/
|
||||
public String substring(int beginIndex, int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new StringIndexOutOfBoundsException(beginIndex);
|
||||
}
|
||||
if (endIndex > value.length) {
|
||||
throw new StringIndexOutOfBoundsException(endIndex);
|
||||
}
|
||||
int subLen = endIndex - beginIndex;
|
||||
if (subLen < 0) {
|
||||
throw new StringIndexOutOfBoundsException(subLen);
|
||||
}
|
||||
return ((beginIndex == 0) && (endIndex == value.length)) ? this
|
||||
: new String(value, beginIndex, subLen);
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
The auther's comment[^2]:
|
||||
<a class="embedly-card" href="https://www.reddit.com/r/programming/comments/1qw73v/til_oracle_changed_the_internal_string/cdhb77f">Card</a>
|
||||
<script async src="//embed.redditmedia.com/widgets/platform.js" charset="UTF-8"></script>
|
||||
|
||||
[^1]: http://java-performance.info/changes-to-string-java-1-7-0_06/
|
||||
[^2]: https://www.reddit.com/r/programming/comments/1qw73v/til_oracle_changed_the_internal_string/cdhb77f/
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
title: Code Block Test
|
||||
date: 2021-06-27 09:00:00
|
||||
tags:
|
||||
- test
|
||||
categories:
|
||||
- tech
|
||||
keywords:
|
||||
- markdown
|
||||
- code block
|
||||
---
|
||||
|
||||
|
||||
`String`
|
||||
|
||||
Using indents:
|
||||
|
||||
text
|
||||
text
|
||||
text
|
||||
|
||||
|
||||
Fenced code block:
|
||||
|
||||
```
|
||||
text
|
||||
text
|
||||
<tag>
|
||||
```
|
||||
|
||||
Fenced code block with language (lineNumbersInTable = false):
|
||||
|
||||
```Java
|
||||
// JavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJavaJava
|
||||
public final class String
|
||||
implements java.io.Serializable, Comparable<String>, CharSequence
|
||||
{
|
||||
/** The value is used for character storage. */
|
||||
private final char value[];
|
||||
|
||||
/** The offset is the first index of the storage that is used. */
|
||||
private final int offset;
|
||||
|
||||
/** The count is the number of characters in the String. */
|
||||
private final int count;
|
||||
|
||||
/** Cache the hash code for the string */
|
||||
private int hash; // Default to 0
|
||||
|
||||
// ...
|
||||
|
||||
// Package private constructor which shares value array for speed.
|
||||
String(int offset, int count, char value[]) {
|
||||
this.value = value;
|
||||
this.offset = offset;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
/**
|
||||
* Returns a new string that is a substring of this string. The
|
||||
* substring begins at the specified <code>beginIndex</code> and
|
||||
* extends to the character at index <code>endIndex - 1</code>.
|
||||
* Thus the length of the substring is <code>endIndex-beginIndex</code>.
|
||||
* <p>
|
||||
* Examples:
|
||||
* <blockquote><pre>
|
||||
* "hamburger".substring(4, 8) returns "urge"
|
||||
* "smiles".substring(1, 5) returns "mile"
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @param beginIndex the beginning index, inclusive.
|
||||
* @param endIndex the ending index, exclusive.
|
||||
* @return the specified substring.
|
||||
* @exception IndexOutOfBoundsException if the
|
||||
* <code>beginIndex</code> is negative, or
|
||||
* <code>endIndex</code> is larger than the length of
|
||||
* this <code>String</code> object, or
|
||||
* <code>beginIndex</code> is larger than
|
||||
* <code>endIndex</code>.
|
||||
*/
|
||||
public String substring(int beginIndex, int endIndex) {
|
||||
if (beginIndex < 0) {
|
||||
throw new StringIndexOutOfBoundsException(beginIndex);
|
||||
}
|
||||
if (endIndex > count) {
|
||||
throw new StringIndexOutOfBoundsException(endIndex);
|
||||
}
|
||||
if (beginIndex > endIndex) {
|
||||
throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
|
||||
}
|
||||
return ((beginIndex == 0) && (endIndex == count)) ? this :
|
||||
new String(offset + beginIndex, endIndex - beginIndex, value);
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Using hugo's `highlight` [shortcode]([highlight](https://gohugo.io/content-management/syntax-highlighting/#highlight-shortcode)) (lineNumbersInTable = true):
|
||||
|
||||
{{< highlight typescript "linenos=table, hl_lines=8 18-21" >}}
|
||||
// TypeScript
|
||||
type OptionalUser = {
|
||||
[K in keyof User]?: User[K]
|
||||
}
|
||||
|
||||
// ! we can remove optional constraint
|
||||
type RequiredUser = {
|
||||
[K in keyof OptionalUser]-?: User[K]
|
||||
}
|
||||
|
||||
type NullableUser = {
|
||||
[K in keyof User]: User[K] | null
|
||||
}
|
||||
type ReadonlyUser = {
|
||||
readonly [K in keyof User]: User[K]
|
||||
}
|
||||
|
||||
// ! we can remove readonly constraint
|
||||
type ModifiableUser = {
|
||||
-readonly [K in keyof User]: User[K]
|
||||
}
|
||||
{{< /highlight >}}
|
||||
|
||||
Without line number
|
||||
|
||||
{{< highlight javascript "linenos=false" >}}
|
||||
(() => {
|
||||
|
||||
function createCopyButton(codeNode) {
|
||||
const copyBtn = document.createElement('button')
|
||||
copyBtn.className = 'code-copy-btn'
|
||||
copyBtn.type = 'button'
|
||||
copyBtn.innerText = 'copy'
|
||||
copyBtn.parentElement = codeNode.parentElement
|
||||
|
||||
let resetTimer
|
||||
copyBtn.addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(codeNode.innerText).then(() => {
|
||||
copyBtn.innerText = 'copied!'
|
||||
}).then(() => {
|
||||
clearTimeout(resetTimer)
|
||||
resetTimer = setTimeout(() => {
|
||||
copyBtn.innerText = 'copy'
|
||||
}, 1000)
|
||||
})
|
||||
})
|
||||
|
||||
return copyBtn
|
||||
}
|
||||
|
||||
document.querySelectorAll('pre > code').forEach((codeNode) => {
|
||||
const copyBtn = createCopyButton(codeNode);
|
||||
codeNode.parentNode.insertBefore(copyBtn, codeNode)
|
||||
})
|
||||
|
||||
})()
|
||||
{{< / highlight >}}
|
||||
14
hugo-content/themes/cactus/exampleSite/data/projects.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"list": [
|
||||
{
|
||||
"name":"Hugo",
|
||||
"url":"https://gohugo.io/",
|
||||
"desc":"The world’s fastest framework for building websites."
|
||||
},
|
||||
{
|
||||
"name":"Hugo Themes",
|
||||
"url":"https://github.com/gohugoio/hugoThemes",
|
||||
"desc":"A curated directory of Hugo themes"
|
||||
}
|
||||
]
|
||||
}
|
||||
27
hugo-content/themes/cactus/exampleSite/deploy.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
# if [ "`git status -s`" ]
|
||||
# then
|
||||
# echo "The working directory is dirty. Please commit any pending changes."
|
||||
# exit 1;
|
||||
# fi
|
||||
|
||||
echo "Deleting old publication"
|
||||
rm -rf public
|
||||
|
||||
echo "Generating site"
|
||||
hugo
|
||||
|
||||
echo "Updating master branch"
|
||||
cd public
|
||||
git init
|
||||
|
||||
git config --global push.default matching
|
||||
git config --global user.email "${GitHubEMail}"
|
||||
git config --global user.name "${GitHubUser}"
|
||||
|
||||
git add --all .
|
||||
git commit -m "Publishing to master (deploy.sh)"
|
||||
|
||||
echo "Pushing to github"
|
||||
git push --quiet --force https://${GitHubKEY}@github.com/${GitHubUser}/${GitHubRepo}.git master
|
||||
@@ -0,0 +1 @@
|
||||
{"Target":"css/styles.94f653e9e151e28067a7c5dbbc4600cbd5a3c721e79faaf971e523c40f3b249b8e4f20bb57810dfffa8d559ca5c140fd56eb4cd9c0853113ad08e66afdb08bdd.css","MediaType":"text/css","Data":{"Integrity":"sha512-lPZT6eFR4oBnp8XbvEYAy9WjxyHnn6r5ceUjxA87JJuOTyC7V4EN//qNVZylwUD9VutM2cCFMROtCOZq/bCL3Q=="}}
|
||||
BIN
hugo-content/themes/cactus/images/screenshot-classic.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
hugo-content/themes/cactus/images/screenshot-dark.png
Normal file
|
After Width: | Height: | Size: 182 KiB |
BIN
hugo-content/themes/cactus/images/screenshot-light.png
Normal file
|
After Width: | Height: | Size: 178 KiB |
BIN
hugo-content/themes/cactus/images/screenshot-tagsOverview.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
hugo-content/themes/cactus/images/screenshot.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
hugo-content/themes/cactus/images/tn.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
20
hugo-content/themes/cactus/layouts/_default/baseof.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ .Site.LanguageCode }}">
|
||||
{{ partial "head.html" . }}
|
||||
<body class="max-width mx-auto px3 ltr">
|
||||
<div class="content index py4">
|
||||
|
||||
{{ partial "header.html" . }}
|
||||
|
||||
{{ block "main" . }}
|
||||
{{ end }}
|
||||
|
||||
{{ partial "footer.html" . }}
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<link rel="stylesheet" href={{ "lib/font-awesome/css/all.min.css" | relURL }}>
|
||||
<script src={{ "lib/jquery/jquery.min.js" | relURL }}></script>
|
||||
<script src={{ "js/main.js" | relURL }}></script>
|
||||
</html>
|
||||
30
hugo-content/themes/cactus/layouts/_default/list.html
Normal file
@@ -0,0 +1,30 @@
|
||||
{{ define "main"}}
|
||||
<div id="archive">
|
||||
<ul class="post-list">
|
||||
|
||||
{{ $pages := .Paginator.Pages }}
|
||||
{{ if .Site.Params.showAllPostsArchive }}
|
||||
{{ $pages = .Pages }}
|
||||
{{ end }}
|
||||
|
||||
{{ range (sort $pages "Date" "desc") }}
|
||||
{{ $pageYear := (.Date.Format "2006") }}
|
||||
{{ if (ne $pageYear ($.Scratch.Get "year")) }}
|
||||
{{ $.Scratch.Set "year" $pageYear }}
|
||||
<h2>{{ $pageYear }}</h2>
|
||||
{{ end }}
|
||||
<li class="post-item">
|
||||
<div class="meta">
|
||||
<time datetime="{{ time .Date }}" itemprop="datePublished">{{ .Date.Format (.Site.Params.dateFormat | default "2006-01-02") }}</time>
|
||||
</div>
|
||||
<span>
|
||||
<a class="" href="{{ .Permalink }}">{{ if .Title }} {{ .Title }} {{ else }} Untitled {{ end }}</a>
|
||||
</span>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ if eq .Site.Params.showAllPostsArchive false }}
|
||||
{{ partial "pagination.html" . }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
12
hugo-content/themes/cactus/layouts/_default/single.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{{ define "main" }}
|
||||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<!-- TODO: gallery -->
|
||||
<div class="content" itemprop="articleBody">
|
||||
{{ if (eq .Type "search") }}
|
||||
<!-- TODO: search https://gohugo.io/tools/search/ -->
|
||||
{{ else }}
|
||||
{{ .Content }}
|
||||
{{ end }}
|
||||
</div>
|
||||
</article>
|
||||
{{ end }}
|
||||
41
hugo-content/themes/cactus/layouts/_default/terms.html
Normal file
@@ -0,0 +1,41 @@
|
||||
{{ define "main" }}
|
||||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<div class="content" itemprop="articleBody">
|
||||
{{ if (eq .Type "tags")}}
|
||||
<div id="tag-cloud">
|
||||
{{ if (eq (len .Data.Terms) 0) }}
|
||||
<div class="tag-cloud-title">
|
||||
No tags
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="tag-cloud-tags">
|
||||
{{ $AllRegularPagesCount := len .Site.RegularPages }}
|
||||
{{ range $elem := .Data.Terms.Alphabetical }}
|
||||
<a style="font-size: {{ (add 0.8 (mul 15 (div (float $elem.Count) $AllRegularPagesCount))) }}rem;" href="{{ $elem.Page.Permalink }}">
|
||||
{{- .Page.Title -}}
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ else if (eq .Type "categories")}}
|
||||
<div id="categories">
|
||||
{{ if (eq (len .Data.Terms) 0) }}
|
||||
<div class="category-list-title">
|
||||
No categories
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="category-list">
|
||||
<ul class="category-list">
|
||||
{{ range .Data.Terms.Alphabetical }}
|
||||
<li class="category-list-item">
|
||||
<a class="category-list-link" href="{{ .Page.Permalink }}">{{ .Page.Title }}</a>
|
||||
<span class="category-list-count">{{ .Count }}</span>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</article>
|
||||
{{ end }}
|
||||
111
hugo-content/themes/cactus/layouts/index.html
Normal file
@@ -0,0 +1,111 @@
|
||||
{{ define "main" }}
|
||||
<section id="about">
|
||||
{{ if isset .Site.Params "description" }}
|
||||
{{ .Site.Params.description | $.Page.RenderString }}
|
||||
{{ end }}
|
||||
{{ if isset .Site.Params "social" }}
|
||||
<p>Find me on
|
||||
{{ $length := (len .Site.Params.social) }}
|
||||
{{ range $index, $elem := .Site.Params.social}}
|
||||
{{ if eq $elem.name "email" }}
|
||||
<a class="icon" target="_blank" rel="noopener" href="mailto:{{ $elem.link }}" aria-label="Email">
|
||||
<i class="fas fa-envelope" aria-hidden="true"></i>
|
||||
</a>
|
||||
{{ else if eq $elem.name "rss" }}
|
||||
<a class="icon" target="_blank" rel="noopener" href="{{ $elem.link }}" aria-label="RSS">
|
||||
<i class="fas fa-rss" aria-hidden="true"></i>
|
||||
</a>
|
||||
{{ else if eq $elem.name "scholar" }}
|
||||
<a class="icon" target="_blank" rel="noopener" href="{{ $elem.link }}" aria-label="Google Scholar">
|
||||
<i class="fas fa-graduation-cap" aria-hidden="true"></i>
|
||||
</a>
|
||||
{{ else }}
|
||||
<a class="icon" target="_blank" rel="noopener" href="{{ $elem.link }}" aria-label="{{ $elem.name }}">
|
||||
<i class="fab fa-{{ lower $elem.name }}" aria-hidden="true"></i>
|
||||
</a>
|
||||
{{ end }}
|
||||
{{ if (lt (add $index 2) $length) }}
|
||||
{{- print " , " -}}
|
||||
{{ else if (lt (add $index 1) $length) }}
|
||||
{{- print " and " -}}
|
||||
{{ else }}
|
||||
{{- print "." -}}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</p>
|
||||
{{ end }}
|
||||
{{ partial "optional-about.html" . }}
|
||||
</section>
|
||||
|
||||
<section id="writing">
|
||||
<span class="h1"><a href="{{ .Site.Params.mainSection | absURL }}">{{ .Site.Params.mainSectionTitle | default "Writings" }}</a></span>
|
||||
{{ if (and (and (isset .Site.Params "tagsoverview") (eq .Site.Params.tagsOverview true)) (gt (len .Site.Taxonomies.tags) 0)) }}
|
||||
<span class="h2">Topics</span>
|
||||
<span class="widget tagcloud">
|
||||
{{ $AllRegularPagesCount := len .Site.RegularPages }}
|
||||
{{ range $elem := .Site.Taxonomies.tags.Alphabetical }}
|
||||
<a style="font-size: {{ (add 0.5 (mul 5 (div (float $elem.Count) $AllRegularPagesCount))) }}rem;" href="{{ $elem.Page.Permalink }}">
|
||||
{{- .Page.Title -}}
|
||||
</a>
|
||||
{{ end }}
|
||||
</span>
|
||||
<span class="h2">Most recent</span>
|
||||
{{ end }}
|
||||
|
||||
{{ $showAllPostsOnHomePage := false }}
|
||||
{{ if (isset .Site.Params "showallpostsonhomepage") }}
|
||||
{{ $showAllPostsOnHomePage = .Site.Params.ShowAllPostsOnHomePage }}
|
||||
{{ end }}
|
||||
{{ $dataFormat := .Site.Params.dateFormat | default "2006-01-02" }}
|
||||
{{ $mainPosts := (sort ( where site.RegularPages "Type" "in" site.Params.mainSections ) "Date" "desc") }}
|
||||
{{ if $showAllPostsOnHomePage }}
|
||||
<ul class="post-list">
|
||||
{{ range (.Paginate $mainPosts).Pages }}
|
||||
<li class="post-item">
|
||||
<div class="meta"><time datetime="{{ time .Date }}" itemprop="datePublished">{{ .Date.Format $dataFormat }}</time></div>
|
||||
<span><a href="{{ .Permalink }}">{{ if .Title }} {{- .Title -}} {{ else }} {{- print "Untitled" -}}{{ end }}</a></span>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
|
||||
{{ partial "pagination.html" . }}
|
||||
|
||||
{{ else }}
|
||||
<ul class="post-list">
|
||||
{{ .Scratch.Set "count" 5 }}
|
||||
{{ if isset .Site.Params "postsonhomepage" }}
|
||||
{{ .Scratch.Set "count" .Site.Params.postsOnHomePage }}
|
||||
{{ end }}
|
||||
{{ range (first (.Scratch.Get "count") $mainPosts) }}
|
||||
<li class="post-item">
|
||||
<div class="meta"><time datetime="{{ time .Date }}" itemprop="datePublished">{{ .Date.Format $dataFormat }}</time></div>
|
||||
<span><a href="{{ .Permalink }}">{{ if .Title }} {{- .Title -}} {{ else }} {{- print "Untitled" -}}{{ end }}</a></span>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ end }}
|
||||
</section>
|
||||
|
||||
{{ $showProjectsList := false }}
|
||||
{{ if (isset .Site.Params "showprojectslist") }}
|
||||
{{ $showProjectsList = .Site.Params.showProjectsList }}
|
||||
{{ else if .Site.Data.projects }}
|
||||
{{ $showProjectsList = true }}
|
||||
{{ end }}
|
||||
{{ if $showProjectsList }}
|
||||
{{ $projectsUrl := "#" }}
|
||||
{{ if isset .Site.Params "projectsurl" }}
|
||||
{{ $projectsUrl = .Site.Params.projectsUrl }}
|
||||
{{ end }}
|
||||
<section id="projects">
|
||||
<span class="h1"><a href="{{ $projectsUrl }}">Projects</a></span>
|
||||
<ul class="project-list">
|
||||
{{ range .Site.Data.projects.list }}
|
||||
<li class="project-item">
|
||||
<a href="{{ .url }}">{{ .name }}</a>: {{ .desc | markdownify }}
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</section>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
22
hugo-content/themes/cactus/layouts/partials/comments.html
Normal file
@@ -0,0 +1,22 @@
|
||||
{{ if (not (isset .Site.Params "comments")) }}
|
||||
{{ .Scratch.Set "enable_comments" false }}
|
||||
{{ else if (isset .Params "comments") }}
|
||||
{{ .Scratch.Set "enable_comments" .Params.comments }}
|
||||
{{ else if (isset .Site.Params.Comments "enabled") }}
|
||||
{{ .Scratch.Set "enable_comments" .Site.Params.Comments.Enabled }}
|
||||
{{ else }}
|
||||
{{ .Scratch.Set "enable_comments" true }}
|
||||
{{ end }}
|
||||
|
||||
{{ $enable_comments := .Scratch.Get "enable_comments" }}
|
||||
{{ if $enable_comments }}
|
||||
<div class="blog-post-comments">
|
||||
{{ if (or (not (isset .Site.Params.Comments "engine")) (eq .Site.Params.Comments.Engine "disqus")) }}
|
||||
{{ partial "comments/disqus.html" . }}
|
||||
{{ else if eq .Site.Params.Comments.Engine "utterances" }}
|
||||
{{ partial "comments/utterances.html" . }}
|
||||
{{ else if eq .Site.Params.Comments.Engine "cactus_comments" }}
|
||||
{{ partial "comments/cactus_comments.html" . }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
@@ -0,0 +1,11 @@
|
||||
<div id="cactus-comments-thread">
|
||||
<script>
|
||||
initComments({
|
||||
node: document.getElementById("cactus-comments-thread"),
|
||||
defaultHomeserverUrl: '{{ default "https://matrix.cactus.chat:8448" .Site.Params.Comments.Cactuscomments.ServerUrl }}',
|
||||
serverName: '{{ default "cactus.chat" .Site.Params.Comments.Cactuscomments.ServerName }}',
|
||||
siteName: "{{ .Site.Params.Comments.Cactuscomments.SiteName }}",
|
||||
commentSectionId: "{{ .RelPermalink }}"
|
||||
})
|
||||
</script>
|
||||
</div>
|
||||
@@ -0,0 +1,17 @@
|
||||
<div id="disqus_thread">
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
// Don't ever inject Disqus on localhost--it creates unwanted
|
||||
// discussions from 'localhost:1313' on your Disqus account...
|
||||
// if (window.location.hostname == "localhost")
|
||||
// return;
|
||||
|
||||
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
|
||||
var disqus_shortname = '{{ if .Site.DisqusShortname }}{{ .Site.DisqusShortname }}{{ else }}{{ .Site.Title }}{{ end }}';
|
||||
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
|
||||
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||||
})();
|
||||
</script>
|
||||
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
|
||||
<a href="https://disqus.com/" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
|
||||
</div>
|
||||
@@ -0,0 +1,10 @@
|
||||
<div id="disquss_thread">
|
||||
<script src="https://utteranc.es/client.js"
|
||||
repo="{{ .Site.Params.Comments.Utterances.Repo }}"
|
||||
issue-term="pathname"
|
||||
label="{{ .Site.Params.Comments.Utterances.Label }}"
|
||||
theme="{{ .Site.Params.Comments.Utterances.Theme }}"
|
||||
crossorigin="anonymous"
|
||||
async>
|
||||
</script>
|
||||
</div>
|
||||
2
hugo-content/themes/cactus/layouts/partials/favicon.html
Normal file
@@ -0,0 +1,2 @@
|
||||
<!-- TODO -->
|
||||
<link rel="icon" type="image/png" href="{{ "images/favicon.ico" | absURL }}" />
|
||||
14
hugo-content/themes/cactus/layouts/partials/footer.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<footer id="footer">
|
||||
<div class="footer-left">
|
||||
Copyright © {{ now.Format "2006" }} {{ if .Site.Copyright }} {{ print .Site.Copyright | markdownify }} {{ else }} {{ print .Site.Title }} {{ end }}
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<nav>
|
||||
<ul>
|
||||
{{ range .Site.Menus.main }}
|
||||
<li><a href="{{ .URL }}">{{ .Name }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</footer>
|
||||
50
hugo-content/themes/cactus/layouts/partials/head.html
Normal file
@@ -0,0 +1,50 @@
|
||||
<head>
|
||||
<link rel="preload" href="{{ "lib/font-awesome/webfonts/fa-brands-400.woff2" | relURL }}" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<link rel="preload" href="{{ "lib/font-awesome/webfonts/fa-regular-400.woff2" | relURL }}" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<link rel="preload" href="{{ "lib/font-awesome/webfonts/fa-solid-900.woff2" | relURL }}" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<link rel="preload" href="{{ "lib/JetBrainsMono/web/woff2/JetBrainsMono-Regular.woff2" | relURL }}" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<script type="text/javascript" src="https://latest.cactus.chat/cactus.js"></script>
|
||||
<link rel="stylesheet" href="https://latest.cactus.chat/style.css" type="text/css">
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>{{ if .IsPage }} {{ .Title }} | {{ end }}{{ .Site.Title }}</title>
|
||||
<link rel = 'canonical' href = '{{ .Permalink }}'>
|
||||
{{ with .Site.Params.description }}<meta name="description" content="{{ . }}">{{ end }}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="all,follow">
|
||||
<meta name="googlebot" content="index,follow,snippet,archive">
|
||||
{{ template "_internal/opengraph.html" . }}
|
||||
{{ template "_internal/twitter_cards.html" . }}
|
||||
{{ .Scratch.Set "colortheme" "white"}}
|
||||
{{ if .Site.Params.Colortheme }}
|
||||
{{ .Scratch.Set "colortheme" .Site.Params.Colortheme }}
|
||||
{{ end }}
|
||||
{{ $colortheme := .Scratch.Get "colortheme" }}
|
||||
|
||||
{{- $options := (dict "targetPath" "css/styles.css" "outputStyle" "compressed" "enableSourceMap" "true") -}}
|
||||
{{- $styles := resources.Get "scss/style.scss" | resources.ExecuteAsTemplate "scss/style.scss" . | resources.ToCSS $options | resources.Fingerprint "sha512" }}
|
||||
<link rel="stylesheet" href="{{ $styles.Permalink }}" integrity="{{ $styles.Data.Integrity }}">
|
||||
|
||||
<!-- Custom CSS -->
|
||||
{{ range .Site.Params.css }} <link rel="stylesheet" href="{{ . | absURL }}"> {{ end }}
|
||||
{{ `
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
` | safeHTML }}
|
||||
|
||||
{{ partial "favicon.html" . }}
|
||||
{{ if .Site.Params.rss }}
|
||||
{{ with .OutputFormats.Get "RSS" }}
|
||||
{{ printf `<link href="%s" rel="%s" type="%s" title="%s" />` .Permalink .Rel .MediaType.Type $.Site.Title | safeHTML }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if .Site.GoogleAnalytics }}
|
||||
{{ if .Site.Params.googleAnalyticsAsync }}
|
||||
{{ template "_internal/google_analytics_async.html" . }}
|
||||
{{ else }}
|
||||
{{ template "_internal/google_analytics.html" . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</head>
|
||||
25
hugo-content/themes/cactus/layouts/partials/header.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<header id="header">
|
||||
<a href="{{ .Site.BaseURL }}">
|
||||
{{ if (isset .Site.Params "gravatar") }}
|
||||
<div id="logo" style="background-image: url(https://www.gravatar.com/avatar/{{ md5 .Site.Params.gravatar }}?s=100&d=identicon)"></div>
|
||||
{{ else if (isset .Site.Params "logo") }}
|
||||
<div id="logo" style="background-image: url({{ .Site.Params.logo | absURL }})"></div>
|
||||
{{ else }}
|
||||
<div id="logo" style="background-image: url({{ "images/logo.png" | absURL }})"></div>
|
||||
{{ end}}
|
||||
<div id="title">
|
||||
<h1>{{ .Site.Title }}</h1>
|
||||
</div>
|
||||
</a>
|
||||
<div id="nav">
|
||||
<ul>
|
||||
<li class="icon">
|
||||
<a href="#" aria-label="Menu"><i class="fas fa-bars fa-2x" aria-hidden="true"></i></a>
|
||||
</li>
|
||||
{{ range .Site.Menus.main }}
|
||||
<li><a href="{{ .URL }}">{{ .Name }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
57
hugo-content/themes/cactus/layouts/partials/page_nav.html
Normal file
@@ -0,0 +1,57 @@
|
||||
<div id="header-post">
|
||||
<a id="menu-icon" href="#"><i class="fas fa-bars fa-lg"></i></a>
|
||||
<a id="menu-icon-tablet" href="#"><i class="fas fa-bars fa-lg"></i></a>
|
||||
<a id="top-icon-tablet" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" style="display:none;" aria-label="Top of Page"><i class="fas fa-chevron-up fa-lg"></i></a>
|
||||
<span id="menu">
|
||||
<span id="nav">
|
||||
<ul>
|
||||
{{ range .Site.Menus.main }}
|
||||
<li><a href="{{ .URL }}">{{ .Name }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</span>
|
||||
<br/>
|
||||
<span id="actions">
|
||||
<ul>
|
||||
{{ if .Prev }}
|
||||
<li>
|
||||
<a class="icon" href=" {{ .Prev.Permalink }}" aria-label="Previous">
|
||||
<i class="fas fa-chevron-left" aria-hidden="true" onmouseover="$('#i-prev').toggle();" onmouseout="$('#i-prev').toggle();"></i>
|
||||
</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
{{ if .Next }}
|
||||
<li>
|
||||
<a class="icon" href="{{ .Next.Permalink }}" aria-label="Next">
|
||||
<i class="fas fa-chevron-right" aria-hidden="true" onmouseover="$('#i-next').toggle();" onmouseout="$('#i-next').toggle();"></i>
|
||||
</a>
|
||||
</li>
|
||||
{{ end }}
|
||||
<li>
|
||||
<a class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" aria-label="Top of Page">
|
||||
<i class="fas fa-chevron-up" aria-hidden="true" onmouseover="$('#i-top').toggle();" onmouseout="$('#i-top').toggle();"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="icon" href="#" aria-label="Share">
|
||||
<i class="fas fa-share-alt" aria-hidden="true" onmouseover="$('#i-share').toggle();" onmouseout="$('#i-share').toggle();" onclick="$('#share').toggle();return false;"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<span id="i-prev" class="info" style="display:none;">Previous post</span>
|
||||
<span id="i-next" class="info" style="display:none;">Next post</span>
|
||||
<span id="i-top" class="info" style="display:none;">Back to top</span>
|
||||
<span id="i-share" class="info" style="display:none;">Share post</span>
|
||||
</span>
|
||||
<br/>
|
||||
<div id="share" style="display: none">
|
||||
{{ .Scratch.Set "icon_class_name" ""}}
|
||||
{{ partial "share.html" . }}
|
||||
</div>
|
||||
{{ if not .Site.Params.tocInline }}
|
||||
<div id="toc">
|
||||
{{ .TableOfContents }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -0,0 +1,38 @@
|
||||
<div id="footer-post-container">
|
||||
<div id="footer-post">
|
||||
|
||||
<div id="nav-footer" style="display: none">
|
||||
<ul>
|
||||
{{ range .Site.Menus.main }}
|
||||
<li><a href="{{ .URL }}">{{ .Name }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{ if not .Site.Params.tocInline }}
|
||||
<div id="toc-footer" style="display: none">
|
||||
{{ .TableOfContents }}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
<div id="share-footer" style="display: none">
|
||||
{{ .Scratch.Set "icon_class_name" "fa-lg" }}
|
||||
{{ partial "share.html" . }}
|
||||
</div>
|
||||
|
||||
<div id="actions-footer">
|
||||
<!-- TODO: rewrite the toggle function. hide the others when one menu is displayed -->
|
||||
<a id="menu-toggle" class="icon" href="#" onclick="$('#nav-footer').toggle();return false;" aria-label="Menu">
|
||||
<i class="fas fa-bars fa-lg" aria-hidden="true"></i> Menu</a>
|
||||
{{ if not .Site.Params.tocInline }}
|
||||
<a id="toc-toggle" class="icon" href="#" onclick="$('#toc-footer').toggle();return false;" aria-label="TOC">
|
||||
<i class="fas fa-list fa-lg" aria-hidden="true"></i> TOC</a>
|
||||
{{ end }}
|
||||
<a id="share-toggle" class="icon" href="#" onclick="$('#share-footer').toggle();return false;" aria-label="Share">
|
||||
<i class="fas fa-share-alt fa-lg" aria-hidden="true"></i> share</a>
|
||||
<a id="top" style="display:none" class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" aria-label="Top of Page">
|
||||
<i class="fas fa-chevron-up fa-lg" aria-hidden="true"></i> Top</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
13
hugo-content/themes/cactus/layouts/partials/pagination.html
Normal file
@@ -0,0 +1,13 @@
|
||||
{{ $pag := $.Paginator }}
|
||||
{{ if gt $pag.TotalPages 1 }}
|
||||
<div class="pagination">
|
||||
{{ if $pag.HasPrev }}
|
||||
<a href="{{ $pag.Prev.URL }}"><i class="fas fa-angle-left"></i></a>
|
||||
{{ end }}
|
||||
<span class="page-number">Page {{ $pag.PageNumber }} of {{ $pag.TotalPages }}</span>
|
||||
{{ if $pag.HasNext }}
|
||||
<a href="{{ $pag.Next.URL }}"><i class="fas fa-angle-right"></i>
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
54
hugo-content/themes/cactus/layouts/partials/share.html
Normal file
@@ -0,0 +1,54 @@
|
||||
<ul>
|
||||
{{ $icon_class_name := .Scratch.Get "icon_class_name"}}
|
||||
{{ if .Description }}
|
||||
{{ .Scratch.Set "description" .Description }}
|
||||
{{ else }}
|
||||
{{ .Scratch.Set "description" .Summary }}
|
||||
{{ end }}
|
||||
{{ $description := .Scratch.Get "description" }}
|
||||
<li>
|
||||
<a class="icon" href="http://www.facebook.com/sharer.php?u={{ .Permalink }}" aria-label="Facebook">
|
||||
<i class="fab fa-facebook {{ $icon_class_name }}" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="icon" href="https://twitter.com/share?url={{ .Permalink }}&text={{ .Title }}" aria-label="Twitter">
|
||||
<i class="fab fa-twitter {{ $icon_class_name }}" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="icon" href="http://www.linkedin.com/shareArticle?url={{ .Permalink }}&title={{ .Title }}" aria-label="Linkedin">
|
||||
<i class="fab fa-linkedin {{ $icon_class_name }}" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="icon" href="https://pinterest.com/pin/create/bookmarklet/?url={{ .Permalink }}&is_video=false&description={{ .Title }}" aria-label="Pinterest">
|
||||
<i class="fab fa-pinterest {{ $icon_class_name }}" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="icon" href="mailto:?subject={{ .Title }}&body=Check out this article: {{ .Permalink }}" aria-label="Email">
|
||||
<i class="fas fa-envelope {{ $icon_class_name }}" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="icon" href="https://getpocket.com/save?url={{ .Permalink }}&title={{ .Title }}" aria-label="Pocket">
|
||||
<i class="fab fa-get-pocket {{ $icon_class_name }}" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="icon" href="http://reddit.com/submit?url={{ .Permalink }}&title={{ .Title }}" aria-label="reddit">
|
||||
<i class="fab fa-reddit {{ $icon_class_name }}" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="icon" href="http://www.tumblr.com/share/link?url={{ .Permalink }}&name={{ .Title }}&description={{ $description }}" aria-label="Tumblr">
|
||||
<i class="fab fa-tumblr {{ $icon_class_name }}" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="icon" href="https://news.ycombinator.com/submitlink?u={{ .Permalink }}&t={{ .Title }}" aria-label="Hacker News">
|
||||
<i class="fab fa-hacker-news {{ $icon_class_name }}" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
117
hugo-content/themes/cactus/layouts/posts/single.html
Normal file
@@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ .Site.LanguageCode }}">
|
||||
{{ partial "head.html" . }}
|
||||
<body class="max-width mx-auto px3 ltr">
|
||||
<div class="content index py4">
|
||||
|
||||
{{ partial "page_nav.html" . }}
|
||||
|
||||
<article class="post" itemscope itemtype="http://schema.org/BlogPosting">
|
||||
<header>
|
||||
<h1 class="posttitle" itemprop="name headline">
|
||||
{{ .Title }}
|
||||
</h1>
|
||||
<div class="meta">
|
||||
{{ if (or (isset .Site "author") (isset .Site "title"))}}
|
||||
<span class="author" itemprop="author" itemscope itemtype="http://schema.org/Person">
|
||||
<span itemprop="name">
|
||||
{{ if isset .Site "author" }}
|
||||
{{ .Site.Author }}
|
||||
{{ else if isset .Site "title" }}
|
||||
{{ .Site.Title }}
|
||||
{{ end }}
|
||||
</span>
|
||||
</span>
|
||||
{{ end }}
|
||||
<div class="postdate">
|
||||
{{ $dataFormat := .Site.Params.dateFormat | default "2006-01-02" }}
|
||||
<time datetime="{{ .Date }}" itemprop="datePublished">{{ .Date.Format $dataFormat }}</time>
|
||||
{{ if (and .Site.Params.show_updated (ne .Lastmod .Date)) }}
|
||||
(Updated: <time datetime="{{ .Lastmod }}" itemprop="dateModified">{{ .Lastmod.Format $dataFormat }}</time>)
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ $showReadTime := .Site.Params.showReadTime | default false }}
|
||||
{{if $showReadTime}}
|
||||
<div class="article-read-time">
|
||||
<i class="far fa-clock"></i>
|
||||
{{ $readTime := math.Round (div (countwords .Content) 220.0) }}
|
||||
{{ $readTime }} minute read
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if gt .Params.categories 0 }}
|
||||
<div class="article-category">
|
||||
<i class="fas fa-archive"></i>
|
||||
{{ range $index, $value := .Params.categories }}
|
||||
{{ if gt $index 0 }} {{ print ", " }} {{ end }}
|
||||
<a class="category-link" href="{{ "/categories/" | relLangURL }}{{ $value | urlize }}">{{ $value }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if gt .Params.tags 0 }}
|
||||
<div class="article-tag">
|
||||
<i class="fas fa-tag"></i>
|
||||
{{ range $index, $value := .Params.tags }}
|
||||
{{ if gt $index 0 }} {{ print ", " }} {{ end }}
|
||||
<a class="tag-link" href="{{ "/tags/" | relLangURL }}{{ $value | urlize }}" rel="tag">{{ $value }}</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{{ with .Resources.ByType "image" }}
|
||||
<div class="article-gallery">
|
||||
{{ range $index, $value := . }}
|
||||
<a class="gallery-item" href="{{ .RelPermalink }}" rel="gallery_{{ $index }}">
|
||||
<img src="{{ .RelPermalink }}" itemprop="image" />
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if .Site.Params.tocInline }}
|
||||
<div id="toc">
|
||||
{{ .TableOfContents }}
|
||||
</div>
|
||||
{{ end }}
|
||||
<div class="content" itemprop="articleBody">
|
||||
{{ .Content}}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{{ partial "comments.html" . }}
|
||||
|
||||
{{ partial "page_nav_mobile.html" . }}
|
||||
|
||||
{{ partial "footer.html" . }}
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<link rel="stylesheet" href={{ "/lib/font-awesome/css/all.min.css" | relURL }}>
|
||||
<script src={{ "/lib/jquery/jquery.min.js" | relURL }}></script>
|
||||
<script src={{ "/js/main.js" | relURL }}></script>
|
||||
{{ if not (isset site.Params "disablecopy") }}
|
||||
<script src={{ "js/code-copy.js" | relURL }}></script>
|
||||
{{ end }}
|
||||
{{ $mathjax := false }}
|
||||
{{ if isset .Params "mathjax" }}
|
||||
{{ $mathjax = .Params.mathjax }}
|
||||
{{ else if isset .Site.Params "mathjax" }}
|
||||
{{ $mathjax = .Site.Params.mathjax }}
|
||||
{{ end }}
|
||||
{{ if $mathjax }}
|
||||
<script>
|
||||
MathJax = {
|
||||
tex: {
|
||||
inlineMath: [['$', '$'], ['\\(', '\\)']]
|
||||
},
|
||||
svg: {
|
||||
fontCache: 'global'
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" id="MathJax-script" async
|
||||
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js">
|
||||
</script>
|
||||
{{ end }}
|
||||
</html>
|
||||
BIN
hugo-content/themes/cactus/static/images/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
hugo-content/themes/cactus/static/images/logo.png
Normal file
|
After Width: | Height: | Size: 219 KiB |
36
hugo-content/themes/cactus/static/js/code-copy.js
Normal file
@@ -0,0 +1,36 @@
|
||||
(() => {
|
||||
|
||||
function createCopyButton(codeNode) {
|
||||
const copyBtn = document.createElement('button')
|
||||
copyBtn.className = 'code-copy-btn'
|
||||
copyBtn.type = 'button'
|
||||
copyBtn.innerText = 'copy'
|
||||
|
||||
let resetTimer
|
||||
copyBtn.addEventListener('click', () => {
|
||||
navigator.clipboard.writeText(codeNode.innerText).then(() => {
|
||||
copyBtn.innerText = 'copied!'
|
||||
}).then(() => {
|
||||
clearTimeout(resetTimer)
|
||||
resetTimer = setTimeout(() => {
|
||||
copyBtn.innerText = 'copy'
|
||||
}, 1000)
|
||||
})
|
||||
})
|
||||
|
||||
return copyBtn
|
||||
}
|
||||
|
||||
document.querySelectorAll('pre > code')
|
||||
.forEach((codeNode) => {
|
||||
const copyBtn = createCopyButton(codeNode);
|
||||
const preNode = codeNode.parentNode
|
||||
codeNode.parentNode.insertBefore(copyBtn, codeNode)
|
||||
})
|
||||
|
||||
document.querySelectorAll('.highlight table > tbody > tr > td:first-child .code-copy-btn')
|
||||
.forEach((btn) => {
|
||||
btn.remove()
|
||||
})
|
||||
|
||||
})()
|
||||
13
hugo-content/themes/cactus/static/js/feather.min.js
vendored
Normal file
113
hugo-content/themes/cactus/static/js/main.js
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Sets up Justified Gallery.
|
||||
*/
|
||||
if (!!$.prototype.justifiedGallery) {
|
||||
var options = {
|
||||
rowHeight: 140,
|
||||
margins: 4,
|
||||
lastRow: "justify"
|
||||
};
|
||||
$(".article-gallery").justifiedGallery(options);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
/**
|
||||
* Shows the responsive navigation menu on mobile.
|
||||
*/
|
||||
$("#header > #nav > ul > .icon").click(function() {
|
||||
$("#header > #nav > ul").toggleClass("responsive");
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Controls the different versions of the menu in blog post articles
|
||||
* for Desktop, tablet and mobile.
|
||||
*/
|
||||
if ($(".post").length) {
|
||||
var menu = $("#menu");
|
||||
var nav = $("#menu > #nav");
|
||||
var menuIcon = $("#menu-icon, #menu-icon-tablet");
|
||||
|
||||
/**
|
||||
* Display the menu on hi-res laptops and desktops.
|
||||
*/
|
||||
if ($(document).width() >= 1440) {
|
||||
menu.css("visibility", "visible");
|
||||
menuIcon.addClass("active");
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the menu if the menu icon is clicked.
|
||||
*/
|
||||
menuIcon.click(function() {
|
||||
if (menu.css("visibility") === "hidden") {
|
||||
menu.css("visibility", "visible");
|
||||
menuIcon.addClass("active");
|
||||
} else {
|
||||
menu.css("visibility", "hidden");
|
||||
menuIcon.removeClass("active");
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a scroll listener to the menu to hide/show the navigation links.
|
||||
*/
|
||||
if (menu.length) {
|
||||
$(window).on("scroll", function() {
|
||||
var topDistance = menu.offset().top;
|
||||
|
||||
// hide only the navigation links on desktop
|
||||
if (!nav.is(":visible") && topDistance < 50) {
|
||||
nav.show();
|
||||
} else if (nav.is(":visible") && topDistance > 100) {
|
||||
nav.hide();
|
||||
}
|
||||
|
||||
// on tablet, hide the navigation icon as well and show a "scroll to top
|
||||
// icon" instead
|
||||
if ( ! $( "#menu-icon" ).is(":visible") && topDistance < 50 ) {
|
||||
$("#menu-icon-tablet").show();
|
||||
$("#top-icon-tablet").hide();
|
||||
} else if (! $( "#menu-icon" ).is(":visible") && topDistance > 100) {
|
||||
$("#menu-icon-tablet").hide();
|
||||
$("#top-icon-tablet").show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show mobile navigation menu after scrolling upwards,
|
||||
* hide it again after scrolling downwards.
|
||||
*/
|
||||
if ($( "#footer-post").length) {
|
||||
var lastScrollTop = 0;
|
||||
$(window).on("scroll", function() {
|
||||
var topDistance = $(window).scrollTop();
|
||||
|
||||
if (topDistance > lastScrollTop){
|
||||
// downscroll -> show menu
|
||||
$("#footer-post").hide();
|
||||
} else {
|
||||
// upscroll -> hide menu
|
||||
$("#footer-post").show();
|
||||
}
|
||||
lastScrollTop = topDistance;
|
||||
|
||||
// close all submenu"s on scroll
|
||||
$("#nav-footer").hide();
|
||||
$("#toc-footer").hide();
|
||||
$("#share-footer").hide();
|
||||
|
||||
// show a "navigation" icon when close to the top of the page,
|
||||
// otherwise show a "scroll to the top" icon
|
||||
if (topDistance < 50) {
|
||||
$("#actions-footer > #top").hide();
|
||||
} else if (topDistance > 100) {
|
||||
$("#actions-footer > #top").show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
154
hugo-content/themes/cactus/static/js/search.js
Normal file
@@ -0,0 +1,154 @@
|
||||
// A local search script with the help of
|
||||
// [hexo-generator-search](https://github.com/PaicHyperionDev/hexo-generator-search)
|
||||
// Copyright (C) 2015
|
||||
// Joseph Pan <http://github.com/wzpan>
|
||||
// Shuhao Mao <http://github.com/maoshuhao>
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as
|
||||
// published by the Free Software Foundation; either version 2.1 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
// 02110-1301 USA
|
||||
//
|
||||
// Modified by:
|
||||
// Pieter Robberechts <http://github.com/probberechts>
|
||||
|
||||
/*exported searchFunc*/
|
||||
var searchFunc = function(path, searchId, contentId) {
|
||||
|
||||
function stripHtml(html) {
|
||||
html = html.replace(/<style([\s\S]*?)<\/style>/gi, "");
|
||||
html = html.replace(/<script([\s\S]*?)<\/script>/gi, "");
|
||||
html = html.replace(/<figure([\s\S]*?)<\/figure>/gi, "");
|
||||
html = html.replace(/<\/div>/ig, "\n");
|
||||
html = html.replace(/<\/li>/ig, "\n");
|
||||
html = html.replace(/<li>/ig, " * ");
|
||||
html = html.replace(/<\/ul>/ig, "\n");
|
||||
html = html.replace(/<\/p>/ig, "\n");
|
||||
html = html.replace(/<br\s*[\/]?>/gi, "\n");
|
||||
html = html.replace(/<[^>]+>/ig, "");
|
||||
return html;
|
||||
}
|
||||
|
||||
function getAllCombinations(keywords) {
|
||||
var i, j, result = [];
|
||||
|
||||
for (i = 0; i < keywords.length; i++) {
|
||||
for (j = i + 1; j < keywords.length + 1; j++) {
|
||||
result.push(keywords.slice(i, j).join(" "));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: path,
|
||||
dataType: "xml",
|
||||
success: function(xmlResponse) {
|
||||
// get the contents from search data
|
||||
var datas = $("entry", xmlResponse).map(function() {
|
||||
return {
|
||||
title: $("title", this).text(),
|
||||
content: $("content", this).text(),
|
||||
url: $("link", this).attr("href")
|
||||
};
|
||||
}).get();
|
||||
|
||||
var $input = document.getElementById(searchId);
|
||||
if (!$input) { return; }
|
||||
var $resultContent = document.getElementById(contentId);
|
||||
|
||||
$input.addEventListener("input", function(){
|
||||
var resultList = [];
|
||||
var keywords = getAllCombinations(this.value.trim().toLowerCase().split(" "))
|
||||
.sort(function(a,b) { return b.split(" ").length - a.split(" ").length; });
|
||||
$resultContent.innerHTML = "";
|
||||
if (this.value.trim().length <= 0) {
|
||||
return;
|
||||
}
|
||||
// perform local searching
|
||||
datas.forEach(function(data) {
|
||||
var matches = 0;
|
||||
if (!data.title || data.title.trim() === "") {
|
||||
data.title = "Untitled";
|
||||
}
|
||||
var dataTitle = data.title.trim().toLowerCase();
|
||||
var dataContent = stripHtml(data.content.trim());
|
||||
var dataUrl = data.url;
|
||||
var indexTitle = -1;
|
||||
var indexContent = -1;
|
||||
var firstOccur = -1;
|
||||
// only match artiles with not empty contents
|
||||
if (dataContent !== "") {
|
||||
keywords.forEach(function(keyword) {
|
||||
indexTitle = dataTitle.indexOf(keyword);
|
||||
indexContent = dataContent.indexOf(keyword);
|
||||
|
||||
if( indexTitle >= 0 || indexContent >= 0 ){
|
||||
matches += 1;
|
||||
if (indexContent < 0) {
|
||||
indexContent = 0;
|
||||
}
|
||||
if (firstOccur < 0) {
|
||||
firstOccur = indexContent;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// show search results
|
||||
if (matches > 0) {
|
||||
var searchResult = {};
|
||||
searchResult.rank = matches;
|
||||
searchResult.str = "<li><a href='"+ dataUrl +"' class='search-result-title'>"+ dataTitle +"</a>";
|
||||
if (firstOccur >= 0) {
|
||||
// cut out 100 characters
|
||||
var start = firstOccur - 20;
|
||||
var end = firstOccur + 80;
|
||||
|
||||
if(start < 0){
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if(start == 0){
|
||||
end = 100;
|
||||
}
|
||||
|
||||
if(end > dataContent.length){
|
||||
end = dataContent.length;
|
||||
}
|
||||
|
||||
var matchContent = dataContent.substr(start, end);
|
||||
|
||||
// highlight all keywords
|
||||
var regS = new RegExp(keywords.join("|"), "gi");
|
||||
matchContent = matchContent.replace(regS, function(keyword) {
|
||||
return "<em class=\"search-keyword\">"+keyword+"</em>";
|
||||
});
|
||||
|
||||
searchResult.str += "<p class=\"search-result\">" + matchContent +"...</p>";
|
||||
}
|
||||
searchResult.str += "</li>";
|
||||
resultList.push(searchResult);
|
||||
}
|
||||
});
|
||||
resultList.sort(function(a, b) {
|
||||
return b.rank - a.rank;
|
||||
});
|
||||
var result ="<ul class=\"search-result-list\">";
|
||||
for (var i = 0; i < resultList.length; i++) {
|
||||
result += resultList[i].str;
|
||||
}
|
||||
result += "</ul>";
|
||||
$resultContent.innerHTML = result;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
201
hugo-content/themes/cactus/static/lib/JetBrainsMono/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||