Extract CSS and JS from templates and serve as static assets
This commit is contained in:
parent
963085ab9a
commit
53c4ff1b5c
12 changed files with 580 additions and 322 deletions
136
Cargo.lock
generated
136
Cargo.lock
generated
|
@ -1,32 +1,11 @@
|
||||||
[root]
|
[root]
|
||||||
name = "sausagewiki"
|
name = "static_resource_derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bart 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bart_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"clap 2.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"diesel 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"diesel_codegen 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"libsqlite3-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"pulldown-cmark 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"r2d2 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sha2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"r2d2-diesel 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 0.10.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -100,6 +79,20 @@ name = "bitflags"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byte-tools"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -188,6 +181,14 @@ dependencies = [
|
||||||
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dtoa"
|
name = "dtoa"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -198,6 +199,11 @@ name = "either"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fake-simd"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.1.14"
|
version = "0.1.14"
|
||||||
|
@ -217,6 +223,15 @@ name = "gcc"
|
||||||
version = "0.3.53"
|
version = "0.3.53"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"typenum 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "httparse"
|
name = "httparse"
|
||||||
version = "1.2.3"
|
version = "1.2.3"
|
||||||
|
@ -399,6 +414,14 @@ dependencies = [
|
||||||
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nodrop"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "2.2.1"
|
version = "2.2.1"
|
||||||
|
@ -444,6 +467,11 @@ dependencies = [
|
||||||
"libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "odds"
|
||||||
|
version = "0.2.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -539,6 +567,38 @@ dependencies = [
|
||||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sausagewiki"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"bart 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"bart_derive 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"clap 2.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"diesel 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"diesel_codegen 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libsqlite3-sys 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"pulldown-cmark 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"r2d2 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"r2d2-diesel 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_derive 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"static_resource_derive 0.1.0",
|
||||||
|
"tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scheduled-thread-pool"
|
name = "scheduled-thread-pool"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -603,6 +663,18 @@ dependencies = [
|
||||||
"url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -739,6 +811,11 @@ dependencies = [
|
||||||
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicase"
|
name = "unicase"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -853,6 +930,8 @@ dependencies = [
|
||||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||||
"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
|
"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
|
||||||
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
|
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
|
||||||
|
"checksum block-buffer 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1339a1042f5d9f295737ad4d9a6ab6bf81c84a933dba110b9200cd6d1448b814"
|
||||||
|
"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
|
||||||
"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d"
|
"checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d"
|
||||||
"checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6"
|
"checksum bytes 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d828f97b58cc5de3e40c421d0cf2132d6b2da4ee0e11b8632fa838f0f9333ad6"
|
||||||
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
|
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
|
||||||
|
@ -863,11 +942,14 @@ dependencies = [
|
||||||
"checksum diesel 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f5b7408ddb7a834c0f191fb49b4398ccae19b226afafeeff5cb1eaac768b89"
|
"checksum diesel 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "67f5b7408ddb7a834c0f191fb49b4398ccae19b226afafeeff5cb1eaac768b89"
|
||||||
"checksum diesel_codegen 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "044abc50a0ee67e195b0ae95c9ffe6903748434294636dd67808301b7df1902f"
|
"checksum diesel_codegen 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "044abc50a0ee67e195b0ae95c9ffe6903748434294636dd67808301b7df1902f"
|
||||||
"checksum diesel_infer_schema 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf0e5b3d40abfc2eadba06df2c1b93b61be579390f4dba620f08e8172cce30c4"
|
"checksum diesel_infer_schema 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf0e5b3d40abfc2eadba06df2c1b93b61be579390f4dba620f08e8172cce30c4"
|
||||||
|
"checksum digest 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e5b29bf156f3f4b3c4f610a25ff69370616ae6e0657d416de22645483e72af0a"
|
||||||
"checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90"
|
"checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90"
|
||||||
"checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a"
|
"checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a"
|
||||||
|
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||||
"checksum futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4b63a4792d4f8f686defe3b39b92127fea6344de5d38202b2ee5a11bbbf29d6a"
|
"checksum futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4b63a4792d4f8f686defe3b39b92127fea6344de5d38202b2ee5a11bbbf29d6a"
|
||||||
"checksum futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a283c84501e92cade5ea673a2a7ca44f71f209ccdd302a3e0896f50083d2c5ff"
|
"checksum futures-cpupool 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a283c84501e92cade5ea673a2a7ca44f71f209ccdd302a3e0896f50083d2c5ff"
|
||||||
"checksum gcc 0.3.53 (registry+https://github.com/rust-lang/crates.io-index)" = "e8310f7e9c890398b0e80e301c4f474e9918d2b27fca8f48486ca775fa9ffc5a"
|
"checksum gcc 0.3.53 (registry+https://github.com/rust-lang/crates.io-index)" = "e8310f7e9c890398b0e80e301c4f474e9918d2b27fca8f48486ca775fa9ffc5a"
|
||||||
|
"checksum generic-array 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fceb69994e330afed50c93524be68c42fa898c2d9fd4ee8da03bd7363acd26f2"
|
||||||
"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
|
"checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07"
|
||||||
"checksum hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "641abc3e3fcf0de41165595f801376e01106bca1fd876dda937730e477ca004c"
|
"checksum hyper 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "641abc3e3fcf0de41165595f801376e01106bca1fd876dda937730e477ca004c"
|
||||||
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
|
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
|
||||||
|
@ -889,12 +971,14 @@ dependencies = [
|
||||||
"checksum mio 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "dbd91d3bfbceb13897065e97b2ef177a09a438cb33612b2d371bf568819a9313"
|
"checksum mio 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "dbd91d3bfbceb13897065e97b2ef177a09a438cb33612b2d371bf568819a9313"
|
||||||
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
||||||
"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09"
|
"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09"
|
||||||
|
"checksum nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "52cd74cd09beba596430cc6e3091b74007169a56246e1262f0ba451ea95117b2"
|
||||||
"checksum nom 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf51a729ecf40266a2368ad335a5fdde43471f545a967109cd62146ecf8b66ff"
|
"checksum nom 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf51a729ecf40266a2368ad335a5fdde43471f545a967109cd62146ecf8b66ff"
|
||||||
"checksum num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "a311b77ebdc5dd4cf6449d81e4135d9f0e3b153839ac90e648a8ef538f923525"
|
"checksum num 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "a311b77ebdc5dd4cf6449d81e4135d9f0e3b153839ac90e648a8ef538f923525"
|
||||||
"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba"
|
"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba"
|
||||||
"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01"
|
"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01"
|
||||||
"checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0"
|
"checksum num-traits 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "99843c856d68d8b4313b03a17e33c4bb42ae8f6610ea81b28abe076ac721b9b0"
|
||||||
"checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584"
|
"checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584"
|
||||||
|
"checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba"
|
||||||
"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
|
"checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
|
||||||
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
|
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
|
||||||
"checksum pulldown-cmark 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7c04a8bb38f80717527edea39c82378c2ef13ecdbc914cbd90653a2e24afdf"
|
"checksum pulldown-cmark 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7c04a8bb38f80717527edea39c82378c2ef13ecdbc914cbd90653a2e24afdf"
|
||||||
|
@ -916,6 +1000,7 @@ dependencies = [
|
||||||
"checksum serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37aee4e0da52d801acfbc0cc219eb1eda7142112339726e427926a6f6ee65d3a"
|
"checksum serde_derive_internals 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37aee4e0da52d801acfbc0cc219eb1eda7142112339726e427926a6f6ee65d3a"
|
||||||
"checksum serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d243424e06f9f9c39e3cd36147470fd340db785825e367625f79298a6ac6b7ac"
|
"checksum serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d243424e06f9f9c39e3cd36147470fd340db785825e367625f79298a6ac6b7ac"
|
||||||
"checksum serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce0fd303af908732989354c6f02e05e2e6d597152870f2c6990efb0577137480"
|
"checksum serde_urlencoded 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce0fd303af908732989354c6f02e05e2e6d597152870f2c6990efb0577137480"
|
||||||
|
"checksum sha2 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d963c78ce367df26d7ea8b8cc655c651b42e8a1e584e869c1e17dae3ccb116a"
|
||||||
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
||||||
"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
|
"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013"
|
||||||
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694"
|
||||||
|
@ -931,6 +1016,7 @@ dependencies = [
|
||||||
"checksum tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ab83e7adb5677e42e405fa4ceff75659d93c4d7d7dd22f52fcec59ee9f02af"
|
"checksum tokio-io 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4ab83e7adb5677e42e405fa4ceff75659d93c4d7d7dd22f52fcec59ee9f02af"
|
||||||
"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
|
"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389"
|
||||||
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
|
"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162"
|
||||||
|
"checksum typenum 1.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a99dc6780ef33c78780b826cf9d2a78840b72cae9474de4bcaf9051e60ebbd"
|
||||||
"checksum unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e01da42520092d0cd2d6ac3ae69eb21a22ad43ff195676b86f8c37f487d6b80"
|
"checksum unicase 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e01da42520092d0cd2d6ac3ae69eb21a22ad43ff195676b86f8c37f487d6b80"
|
||||||
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
|
||||||
"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f"
|
"checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f"
|
||||||
|
|
|
@ -45,6 +45,9 @@ version = "0.1"
|
||||||
version = "0.0.11"
|
version = "0.0.11"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
|
[dependencies.static_resource_derive]
|
||||||
|
path = "libs/static_resource_derive"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
quote = "0.3.10"
|
quote = "0.3.10"
|
||||||
walkdir = "1"
|
walkdir = "1"
|
||||||
|
@ -53,3 +56,5 @@ walkdir = "1"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["sqlite", "chrono"]
|
features = ["sqlite", "chrono"]
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
97
assets/script.js
Normal file
97
assets/script.js
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
function autosizeTextarea(textarea, shadow) {
|
||||||
|
shadow.style.width = textarea.clientWidth + "px";
|
||||||
|
shadow.value = textarea.value;
|
||||||
|
textarea.style.height = shadow.scrollHeight + "px";
|
||||||
|
}
|
||||||
|
|
||||||
|
function queryArgsFromForm(form) {
|
||||||
|
const items = [];
|
||||||
|
for (const {name, value} of form.elements) {
|
||||||
|
if (!name) continue;
|
||||||
|
items.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));
|
||||||
|
}
|
||||||
|
return items.join('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasBeenOpen = false;
|
||||||
|
function openEditor() {
|
||||||
|
const article = document.querySelector("article");
|
||||||
|
const rendered = article.querySelector(".rendered");
|
||||||
|
const editor = article.querySelector(".editor");
|
||||||
|
const textarea = editor.querySelector('textarea[name="body"]');
|
||||||
|
const shadow = editor.querySelector('textarea.shadow-control');
|
||||||
|
const form = editor.querySelector("form");
|
||||||
|
const cancel = editor.querySelector('.cancel');
|
||||||
|
|
||||||
|
const footer = document.querySelector("footer");
|
||||||
|
const revision = footer.querySelector(".revision");
|
||||||
|
const lastUpdated = footer.querySelector(".last-updated");
|
||||||
|
|
||||||
|
textarea.style.height = rendered.clientHeight + "px";
|
||||||
|
|
||||||
|
article.classList.add('edit');
|
||||||
|
|
||||||
|
autosizeTextarea(textarea, shadow);
|
||||||
|
|
||||||
|
textarea.focus();
|
||||||
|
|
||||||
|
if (hasBeenOpen) return;
|
||||||
|
hasBeenOpen = true;
|
||||||
|
|
||||||
|
textarea.addEventListener('input', () => autosizeTextarea(textarea, shadow));
|
||||||
|
window.addEventListener('resize', () => autosizeTextarea(textarea, shadow));
|
||||||
|
|
||||||
|
form.addEventListener("submit", function (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
(async function () {
|
||||||
|
const body = queryArgsFromForm(form);
|
||||||
|
textarea.disabled = true;
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
form.getAttribute("action"),
|
||||||
|
{
|
||||||
|
method: 'PUT',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body: body,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) throw new Error("Unexpected status code (" + response.status + ")");
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
form.elements.base_revision.value = result.revision;
|
||||||
|
revision.textContent = result.revision;
|
||||||
|
lastUpdated.textContent = result.created;
|
||||||
|
rendered.innerHTML = result.rendered;
|
||||||
|
article.classList.remove('edit');
|
||||||
|
|
||||||
|
textarea.disabled = false;
|
||||||
|
}()
|
||||||
|
.catch(err => {
|
||||||
|
textarea.disabled = false;
|
||||||
|
console.error(err);
|
||||||
|
alert(err);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
cancel.addEventListener('click', function (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
article.classList.remove('edit');
|
||||||
|
form.reset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document
|
||||||
|
.getElementById("openEditor")
|
||||||
|
.addEventListener("click", function (ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
openEditor();
|
||||||
|
})
|
183
assets/style.css
Normal file
183
assets/style.css
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
html {
|
||||||
|
font-family: "Apple Garamond", "Baskerville",
|
||||||
|
"Times New Roman", "Droid Serif", "Times",
|
||||||
|
"Source Serif Pro", serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: 'Amatic SC', cursive;
|
||||||
|
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
font-size: 40px;
|
||||||
|
line-height: 54px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: normal;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 32px;
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
max-width: 600px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 40px auto 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 32px;
|
||||||
|
max-width: 600px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto 120px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
margin-left: 0;
|
||||||
|
padding-left: 12px;
|
||||||
|
border-left: 4px solid #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 0 28px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
code, pre {
|
||||||
|
background: #f8f8f8;
|
||||||
|
font-family: "SF Mono", "Monaco",
|
||||||
|
"Inconsolata", "Fira Mono",
|
||||||
|
"Droid Sans Mono", "Source Code Pro",
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #5e90af;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #79b9e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Sticky footer */
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
article {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
footer {
|
||||||
|
padding: 16px 0;
|
||||||
|
background: #f8f8f8;
|
||||||
|
color: #444;
|
||||||
|
text-align: center;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont,
|
||||||
|
"Segoe UI", "Roboto", "Oxygen",
|
||||||
|
"Ubuntu", "Cantarell", "Fira Sans",
|
||||||
|
"Droid Sans", "Helvetica Neue", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer dl {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer dt, footer dd {
|
||||||
|
display: inline;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer dt::after {
|
||||||
|
content: ": ";
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer dd::after {
|
||||||
|
content: "|";
|
||||||
|
margin: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer dd:last-child::after {
|
||||||
|
content: "";
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: "SF Mono", "Monaco",
|
||||||
|
"Inconsolata", "Fira Mono",
|
||||||
|
"Droid Sans Mono", "Source Code Pro",
|
||||||
|
monospace;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
resize: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-control {
|
||||||
|
visibility: hidden;
|
||||||
|
position: fixed;
|
||||||
|
height: auto;
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor textarea[name="body"] {
|
||||||
|
height: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit .editor {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit .rendered {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-controls {
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
background: #91A238;
|
||||||
|
padding: 10px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 600px) {
|
||||||
|
.editor-controls {
|
||||||
|
position: fixed;
|
||||||
|
left: auto;
|
||||||
|
right: 20px;
|
||||||
|
bottom: 20px;
|
||||||
|
|
||||||
|
box-shadow: 2px 2px 8px rgba(0,0,0, 0.25);
|
||||||
|
}
|
||||||
|
}
|
13
libs/static_resource_derive/Cargo.toml
Normal file
13
libs/static_resource_derive/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "static_resource_derive"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Magnus Hoff <maghoff@gmail.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "0.3.10"
|
||||||
|
syn = "0.10.5"
|
||||||
|
sha2 = "0.6"
|
||||||
|
base64 = "0.6"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
115
libs/static_resource_derive/src/lib.rs
Normal file
115
libs/static_resource_derive/src/lib.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#![recursion_limit="128"]
|
||||||
|
|
||||||
|
#[macro_use] extern crate quote;
|
||||||
|
|
||||||
|
extern crate base64;
|
||||||
|
extern crate proc_macro;
|
||||||
|
extern crate sha2;
|
||||||
|
extern crate syn;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::prelude::*;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
fn user_crate_root() -> PathBuf {
|
||||||
|
std::env::current_dir().expect("Unable to get current directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_attr<'a>(attrs: &'a Vec<syn::Attribute>, name: &str) -> Option<&'a str> {
|
||||||
|
attrs.iter()
|
||||||
|
.find(|&x| x.name() == name)
|
||||||
|
.and_then(|ref attr| match &attr.value {
|
||||||
|
&syn::MetaItem::NameValue(_, syn::Lit::Str(ref template, _)) => Some(template),
|
||||||
|
_ => None
|
||||||
|
})
|
||||||
|
.map(|x| x.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn buf_file<P: AsRef<Path>>(filename: P) -> Vec<u8> {
|
||||||
|
let mut f = File::open(filename)
|
||||||
|
.expect("Unable to open file for reading");
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
f.read_to_end(&mut buf)
|
||||||
|
.expect("Unable to read file");
|
||||||
|
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_checksum<P: AsRef<Path>>(filename: P) -> String {
|
||||||
|
use base64::*;
|
||||||
|
use sha2::{Sha256, Digest};
|
||||||
|
|
||||||
|
encode_config(&Sha256::digest(&buf_file(filename)), URL_SAFE)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(StaticResource, attributes(filename, mime))]
|
||||||
|
pub fn static_resource(input: TokenStream) -> TokenStream {
|
||||||
|
let s = input.to_string();
|
||||||
|
let ast = syn::parse_macro_input(&s).unwrap();
|
||||||
|
|
||||||
|
let filename = find_attr(&ast.attrs, "filename")
|
||||||
|
.expect("The `filename` attribute must be specified");
|
||||||
|
let abs_filename = user_crate_root().join(filename);
|
||||||
|
let abs_filename = abs_filename.to_str().expect("Absolute file path must be valid Unicode");
|
||||||
|
|
||||||
|
let checksum = calculate_checksum(&abs_filename);
|
||||||
|
|
||||||
|
let mime = find_attr(&ast.attrs, "mime")
|
||||||
|
.expect("The `mime` attribute must be specified");
|
||||||
|
|
||||||
|
let name = &ast.ident;
|
||||||
|
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
|
||||||
|
|
||||||
|
let gen = quote! {
|
||||||
|
#[allow(unused_attributes, unused_qualifications, unknown_lints, clippy)]
|
||||||
|
#[automatically_derived]
|
||||||
|
impl #impl_generics Resource for #name #ty_generics #where_clause {
|
||||||
|
fn allow(&self) -> Vec<::hyper::Method> {
|
||||||
|
use ::hyper::Method::*;
|
||||||
|
vec![Options, Head, Get]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn head(&self) -> futures::BoxFuture<Response, Box<::std::error::Error + Send + Sync>> {
|
||||||
|
futures::finished(Response::new()
|
||||||
|
.with_status(::hyper::StatusCode::Ok)
|
||||||
|
.with_header(::hyper::header::ContentType(
|
||||||
|
#mime.parse().expect("Statically supplied mime type must be parseable")))
|
||||||
|
.with_header(::hyper::header::CacheControl(vec![
|
||||||
|
::hyper::header::CacheDirective::Extension("immutable".to_owned(), None),
|
||||||
|
::hyper::header::CacheDirective::MaxAge(31556926),
|
||||||
|
::hyper::header::CacheDirective::Public,
|
||||||
|
]))
|
||||||
|
.with_header(::hyper::header::ETag(Self::etag()))
|
||||||
|
).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(self: Box<Self>) -> futures::BoxFuture<Response, Box<::std::error::Error + Send + Sync>> {
|
||||||
|
let body = include_bytes!(#abs_filename);
|
||||||
|
|
||||||
|
self.head().map(move |head|
|
||||||
|
head
|
||||||
|
.with_header(::hyper::header::ContentLength(body.len() as u64))
|
||||||
|
.with_body(body as &'static [u8])
|
||||||
|
).boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn put(self: Box<Self>, _body: hyper::Body) -> futures::BoxFuture<Response, Box<::std::error::Error + Send + Sync>> {
|
||||||
|
futures::finished(self.method_not_allowed()).boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl #impl_generics #name #ty_generics #where_clause {
|
||||||
|
fn checksum() -> &'static str {
|
||||||
|
#checksum
|
||||||
|
}
|
||||||
|
|
||||||
|
fn etag() -> ::hyper::header::EntityTag {
|
||||||
|
::hyper::header::EntityTag::new(false, Self::checksum().to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
gen.parse().unwrap()
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
#[macro_use] extern crate diesel_codegen;
|
#[macro_use] extern crate diesel_codegen;
|
||||||
#[macro_use] extern crate lazy_static;
|
#[macro_use] extern crate lazy_static;
|
||||||
#[macro_use] extern crate serde_derive;
|
#[macro_use] extern crate serde_derive;
|
||||||
|
#[macro_use] extern crate static_resource_derive;
|
||||||
|
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
|
|
56
src/site.rs
56
src/site.rs
|
@ -1,6 +1,7 @@
|
||||||
// #[derive(BartDisplay)] can cause unused extern crates warning:
|
// #[derive(BartDisplay)] can cause unused extern crates warning:
|
||||||
#![allow(unused_extern_crates)]
|
#![allow(unused_extern_crates)]
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use futures::{self, Future};
|
use futures::{self, Future};
|
||||||
|
@ -60,6 +61,7 @@ lazy_static! {
|
||||||
struct Layout<'a, T: 'a + fmt::Display> {
|
struct Layout<'a, T: 'a + fmt::Display> {
|
||||||
pub title: &'a str,
|
pub title: &'a str,
|
||||||
pub body: &'a T,
|
pub body: &'a T,
|
||||||
|
pub style_css_checksum: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(BartDisplay)]
|
#[derive(BartDisplay)]
|
||||||
|
@ -70,12 +72,43 @@ struct NotFound;
|
||||||
#[template = "templates/500.html"]
|
#[template = "templates/500.html"]
|
||||||
struct InternalServerError;
|
struct InternalServerError;
|
||||||
|
|
||||||
|
#[derive(StaticResource)]
|
||||||
|
#[filename = "assets/style.css"]
|
||||||
|
#[mime = "text/css"]
|
||||||
|
struct StyleCss;
|
||||||
|
|
||||||
|
#[derive(StaticResource)]
|
||||||
|
#[filename = "assets/script.js"]
|
||||||
|
#[mime = "application/javascript"]
|
||||||
|
struct ScriptJs;
|
||||||
|
|
||||||
struct WikiLookup {
|
struct WikiLookup {
|
||||||
state: State,
|
state: State,
|
||||||
|
lookup_map: HashMap<String, Box<Fn() -> Box<Resource + Sync + Send>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WikiLookup {
|
||||||
|
fn new(state: State) -> WikiLookup {
|
||||||
|
let mut lookup_map = HashMap::new();
|
||||||
|
|
||||||
|
lookup_map.insert(
|
||||||
|
format!("/_assets/style-{}.css", StyleCss::checksum()),
|
||||||
|
Box::new(|| Box::new(StyleCss) as Box<Resource + Sync + Send>)
|
||||||
|
as Box<Fn() -> Box<Resource + Sync + Send>>
|
||||||
|
);
|
||||||
|
|
||||||
|
lookup_map.insert(
|
||||||
|
format!("/_assets/script-{}.js", ScriptJs::checksum()),
|
||||||
|
Box::new(|| Box::new(ScriptJs) as Box<Resource + Sync + Send>)
|
||||||
|
as Box<Fn() -> Box<Resource + Sync + Send>>
|
||||||
|
);
|
||||||
|
|
||||||
|
WikiLookup { state, lookup_map }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lookup for WikiLookup {
|
impl Lookup for WikiLookup {
|
||||||
type Resource = ArticleResource;
|
type Resource = Box<Resource + Send + Sync>;
|
||||||
type Error = Box<::std::error::Error + Send + Sync>;
|
type Error = Box<::std::error::Error + Send + Sync>;
|
||||||
type Future = futures::BoxFuture<Option<Self::Resource>, Self::Error>;
|
type Future = futures::BoxFuture<Option<Self::Resource>, Self::Error>;
|
||||||
|
|
||||||
|
@ -84,14 +117,17 @@ impl Lookup for WikiLookup {
|
||||||
|
|
||||||
if path.starts_with("/_") {
|
if path.starts_with("/_") {
|
||||||
// Reserved namespace
|
// Reserved namespace
|
||||||
return futures::finished(None).boxed();
|
|
||||||
|
return futures::finished(
|
||||||
|
self.lookup_map.get(path).map(|x| x())
|
||||||
|
).boxed();
|
||||||
}
|
}
|
||||||
|
|
||||||
let slug = &path[1..];
|
let slug = &path[1..];
|
||||||
if let Ok(article_id) = slug.parse() {
|
if let Ok(article_id) = slug.parse() {
|
||||||
let state = self.state.clone();
|
let state = self.state.clone();
|
||||||
self.state.get_article_revision_by_id(article_id)
|
self.state.get_article_revision_by_id(article_id)
|
||||||
.and_then(|x| Ok(x.map(move |article| ArticleResource::new(state, article))))
|
.and_then(|x| Ok(x.map(move |article| Box::new(ArticleResource::new(state, article)) as Box<Resource + Sync + Send>)))
|
||||||
.boxed()
|
.boxed()
|
||||||
} else {
|
} else {
|
||||||
futures::finished(None).boxed()
|
futures::finished(None).boxed()
|
||||||
|
@ -123,7 +159,7 @@ impl Resource for ArticleResource {
|
||||||
).boxed()
|
).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(self) -> futures::BoxFuture<Response, Box<::std::error::Error + Send + Sync>> {
|
fn get(self: Box<Self>) -> futures::BoxFuture<Response, Box<::std::error::Error + Send + Sync>> {
|
||||||
use chrono::{self, TimeZone, Local};
|
use chrono::{self, TimeZone, Local};
|
||||||
|
|
||||||
#[derive(BartDisplay)]
|
#[derive(BartDisplay)]
|
||||||
|
@ -136,6 +172,8 @@ impl Resource for ArticleResource {
|
||||||
title: &'a str,
|
title: &'a str,
|
||||||
raw: &'a str,
|
raw: &'a str,
|
||||||
rendered: String,
|
rendered: String,
|
||||||
|
|
||||||
|
script_js_checksum: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.head().map(move |head|
|
self.head().map(move |head|
|
||||||
|
@ -149,12 +187,14 @@ impl Resource for ArticleResource {
|
||||||
title: &self.data.title,
|
title: &self.data.title,
|
||||||
raw: &self.data.body,
|
raw: &self.data.body,
|
||||||
rendered: render_markdown(&self.data.body),
|
rendered: render_markdown(&self.data.body),
|
||||||
}
|
script_js_checksum: ScriptJs::checksum(),
|
||||||
|
},
|
||||||
|
style_css_checksum: StyleCss::checksum(),
|
||||||
}.to_string())
|
}.to_string())
|
||||||
).boxed()
|
).boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put(self, body: hyper::Body) -> futures::BoxFuture<Response, Box<::std::error::Error + Send + Sync>> {
|
fn put(self: Box<Self>, body: hyper::Body) -> futures::BoxFuture<Response, Box<::std::error::Error + Send + Sync>> {
|
||||||
// TODO Check incoming Content-Type
|
// TODO Check incoming Content-Type
|
||||||
|
|
||||||
use chrono::{TimeZone, Local};
|
use chrono::{TimeZone, Local};
|
||||||
|
@ -206,7 +246,7 @@ pub struct Site {
|
||||||
impl Site {
|
impl Site {
|
||||||
pub fn new(state: State) -> Site {
|
pub fn new(state: State) -> Site {
|
||||||
Site {
|
Site {
|
||||||
root: WikiLookup { state }
|
root: WikiLookup::new(state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,6 +256,7 @@ impl Site {
|
||||||
.with_body(Layout {
|
.with_body(Layout {
|
||||||
title: "Not found",
|
title: "Not found",
|
||||||
body: &NotFound,
|
body: &NotFound,
|
||||||
|
style_css_checksum: StyleCss::checksum(),
|
||||||
}.to_string())
|
}.to_string())
|
||||||
.with_status(hyper::StatusCode::NotFound)
|
.with_status(hyper::StatusCode::NotFound)
|
||||||
}
|
}
|
||||||
|
@ -228,6 +269,7 @@ impl Site {
|
||||||
.with_body(Layout {
|
.with_body(Layout {
|
||||||
title: "Internal server error",
|
title: "Internal server error",
|
||||||
body: &InternalServerError,
|
body: &InternalServerError,
|
||||||
|
style_css_checksum: StyleCss::checksum(),
|
||||||
}.to_string())
|
}.to_string())
|
||||||
.with_status(hyper::StatusCode::InternalServerError)
|
.with_status(hyper::StatusCode::InternalServerError)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use super::resource;
|
|
||||||
|
|
||||||
use futures;
|
use futures;
|
||||||
|
|
||||||
pub trait Lookup {
|
pub trait Lookup {
|
||||||
type Resource: resource::Resource;
|
type Resource;
|
||||||
type Error;
|
type Error;
|
||||||
type Future: futures::Future<Item=Option<Self::Resource>, Error=Self::Error>;
|
type Future: futures::Future<Item=Option<Self::Resource>, Error=Self::Error>;
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ type Error = Box<std::error::Error + Send + Sync>;
|
||||||
pub trait Resource {
|
pub trait Resource {
|
||||||
fn allow(&self) -> Vec<hyper::Method>;
|
fn allow(&self) -> Vec<hyper::Method>;
|
||||||
fn head(&self) -> futures::BoxFuture<server::Response, Error>;
|
fn head(&self) -> futures::BoxFuture<server::Response, Error>;
|
||||||
fn get(self) -> futures::BoxFuture<server::Response, Error>;
|
fn get(self: Box<Self>) -> futures::BoxFuture<server::Response, Error>;
|
||||||
fn put(self, body: hyper::Body) -> futures::BoxFuture<server::Response, Error>;
|
fn put(self: Box<Self>, body: hyper::Body) -> futures::BoxFuture<server::Response, Error>;
|
||||||
|
|
||||||
fn options(&self) -> Response {
|
fn options(&self) -> Response {
|
||||||
Response::new()
|
Response::new()
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
<script src="_assets/script-{{script_js_checksum}}.js" defer></script>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<h1>{{title}}</h1>
|
<h1>{{title}}</h1>
|
||||||
</header>
|
</header>
|
||||||
|
@ -32,103 +34,3 @@
|
||||||
<dd class="last-updated">{{created}}</dd>
|
<dd class="last-updated">{{created}}</dd>
|
||||||
</dl>
|
</dl>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script>
|
|
||||||
function autosizeTextarea(textarea, shadow) {
|
|
||||||
shadow.style.width = textarea.clientWidth + "px";
|
|
||||||
shadow.value = textarea.value;
|
|
||||||
textarea.style.height = shadow.scrollHeight + "px";
|
|
||||||
}
|
|
||||||
|
|
||||||
function queryArgsFromForm(form) {
|
|
||||||
const items = [];
|
|
||||||
for (const {name, value} of form.elements) {
|
|
||||||
if (!name) continue;
|
|
||||||
items.push(encodeURIComponent(name) + '=' + encodeURIComponent(value));
|
|
||||||
}
|
|
||||||
return items.join('&');
|
|
||||||
}
|
|
||||||
|
|
||||||
let hasBeenOpen = false;
|
|
||||||
function openEditor() {
|
|
||||||
const article = document.querySelector("article");
|
|
||||||
const rendered = article.querySelector(".rendered");
|
|
||||||
const editor = article.querySelector(".editor");
|
|
||||||
const textarea = editor.querySelector('textarea[name="body"]');
|
|
||||||
const shadow = editor.querySelector('textarea.shadow-control');
|
|
||||||
const form = editor.querySelector("form");
|
|
||||||
const cancel = editor.querySelector('.cancel');
|
|
||||||
|
|
||||||
const footer = document.querySelector("footer");
|
|
||||||
const revision = footer.querySelector(".revision");
|
|
||||||
const lastUpdated = footer.querySelector(".last-updated");
|
|
||||||
|
|
||||||
textarea.style.height = rendered.clientHeight + "px";
|
|
||||||
|
|
||||||
article.classList.add('edit');
|
|
||||||
|
|
||||||
autosizeTextarea(textarea, shadow);
|
|
||||||
|
|
||||||
textarea.focus();
|
|
||||||
|
|
||||||
if (hasBeenOpen) return;
|
|
||||||
hasBeenOpen = true;
|
|
||||||
|
|
||||||
textarea.addEventListener('input', () => autosizeTextarea(textarea, shadow));
|
|
||||||
window.addEventListener('resize', () => autosizeTextarea(textarea, shadow));
|
|
||||||
|
|
||||||
form.addEventListener("submit", function (ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
|
|
||||||
(async function () {
|
|
||||||
const body = queryArgsFromForm(form);
|
|
||||||
textarea.disabled = true;
|
|
||||||
|
|
||||||
const response = await fetch(
|
|
||||||
form.getAttribute("action"),
|
|
||||||
{
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/x-www-form-urlencoded"
|
|
||||||
},
|
|
||||||
body: body,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) throw new Error("Unexpected status code (" + response.status + ")");
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
form.elements.base_revision.value = result.revision;
|
|
||||||
revision.textContent = result.revision;
|
|
||||||
lastUpdated.textContent = result.created;
|
|
||||||
rendered.innerHTML = result.rendered;
|
|
||||||
article.classList.remove('edit');
|
|
||||||
|
|
||||||
textarea.disabled = false;
|
|
||||||
}()
|
|
||||||
.catch(err => {
|
|
||||||
textarea.disabled = false;
|
|
||||||
console.error(err);
|
|
||||||
alert(err);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
cancel.addEventListener('click', function (ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
|
|
||||||
article.classList.remove('edit');
|
|
||||||
form.reset();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document
|
|
||||||
.getElementById("openEditor")
|
|
||||||
.addEventListener("click", function (ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
ev.stopPropagation();
|
|
||||||
|
|
||||||
openEditor();
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -4,191 +4,7 @@
|
||||||
<title>{{title}}</title>
|
<title>{{title}}</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link href="https://fonts.googleapis.com/css?family=Amatic+SC" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css?family=Amatic+SC" rel="stylesheet">
|
||||||
<style>
|
<link href="_assets/style-{{style_css_checksum}}.css" rel="stylesheet">
|
||||||
html {
|
|
||||||
font-family: "Apple Garamond", "Baskerville",
|
|
||||||
"Times New Roman", "Droid Serif", "Times",
|
|
||||||
"Source Serif Pro", serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-family: 'Amatic SC', cursive;
|
|
||||||
|
|
||||||
font-weight: normal;
|
|
||||||
font-style: normal;
|
|
||||||
|
|
||||||
font-size: 40px;
|
|
||||||
line-height: 54px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-family: inherit;
|
|
||||||
font-weight: bold;
|
|
||||||
font-style: normal;
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 32px;
|
|
||||||
margin-top: 32px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
header {
|
|
||||||
max-width: 600px;
|
|
||||||
width: 100%;
|
|
||||||
margin: 40px auto 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
article {
|
|
||||||
font-size: 18px;
|
|
||||||
line-height: 32px;
|
|
||||||
max-width: 600px;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0 auto 120px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
margin-left: 0;
|
|
||||||
padding-left: 12px;
|
|
||||||
border-left: 4px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0 0 28px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
code, pre {
|
|
||||||
background: #f8f8f8;
|
|
||||||
font-family: "SF Mono", "Monaco",
|
|
||||||
"Inconsolata", "Fira Mono",
|
|
||||||
"Droid Sans Mono", "Source Code Pro",
|
|
||||||
monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #5e90af;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #79b9e1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Sticky footer */
|
|
||||||
html, body {
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
article {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
footer {
|
|
||||||
padding: 16px 0;
|
|
||||||
background: #f8f8f8;
|
|
||||||
color: #444;
|
|
||||||
text-align: center;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont,
|
|
||||||
"Segoe UI", "Roboto", "Oxygen",
|
|
||||||
"Ubuntu", "Cantarell", "Fira Sans",
|
|
||||||
"Droid Sans", "Helvetica Neue", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer dl {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer dt, footer dd {
|
|
||||||
display: inline;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer dt::after {
|
|
||||||
content: ": ";
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer dd::after {
|
|
||||||
content: "|";
|
|
||||||
margin: 0 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer dd:last-child::after {
|
|
||||||
content: "";
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
border: none;
|
|
||||||
background: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
font-family: "SF Mono", "Monaco",
|
|
||||||
"Inconsolata", "Fira Mono",
|
|
||||||
"Droid Sans Mono", "Source Code Pro",
|
|
||||||
monospace;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
resize: none;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shadow-control {
|
|
||||||
visibility: hidden;
|
|
||||||
position: fixed;
|
|
||||||
height: auto;
|
|
||||||
min-height: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor textarea[name="body"] {
|
|
||||||
height: 600px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit .editor {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edit .rendered {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.editor-controls {
|
|
||||||
position: fixed;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
|
|
||||||
background: #91A238;
|
|
||||||
padding: 10px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 600px) {
|
|
||||||
.editor-controls {
|
|
||||||
position: fixed;
|
|
||||||
left: auto;
|
|
||||||
right: 20px;
|
|
||||||
bottom: 20px;
|
|
||||||
|
|
||||||
box-shadow: 2px 2px 8px rgba(0,0,0, 0.25);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{{{body}}}
|
{{{body}}}
|
||||||
|
|
Loading…
Reference in a new issue