- Work done to clarify a bit every section, and a hugh code refractoring also was made to the code base.
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
@ -15,93 +15,42 @@ class EntityDescriptor
|
|||
{
|
||||
use DescriptorTrait;
|
||||
|
||||
public string $entityTitle = <<<HTML
|
||||
<div style="margin-left:15px">
|
||||
<h4 class='entity-name'>%s</h4>
|
||||
<div class='description'>%s</div>
|
||||
<hr style="margin-top:15px">
|
||||
<h5>Champs</h5>
|
||||
<ol class='fields'>%s</ol>
|
||||
<hr style="margin-top:15px;">
|
||||
<h4 class='request-name'>%s</h4>
|
||||
<h5>Requêtes</h5>
|
||||
<div class='description' style="margin-bottom:10px">%s</div>
|
||||
<ol class='requests'>%s</ol>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
public string $entityField = <<<HTML
|
||||
<li class="odd-even">
|
||||
<div style="display:flex; justify-content: space-between">
|
||||
<div><strong style="font-family:monospace">$%s</strong> <span style='margin-left:15px'>%s</span></div>
|
||||
</div>
|
||||
|
||||
<div class="field-desc" style="margin-top:10px;;background:#fff;padding:5px;font-size:0.9em">
|
||||
<div style="padding:5px"><u>Champ SQL</u> : %s</div>
|
||||
<div style="padding:5px"><u>Attribut</u> : %s</div>
|
||||
<div style="padding:5px"><u>Type</u> : %s</div>
|
||||
<div style="padding:5px"><u>Nullable</u> %s</div>
|
||||
<div style="padding:5px"><u>Taille</u> : %s</div>
|
||||
<div style="padding:5px"><u>Lecture seule</u> : %s</div>
|
||||
</div>
|
||||
</li>
|
||||
HTML;
|
||||
|
||||
public string $searchRequestField = <<<HTML
|
||||
<li class="odd-even">
|
||||
<div style="display:flex; justify-content: space-between">
|
||||
<div><strong style="font-family:monospace">$%s</strong> <span style='margin-left:15px'>%s</span></div>
|
||||
</div>
|
||||
|
||||
<div class="field-desc" style="margin-top:10px;;background:#fff;padding:5px;font-size:0.9em">
|
||||
<div style="padding:5px"><u>Paramètre de requête (GET)</u> : %s</div>
|
||||
<div style="padding:5px"><u>Attribut</u> : %s</div>
|
||||
<div style="padding:5px"><u>Type</u> : %s</div>
|
||||
<div style="padding:5px"><u>Nullable</u> : %s</div>
|
||||
<div style="padding:5px"><u>Valeur par défault</u> : %s</div>
|
||||
</div>
|
||||
</li>
|
||||
HTML;
|
||||
|
||||
|
||||
public function __construct() {}
|
||||
|
||||
/**
|
||||
* @return Returns HTML describing entity fields
|
||||
*/
|
||||
public function describe(...$entities) : string
|
||||
public function getEntities(...$entities) : array
|
||||
{
|
||||
$html = "";
|
||||
$list = [];
|
||||
|
||||
foreach($entities as $entity) {
|
||||
$propertyHtml = $searchRequestHtml = "";
|
||||
$fields = [];
|
||||
|
||||
$entityName = is_object($entity) ? $entity::class : $entity;
|
||||
$reflector = new ObjectReflection($entityName);
|
||||
|
||||
$table = $reflector->reflectClass()->getAttribute(Table::class);
|
||||
|
||||
foreach($reflector->reflectProperties() as $property) {
|
||||
foreach ($reflector->reflectProperties() as $property) {
|
||||
$entityField = $property->getAttribute(EntityField::class);
|
||||
$field = $property->getAttribute(Field::class);
|
||||
|
||||
if ($field) {
|
||||
$types = $property->getTypes();
|
||||
$propertyHtml .= sprintf(
|
||||
$this->entityField,
|
||||
$property->name,
|
||||
$entityField->object->description ?? "",
|
||||
$field->object->name ?: $property->name,
|
||||
$field->tag,
|
||||
$field->object->type ?? implode(' | ', array_map(fn($e) => $e->type, $types)),
|
||||
$this->yesOrNo($property->allowsNull()),
|
||||
$this->length($field->object->length ?? null),
|
||||
$this->yesOrNo($field->object->readonly),
|
||||
);
|
||||
|
||||
$fields[] = [
|
||||
'name' => $property->name,
|
||||
'description' => $entityField->object->description ?? "",
|
||||
'fieldName' => $field->object->name ?: $property->name,
|
||||
'tag' => $field->tag,
|
||||
'type' => $field->object->type ?? implode(' | ', array_map(fn($e) => $e->type, $types)),
|
||||
'allowNulls' => $property->allowsNull(),
|
||||
'length' => $field->object->length ?? null,
|
||||
'readonly' => $field->object->readonly,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$searchFields = [];
|
||||
$searchRequestReflector = new ObjectReflection($entityName::searchRequest()::class);
|
||||
|
||||
$searchRequestParameter = $searchRequestReflector->reflectClass()->getAttribute(SearchRequestParameter::class);
|
||||
|
||||
foreach($searchRequestReflector->reflectProperties() as $property) {
|
||||
|
@ -110,23 +59,27 @@ class EntityDescriptor
|
|||
if ($field) {
|
||||
$types = $property->getTypes();
|
||||
|
||||
$searchRequestHtml .= sprintf(
|
||||
$this->searchRequestField,
|
||||
$property->name,
|
||||
$field->object->description,
|
||||
$field->object->parameter ? implode(', ', $field->object->getParameters()) : $property->name,
|
||||
$field->tag,
|
||||
implode(' | ', array_map(fn($e) => $e->type, $types)),
|
||||
$this->yesOrNo($property->allowsNull()),
|
||||
$this->displayValue($property->value ?? "<i>aucune</i>")
|
||||
);
|
||||
$searchFields[] = [
|
||||
'name' => $property->name,
|
||||
'description' => $field->object->description,
|
||||
'parameter' => $field->object->parameter ? implode(', ', $field->object->getParameters()) : $property->name,
|
||||
'tag' => $field->tag,
|
||||
'type' => implode(' | ', array_map(fn($e) => $e->type, $types)),
|
||||
'allowNulls' => $property->allowsNull(),
|
||||
'default' => $this->displayValue($property->value ?? "")
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$html .= sprintf($this->entityTitle, $reflector->reflectClass()->getClassName(), $table->object->description ?? null, $propertyHtml, $reflector->reflectClass()->getClassName() . "::searchRequest()", $searchRequestParameter->object->description ?? "", $searchRequestHtml);
|
||||
$list[$entity] = [
|
||||
'className' => $reflector->reflectClass()->getClassName(),
|
||||
'description' => $table->object->description ?? null,
|
||||
'fields' => $fields,
|
||||
'searchRequestDescription' => $searchRequestParameter->object->description ?? "",
|
||||
'searchRequestFields' => $searchFields,
|
||||
];
|
||||
}
|
||||
|
||||
return $html;
|
||||
return $list;
|
||||
}
|
||||
|
||||
}
|
|
@ -9,46 +9,15 @@ class FormDescriptor
|
|||
{
|
||||
use DescriptorTrait;
|
||||
|
||||
public string $formTitle = <<<HTML
|
||||
<div style="margin-left:15px">
|
||||
<h4 style="background:#e0ecf7" class='form-name'>%s</h4>
|
||||
<div class='description'>%s</div>
|
||||
<hr style="margin-top:15px">
|
||||
<h5>Champs</h5>
|
||||
<ol class='fields'>%s</ol>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
|
||||
public string $formField = <<<HTML
|
||||
<li class="odd-even">
|
||||
<div style="display:flex; justify-content: space-between">
|
||||
<div><strong style="font-family:monospace">$%s</strong> <span style='margin-left:15px'>%s</span></div>
|
||||
</div>
|
||||
|
||||
<div class="field-desc" style="margin-top:10px;;background:#fff;padding:5px;font-size:0.9em">
|
||||
<div style="padding:5px"><u>Variable POST / champ JSON</u> : %s</div>
|
||||
<div style="padding:5px"><u>Type</u> : %s</div>
|
||||
<div style="padding:5px"><u>Nullable</u> : %s</div>
|
||||
<div style="padding:5px"><u>Pattern regex</u> : %s</div>
|
||||
<div style="padding:5px"><u>Taille min.</u> : %s</div>
|
||||
<div style="padding:5px"><u>Taille max.</u> : %s</div>
|
||||
<div style="padding:5px"><u>Exemple</u> : %s</div>
|
||||
</div>
|
||||
</li>
|
||||
HTML;
|
||||
|
||||
/**
|
||||
* @return Returns HTML describing given controller's routes
|
||||
*/
|
||||
public function describe(...$forms) : string
|
||||
public function getForms(...$forms) : array
|
||||
{
|
||||
$html = "";
|
||||
$list = [];
|
||||
|
||||
foreach($forms as $form) {
|
||||
$fields = [];
|
||||
$propertyHtml = "";
|
||||
$entityName = is_object($form) ? $form::class : $form;
|
||||
$reflector = new ObjectReflection($entityName);
|
||||
$formName = is_object($form) ? $form::class : $form;
|
||||
$reflector = new ObjectReflection($formName);
|
||||
|
||||
$context = $reflector->reflectClass()->getAttribute(ContextField::class);
|
||||
|
||||
|
@ -57,24 +26,26 @@ class FormDescriptor
|
|||
|
||||
if ($field) {
|
||||
$types = $property->getTypes();
|
||||
$propertyHtml .= sprintf(
|
||||
$this->formField,
|
||||
$property->name,
|
||||
$field->object->description,
|
||||
$property->name,
|
||||
$field->object->type ?? implode(' | ', array_map(fn($e) => $e->type, $types)),
|
||||
$this->yesOrNo($property->allowsNull()),
|
||||
$field->object->regexFormat ?? "aucun",
|
||||
$field->object->minLength ?? "aucune",
|
||||
$field->object->maxLength ?? "aucune",
|
||||
$field->object->example
|
||||
);
|
||||
$fields[] = [
|
||||
'name' => $property->name,
|
||||
'description' => $field->object->description,
|
||||
'type' => $field->object->type ?? implode(' | ', array_map(fn($e) => $e->type, $types)),
|
||||
'allowNulls' => $property->allowsNull(),
|
||||
'regexPattern' =>$field->object->regexFormat ?? "aucun",
|
||||
'minLength' =>$field->object->minLength ?? "aucune",
|
||||
'maxLength' =>$field->object->maxLength ?? "aucune",
|
||||
'example' =>$field->object->example,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$html .= sprintf($this->formTitle, $reflector->reflectClass()->getClassName(), $context->object->description ?? null, $propertyHtml, );
|
||||
$list[$formName] = [
|
||||
'className' => $reflector->reflectClass()->getClassName(),
|
||||
'description' => $context->object->description ?? null,
|
||||
'fields' => $fields,
|
||||
];
|
||||
}
|
||||
|
||||
return $html;
|
||||
return $list;
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@ use Psr\Http\Message\ResponseInterface;
|
|||
|
||||
trait LeanApiTrait
|
||||
{
|
||||
use DescriptorTrait;
|
||||
|
||||
#[Ignore]
|
||||
public function renderMarkdown(string $filepath, array $entities = [], array $forms = [], int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
|
@ -19,21 +21,23 @@ trait LeanApiTrait
|
|||
|
||||
if (str_contains($markdown, '{route:descriptor}'))
|
||||
{
|
||||
$describe = (new RouteDescriptor($this, $this->picea->compiler->getExtensionFromToken('url')))->describe();
|
||||
$markdown = str_replace('{route:descriptor}', $describe, $markdown);
|
||||
$markdown = str_replace('{route:descriptor}', $this->renderRawView('lean-api/route_descriptor', [
|
||||
'routes' => (new RouteDescriptor($this, $this->picea->compiler->getExtensionFromToken('url')))->getRoutes()
|
||||
]), $markdown);
|
||||
}
|
||||
|
||||
if (str_contains($markdown, '{entity:descriptor}'))
|
||||
{
|
||||
$describe = (new EntityDescriptor())->describe(... $entities);
|
||||
$markdown = str_replace('{entity:descriptor}', $describe, $markdown);
|
||||
$markdown = str_replace('{entity:descriptor}', $this->renderRawView('lean-api/entity_descriptor', [
|
||||
'entities' => (new EntityDescriptor())->getEntities(... $entities)
|
||||
]), $markdown);
|
||||
}
|
||||
|
||||
|
||||
if (str_contains($markdown, '{form:descriptor}'))
|
||||
{
|
||||
$describe = (new FormDescriptor())->describe(... $forms);
|
||||
$markdown = str_replace('{form:descriptor}', $describe, $markdown);
|
||||
$markdown = str_replace('{form:descriptor}', $this->renderRawView('lean-api/form_descriptor', [
|
||||
'forms' => (new FormDescriptor())->getForms(... $forms)
|
||||
]), $markdown);
|
||||
}
|
||||
|
||||
return $this->renderView("lean/layout/docs", get_defined_vars());
|
||||
|
|
|
@ -22,12 +22,9 @@ HTML;
|
|||
protected UrlExtension $urlExtension,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @return Returns HTML describing given controller's routes
|
||||
*/
|
||||
public function describe() : string
|
||||
public function getRoutes() : array
|
||||
{
|
||||
$html = "";
|
||||
$routes = [];
|
||||
|
||||
$reflector = new ObjectReflection($this->controller::class);
|
||||
|
||||
|
@ -42,11 +39,18 @@ HTML;
|
|||
$cleaned = $this->cleanRouteFromRegex($base.$path);
|
||||
$url = $this->urlExtension->buildUrl($cleaned);
|
||||
|
||||
$html .= sprintf($this->routeLine, $url, $base.$path, $cleaned, $route->description, implode(', ', (array)$route->method), $route->name );
|
||||
$routes[] = [
|
||||
'name' => $route->name,
|
||||
'route' => $url,
|
||||
'path' => $base.$path,
|
||||
'cleaned' => $cleaned,
|
||||
'description'=> $route->description,
|
||||
'methods' =>implode(', ', (array)$route->method),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return sprintf('<ul>%s</ul>', $html);
|
||||
return $routes;
|
||||
}
|
||||
|
||||
protected function cleanRouteFromRegex(string $route) : string
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
{% function yesOrNo(bool $toggle) : void %}
|
||||
<span style="color: {{ $toggle ? 'green' : '#ac1b1b' }}">{{ $toggle ? 'oui' : 'non' }}</span>
|
||||
{% endfunction %}
|
||||
|
||||
{% function length(int|null $length): void %}
|
||||
<span style="color:{{ $length ? 'black' : 'gray' }}">{{ $length ?? "non-défini" }}</span>
|
||||
{% endfunction %}
|
||||
|
||||
<div class="entities">
|
||||
{% foreach $entities as $name => $entity %}
|
||||
<div class="entity-wrapper" style="padding-left: 15px;border-left: 3px solid #eaa1af;">
|
||||
<h4 class='entity-name'>{{ $entity['className'] }}</h4>
|
||||
<div class='description'>{{ $entity['description'] }}</div>
|
||||
|
||||
<hr style="margin-top:15px">
|
||||
|
||||
<h5>Champs</h5>
|
||||
<ol class='fields'>
|
||||
{% foreach $entity['fields'] as $field %}
|
||||
<li class="odd-even">
|
||||
<div class="title">
|
||||
<div><strong style="font-family:monospace">${{ $field['name'] }}</strong> <span style='margin-left:15px'>{{ $field['description'] }}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="field-desc" style="margin-top:10px;;background:#fff;padding:5px;font-size:0.9em">
|
||||
<div style="padding:5px"><u>Champ SQL</u> : {{ $field['fieldName'] }}</div>
|
||||
<div style="padding:5px"><u>Attribut</u> : {{ $field['tag'] }}</div>
|
||||
<div style="padding:5px"><u>Type(s)</u> : {{ $field['type'] }}</div>
|
||||
<div style="padding:5px"><u>Nullable</u> {{ yesOrNo($field['allowNulls']) }}</div>
|
||||
<div style="padding:5px"><u>Taille</u> : {{ length($field['length']) }}</div>
|
||||
<div style="padding:5px"><u>Lecture seule</u> : {{ yesOrNo($field['readonly']) }}</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endforeach %}
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="search-request-wrapper">
|
||||
<h4 class='search-request-name'>{{ $entity['className'] }}::searchRequest()</h4>
|
||||
|
||||
<h5>Requêtes</h5>
|
||||
<div class='description' style="margin-bottom:10px">{{ $entity['searchRequestDescription'] }}</div>
|
||||
|
||||
<ol class='requests'>
|
||||
{% foreach $entity['searchRequestFields'] as $field %}
|
||||
<li class="odd-even">
|
||||
<div class="title">
|
||||
<div><strong style="font-family:monospace">{{ $field['name'] }}</strong> <span style='margin-left:15px'>{{ $field['description'] }}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="field-desc" style="margin-top:10px;;background:#fff;padding:5px;font-size:0.9em">
|
||||
<div><u>Paramètre de requête (GET)</u> : {{ $field['parameter'] }}</div>
|
||||
<div><u>Attribut</u> : {{ $field['tag'] }}</div>
|
||||
<div><u>Type(s)</u> : {{ $field['type'] }}</div>
|
||||
<div><u>Nullable</u> : {{ yesOrNo($field['allowNulls']) }}</div>
|
||||
{% if $field['default'] !== "" %}
|
||||
<div><u>Valeur par défault</u> : {{ $this->displayValue($field['default']) }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</li>
|
||||
{% endforeach %}
|
||||
</ol>
|
||||
</div>
|
||||
{% endforeach %}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.entity-name {background:#eaa1af}
|
||||
.entity-wrapper ol {background: #f0ddcd;}
|
||||
.entity-wrapper li {border-color: #ae8585;}
|
||||
|
||||
.search-request-name {background: #b6e6ae}
|
||||
.search-request-wrapper {padding-left: 15px;border-left: 3px solid #72886a;}
|
||||
.search-request-wrapper ol {background: #d8f0cd;}
|
||||
.search-request-wrapper li {border-color: #72886a;}
|
||||
</style>
|
|
@ -0,0 +1,38 @@
|
|||
{% function yesOrNo(bool $toggle) : void %}
|
||||
<span style="color: {{ $toggle ? 'green' : '#ac1b1b' }}">{{ $toggle ? 'oui' : 'non' }}</span>
|
||||
{% endfunction %}
|
||||
|
||||
<div class="forms">
|
||||
{% foreach $forms as $form %}
|
||||
<div class="single-form" style="padding-left:15px;border-left: 3px solid #9ce6bc;">
|
||||
<h4 style="background:#9ce6bc" class='form-name'>{{ $form['className'] }}</h4>
|
||||
<div class='description'>{{ $form['description'] }}</div>
|
||||
<hr style="margin-top:15px">
|
||||
|
||||
<h5>Champs</h5>
|
||||
|
||||
<ol class='fields'>
|
||||
{% foreach $form['fields'] as $field %}
|
||||
<li class="odd-even">
|
||||
<div class="title">
|
||||
<div>
|
||||
<strong style="font-family:monospace">{{ $field['name'] }}</strong>
|
||||
<span style='margin-left:15px'>{{ $field['description'] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field-desc" style="margin-top:10px;;background:#fff;padding:5px;font-size:0.9em">
|
||||
<div><u>Variable POST / champ JSON</u> : {{ $field['name'] }}</div>
|
||||
<div><u>Type(s)</u> : {{ $field['type'] }}</div>
|
||||
<div><u>Nullable</u> : {{ yesOrNo($field['allowNulls']) }}</div>
|
||||
<div><u>Pattern regex</u> : {{ $field['regexPattern'] }}</div>
|
||||
<div><u>Taille min.</u> : {{ $field['minLength'] }}</div>
|
||||
<div><u>Taille max.</u> : {{ $field['maxLength'] }}</div>
|
||||
<div><u>Exemple</u> : {{ $field['example'] }}</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endforeach %}
|
||||
</ol>
|
||||
</div>
|
||||
{% endforeach %}
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
<ul>
|
||||
{% foreach $routes as $route %}
|
||||
<li>
|
||||
<span><a href="{{ $route['route'] }}" title="{{ $route['path'] }}" style='font-family:monospace;font-size:.85em'>{{ $route['cleaned'] }}</a> - {{ $route['description'] }}</span>
|
||||
<span style='color:#ac1b1b'>{{ $route['methods'] }}</span>
|
||||
<small style="color:#374300">{{ $route['name'] }}</small>
|
||||
</li>
|
||||
{% endforeach %}
|
||||
</ul>
|
|
@ -0,0 +1,60 @@
|
|||
{% title "Documentation" %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
{% section "head" %}
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>{% section "head.title" %}{% lang 'api.application_name' %} - {{ title() }}{% endsection %}</title>
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% asset 'static/lean-api/favicon/apple-touch-icon.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="{% asset 'static/lean-api/favicon/favicon-96x96.png' %}">
|
||||
<link rel="manifest" href="{% asset 'static/lean-api/favicon/site.webmanifest' %}">
|
||||
<link rel="mask-icon" href="{% asset 'static/lean-api/favicon/safari-pinned-tab.svg' %}" color="#5bbad5">
|
||||
<meta name="msapplication-TileColor" content="#2d89ef">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<style>
|
||||
a{color:#e44a00}h3{background:#e1e1e1;padding:6px 12px}ul{background:#e9e9fd;padding-top:20px;padding-bottom:20px;border:1px solid #e3e3ec}ul li + li{margin-top:8px}li > em{font-size:0.75rem;color:gray}body{font-family:Helvetica, 'Helvetica Neuve', Arial, Tahoma, sans-serif;font-size:17px;color:#333}h1,h2,h3,h4,h5,h6{color:#222;margin:0 0 20px}dl,ol,p,pre,table,ul{margin:0 0 20px}h1,h2,h3{line-height:1.1}h1{font-size:20px;text-align:right;color:#387eea;font-weight:bold}h2{color:#393939}h3,h4,h5,h6{color:#494949}h3{display:flex}h3 > code{margin-right:5px;color:#b52dac}h3 > strong{margin-left:auto}a{color:#39c;font-weight:400;text-decoration:none}a small{font-size:11px;color:#777;margin-top:-0.6em;display:block}.wrapper{width:860px;margin:0 auto}blockquote{border-left:1px solid #e5e5e5;margin:0;padding:0 0 0 20px;font-style:italic}code,pre{font-size:12px}pre{padding:8px 15px;background:#f8f8f8;border-radius:5px;border:1px solid #e5e5e5;overflow-x:auto}table{width:100%;border-collapse:collapse}td,th{text-align:left;padding:5px 10px;border-bottom:1px solid #e5e5e5}dt{color:#444;font-weight:700}th{color:#444}img{max-width:100%}header{width:270px;float:left;position:fixed}header ul{list-style:none;height:40px;padding:0;background:#eee;background:-moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%);background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd));background:-webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);background:-o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);background:-ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);background:linear-gradient(top, #f8f8f8 0%,#dddddd 100%);border-radius:5px;border:1px solid #d2d2d2;box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0;width:270px}header li{width:89px;float:left;border-right:1px solid #d2d2d2;height:40px}header ul a{line-height:1;font-size:11px;color:#999;display:block;text-align:center;padding-top:6px;height:40px}strong{color:#222;font-weight:700}header ul li + li{width:88px;border-left:1px solid #fff}header ul li + li + li{border-right:none;width:89px}header ul a strong{font-size:14px;display:block;color:#222}section{width:500px;float:right;padding-bottom:50px}small{font-size:11px}hr{border:0;background:#e5e5e5;height:1px;margin:0 0 20px}footer{width:270px;float:left;position:fixed;bottom:50px}@media print, screen and (max-width: 960px){div.wrapper{width:auto;margin:0}footer,header,section{float:none;position:static;width:auto}header{padding-right:320px}section{border:1px solid #e5e5e5;border-width:1px 0;padding:20px 0;margin:0 0 20px}header a small{display:inline}header ul{position:absolute;right:50px;top:52px}}@media print, screen and (max-width: 720px){body{word-wrap:break-word}header{padding:0}header p.view,header ul{position:static}code,pre{word-wrap:normal}}@media print, screen and (max-width: 480px){body{padding:15px}header ul{display:none}}@media print{body{padding:0.4in;font-size:12pt;color:#444}}#wrapper{margin-left:auto;margin-right:auto;background-color:white}.ca-menu{list-style:none;padding:0;margin:20px auto}#navi{padding-top:15px;padding-right:15px;float:right;width:420px}#title{padding-left:15px;width:460px;float:left}div.clear{clear:both}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.2em}h5{font-size:1em;font-weight:bold}h6{font-size:1em;font-weight:bold}h1,h2,h3,h4,h5,h6{font-weight:normal;line-height:2.5rem;margin:1rem 0}.post p{max-width:580px}ol.list,ul.list{padding-left:3.333em;max-width:580px}.post h2{border-bottom:1px solid #EDEDED}h1:nth-child(1),h2:nth-child(1),h3:nth-child(1),h4:nth-child(1),h5:nth-child(1),h6:nth-child(1){margin-top:0}body{padding:1em}#wrapper{padding:1em}@media (min-width: 43.75em){body{padding:2em}#wrapper{padding:2em}}@media (min-width: 62em){body{padding:3em}#wrapper{max-width:740px;padding:3em}}
|
||||
ol{background: #eff4f2;padding-top:20px;padding-bottom:20px;border:1px solid #e3e3ec}
|
||||
h4{background:#e0f7ed;padding:6px 12px; font-weight: bold!important;font-size:100%;margin-top:0}
|
||||
h5{text-decoration: underline}
|
||||
h3 {display: flex;align-items: center;height: 60px;padding: 0 15px 0 15px;font-variant: small-caps;}
|
||||
li.odd-even{border-top:1px solid #ccc;margin:10px 0;padding:15px 15px 10px 5px}
|
||||
li.odd-even:first-child{border:0}
|
||||
input, button {padding:5px; font-size:1em;margin-top:10px}
|
||||
ul {background:#f4f4f4; list-style: none; padding-left:20px}
|
||||
ul ul {margin: 0;border: 0;padding: 5px 30px;}
|
||||
ul li + li {margin-top: 12px;}
|
||||
ol .title { display: flex;justify-content: space-between;background: #ffffffb2;padding: 9px 5px;border: 1px solid #fff;}
|
||||
.field-desc > div {padding:5px;}
|
||||
|
||||
.forms ol {background: #ccf2dd;}
|
||||
.forms li {border-color: #85ae97;}
|
||||
</style>
|
||||
</head>
|
||||
{% endsection %}
|
||||
|
||||
{% section "body" %}
|
||||
<body>
|
||||
{% section "body.header" %}{% endsection %}
|
||||
|
||||
{% section "header" %}{% endsection %}
|
||||
|
||||
{% section "message" %}
|
||||
{% view "lean/widget/message" %}
|
||||
{% endsection %}
|
||||
|
||||
<main role="main" class="content">
|
||||
{% section "main" %}
|
||||
<div>{{= $markdown }}</div>
|
||||
{% endsection %}
|
||||
</main>
|
||||
|
||||
<footer>{% section "footer" %}{% endsection %}</footer>
|
||||
|
||||
{% section "body.footer" %}{% endsection %}
|
||||
</body>
|
||||
{% endsection %}
|
||||
</html>
|