Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 52f90c5673 | |||
| 428303c9fb | |||
| 4811fcc29e | |||
| ff70556d8e | |||
| fb20358834 | |||
| 8a43a8181e | |||
| 2e9666521b | |||
| b75ec5aba8 | |||
| 657d5ccd31 | |||
| 5801030e31 | |||
| 8adadb0be1 | |||
| 9af6849a82 | |||
| 650664a95d | |||
| 6c21f36d77 | |||
| 378dbec981 | |||
| b10e5696e2 | |||
| f05f429728 | |||
| d9b52860b6 | |||
| 242f69c731 | |||
| 1a0191a716 | |||
| 17f5355557 | |||
| 20d9ea66e5 | |||
| 0f6cabe3ee | |||
| d3739c2f81 | |||
| cfdeee6a46 | |||
| 4f6ce8695c | |||
| 39789220a8 | |||
| 12eb8a2111 | |||
| da4cf9b528 | |||
| 0e561b38f7 | |||
| 0240db512f | |||
| 85ef555bfd | |||
| 11336d3451 | |||
| 1a350aab89 | |||
| e6df007875 | |||
| a4d6e7a0b2 | |||
| a0c64ad5b2 | |||
| 08892bd0b8 | |||
| 5b98cd36cd | |||
| 662496c214 | |||
| 1d8ccdb5ad | |||
| 138a3c3342 | |||
| fc28e8c5c8 | |||
| d67c7fc926 | |||
| 8a9b36d3db | |||
| deea763923 | |||
| 8f34df0cbd | |||
| 87f6da2957 | |||
| 766fee12d5 | |||
| 6488bed03c | |||
| ea7092ebef | |||
| ac79051db4 | |||
| 1194223ddd | |||
| ad76f6fb7a | |||
| f39a05ba59 | |||
| 67f20f4379 | |||
| 2c857250c0 | |||
| 931b6e0208 | |||
| 8ea9ca2b2f | |||
| 9def31abeb | |||
| 1878d023bf | |||
| fef327ec61 | |||
| 5ddd86e4aa | |||
| 87dd96adbd | |||
| 9de0095c8d | |||
| 7480da7568 | |||
| 81c65b3cce | |||
| 441c05f096 | |||
| 671573bc2e | |||
| 17b0bea213 | |||
| 0ad96058d5 | |||
| 092efca34d | |||
| 0303019641 | |||
| 8efff11ac2 |
@@ -2,3 +2,5 @@ mode:regex
|
|||||||
^_dist/
|
^_dist/
|
||||||
^sites/[^/]+/data/
|
^sites/[^/]+/data/
|
||||||
^sites/[^/]+/wwwroot/
|
^sites/[^/]+/wwwroot/
|
||||||
|
|
||||||
|
^shields_cache/
|
||||||
|
|||||||
6
.hgtags
6
.hgtags
@@ -1 +1,7 @@
|
|||||||
42a17645b5b21d7fe395767de7fa3e26ee999014 release-r54
|
42a17645b5b21d7fe395767de7fa3e26ee999014 release-r54
|
||||||
|
0f89ae041c2ee60cc1ea308d047fce816b19c490 release-r64
|
||||||
|
d4733a95c3428db8722ce0d0350d17bbbabc8720 release-r72
|
||||||
|
7c92f9e2e4818d74eded59ad516d7d58b4072f8d release-r97
|
||||||
|
b1426986ff5f265f79d6412c0e81ccc7cae652ff release-r118
|
||||||
|
2b11aaaad98718025d020c9fbaa451f65d413477 release-r126
|
||||||
|
36cb5fdcbd0450a32ba9e88f7582a78b0b50db94 release-r132
|
||||||
|
|||||||
12
TODO.txt
12
TODO.txt
@@ -1,16 +1,8 @@
|
|||||||
|
|
||||||
- Support project redirection (e.g. `code.ivysaur.me` was renamed to `codesite`, breaking links)
|
|
||||||
|
|
||||||
- If no projects have any images, don't generate spritesheet
|
|
||||||
|
|
||||||
- Move tag javascript and no-image image out of per-site configuration
|
|
||||||
|
|
||||||
- Merge "written in" and "tags"
|
- Merge "written in" and "tags"
|
||||||
|
|
||||||
- Per-project directories, to prevent overwriting files (e.g. 'screenshot.jpg')
|
|
||||||
|
|
||||||
- Cache recent changes somehow
|
|
||||||
|
|
||||||
- RSS for recent changes
|
- RSS for recent changes
|
||||||
|
|
||||||
- RSS for all projects
|
- RSS for all projects
|
||||||
|
|
||||||
|
- Switchable CSS (reddit theme, 4chan theme, HN theme)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
A static site generator for a portfolio website.
|
A static site generator for a portfolio website.
|
||||||
|
|
||||||
The scripts used to generate the code.ivysaur.me website.
|
This script is currently in use to generate the code.ivysaur.me website.
|
||||||
|
|
||||||
Written in PHP, Bash
|
Written in PHP, Bash
|
||||||
|
|
||||||
@@ -14,17 +14,51 @@ Written in PHP, Bash
|
|||||||
|
|
||||||
=CHANGELOG=
|
=CHANGELOG=
|
||||||
|
|
||||||
2015-04-05: r64
|
2017-10-28: v132
|
||||||
|
- Fix an issue with not applying project rename redirections in the new URL format
|
||||||
|
- Fix a cosmetic issue with classifying downloads named with numeric strings
|
||||||
|
- Fix a cosmetic issue with truncated sheilds caused by ID collisions
|
||||||
|
|
||||||
|
2017-04-23: v126
|
||||||
|
- Breaking: BASEURL is now a mandatory field
|
||||||
|
- Feature: Support `[go-get]` tags
|
||||||
|
- Feature: Canonical paths for SEO
|
||||||
|
- Feature: Use extension-less paths by default (old .html paths still supported via redirect)
|
||||||
|
- Feature: Support `[url]` tags
|
||||||
|
- Enhancement: Cache-busting parameters on homepage thumbnails
|
||||||
|
|
||||||
|
2016-04-19: v118
|
||||||
|
- Feature: Classify download artefacts by matching CHANGELOG entry
|
||||||
|
- Feature: Allow sorting by lifespan, artefacts, release entries
|
||||||
|
- Feature: Separate site generation code from site data repositories
|
||||||
|
- Enhancement: Update supplied CSS normalize script
|
||||||
|
- Enhancement: Read parallelism from number of CPUs
|
||||||
|
|
||||||
|
2015-11-08: v97
|
||||||
|
- Feature: Support BBCode b/i/spoiler/entry tags
|
||||||
|
- Feature: Add more CSS styles to allow per-site customistaion
|
||||||
|
- Feature: Use http://shields.io images where appropriate
|
||||||
|
- Feature: Optional blurbs=off, article_header={string}, shields_prefix=true configuration directives
|
||||||
|
- Fix an issue with project update-time detection
|
||||||
|
- Fix a cosmetic issue with whitespace
|
||||||
|
|
||||||
|
2015-04-05: v72
|
||||||
|
- Feature: Support redirecting old project names
|
||||||
|
- Feature: Add file hash in download URLs to prevent filename collisions
|
||||||
|
- Fix an issue generating spritesheets even if no project images are present
|
||||||
|
- Don't include `ctime` when estimating project update time
|
||||||
|
|
||||||
|
2015-04-05: v64
|
||||||
- Feature: Support sorting projects
|
- Feature: Support sorting projects
|
||||||
|
|
||||||
2015-04-04: r54
|
2015-04-04: v54
|
||||||
- Feature: Support multiple code sites
|
- Feature: Support multiple code sites
|
||||||
- Fix an issue with parallel builds on some versions of windows
|
- Fix an issue with parallel builds on some versions of windows
|
||||||
- Fix an issue corrupting URL links with multiple parameters
|
- Fix an issue corrupting URL links with multiple parameters
|
||||||
- Fix a cosmetic issue with page overflow
|
- Fix a cosmetic issue with page overflow
|
||||||
- Fix a cosmetic issue with whitespace on code elements
|
- Fix a cosmetic issue with whitespace on code elements
|
||||||
|
|
||||||
2014-07-02: r39
|
2014-07-02: v39
|
||||||
- Feature: Tags
|
- Feature: Tags
|
||||||
- Feature: Generate pages in parallel
|
- Feature: Generate pages in parallel
|
||||||
- Enhancement: Support raw HTML sections in page content
|
- Enhancement: Support raw HTML sections in page content
|
||||||
@@ -33,8 +67,8 @@ Written in PHP, Bash
|
|||||||
- Fix a cosmetic issue with download sort order
|
- Fix a cosmetic issue with download sort order
|
||||||
- Fix a cosmetic issue with page layout
|
- Fix a cosmetic issue with page layout
|
||||||
|
|
||||||
2013-09-28: r13
|
2013-09-28: v13
|
||||||
- Initial public source code release
|
- Initial public source code release
|
||||||
|
|
||||||
2013-09-21: r3
|
2013-09-21: v3
|
||||||
- Initial deployment
|
- Initial deployment
|
||||||
|
|||||||
16
deploy.sh
16
deploy.sh
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
LOCALDIR=/cygdrive/c/www/m6/code/sites/code.ivysaur.me/wwwroot
|
|
||||||
|
|
||||||
chmod -R 644 "$LOCALDIR"
|
|
||||||
chmod -R a+X "$LOCALDIR"
|
|
||||||
|
|
||||||
rsync --delete -avz -e "ssh -i /cygdrive/c/www/ms1_deploy_key -p 2222" --progress \
|
|
||||||
"$LOCALDIR" \
|
|
||||||
www-data@ms1.ivysaur.me:~/code.ivysaur.me/
|
|
||||||
|
|
||||||
# n.b. set chmod -R a+rX remote side?
|
|
||||||
|
|
||||||
read -p "Press any key to continue..."
|
|
||||||
395
lib/CProject.php
Normal file
395
lib/CProject.php
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class CProject {
|
||||||
|
|
||||||
|
private $dir;
|
||||||
|
|
||||||
|
public $projname;
|
||||||
|
public $shortdesc = '(no description)';
|
||||||
|
public $subtag = '';
|
||||||
|
public $lastupdate = 0;
|
||||||
|
public $numreleases = 0;
|
||||||
|
|
||||||
|
private $longdesc = '';
|
||||||
|
private $prefix_html = '';
|
||||||
|
private $images = array();
|
||||||
|
private $downloads = array();
|
||||||
|
private $downloads_hashes = array();
|
||||||
|
|
||||||
|
public $downloads_section_was_replaced = false;
|
||||||
|
|
||||||
|
public $lifespan = 0;
|
||||||
|
|
||||||
|
public $tags = array();
|
||||||
|
|
||||||
|
public $homeimage = null;
|
||||||
|
|
||||||
|
protected $go_get_target = '';
|
||||||
|
protected $git_repo = '';
|
||||||
|
|
||||||
|
public function __construct($dirname, $projname) {
|
||||||
|
$this->dir = BASEDIR.'data/'.$dirname.'/';
|
||||||
|
$this->projname = $projname;
|
||||||
|
|
||||||
|
$matches = [];
|
||||||
|
|
||||||
|
// Identify resources in folder
|
||||||
|
|
||||||
|
$ls = scandir($this->dir);
|
||||||
|
$found_real_lastupdate = false;
|
||||||
|
foreach($ls as $file) {
|
||||||
|
if ($file[0] == '.') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($file == 'README.txt') {
|
||||||
|
|
||||||
|
$this->longdesc = file_get_contents($this->dir.'README.txt');
|
||||||
|
$this->longdesc = str_replace("\r", "", $this->longdesc); // filter windows CR
|
||||||
|
$this->longdesc = preg_replace("~[\s\t]*$~s", "", $this->longdesc); // filter trailing spaces at line endings
|
||||||
|
|
||||||
|
// Guess 'last update' time
|
||||||
|
if (preg_match('~\n(\d\d\d\d-\d\d-\d\d)~', $this->longdesc, $matches)) {
|
||||||
|
// Use first date entry (assumed to be a CHANGELOG)
|
||||||
|
$this->lastupdate = strtotime($matches[1]);
|
||||||
|
$found_real_lastupdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find number of releases
|
||||||
|
preg_match_all('~\n(\d\d\d\d-\d\d-\d\d)~', $this->longdesc, $matches, PREG_SET_ORDER);
|
||||||
|
$this->numreleases = count($matches);
|
||||||
|
|
||||||
|
// Find support lifespan (newest minus youngest)
|
||||||
|
$eldest = time();
|
||||||
|
$newest = 0;
|
||||||
|
foreach($matches as $match) {
|
||||||
|
$stamp = strtotime($match[1]);
|
||||||
|
$eldest = min($stamp, $eldest);
|
||||||
|
$newest = max($stamp, $newest);
|
||||||
|
}
|
||||||
|
$this->lifespan = floor(max(0, $newest - $eldest) / 3600); // could divide by 86400 but it doesn't matter
|
||||||
|
|
||||||
|
// Find 'written in'
|
||||||
|
if (preg_match('~Written in ([^\\n]+)~', $this->longdesc, $matches)) {
|
||||||
|
$this->subtag = rtrim($matches[1], ' .');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find `go-get` tags
|
||||||
|
$this->longdesc = preg_replace_callback('~\[go-get\](.+)\[/go-get\]~', function($matches) {
|
||||||
|
$this->go_get_target = $matches[1];
|
||||||
|
return '';
|
||||||
|
}, $this->longdesc);
|
||||||
|
|
||||||
|
// Find tags
|
||||||
|
if (preg_match('~Tags: ([^\\n]+)~', $this->longdesc, $matches)) {
|
||||||
|
$this->tags = array_map('trim', explode(',', $matches[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find `entry` tags
|
||||||
|
$this->longdesc = preg_replace(
|
||||||
|
'~\\[entry=([^\\]]+?)\\](.+?)\\[/entry\\]~m',
|
||||||
|
'[url='.BASEURL.'\\1/]\\2[/url]', // hesc still hasn't happened, transform bbcode->bbcode
|
||||||
|
$this->longdesc
|
||||||
|
);
|
||||||
|
|
||||||
|
// Strip out any markdown image links
|
||||||
|
// [](doc/image1.png)
|
||||||
|
$this->longdesc = preg_replace('~\\[!.+?\\)\\]\\(.+?\\)~m', '', $this->longdesc);
|
||||||
|
|
||||||
|
// Find "Written in" tags
|
||||||
|
$this->prefix_html = '';
|
||||||
|
$this->longdesc = preg_replace_callback('~\nWritten in ([^\\n]+)~ms', function($matches) {
|
||||||
|
$this->prefix_html .= (
|
||||||
|
(SHIELDS_PREFIX ? mkshield('build', 'success', 'brightgreen').' ' : '').
|
||||||
|
mkshield('written in', rtrim($matches[1], '.'), 'blue')
|
||||||
|
);
|
||||||
|
return '';
|
||||||
|
}, $this->longdesc);
|
||||||
|
|
||||||
|
// Find 'git-repository' tags
|
||||||
|
$this->longdesc = preg_replace_callback('~\[git\](.+)\[/git\]~', function($matches) {
|
||||||
|
$this->git_repo = $matches[1];
|
||||||
|
if (strlen($this->prefix_html) > 0) {
|
||||||
|
$this->prefix_html .= ' ';
|
||||||
|
}
|
||||||
|
$this->prefix_html .= '<a href="'.hesc($this->git_repo).'">'.mkshield('vcs', 'git', 'yellowgreen', ['logo' => 'git']).'</a>';
|
||||||
|
return '';
|
||||||
|
}, $this->longdesc);
|
||||||
|
|
||||||
|
// Collapse multiple blank lines
|
||||||
|
$this->longdesc = ltrim($this->longdesc, "\n");
|
||||||
|
while(strpos($this->longdesc, "\n\n\n") !== false) {
|
||||||
|
$this->longdesc = str_replace("\n\n\n", "\n\n", $this->longdesc);
|
||||||
|
}
|
||||||
|
$this->longdesc = rtrim($this->longdesc, "\n")."\n";
|
||||||
|
|
||||||
|
// Extract short description (last)
|
||||||
|
$parts = explode("\n", $this->longdesc);
|
||||||
|
$this->shortdesc = array_shift($parts);
|
||||||
|
$this->shortdesc[0] = strtolower($this->shortdesc[0]); // cosmetic lowercase
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $found_real_lastupdate) {
|
||||||
|
$this->lastupdate = max(
|
||||||
|
$this->lastupdate,
|
||||||
|
// filectime($this->dir.$file),
|
||||||
|
($file == 'README.txt' ? filectime($this->dir.$file) : filemtime($this->dir.$file)) // Don't count README updates
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_image($file)) {
|
||||||
|
$this->images[] = $file;
|
||||||
|
} else {
|
||||||
|
$this->downloads[] = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
natcasesort($this->downloads);
|
||||||
|
$this->downloads = array_reverse($this->downloads);
|
||||||
|
|
||||||
|
for($i = 0, $e = count($this->downloads); $i !== $e; ++$i) {
|
||||||
|
$this->downloads_hashes[] = (
|
||||||
|
sha1_file($this->dir.$this->downloads[$i])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function genHomeImage() {
|
||||||
|
if (count($this->images)) {
|
||||||
|
|
||||||
|
$this->homeimage = mkthumbnail(
|
||||||
|
$this->dir.$this->images[0],
|
||||||
|
null, // raw handle
|
||||||
|
INDEX_THUMB_W, INDEX_THUMB_H
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function filterLongDescArea() {
|
||||||
|
|
||||||
|
// If *all* downloads can be assigned to a changelog release entry,
|
||||||
|
// then move the files
|
||||||
|
// A release entry is marked by any string following the date field.
|
||||||
|
|
||||||
|
// Add one more NL than we really want, for regex reasons
|
||||||
|
$this->longdesc .= "\n";
|
||||||
|
|
||||||
|
preg_match_all('~^(\d\d\d\d-\d\d-\d\d)\s?:? (.+?)\n\n~ms', $this->longdesc, $matches, PREG_SET_ORDER);
|
||||||
|
|
||||||
|
do {
|
||||||
|
// Ensure changelog exists
|
||||||
|
if (! count($matches)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all tags
|
||||||
|
$known_tags = [];
|
||||||
|
foreach($matches as $i => $match) {
|
||||||
|
$tag = trim(explode("\n", $match[2])[0]);
|
||||||
|
$known_tags[$tag] = $i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure all release entries have tags
|
||||||
|
if (count($matches) != count($known_tags)) {
|
||||||
|
error_log("[".$this->projname."] not all release entries have tags\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure all downloads can be assigned to tags.
|
||||||
|
// In the event of a download matching multiple tags, it'll
|
||||||
|
// be assigned to the newest (topmost) entry
|
||||||
|
$found_idx = [];
|
||||||
|
$render_per_tag = [];
|
||||||
|
foreach(array_keys($known_tags) as $tagname) {
|
||||||
|
$render_per_tag[$tagname] = [];
|
||||||
|
}
|
||||||
|
foreach($this->downloads as $idx => $filename) {
|
||||||
|
foreach(array_keys($known_tags) as $tagname) {
|
||||||
|
if (stripos($filename, (string)$tagname) !== false) {
|
||||||
|
$found_idx[$idx] = $tagname;
|
||||||
|
|
||||||
|
$render_per_tag[$tagname][$idx] = $filename;
|
||||||
|
break; // next file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count($found_idx) != count($this->downloads)) {
|
||||||
|
error_log("[".$this->projname."] not all downloads have matching tags");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make HTML modifications
|
||||||
|
foreach($known_tags as $tag_name => $tag_idx) {
|
||||||
|
$find = rtrim($matches[$tag_idx][0]);
|
||||||
|
$this->longdesc = str_replace(
|
||||||
|
$find,
|
||||||
|
$find.'${{TAG_'.$tag_idx.'}}',
|
||||||
|
$this->longdesc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->longdesc = substr($this->longdesc, 0, strlen($this->longdesc)-1); // Strip the extra NL we added
|
||||||
|
|
||||||
|
$this->longdesc = text2html($this->longdesc);
|
||||||
|
foreach($known_tags as $tag_name => $tag_idx) {
|
||||||
|
$this->longdesc = str_replace(
|
||||||
|
'${{TAG_'.$tag_idx.'}}',
|
||||||
|
$this->renderDownloadsBlock($render_per_tag[$tag_name], false),
|
||||||
|
$this->longdesc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->longdesc = str_replace("</ul>\n<br />", "</ul>", $this->longdesc);
|
||||||
|
|
||||||
|
// Skip displaying the global downloads area
|
||||||
|
// This flag also indicates that the content has been pre-HTMLified
|
||||||
|
$this->downloads_section_was_replaced = true;
|
||||||
|
|
||||||
|
// Successful upgrade
|
||||||
|
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
if (! $this->downloads_section_was_replaced) {
|
||||||
|
$this->longdesc = substr($this->longdesc, 0, strlen($this->longdesc)-1); // Strip the extra NL we added
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function numDownloads() {
|
||||||
|
return count($this->downloads);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write() {
|
||||||
|
|
||||||
|
// Generate image thumbnails
|
||||||
|
|
||||||
|
foreach($this->images as $idx => $image) {
|
||||||
|
$outfile = BASEDIR.'wwwroot/img/'.$this->projname.'_'.$idx;
|
||||||
|
copy($this->dir.$image, $outfile.'.'.str_ext($image));
|
||||||
|
|
||||||
|
mkthumbnail($outfile.'.'.str_ext($image), $outfile.'_thumb.jpg', PAGE_THUMB_W, PAGE_THUMB_H);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy downloads to wwwroot
|
||||||
|
|
||||||
|
foreach($this->downloads as $idx => $filename) {
|
||||||
|
$cmkdir = @mkdir( BASEDIR.'wwwroot/srv/'.$this->downloads_hashes[$idx] );
|
||||||
|
|
||||||
|
if (! $cmkdir) {
|
||||||
|
fputs(
|
||||||
|
STDOUT,
|
||||||
|
"WARNING: Couldn't create directory ".$this->downloads_hashes[$idx].
|
||||||
|
" for file '${filename}'".
|
||||||
|
" in project '".$this->projname."'!\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(
|
||||||
|
$this->dir.$filename,
|
||||||
|
BASEDIR.'wwwroot/srv/'.$this->downloads_hashes[$idx].'/'.$filename
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate index page
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$this->index();
|
||||||
|
|
||||||
|
$extra_head_items = [];
|
||||||
|
|
||||||
|
$extra_head_items[] = '<link rel="canonical" href="'.hesc(BASEURL.$this->projname).'">'; // TODO include golang `go get` meta if necessary
|
||||||
|
|
||||||
|
if (strlen($this->go_get_target) > 0) {
|
||||||
|
$extra_head_items[] = '<meta name="go-import" content="'.hesc($this->go_get_target).'">'; // TODO include golang `go get` meta if necessary
|
||||||
|
}
|
||||||
|
|
||||||
|
$idxfile = template($this->projname.' | '.SITE_TITLE, ob_get_clean(), implode("\n", $extra_head_items));
|
||||||
|
mkdir(BASEDIR.'wwwroot/'.$this->projname);
|
||||||
|
|
||||||
|
file_put_contents(BASEDIR.'wwwroot/'.$this->projname.'/index.html', $idxfile); // new URL format
|
||||||
|
file_put_contents(BASEDIR.'wwwroot/'.$this->projname.'.html', redirecthtml(BASEURL.$this->projname)); // old URL format
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassAttr() {
|
||||||
|
if (count($this->tags)) {
|
||||||
|
return 'taggedWith-'.implode(' taggedWith-', $this->tags);
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderDownloadsBlock($render_downloads, $include_header=false) {
|
||||||
|
if (! count($render_downloads)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<?php if ($include_header) { ?>
|
||||||
|
<strong>DOWNLOAD</strong>
|
||||||
|
<?php } ?>
|
||||||
|
<ul class="<?=$include_header ? 'downloads-large' : 'downloads-small' ?>">
|
||||||
|
<?php foreach($render_downloads as $idx => $filename) { ?>
|
||||||
|
<li>
|
||||||
|
<a href="<?=BASEURL?>srv/<?=hesc($this->downloads_hashes[$idx])?>/<?=hesc(rawurlencode($filename))?>"><?=hesc($filename)?></a>
|
||||||
|
<small>
|
||||||
|
<?=hesc(fbytes(filesize(BASEDIR.'wwwroot/srv/'.$this->downloads_hashes[$idx].'/'.$filename)))?>
|
||||||
|
</small>
|
||||||
|
</li>
|
||||||
|
<?php } ?>
|
||||||
|
</ul>
|
||||||
|
<?php
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
|
||||||
|
$this->filterLongDescArea();
|
||||||
|
$longdesc_html = $this->downloads_section_was_replaced ? $this->longdesc : text2html($this->longdesc);
|
||||||
|
|
||||||
|
?>
|
||||||
|
<h2><?=hesc(str_replace('_', ' ', $this->projname))?></h2>
|
||||||
|
|
||||||
|
<div class="projinfo">
|
||||||
|
|
||||||
|
<div class="projbody projbody_<?=(count($this->images) ? 'half' : 'full')?>w">
|
||||||
|
|
||||||
|
<?php if (strlen($this->prefix_html)) { ?>
|
||||||
|
<p style="margin-top:0;"><?=$this->prefix_html?></p>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<strong><?=hesc(strtoupper(ARTICLE_HEADER))?></strong>
|
||||||
|
|
||||||
|
<div class="content-paragraph">
|
||||||
|
<?=$longdesc_html?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?=file_get_contents(BASEDIR.'/footer.htm')?>
|
||||||
|
|
||||||
|
<?php if (! $this->downloads_section_was_replaced) { ?>
|
||||||
|
<?=$this->renderDownloadsBlock($this->downloads, true)?>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (count($this->images)) { ?>
|
||||||
|
<div class="projimg">
|
||||||
|
<?php foreach($this->images as $idx => $origname) { ?>
|
||||||
|
<a href="<?=BASEURL?>img/<?=hesc(urlencode($this->projname))?>_<?=$idx?>.<?=str_ext($origname)?>"><img src="<?=BASEURL?>img/<?=hesc(urlencode($this->projname))?>_<?=$idx?>_thumb.jpg" class="thumbimage"></a>
|
||||||
|
<?php } ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="clear:both;"></div>
|
||||||
|
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
lib/bootstrap.php
Executable file
57
lib/bootstrap.php
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
ini_set('display_errors', 'On');
|
||||||
|
date_default_timezone_set('Etc/UTC');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
require __DIR__.'/util.php';
|
||||||
|
require __DIR__.'/template.php';
|
||||||
|
require __DIR__.'/CProject.php';
|
||||||
|
|
||||||
|
define('SHIELDS_CACHE_DIR', __DIR__.'/../shields_cache/');
|
||||||
|
|
||||||
|
function main($args) {
|
||||||
|
$basedir = './';
|
||||||
|
$total = $args[0];
|
||||||
|
$pos = $args[1];
|
||||||
|
|
||||||
|
// Parse configuration
|
||||||
|
|
||||||
|
$config = @parse_ini_file(
|
||||||
|
$basedir . 'config.ini',
|
||||||
|
true,
|
||||||
|
INI_SCANNER_RAW
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($config === false) {
|
||||||
|
die("[FATAL] Couldn't load '${basedir}/config.ini'!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
define('BASEDIR', $basedir);
|
||||||
|
define('BASEURL', trim($config['codesite']['baseurl']));
|
||||||
|
define('SITE_TITLE', trim($config['codesite']['title']));
|
||||||
|
define('PAGE_THUMB_W', intval($config['codesite']['page_thumb_w']));
|
||||||
|
define('PAGE_THUMB_H', intval($config['codesite']['page_thumb_h']));
|
||||||
|
define('INDEX_THUMB_W', intval($config['codesite']['index_thumb_w']));
|
||||||
|
define('INDEX_THUMB_H', intval($config['codesite']['index_thumb_h']));
|
||||||
|
define('SHOW_BLURBS', !(isset($config['codesite']['blurbs']) && $config['codesite']['blurbs'] === 'off') );
|
||||||
|
define('ARTICLE_HEADER', (isset($config['codesite']['article_header']) ? $config['codesite']['article_header'] : 'ABOUT') );
|
||||||
|
define('SHIELDS_PREFIX', isset($config['codesite']['shields_prefix']));
|
||||||
|
|
||||||
|
// Perform build tasks
|
||||||
|
|
||||||
|
if ($pos == 0) {
|
||||||
|
buildcommon();
|
||||||
|
if (array_key_exists('redirect', $config)) {
|
||||||
|
buildredirects( $config['redirect'] );
|
||||||
|
}
|
||||||
|
if (array_key_exists('golang-subpackages', $config)) {
|
||||||
|
buildgosubpackages( $config['golang-subpackages'] );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buildprojects($pos, array_decimate(listprojects(), $total, $pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main(array_slice($_SERVER['argv'], 1));
|
||||||
214
lib/template.php
Normal file
214
lib/template.php
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function template($title, $content, $extra_head='') {
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title><?=hesc($title)?></title>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
|
||||||
|
<meta name="viewport" content="width=960" >
|
||||||
|
<?=$extra_head?>
|
||||||
|
<link rel="icon" href="<?=BASEURL?>static/favicon.ico" type="image/x-icon">
|
||||||
|
<link type="text/css" rel="stylesheet" href="<?=BASEURL?>static/normalize.css">
|
||||||
|
<link type="text/css" rel="stylesheet" href="<?=BASEURL?>static/style.css">
|
||||||
|
<script type="text/javascript" src="<?=BASEURL?>static/site.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container">
|
||||||
|
<div id="content">
|
||||||
|
<?=file_get_contents(BASEDIR.'/header.htm')?>
|
||||||
|
<?=$content?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
<?php
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function listprojects() {
|
||||||
|
// List projects
|
||||||
|
|
||||||
|
$ls = scandir(BASEDIR.'data');
|
||||||
|
rsort($ls);
|
||||||
|
$projects = array();
|
||||||
|
foreach($ls as $dirname) {
|
||||||
|
if ($dirname[0] == '.') continue;
|
||||||
|
if (! is_dir(BASEDIR.'data/'.$dirname)) continue;
|
||||||
|
$matches = array();
|
||||||
|
|
||||||
|
if (preg_match('~(?:\d+-)?(.+)~', $dirname, $matches)) {
|
||||||
|
$projects[$dirname] = $matches[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $projects;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildprojects($id, $projects) {
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
foreach($projects as $dirname => $projectname) {
|
||||||
|
|
||||||
|
echo sprintf("@%1d [%3d/%3d] ".$projectname."...\n", $id, ++$count, count($projects));
|
||||||
|
|
||||||
|
$pr = new CProject($dirname, $projectname);
|
||||||
|
$pr->write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildcommon() {
|
||||||
|
|
||||||
|
echo "@0 [ 0/ ?] Common files...\n";
|
||||||
|
|
||||||
|
$projects = listprojects();
|
||||||
|
|
||||||
|
// Build all projects
|
||||||
|
|
||||||
|
$plist = array();
|
||||||
|
|
||||||
|
$handles = array();
|
||||||
|
$handle_lookup = array();
|
||||||
|
|
||||||
|
$alphasort = [];
|
||||||
|
|
||||||
|
foreach($projects as $dirname => $projectname) {
|
||||||
|
|
||||||
|
$pr = new CProject($dirname, $projectname);
|
||||||
|
$pr->genHomeImage(); // thumbnail
|
||||||
|
|
||||||
|
$plist[] = $pr;
|
||||||
|
|
||||||
|
if (is_null($pr->homeimage)) {
|
||||||
|
$handle_lookup[$projectname] = null;
|
||||||
|
} else {
|
||||||
|
$handle_lookup[$projectname] = count($handles);
|
||||||
|
$handles[] = $pr->homeimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
$alphasort[] = [$pr->projname, count($plist)-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
usort($alphasort, function($a, $b) {
|
||||||
|
return strcasecmp($a[0], $b[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
$alphaidx = [];
|
||||||
|
|
||||||
|
foreach($alphasort as $a) {
|
||||||
|
$alphaidx[ $a[1] ] = count($alphaidx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build homepage spritesheet
|
||||||
|
|
||||||
|
if (count($handles)) {
|
||||||
|
mkspritesheet($handles, BASEDIR.'wwwroot/logos.jpg', INDEX_THUMB_W, INDEX_THUMB_H);
|
||||||
|
array_map('imagedestroy', $handles); // free
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache-busting stylesheet
|
||||||
|
|
||||||
|
$style = '.homeimage-sprite { background-image: url("logos.jpg?'.md5_file(BASEDIR.'wwwroot/logos.jpg').'"); }';
|
||||||
|
|
||||||
|
// Build index page
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if (file_exists(BASEDIR.'homepage_blurb.htm')) { ?>
|
||||||
|
<!-- homepage blurb {{ -->
|
||||||
|
<?=file_get_contents(BASEDIR.'homepage_blurb.htm')?>
|
||||||
|
<!-- }} -->
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
<?php echo $style; ?>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<table id="projtable-main" class="projtable">
|
||||||
|
<?php foreach ($plist as $i => $pr) { ?>
|
||||||
|
<tr class="<?=$pr->getClassAttr()?>"
|
||||||
|
data-sort-mt="-<?=$pr->lastupdate?>"
|
||||||
|
data-sort-ct="<?=$i?>"
|
||||||
|
data-sort-al="<?=$alphaidx[$i]?>"
|
||||||
|
data-sort-nr="-<?=$pr->numreleases?>"
|
||||||
|
data-sort-nf="-<?=$pr->numDownloads()?>"
|
||||||
|
data-sort-ls="-<?=$pr->lifespan?>"
|
||||||
|
>
|
||||||
|
<td>
|
||||||
|
<a href="<?=hesc(BASEURL.urlencode($pr->projname))?>/"><?=(is_null($handle_lookup[$pr->projname]) ? '<div class="no-image"></div>' : '<div class="homeimage homeimage-sprite" style="background-position:0 -'.($handle_lookup[$pr->projname]*INDEX_THUMB_H).'px"></div>')?></a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<strong><?=hesc(str_replace('_', ' ', $pr->projname))?></strong><?php if (SHOW_BLURBS) { ?>,
|
||||||
|
<?=hesc($pr->shortdesc)?>
|
||||||
|
<?php } ?>
|
||||||
|
<a href="<?=hesc(BASEURL.urlencode($pr->projname))?>/" class="article-read-more">more...</a>
|
||||||
|
<?php if (strlen($pr->subtag) || count($pr->tags)) { ?>
|
||||||
|
<br>
|
||||||
|
<small>
|
||||||
|
<?=hesc($pr->subtag)?>
|
||||||
|
<?php if (strlen($pr->subtag) && count($pr->tags)) { ?>
|
||||||
|
::
|
||||||
|
<?php } ?>
|
||||||
|
<?php foreach($pr->tags as $tag) { ?>
|
||||||
|
<a class="tag tag-link" data-tag="<?=hesc($tag)?>"><?=hesc($tag)?></a>
|
||||||
|
<?php } ?>
|
||||||
|
</small>
|
||||||
|
<?php } ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php } ?>
|
||||||
|
</table>
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$extra_head = '<link rel="canonical" href="'.hesc(BASEURL).'">';
|
||||||
|
|
||||||
|
$index = template(SITE_TITLE, ob_get_clean(), $extra_head);
|
||||||
|
file_put_contents(BASEDIR.'wwwroot/index.html', $index);
|
||||||
|
|
||||||
|
// Done
|
||||||
|
}
|
||||||
|
|
||||||
|
function redirecthtml($target) {
|
||||||
|
ob_start();
|
||||||
|
?>
|
||||||
|
<meta http-equiv="refresh" content="0; url=<?=hesc($target)?>">
|
||||||
|
<a href="<?=hesc($target)?>">Moved »</a>
|
||||||
|
<?php
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildredirects($redirects) {
|
||||||
|
foreach($redirects as $oldname => $newname) {
|
||||||
|
|
||||||
|
$page = redirecthtml(BASEURL.$newname.'/');
|
||||||
|
|
||||||
|
// old format
|
||||||
|
file_put_contents(BASEDIR.'wwwroot/'.$oldname.'.html', $page);
|
||||||
|
|
||||||
|
// new format
|
||||||
|
mkdir(BASEDIR.'wwwroot/'.$oldname);
|
||||||
|
file_put_contents(BASEDIR.'wwwroot/'.$oldname.'/index.html', $page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildgosubpackages($packages) {
|
||||||
|
foreach($packages as $path => $goGetStr) {
|
||||||
|
|
||||||
|
$page = (
|
||||||
|
'<meta name="go-import" content="'.hesc($goGetStr).'">'.
|
||||||
|
"\n".
|
||||||
|
redirecthtml(BASEURL)
|
||||||
|
);
|
||||||
|
|
||||||
|
// new directory format only
|
||||||
|
mkdir_all(BASEDIR.'wwwroot/'.$path);
|
||||||
|
file_put_contents(BASEDIR.'wwwroot/'.$path.'/index.html', $page);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
191
lib/util.php
Normal file
191
lib/util.php
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
function mkshield($left_str, $right_str, $color_str, $params=[]) {
|
||||||
|
$filename = sprintf(
|
||||||
|
"%s-%s-%s.svg",
|
||||||
|
rawurlencode(str_replace('-', '--', $left_str)),
|
||||||
|
rawurlencode(str_replace('-', '--', $right_str)),
|
||||||
|
rawurlencode($color_str)
|
||||||
|
);
|
||||||
|
if (count($params) > 0) {
|
||||||
|
$filename .= '?' . http_build_query($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
$cache_path = SHIELDS_CACHE_DIR.urlencode($filename);
|
||||||
|
|
||||||
|
if (file_exists($cache_path)) {
|
||||||
|
return file_get_contents($cache_path);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$retn = file_get_contents('https://img.shields.io/badge/'.$filename);
|
||||||
|
|
||||||
|
// We need unique IDs
|
||||||
|
$prefix = substr(sha1($filename), 8).'-';
|
||||||
|
$retn = str_replace('id="', 'id="'.$prefix, $retn);
|
||||||
|
$retn = str_replace('url(#', 'url(#'.$prefix, $retn);
|
||||||
|
|
||||||
|
file_put_contents($cache_path, $retn);
|
||||||
|
return $retn;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a thumbnail of an image. It overscales, centers, and crops to fit the
|
||||||
|
* target dimensions.
|
||||||
|
*
|
||||||
|
* @param string $src_file
|
||||||
|
* @param string $dest_file Null to return an image handle
|
||||||
|
* @param int $width
|
||||||
|
* @param int $height
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
function mkthumbnail($src_file, $dest_file, $width, $height) {
|
||||||
|
list($src_width, $src_height) = getimagesize($src_file);
|
||||||
|
|
||||||
|
$im = imagecreatefromstring(file_get_contents($src_file));
|
||||||
|
|
||||||
|
$dest = imagecreatetruecolor($width, $height);
|
||||||
|
imagefilledrectangle($dest, 0, 0, $width, $height, imagecolorallocate($dest, 0xFF, 0xFF, 0xFF));
|
||||||
|
|
||||||
|
$scale = max( $width/$src_width, $height/$src_height ); // overscale + crop
|
||||||
|
|
||||||
|
$box_w = $width/$scale;
|
||||||
|
$box_h = $height/$scale;
|
||||||
|
|
||||||
|
$box_xoff = floor(($src_width - $box_w)/2);
|
||||||
|
$box_yoff = floor(($src_height - $box_h)/2);
|
||||||
|
|
||||||
|
imagecopyresampled(
|
||||||
|
$dest, $im,
|
||||||
|
0, 0,
|
||||||
|
$box_xoff, $box_yoff,
|
||||||
|
$width, $height, $box_w, $box_h
|
||||||
|
);
|
||||||
|
|
||||||
|
imagedestroy($im);
|
||||||
|
|
||||||
|
if (is_null($dest_file)) {
|
||||||
|
return $dest;
|
||||||
|
} else {
|
||||||
|
return imagejpeg($dest, $dest_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mkspritesheet(array $handles, $dest_file, $width, $height) {
|
||||||
|
$im = imagecreatetruecolor($width, $height * count($handles));
|
||||||
|
|
||||||
|
for($i = 0, $e = count($handles); $i != $e; ++$i) {
|
||||||
|
imagecopy($im, $handles[$i], 0, $i * $height, 0, 0, $width, $height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null($dest_file)) {
|
||||||
|
return $dest_file;
|
||||||
|
} else {
|
||||||
|
return imagejpeg($im, $dest_file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fbytes($size, $suffixes='B|KiB|MiB|GiB|TiB') {
|
||||||
|
$sxlist = explode('|', $suffixes);
|
||||||
|
if ($size < 1024) {
|
||||||
|
return $size.$sxlist[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($size > 1024 && count($sxlist) >= 2) {
|
||||||
|
array_shift($sxlist);
|
||||||
|
$size /= 1024;
|
||||||
|
}
|
||||||
|
return number_format($size, 2).array_shift($sxlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
function str_ext($sz) {
|
||||||
|
$dpos = strrpos($sz, '.');
|
||||||
|
return substr($sz, $dpos+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_image($sz) {
|
||||||
|
return in_array(strtolower(str_ext($sz)), ['jpg', 'png', 'jpeg']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hesc($sz) {
|
||||||
|
return @htmlentities($sz, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
function mkdir_all($path) {
|
||||||
|
$epath = escapeshellarg($path);
|
||||||
|
`mkdir -p ${epath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function text2html($sz) {
|
||||||
|
|
||||||
|
$identity = function($sz) {
|
||||||
|
return $sz;
|
||||||
|
};
|
||||||
|
|
||||||
|
$splitInside = function($begin, $end, $sz) {
|
||||||
|
$parts = explode($begin, $sz);
|
||||||
|
if (count($parts) == 1) return [$sz];
|
||||||
|
|
||||||
|
$ret = [$parts[0]];
|
||||||
|
for($i = 1, $e = count($parts); $i !== $e; ++$i) {
|
||||||
|
$inner = explode($end, $parts[$i], 2);
|
||||||
|
$ret = array_merge($ret, $inner);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
$oddEven = function(array $parts, $odd, $even, $join='') {
|
||||||
|
$ret = [];
|
||||||
|
for($i = 0, $e = count($parts); $i != $e; ++$i) {
|
||||||
|
$ret[] = ($i % 2) ? $odd($parts[$i]) : $even($parts[$i]);
|
||||||
|
}
|
||||||
|
return implode($join, $ret);
|
||||||
|
};
|
||||||
|
|
||||||
|
$sectionFmt = function($sz) use($oddEven, $identity) {
|
||||||
|
$base = hesc($sz);
|
||||||
|
|
||||||
|
$base = preg_replace('~^=+(.+)=+~m', '<strong>\\1</strong>', $base);
|
||||||
|
$base = preg_replace('~\\[url=([^\\]]+?)\\](.+?)\\[/url\\]~m', '<a href="\\1">\\2</a>', $base);
|
||||||
|
|
||||||
|
$base = preg_replace('~\\[([^\\]]+?)\\]\\((https?://.+?)\\)~m', '<a href="\\2">\\1</a>', $base); // Support markdown-style URLs
|
||||||
|
|
||||||
|
$base = preg_replace('~([^="])(https?://[^ \\r\\n\\t]+)~i', '\\1<a href="\\2">\\2</a>', $base); // Support standalone URLs
|
||||||
|
$base = preg_replace('~\\[b\\](.+?)\\[/b\\]~m', '<strong>\\1</strong>', $base);
|
||||||
|
$base = preg_replace('~\\[i\\](.+?)\\[/i\\]~m', '<i>\\1</i>', $base);
|
||||||
|
$base = preg_replace('~\\[spoiler\\](.+?)\\[/spoiler\\]~m', '<span class="spoiler">\\1</span>', $base);
|
||||||
|
$base = preg_replace('~\n- ~ms', "\n• ", $base);
|
||||||
|
|
||||||
|
$base = preg_replace('~^```.+$~m', '`', $base); // Convert ```html to single `{}` element
|
||||||
|
|
||||||
|
// TODO support markdown tables
|
||||||
|
|
||||||
|
$btparts = explode('`', $base);
|
||||||
|
if (count($btparts) > 1 && (count($btparts) % 2)) {
|
||||||
|
for ($i = 1, $e = count($btparts); $i < $e; $i += 2) {
|
||||||
|
$class = 'code';
|
||||||
|
if (strpos($btparts[$i], "\n") !== false) {
|
||||||
|
$class .= ' code-multiline';
|
||||||
|
}
|
||||||
|
$btparts[$i] = '<span class="'.$class.'">'.ltrim($btparts[$i], "\n").'</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oddEven($btparts, $identity, 'nl2br');
|
||||||
|
};
|
||||||
|
|
||||||
|
$htmlSections = $splitInside('<html>', '</html>', $sz);
|
||||||
|
return $oddEven($htmlSections, $identity, $sectionFmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function array_decimate($array, $total, $partno) {
|
||||||
|
$ct = 0;
|
||||||
|
$ret = [];
|
||||||
|
foreach($array as $k => $v) {
|
||||||
|
if (++$ct % $total == ($partno - 1)) {
|
||||||
|
$ret[$k] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
7
mkdist.sh
Normal file → Executable file
7
mkdist.sh
Normal file → Executable file
@@ -3,7 +3,12 @@
|
|||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
tar cJvf "codesite-$(date +%s).tar.xz" \
|
tar cJvf "codesite-$(date +%s).tar.xz" \
|
||||||
rebuild.php rebuild.sh sites/codesite.example.com static_global \
|
--exclude='sites/codesite.example.com/wwwroot' \
|
||||||
|
rebuild.sh \
|
||||||
|
sites/codesite.example.com \
|
||||||
|
static \
|
||||||
|
lib \
|
||||||
--owner=0 --group=0
|
--owner=0 --group=0
|
||||||
|
|
||||||
read -p "Press any key to continue..."
|
read -p "Press any key to continue..."
|
||||||
|
|
||||||
|
|||||||
494
rebuild.php
494
rebuild.php
@@ -1,494 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
// Code-hosting website
|
|
||||||
// ````````````````````
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a thumbnail of an image. It overscales, centers, and crops to fit the
|
|
||||||
* target dimensions.
|
|
||||||
*
|
|
||||||
* @param string $src_file
|
|
||||||
* @param string $dest_file Null to return an image handle
|
|
||||||
* @param int $width
|
|
||||||
* @param int $height
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
function mkthumbnail($src_file, $dest_file, $width, $height) {
|
|
||||||
list($src_width, $src_height) = getimagesize($src_file);
|
|
||||||
|
|
||||||
$im = imagecreatefromstring(file_get_contents($src_file));
|
|
||||||
|
|
||||||
$dest = imagecreatetruecolor($width, $height);
|
|
||||||
imagefilledrectangle($dest, 0, 0, $width, $height, imagecolorallocate($dest, 0xFF, 0xFF, 0xFF));
|
|
||||||
|
|
||||||
$scale = max( $width/$src_width, $height/$src_height ); // overscale + crop
|
|
||||||
|
|
||||||
$box_w = $width/$scale;
|
|
||||||
$box_h = $height/$scale;
|
|
||||||
|
|
||||||
$box_xoff = floor(($src_width - $box_w)/2);
|
|
||||||
$box_yoff = floor(($src_height - $box_h)/2);
|
|
||||||
|
|
||||||
imagecopyresampled(
|
|
||||||
$dest, $im,
|
|
||||||
0, 0,
|
|
||||||
$box_xoff, $box_yoff,
|
|
||||||
$width, $height, $box_w, $box_h
|
|
||||||
);
|
|
||||||
|
|
||||||
imagedestroy($im);
|
|
||||||
|
|
||||||
if (is_null($dest_file)) {
|
|
||||||
return $dest;
|
|
||||||
} else {
|
|
||||||
return imagejpeg($dest, $dest_file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mkspritesheet(array $handles, $dest_file, $width, $height) {
|
|
||||||
$im = imagecreatetruecolor($width, $height * count($handles));
|
|
||||||
|
|
||||||
for($i = 0, $e = count($handles); $i != $e; ++$i) {
|
|
||||||
imagecopy($im, $handles[$i], 0, $i * $height, 0, 0, $width, $height);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($dest_file)) {
|
|
||||||
return $dest_file;
|
|
||||||
} else {
|
|
||||||
return imagejpeg($im, $dest_file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fbytes($size, $suffixes='B|KiB|MiB|GiB|TiB') {
|
|
||||||
$sxlist = explode('|', $suffixes);
|
|
||||||
if ($size < 1024) {
|
|
||||||
return $size.$sxlist[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
while ($size > 1024 && count($sxlist) >= 2) {
|
|
||||||
array_shift($sxlist);
|
|
||||||
$size /= 1024;
|
|
||||||
}
|
|
||||||
return number_format($size, 2).array_shift($sxlist);
|
|
||||||
}
|
|
||||||
|
|
||||||
function str_ext($sz) {
|
|
||||||
$dpos = strrpos($sz, '.');
|
|
||||||
return substr($sz, $dpos+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_image($sz) {
|
|
||||||
return in_array(strtolower(str_ext($sz)), ['jpg', 'png', 'jpeg']);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hesc($sz) {
|
|
||||||
return @htmlentities($sz, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
|
||||||
}
|
|
||||||
|
|
||||||
function text2html($sz) {
|
|
||||||
|
|
||||||
$identity = function($sz) {
|
|
||||||
return $sz;
|
|
||||||
};
|
|
||||||
|
|
||||||
$splitInside = function($begin, $end, $sz) {
|
|
||||||
$parts = explode($begin, $sz);
|
|
||||||
if (count($parts) == 1) return [$sz];
|
|
||||||
|
|
||||||
$ret = [$parts[0]];
|
|
||||||
for($i = 1, $e = count($parts); $i !== $e; ++$i) {
|
|
||||||
$inner = explode($end, $parts[$i], 2);
|
|
||||||
$ret = array_merge($ret, $inner);
|
|
||||||
}
|
|
||||||
return $ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
$oddEven = function(array $parts, $odd, $even, $join='') {
|
|
||||||
$ret = [];
|
|
||||||
for($i = 0, $e = count($parts); $i != $e; ++$i) {
|
|
||||||
$ret[] = ($i % 2) ? $odd($parts[$i]) : $even($parts[$i]);
|
|
||||||
}
|
|
||||||
return implode($join, $ret);
|
|
||||||
};
|
|
||||||
|
|
||||||
$sectionFmt = function($sz) use($oddEven, $identity) {
|
|
||||||
$base = hesc($sz);
|
|
||||||
|
|
||||||
$base = preg_replace('~^=+(.+)=+~m', '<strong>\\1</strong>', $base);
|
|
||||||
$base = preg_replace('~(https?://[^ \\r\\n\\t]+)~i', '<a href="\\1">\\1</a>', $base);
|
|
||||||
|
|
||||||
$btparts = explode('`', $base);
|
|
||||||
if (count($btparts) > 1 && (count($btparts) % 2)) {
|
|
||||||
for ($i = 1, $e = count($btparts); $i < $e; $i += 2) {
|
|
||||||
$btparts[$i] = '<span class="code">'.$btparts[$i].'</span>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $oddEven($btparts, $identity, 'nl2br');
|
|
||||||
};
|
|
||||||
|
|
||||||
$htmlSections = $splitInside('<html>', '</html>', $sz);
|
|
||||||
return $oddEven($htmlSections, $identity, $sectionFmt);
|
|
||||||
}
|
|
||||||
|
|
||||||
function array_decimate($array, $total, $partno) {
|
|
||||||
$ct = 0;
|
|
||||||
$ret = [];
|
|
||||||
foreach($array as $k => $v) {
|
|
||||||
if (++$ct % $total == ($partno - 1)) {
|
|
||||||
$ret[$k] = $v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class CProject {
|
|
||||||
|
|
||||||
private $dir;
|
|
||||||
public $projname;
|
|
||||||
public $shortdesc = '(no description)';
|
|
||||||
public $subtag = '';
|
|
||||||
public $lastupdate = 0;
|
|
||||||
private $longdesc = '';
|
|
||||||
private $images = array();
|
|
||||||
private $downloads = array();
|
|
||||||
public $tags = array();
|
|
||||||
|
|
||||||
public $homeimage = null;
|
|
||||||
|
|
||||||
public function __construct($dirname, $projname) {
|
|
||||||
$this->dir = BASEDIR.'data/'.$dirname.'/';
|
|
||||||
$this->projname = $projname;
|
|
||||||
|
|
||||||
// Identify resources in folder
|
|
||||||
|
|
||||||
$ls = scandir($this->dir);
|
|
||||||
foreach($ls as $file) {
|
|
||||||
if ($file[0] == '.') continue;
|
|
||||||
|
|
||||||
if ($file == 'README.txt') {
|
|
||||||
$this->lastupdate = max($this->lastupdate, filectime($this->dir.$file)); // don't count README updates
|
|
||||||
|
|
||||||
$this->longdesc = file_get_contents($this->dir.'README.txt');
|
|
||||||
$matches = array();
|
|
||||||
if (preg_match('~Written in ([^\\r\\n]+)~', $this->longdesc, $matches)) {
|
|
||||||
$this->subtag = rtrim($matches[1], ' .');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preg_match('~Tags: ([^\\r\\n]+)~', $this->longdesc, $matches)) {
|
|
||||||
$this->tags = array_map('trim', explode(',', $matches[1]));
|
|
||||||
}
|
|
||||||
|
|
||||||
$parts = explode("\n", $this->longdesc);
|
|
||||||
$this->shortdesc = array_shift($parts);
|
|
||||||
$this->shortdesc[0] = strtolower($this->shortdesc[0]); // cosmetic lowercase
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->lastupdate = max(
|
|
||||||
$this->lastupdate,
|
|
||||||
filemtime($this->dir.$file),
|
|
||||||
filectime($this->dir.$file)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (is_image($file)) {
|
|
||||||
$this->images[] = $file;
|
|
||||||
} else {
|
|
||||||
$this->downloads[] = $file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
natcasesort($this->downloads);
|
|
||||||
$this->downloads = array_reverse($this->downloads);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function genHomeImage() {
|
|
||||||
if (count($this->images)) {
|
|
||||||
|
|
||||||
$this->homeimage = mkthumbnail(
|
|
||||||
$this->dir.$this->images[0],
|
|
||||||
null, // raw handle
|
|
||||||
INDEX_THUMB_W, INDEX_THUMB_H
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function write() {
|
|
||||||
|
|
||||||
// Generate image thumbnails
|
|
||||||
|
|
||||||
foreach($this->images as $idx => $image) {
|
|
||||||
$outfile = BASEDIR.'wwwroot/srv/'.$this->projname.'_'.$idx;
|
|
||||||
copy($this->dir.$image, $outfile.'.'.str_ext($image));
|
|
||||||
|
|
||||||
mkthumbnail($outfile.'.'.str_ext($image), $outfile.'_thumb.jpg', PAGE_THUMB_W, PAGE_THUMB_H);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy downloads to wwwroot
|
|
||||||
|
|
||||||
foreach($this->downloads as $idx => $filename) {
|
|
||||||
copy($this->dir.$filename, BASEDIR.'wwwroot/srv/'.$filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate index page
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
$this->index();
|
|
||||||
$idxfile = template($this->projname.' | '.SITE_TITLE, ob_get_clean());
|
|
||||||
file_put_contents(BASEDIR.'wwwroot/'.$this->projname.'.html', $idxfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getClassAttr() {
|
|
||||||
if (count($this->tags)) {
|
|
||||||
return 'taggedWith-'.implode(' taggedWith-', $this->tags);
|
|
||||||
} else {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index() {
|
|
||||||
?>
|
|
||||||
<h2><?=hesc($this->projname)?></h2>
|
|
||||||
|
|
||||||
<div class="projinfo">
|
|
||||||
|
|
||||||
<div class="projbody projbody_<?=(count($this->images) ? 'half' : 'full')?>w">
|
|
||||||
|
|
||||||
<strong>ABOUT</strong>
|
|
||||||
|
|
||||||
<p><?=text2html($this->longdesc)?></p>
|
|
||||||
|
|
||||||
<?=file_get_contents(BASEDIR.'/footer.htm')?>
|
|
||||||
|
|
||||||
<?php if (count($this->downloads)) { ?>
|
|
||||||
|
|
||||||
<strong>DOWNLOAD</strong>
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<?php foreach($this->downloads as $filename) { ?>
|
|
||||||
<li>
|
|
||||||
<a href="srv/<?=hesc(rawurlencode($filename))?>"><?=hesc($filename)?></a>
|
|
||||||
<small>
|
|
||||||
<?=hesc(fbytes(filesize(BASEDIR.'wwwroot/srv/'.$filename)))?>
|
|
||||||
</small>
|
|
||||||
</li>
|
|
||||||
<?php } ?>
|
|
||||||
</ul>
|
|
||||||
<?php } ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if (count($this->images)) { ?>
|
|
||||||
<div class="projimg">
|
|
||||||
<?php foreach($this->images as $idx => $origname) { ?>
|
|
||||||
<a href="srv/<?=hesc(urlencode($this->projname))?>_<?=$idx?>.<?=str_ext($origname)?>"><img src="srv/<?=hesc(urlencode($this->projname))?>_<?=$idx?>_thumb.jpg" class="thumbimage"></a>
|
|
||||||
<?php } ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="clear:both;"></div>
|
|
||||||
|
|
||||||
<?php } ?>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function template($title, $content) {
|
|
||||||
ob_start();
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
|
|
||||||
<meta name="viewport" content="width=768px" >
|
|
||||||
<link type="text/css" rel="stylesheet" href="normalize.css">
|
|
||||||
<link type="text/css" rel="stylesheet" href="style.css">
|
|
||||||
<script type="text/javascript" src="site.js"></script>
|
|
||||||
<title><?=hesc($title)?></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="container">
|
|
||||||
<div id="content">
|
|
||||||
<?=file_get_contents(BASEDIR.'/header.htm')?>
|
|
||||||
<?=$content?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
<?php
|
|
||||||
return ob_get_clean();
|
|
||||||
}
|
|
||||||
|
|
||||||
function listprojects() {
|
|
||||||
// List projects
|
|
||||||
|
|
||||||
$ls = scandir(BASEDIR.'data');
|
|
||||||
rsort($ls);
|
|
||||||
$projects = array();
|
|
||||||
foreach($ls as $dirname) {
|
|
||||||
if ($dirname[0] == '.') continue;
|
|
||||||
$matches = array();
|
|
||||||
|
|
||||||
if (preg_match('~(?:\d+-)?(.+)~', $dirname, $matches)) {
|
|
||||||
$projects[$dirname] = $matches[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $projects;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildprojects($id, $projects) {
|
|
||||||
$count = 0;
|
|
||||||
|
|
||||||
foreach($projects as $dirname => $projectname) {
|
|
||||||
|
|
||||||
echo sprintf("@%1d [%3d/%3d] ".$projectname."...\n", $id, ++$count, count($projects));
|
|
||||||
|
|
||||||
$pr = new CProject($dirname, $projectname);
|
|
||||||
$pr->write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildcommon() {
|
|
||||||
|
|
||||||
echo "@0 [ 0/ ?] Common files...\n";
|
|
||||||
|
|
||||||
$projects = listprojects();
|
|
||||||
|
|
||||||
// Build all projects
|
|
||||||
|
|
||||||
$plist = array();
|
|
||||||
|
|
||||||
$handles = array();
|
|
||||||
$handle_lookup = array();
|
|
||||||
|
|
||||||
$alphasort = [];
|
|
||||||
|
|
||||||
foreach($projects as $dirname => $projectname) {
|
|
||||||
|
|
||||||
$pr = new CProject($dirname, $projectname);
|
|
||||||
$pr->genHomeImage(); // thumbnail
|
|
||||||
|
|
||||||
$plist[] = $pr;
|
|
||||||
|
|
||||||
if (is_null($pr->homeimage)) {
|
|
||||||
$handle_lookup[$projectname] = null;
|
|
||||||
} else {
|
|
||||||
$handle_lookup[$projectname] = count($handles);
|
|
||||||
$handles[] = $pr->homeimage;
|
|
||||||
}
|
|
||||||
|
|
||||||
$alphasort[] = [$pr->projname, count($plist)-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
usort($alphasort, function($a, $b) {
|
|
||||||
return strcasecmp($a[0], $b[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
$alphaidx = [];
|
|
||||||
|
|
||||||
foreach($alphasort as $a) {
|
|
||||||
$alphaidx[ $a[1] ] = count($alphaidx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build homepage spritesheet
|
|
||||||
|
|
||||||
mkspritesheet($handles, BASEDIR.'wwwroot/logos.jpg', INDEX_THUMB_W, INDEX_THUMB_H);
|
|
||||||
array_map('imagedestroy', $handles); // free
|
|
||||||
|
|
||||||
// Build index page
|
|
||||||
|
|
||||||
ob_start();
|
|
||||||
?>
|
|
||||||
|
|
||||||
<?php if (file_exists(BASEDIR.'homepage_blurb.htm')) { ?>
|
|
||||||
<!-- homepage blurb {{ -->
|
|
||||||
<?=file_get_contents(BASEDIR.'homepage_blurb.htm')?>
|
|
||||||
<!-- }} -->
|
|
||||||
<?php } ?>
|
|
||||||
|
|
||||||
<table id="projtable-main" class="projtable">
|
|
||||||
<?php foreach ($plist as $i => $pr) { ?>
|
|
||||||
<tr class="<?=$pr->getClassAttr()?>"
|
|
||||||
data-sort-mt="-<?=$pr->lastupdate?>"
|
|
||||||
data-sort-ct="<?=$i?>"
|
|
||||||
data-sort-al="<?=$alphaidx[$i]?>"
|
|
||||||
>
|
|
||||||
<td>
|
|
||||||
<a href="<?=hesc(urlencode($pr->projname))?>.html"><?=(is_null($handle_lookup[$pr->projname]) ? '<div class="no-image"></div>' : '<div class="homeimage homeimage-sprite" style="background-position:0 -'.($handle_lookup[$pr->projname]*INDEX_THUMB_H).'px"></div>')?></a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<strong><?=hesc($pr->projname)?></strong>,
|
|
||||||
<?=hesc($pr->shortdesc)?>
|
|
||||||
<a href="<?=hesc(urlencode($pr->projname))?>.html">more...</a>
|
|
||||||
<?php if (strlen($pr->subtag) || count($pr->tags)) { ?>
|
|
||||||
<br>
|
|
||||||
<small>
|
|
||||||
<?=hesc($pr->subtag)?>
|
|
||||||
<?php if (strlen($pr->subtag) && count($pr->tags)) { ?>
|
|
||||||
::
|
|
||||||
<?php } ?>
|
|
||||||
<?php foreach($pr->tags as $tag) { ?>
|
|
||||||
<a class="tag tag-link" data-tag="<?=hesc($tag)?>"><?=hesc($tag)?></a>
|
|
||||||
<?php } ?>
|
|
||||||
</small>
|
|
||||||
<?php } ?>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php } ?>
|
|
||||||
</table>
|
|
||||||
<?php
|
|
||||||
|
|
||||||
$index = template(SITE_TITLE, ob_get_clean());
|
|
||||||
file_put_contents(BASEDIR.'wwwroot/index.html', $index);
|
|
||||||
|
|
||||||
// Done
|
|
||||||
}
|
|
||||||
|
|
||||||
function main($args) {
|
|
||||||
$basedir = './';
|
|
||||||
$total = $args[0];
|
|
||||||
$pos = $args[1];
|
|
||||||
|
|
||||||
// Parse configuration
|
|
||||||
|
|
||||||
$config = @parse_ini_file(
|
|
||||||
$basedir . 'config.ini',
|
|
||||||
true,
|
|
||||||
INI_SCANNER_RAW
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($config === false) {
|
|
||||||
die("[FATAL] Couldn't load '${basedir}/config.ini'!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
define('BASEDIR', $basedir);
|
|
||||||
define('SITE_TITLE', trim($config['codesite']['title']));
|
|
||||||
define('PAGE_THUMB_W', intval($config['codesite']['page_thumb_w']));
|
|
||||||
define('PAGE_THUMB_H', intval($config['codesite']['page_thumb_h']));
|
|
||||||
define('INDEX_THUMB_W', intval($config['codesite']['index_thumb_w']));
|
|
||||||
define('INDEX_THUMB_H', intval($config['codesite']['index_thumb_h']));
|
|
||||||
|
|
||||||
// Perform build tasks
|
|
||||||
|
|
||||||
if ($pos == 0) {
|
|
||||||
buildcommon();
|
|
||||||
} else {
|
|
||||||
buildprojects($pos, array_decimate(listprojects(), $total, $pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry point
|
|
||||||
//
|
|
||||||
|
|
||||||
ini_set('display_errors', 'On');
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
main(array_slice($_SERVER['argv'], 1));
|
|
||||||
55
rebuild.sh
Normal file → Executable file
55
rebuild.sh
Normal file → Executable file
@@ -1,35 +1,34 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
PHP=/cygdrive/c/bin/php/php.exe
|
|
||||||
THREADS=4
|
APP_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
|
||||||
|
numcpus() {
|
||||||
|
cat /proc/cpuinfo | grep '^processor' | wc -l
|
||||||
|
}
|
||||||
|
|
||||||
buildsite() {
|
buildsite() {
|
||||||
|
local site="$1"
|
||||||
|
|
||||||
local basedir="$(realpath .)"
|
echo "Site: ${site}"
|
||||||
local rebuild="${basedir}/rebuild.php"
|
(
|
||||||
|
cd "$site"
|
||||||
|
|
||||||
if [[ "$(uname -o)" == "Cygwin" ]] ; then
|
echo "Cleaning wwwroot directory..."
|
||||||
rebuild="$(cygpath -w "$rebuild")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Site: ${1}"
|
|
||||||
|
|
||||||
pushd "$1" >/dev/null
|
|
||||||
|
|
||||||
echo "Cleaning target directory..."
|
|
||||||
|
|
||||||
if [[ -d wwwroot ]] ; then
|
if [[ -d wwwroot ]] ; then
|
||||||
rm -r wwwroot
|
rm -r wwwroot
|
||||||
fi
|
fi
|
||||||
|
mkdir -p wwwroot/{img,srv,static}
|
||||||
|
|
||||||
mkdir -p wwwroot/srv
|
echo "Copying static resources..."
|
||||||
|
|
||||||
if [[ ! -d static ]] ; then
|
if [[ ! -d static ]] ; then
|
||||||
mkdir static
|
mkdir static
|
||||||
fi
|
fi
|
||||||
cp "${basedir}/static_global/"* wwwroot || true
|
cp "${APP_DIR}/static/"* wwwroot/static || true
|
||||||
cp static/* wwwroot || true
|
cp static/* wwwroot/static || true
|
||||||
|
|
||||||
for htm in footer header homepage_blurb ; do
|
for htm in footer header homepage_blurb ; do
|
||||||
if [[ ! -f "${htm}.htm" ]] ; then
|
if [[ ! -f "${htm}.htm" ]] ; then
|
||||||
@@ -39,23 +38,29 @@ buildsite() {
|
|||||||
|
|
||||||
echo "Building pages..."
|
echo "Building pages..."
|
||||||
|
|
||||||
for i in $(seq 0 "$THREADS") ; do
|
local threadcount=$(numcpus)
|
||||||
$PHP "$rebuild" "$THREADS" "$i" &
|
for i in $(seq 0 "$threadcount") ; do
|
||||||
|
"${APP_DIR}/lib/bootstrap.php" "$threadcount" "$i" &
|
||||||
done
|
done
|
||||||
wait
|
wait
|
||||||
|
|
||||||
echo "Site: ${1} finished."
|
echo "Site: ${site} finished."
|
||||||
echo ""
|
echo ""
|
||||||
|
)
|
||||||
|
|
||||||
popd >/dev/null
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "USAGE: ./rebuild.sh path-to-siteroot"
|
||||||
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
for site in sites/* ; do
|
if [[ $# -ne 1 ]] ; then
|
||||||
buildsite "$site"
|
usage
|
||||||
done
|
fi
|
||||||
|
|
||||||
|
buildsite "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|
||||||
read -p "Press any key to continue..."
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
[codesite]
|
|
||||||
title=code.ivysaur.me
|
|
||||||
page_thumb_w=60
|
|
||||||
page_thumb_h=60
|
|
||||||
index_thumb_w=90
|
|
||||||
index_thumb_h=32
|
|
||||||
|
|
||||||
; n.b. Recommend a multiple of the JPEG iDCT block size for index_thumb_h
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<p>
|
|
||||||
<strong>CONTACT</strong>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
For bug reports, feature requests, or if you need any help, please
|
|
||||||
<a
|
|
||||||
href="http://www.google.com/recaptcha/mailhide/d?k=01GuAWzMc9JjSdooo-2KCMQA==&c=kgR3dBrP39yhPIy8FvLFbuBLmWqorQBDc_Zjbw6NAmU="
|
|
||||||
onclick="window.open('http://www.google.com/recaptcha/mailhide/d?k\07501GuAWzMc9JjSdooo-2KCMQA\75\75\46c\75kgR3dBrP39yhPIy8FvLFbuBLmWqorQBDc_Zjbw6NAmU\075', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;"
|
|
||||||
title="Reveal this e-mail address"
|
|
||||||
>click here</a> to email me.
|
|
||||||
</p>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<h1><a href="index.html"><div id="ivylogo"></div>code.ivysaur.me</a></h1>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
<p>
|
|
||||||
It's said that if you're not disgusted by code you wrote six months ago, you've stopped learning.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<strong>OVERVIEW</strong>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
This page contains several assorted spare-time projects, which no longer have any relation. They are listed in roughly chronological order (newest first). <strong>Unless specified otherwise</strong>, you may feel free to use and modify both the binaries and any source code, for any purpose, on the general condition you do not misrepresent who the author is (BSD license).
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Some of these projects formerly appeared on Google Code <a href="https://code.google.com/p/mappy/">here</a>, but were moved following the discontinuation of the Google Code binary download system.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<select id="sortorder" style="float:right;">
|
|
||||||
<option value="a">Youngest project first</option>
|
|
||||||
<option value="b">Recent updates first</option>
|
|
||||||
<option value="c">Alphabetical</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<strong>PROJECTS</strong>
|
|
||||||
</p>
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 233 B |
Binary file not shown.
|
Before Width: | Height: | Size: 264 B |
@@ -1,173 +0,0 @@
|
|||||||
/* style.css */
|
|
||||||
|
|
||||||
html {
|
|
||||||
overflow-y:scroll; /* always display scrollbar to prevent horizontal lurch */
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
border:0;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color:black;
|
|
||||||
text-decoration:underline;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
color:blue;
|
|
||||||
cursor:pointer;
|
|
||||||
}
|
|
||||||
h1 a {
|
|
||||||
text-decoration:none;
|
|
||||||
}
|
|
||||||
h1 a:hover {
|
|
||||||
color:black;
|
|
||||||
}
|
|
||||||
h1,h2,h3 {
|
|
||||||
margin-top:0;
|
|
||||||
}
|
|
||||||
.code {
|
|
||||||
background: #F8F8F8;
|
|
||||||
font-family:Consolas,monospace;
|
|
||||||
white-space:pre;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* */
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
/* structural */
|
|
||||||
height:100%;
|
|
||||||
min-height:100%;
|
|
||||||
margin:0;
|
|
||||||
border:0;
|
|
||||||
padding:0;
|
|
||||||
|
|
||||||
/* cosmetic */
|
|
||||||
font-family:"Helvetica Neue","Segoe UI",Arial,sans-serif;
|
|
||||||
font-size:12px;
|
|
||||||
background:#DDD url('pixel_weave.png'); /* thanks subtlepatterns.com ! */
|
|
||||||
color:#333;
|
|
||||||
}
|
|
||||||
|
|
||||||
#container {
|
|
||||||
margin:0 auto;
|
|
||||||
width:768px;
|
|
||||||
position:relative;
|
|
||||||
|
|
||||||
height:auto !important;
|
|
||||||
height:100%; /* oldIE */
|
|
||||||
min-height:100%;
|
|
||||||
|
|
||||||
/* cosmetic */
|
|
||||||
background:white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
padding:14px;
|
|
||||||
background:white;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* */
|
|
||||||
|
|
||||||
.tag::before {
|
|
||||||
content:"";
|
|
||||||
|
|
||||||
display:inline-block;
|
|
||||||
width:7px;
|
|
||||||
height:7px;
|
|
||||||
|
|
||||||
margin-right:2px;
|
|
||||||
|
|
||||||
background:transparent url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAZUlEQVQI12P4//8/Awyrqqp6KSkp/QdhFRWV7cgS+kDBN0B8RVlZuRikACahB+T8ger6BMTXwJJAiTwg4xvMOCj+ART3ZwCa3YYm8QcopgsyEWasDVT3W5AVMHcgO6gViNORXQ8A84NToxbSsJcAAAAASUVORK5CYII=') no-repeat 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tag-filter-warn {
|
|
||||||
position:fixed;
|
|
||||||
top:0;
|
|
||||||
right:0;
|
|
||||||
|
|
||||||
padding:4px;
|
|
||||||
|
|
||||||
background:lightyellow;
|
|
||||||
border-bottom: 1px solid #888;
|
|
||||||
border-left:1px solid #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* */
|
|
||||||
|
|
||||||
.projtable {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width:100%;
|
|
||||||
}
|
|
||||||
.projtable tr {
|
|
||||||
transition:0.2s linear;
|
|
||||||
}
|
|
||||||
.projtable tr:hover {
|
|
||||||
background:#F8F8F8;
|
|
||||||
}
|
|
||||||
.projtable td {
|
|
||||||
padding: 2px 4px;
|
|
||||||
}
|
|
||||||
.projtable small {
|
|
||||||
color:grey;
|
|
||||||
font-style:italic;
|
|
||||||
}
|
|
||||||
.projtable tr td:first-child {
|
|
||||||
width:95px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.projinfo {
|
|
||||||
}
|
|
||||||
.projbody {
|
|
||||||
}
|
|
||||||
.projbody_halfw {
|
|
||||||
float:left;
|
|
||||||
width: 678px; /* 740px full - 60px rhs column - 2px border */
|
|
||||||
}
|
|
||||||
.projbody_fullw {
|
|
||||||
|
|
||||||
}
|
|
||||||
.projimg {
|
|
||||||
float:right;
|
|
||||||
width:62px; /* 60px + 2px border */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* */
|
|
||||||
|
|
||||||
#ivylogo {
|
|
||||||
background:transparent url('ivysaur24.png') no-repeat 0 0;
|
|
||||||
width:24px;
|
|
||||||
height:24px;
|
|
||||||
display:inline-block;
|
|
||||||
*display:block;
|
|
||||||
*zoom:1;
|
|
||||||
margin-right:4px;
|
|
||||||
position:relative;
|
|
||||||
top:4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* */
|
|
||||||
|
|
||||||
.homeimage {
|
|
||||||
width:90px;
|
|
||||||
height:32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.homeimage-sprite {
|
|
||||||
background: white url('logos.jpg') no-repeat 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumbimage {
|
|
||||||
width:60px;
|
|
||||||
height:60px;
|
|
||||||
opacity: 0.8;
|
|
||||||
transition:0.2s opacity;
|
|
||||||
border:1px solid lightgrey;
|
|
||||||
}
|
|
||||||
.thumbimage:hover {
|
|
||||||
opacity:1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-image {
|
|
||||||
width:90px;
|
|
||||||
height:32px;
|
|
||||||
display:block;
|
|
||||||
background: white url('no_image.png') no-repeat 0 0;
|
|
||||||
}
|
|
||||||
@@ -6,3 +6,8 @@ index_thumb_w=90
|
|||||||
index_thumb_h=32
|
index_thumb_h=32
|
||||||
|
|
||||||
; n.b. Recommend a multiple of the JPEG iDCT block size for index_thumb_h
|
; n.b. Recommend a multiple of the JPEG iDCT block size for index_thumb_h
|
||||||
|
|
||||||
|
[redirect]
|
||||||
|
; old project name = new project name
|
||||||
|
old-project-name=example-project
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 436 B After Width: | Height: | Size: 436 B |
419
static/normalize.css
vendored
Normal file
419
static/normalize.css
vendored
Normal file
@@ -0,0 +1,419 @@
|
|||||||
|
/*! normalize.css v4.1.1 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the default font family in all browsers (opinionated).
|
||||||
|
* 2. Prevent adjustments of font size after orientation changes in IE and iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: sans-serif; /* 1 */
|
||||||
|
-ms-text-size-adjust: 100%; /* 2 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers (opinionated).
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HTML5 display definitions
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||||
|
* 2. Add the correct display in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
details, /* 1 */
|
||||||
|
figcaption,
|
||||||
|
figure,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
main, /* 2 */
|
||||||
|
menu,
|
||||||
|
nav,
|
||||||
|
section,
|
||||||
|
summary { /* 1 */
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio,
|
||||||
|
canvas,
|
||||||
|
progress,
|
||||||
|
video {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in iOS 4-7.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio:not([controls]) {
|
||||||
|
display: none;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10-.
|
||||||
|
* 1. Add the correct display in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template, /* 1 */
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the gray background on active links in IE 10.
|
||||||
|
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent; /* 1 */
|
||||||
|
-webkit-text-decoration-skip: objects; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the outline on focused links when they are also active or hovered
|
||||||
|
* in all browsers (opinionated).
|
||||||
|
*/
|
||||||
|
|
||||||
|
a:active,
|
||||||
|
a:hover {
|
||||||
|
outline-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Firefox 39-.
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font style in Android 4.3-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
dfn {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct background and color in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mark {
|
||||||
|
background-color: #ff0;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the overflow in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
svg:not(:root) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
pre,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct margin in IE 8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 1em 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change font properties to `inherit` in all browsers (opinionated).
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font: inherit; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the font weight unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
optgroup {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input { /* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select { /* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||||
|
* controls in Android 4.
|
||||||
|
* 2. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
html [type="button"], /* 1 */
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the border, margin, and padding in all browsers (opinionated).
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 1px solid #c0c0c0;
|
||||||
|
margin: 0 2px;
|
||||||
|
padding: 0.35em 0.625em 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10-.
|
||||||
|
* 2. Remove the padding in IE 10-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding and cancel buttons in Chrome and Safari on OS X.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-cancel-button,
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the text style of placeholders in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-input-placeholder {
|
||||||
|
color: inherit;
|
||||||
|
opacity: 0.54;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
(function() {
|
(function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
//
|
||||||
|
// Tag support
|
||||||
|
//
|
||||||
|
|
||||||
var show_all = function() {
|
var show_all = function() {
|
||||||
var tr = document.querySelectorAll(".projtable tr");
|
var tr = document.querySelectorAll(".projtable tr");
|
||||||
for (var i = 0, e = tr.length; i !== e; ++i) {
|
for (var i = 0, e = tr.length; i !== e; ++i) {
|
||||||
@@ -39,6 +43,18 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.addEventListener('load', function() {
|
||||||
|
var taglinks = document.querySelectorAll(".tag-link");
|
||||||
|
for (var i = 0, e = taglinks.length; i !== e; ++i) {
|
||||||
|
var tag = taglinks[i].getAttribute("data-tag");
|
||||||
|
taglinks[i].addEventListener('click', get_show_tag(tag));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sort support (theme opt-in)
|
||||||
|
//
|
||||||
|
|
||||||
var sort_rows = function(cb) {
|
var sort_rows = function(cb) {
|
||||||
var tr = document.querySelectorAll(".projtable tr");
|
var tr = document.querySelectorAll(".projtable tr");
|
||||||
var items = [];
|
var items = [];
|
||||||
@@ -56,42 +72,12 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var sort_update = function() {
|
var sort_update = function(sort_by) {
|
||||||
var cb;
|
sort_rows(function(el) {
|
||||||
switch(document.getElementById('sortorder').value) {
|
return el.getAttribute(sort_by);
|
||||||
case 'a':
|
|
||||||
default: {
|
|
||||||
cb = function(el) {
|
|
||||||
return el.getAttribute('data-sort-ct');
|
|
||||||
};
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'b': {
|
|
||||||
cb = function(el) {
|
|
||||||
return el.getAttribute('data-sort-mt');
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'c': {
|
|
||||||
cb = function(el) {
|
|
||||||
return el.getAttribute('data-sort-al');
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
};
|
|
||||||
sort_rows(cb);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('load', function() {
|
|
||||||
var taglinks = document.querySelectorAll(".tag-link");
|
|
||||||
for (var i = 0, e = taglinks.length; i !== e; ++i) {
|
|
||||||
var tag = taglinks[i].getAttribute("data-tag");
|
|
||||||
taglinks[i].addEventListener('click', get_show_tag(tag));
|
|
||||||
}
|
|
||||||
|
|
||||||
var so = document.getElementById('sortorder');
|
|
||||||
if (so) {
|
|
||||||
so.addEventListener('change', sort_update);
|
|
||||||
sort_update();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
window.sortUpdate = sort_update;
|
||||||
|
|
||||||
})();
|
})();
|
||||||
406
static_global/normalize.css
vendored
406
static_global/normalize.css
vendored
@@ -1,406 +0,0 @@
|
|||||||
/*! normalize.css v2.1.3 | MIT License | git.io/normalize */
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
HTML5 display definitions
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Correct `block` display not defined in IE 8/9.
|
|
||||||
*/
|
|
||||||
|
|
||||||
article,
|
|
||||||
aside,
|
|
||||||
details,
|
|
||||||
figcaption,
|
|
||||||
figure,
|
|
||||||
footer,
|
|
||||||
header,
|
|
||||||
hgroup,
|
|
||||||
main,
|
|
||||||
nav,
|
|
||||||
section,
|
|
||||||
summary {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Correct `inline-block` display not defined in IE 8/9.
|
|
||||||
*/
|
|
||||||
|
|
||||||
audio,
|
|
||||||
canvas,
|
|
||||||
video {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prevent modern browsers from displaying `audio` without controls.
|
|
||||||
* Remove excess height in iOS 5 devices.
|
|
||||||
*/
|
|
||||||
|
|
||||||
audio:not([controls]) {
|
|
||||||
display: none;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address `[hidden]` styling not present in IE 8/9.
|
|
||||||
* Hide the `template` element in IE, Safari, and Firefox < 22.
|
|
||||||
*/
|
|
||||||
|
|
||||||
[hidden],
|
|
||||||
template {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
Base
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Set default font family to sans-serif.
|
|
||||||
* 2. Prevent iOS text size adjust after orientation change, without disabling
|
|
||||||
* user zoom.
|
|
||||||
*/
|
|
||||||
|
|
||||||
html {
|
|
||||||
font-family: sans-serif; /* 1 */
|
|
||||||
-ms-text-size-adjust: 100%; /* 2 */
|
|
||||||
-webkit-text-size-adjust: 100%; /* 2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove default margin.
|
|
||||||
*/
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
Links
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the gray background color from active links in IE 10.
|
|
||||||
*/
|
|
||||||
|
|
||||||
a {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address `outline` inconsistency between Chrome and other browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
a:focus {
|
|
||||||
outline: thin dotted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Improve readability when focused and also mouse hovered in all browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
a:active,
|
|
||||||
a:hover {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
Typography
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address variable `h1` font-size and margin within `section` and `article`
|
|
||||||
* contexts in Firefox 4+, Safari 5, and Chrome.
|
|
||||||
*/
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 2em;
|
|
||||||
margin: 0.67em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address styling not present in IE 8/9, Safari 5, and Chrome.
|
|
||||||
*/
|
|
||||||
|
|
||||||
abbr[title] {
|
|
||||||
border-bottom: 1px dotted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
|
|
||||||
*/
|
|
||||||
|
|
||||||
b,
|
|
||||||
strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address styling not present in Safari 5 and Chrome.
|
|
||||||
*/
|
|
||||||
|
|
||||||
dfn {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address differences between Firefox and other browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
hr {
|
|
||||||
-moz-box-sizing: content-box;
|
|
||||||
box-sizing: content-box;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address styling not present in IE 8/9.
|
|
||||||
*/
|
|
||||||
|
|
||||||
mark {
|
|
||||||
background: #ff0;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Correct font family set oddly in Safari 5 and Chrome.
|
|
||||||
*/
|
|
||||||
|
|
||||||
code,
|
|
||||||
kbd,
|
|
||||||
pre,
|
|
||||||
samp {
|
|
||||||
font-family: monospace, serif;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Improve readability of pre-formatted text in all browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
pre {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set consistent quote types.
|
|
||||||
*/
|
|
||||||
|
|
||||||
q {
|
|
||||||
quotes: "\201C" "\201D" "\2018" "\2019";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address inconsistent and variable font size in all browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
small {
|
|
||||||
font-size: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
sub,
|
|
||||||
sup {
|
|
||||||
font-size: 75%;
|
|
||||||
line-height: 0;
|
|
||||||
position: relative;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
sup {
|
|
||||||
top: -0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub {
|
|
||||||
bottom: -0.25em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
Embedded content
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove border when inside `a` element in IE 8/9.
|
|
||||||
*/
|
|
||||||
|
|
||||||
img {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Correct overflow displayed oddly in IE 9.
|
|
||||||
*/
|
|
||||||
|
|
||||||
svg:not(:root) {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
Figures
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address margin not present in IE 8/9 and Safari 5.
|
|
||||||
*/
|
|
||||||
|
|
||||||
figure {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
Forms
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define consistent border, margin, and padding.
|
|
||||||
*/
|
|
||||||
|
|
||||||
fieldset {
|
|
||||||
border: 1px solid #c0c0c0;
|
|
||||||
margin: 0 2px;
|
|
||||||
padding: 0.35em 0.625em 0.75em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Correct `color` not being inherited in IE 8/9.
|
|
||||||
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
|
|
||||||
*/
|
|
||||||
|
|
||||||
legend {
|
|
||||||
border: 0; /* 1 */
|
|
||||||
padding: 0; /* 2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Correct font family not being inherited in all browsers.
|
|
||||||
* 2. Correct font size not being inherited in all browsers.
|
|
||||||
* 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button,
|
|
||||||
input,
|
|
||||||
select,
|
|
||||||
textarea {
|
|
||||||
font-family: inherit; /* 1 */
|
|
||||||
font-size: 100%; /* 2 */
|
|
||||||
margin: 0; /* 3 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
|
|
||||||
* the UA stylesheet.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button,
|
|
||||||
input {
|
|
||||||
line-height: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Address inconsistent `text-transform` inheritance for `button` and `select`.
|
|
||||||
* All other form control elements do not inherit `text-transform` values.
|
|
||||||
* Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
|
|
||||||
* Correct `select` style inheritance in Firefox 4+ and Opera.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button,
|
|
||||||
select {
|
|
||||||
text-transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
|
||||||
* and `video` controls.
|
|
||||||
* 2. Correct inability to style clickable `input` types in iOS.
|
|
||||||
* 3. Improve usability and consistency of cursor style between image-type
|
|
||||||
* `input` and others.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button,
|
|
||||||
html input[type="button"], /* 1 */
|
|
||||||
input[type="reset"],
|
|
||||||
input[type="submit"] {
|
|
||||||
-webkit-appearance: button; /* 2 */
|
|
||||||
cursor: pointer; /* 3 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Re-set default cursor for disabled elements.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button[disabled],
|
|
||||||
html input[disabled] {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Address box sizing set to `content-box` in IE 8/9/10.
|
|
||||||
* 2. Remove excess padding in IE 8/9/10.
|
|
||||||
*/
|
|
||||||
|
|
||||||
input[type="checkbox"],
|
|
||||||
input[type="radio"] {
|
|
||||||
box-sizing: border-box; /* 1 */
|
|
||||||
padding: 0; /* 2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
|
|
||||||
* 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
|
|
||||||
* (include `-moz` to future-proof).
|
|
||||||
*/
|
|
||||||
|
|
||||||
input[type="search"] {
|
|
||||||
-webkit-appearance: textfield; /* 1 */
|
|
||||||
-moz-box-sizing: content-box;
|
|
||||||
-webkit-box-sizing: content-box; /* 2 */
|
|
||||||
box-sizing: content-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove inner padding and search cancel button in Safari 5 and Chrome
|
|
||||||
* on OS X.
|
|
||||||
*/
|
|
||||||
|
|
||||||
input[type="search"]::-webkit-search-cancel-button,
|
|
||||||
input[type="search"]::-webkit-search-decoration {
|
|
||||||
-webkit-appearance: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove inner padding and border in Firefox 4+.
|
|
||||||
*/
|
|
||||||
|
|
||||||
button::-moz-focus-inner,
|
|
||||||
input::-moz-focus-inner {
|
|
||||||
border: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Remove default vertical scrollbar in IE 8/9.
|
|
||||||
* 2. Improve readability and alignment in all browsers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
overflow: auto; /* 1 */
|
|
||||||
vertical-align: top; /* 2 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==========================================================================
|
|
||||||
Tables
|
|
||||||
========================================================================== */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove most spacing between table cells.
|
|
||||||
*/
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user