Skip to content

Commit

Permalink
Merge pull request #1 from lostgeek/opengraph
Browse files Browse the repository at this point in the history
Serve OpenGraph information with adjusted HTML export
  • Loading branch information
rubenpieters committed Oct 5, 2023
2 parents 3e9a213 + bb8ebf5 commit de0de65
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 76 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -2,6 +2,7 @@
/latex/
/json/
/latex_annotated/
/php/
__pycache__/
.venv
.vscode
113 changes: 49 additions & 64 deletions data/templates/html/rules.css
Expand Up @@ -2,6 +2,15 @@ body {
font-family: 'Atkinson Hyperlegible';
}

.material-symbols-outlined {
vertical-align: bottom;
font-variation-settings:
'FILL' 0,
'wght' 400,
'GRAD' 0,
'opsz' 24;
}

.RulesGrid {
display: grid;
grid-template-columns: 15% 85%;
Expand All @@ -14,30 +23,54 @@ body {

.RulesContent {
grid-column: 2;
counter-reset: hdr_counter;
margin-left: 1em;
}

.Chapter {
counter-reset: sec_counter;
ol.Rules, ol.SubRules {
list-style: none;
line-height: 1.5em;
}

.Rules, .SubRules {
margin-top: 0;
}
.Rules {
counter-reset: rule_counter;
list-style: none;
line-height: 1.5em;
margin-left: 3.5em;
text-indent: -3.5em;
margin-left: 2em;
}
.SubRules {
margin-left: 1em;
}

.RuleLinkOuterWrapper{
display: inline-block;
width: 0;
position: relative;
left: -.5em;
}
.RuleLinkInnerWrapper{
display: inline-block;
transform: translateX(-100%);
}
a.RuleLink:link, a.RuleLink:visited {
color: inherit;
}

/* The symbol is disabled for the HTML stand-alone version. Overridden in extended.css */
.RuleLinkSymbol{
display:none;
}

.Section a.RuleLink, .Chapter a.RuleLink {
text-decoration: none;
}

.SubType {
font-weight: bold;
}

.SubRules {
counter-reset: sub_rule_counter;
list-style: none;
line-height: 1.5em;
text-indent: -1.5em;
}

.SubSection {
Expand All @@ -46,30 +79,6 @@ body {
font-size: 1.2em;
}

.Chapter:before {
content: counter(hdr_counter) ".";
counter-increment: hdr_counter;
padding-right: 10px;
}

.Section:before {
content: counter(hdr_counter) "." counter(sec_counter) ".";
counter-increment: sec_counter;
padding-right: 10px;
}

.Rule:before {
content: counter(hdr_counter) "." counter(sec_counter) "." counter(rule_counter) ".";
counter-increment: rule_counter;
padding-right: 10px;
}

.SubRule:before {
content: counters(sub_rule_counter, "", lower-alpha) ".";
counter-increment: sub_rule_counter;
padding-right: 10px;
}

.Card {
font-style: italic;
}
Expand All @@ -92,11 +101,11 @@ body {
}

.ExamplesSubRule {
margin-left: -1.5em;
margin-left: -1.3em;
}

.ExamplesRule {
margin-left: -3.6em;
margin-left: -2em;
}

.ExamplesSubSection {
Expand All @@ -115,10 +124,6 @@ body {
font-style: italic;
}

.TimingStructureList {
counter-reset: timing_structure_l1;
}

.TimingStructureBold {
font-weight: bold;
}
Expand All @@ -128,33 +133,13 @@ body {
}

.TimingStructureL1 {
counter-reset: timing_structure_l2;
list-style: none;
list-style: decimal;
}

.TimingStructureL2 {
counter-reset: timing_structure_l3;
list-style: none;
list-style: lower-alpha;
}

.TimingStructureL3 {
list-style: none;
}

.TimingStructureL1:before {
content: counter(timing_structure_l1) ")";
counter-increment: timing_structure_l1;
padding-right: 7px;
}

.TimingStructureL2:before {
content: counters(timing_structure_l2, "", lower-alpha) ")";
counter-increment: timing_structure_l2;
padding-right: 7px;
}

.TimingStructureL3:before {
content: counters(timing_structure_l3, "", lower-roman) ")";
counter-increment: timing_structure_l3;
padding-right: 7px;
}
list-style: lower-roman;
}
34 changes: 34 additions & 0 deletions data/templates/php/extended.css
@@ -0,0 +1,34 @@
.RuleLinkSymbol{
display:inline!important;
opacity:0;
transition: opacity ease-in 200ms;
}

a.RuleLink:hover+.RuleLinkSymbol{
opacity:1;
}

.Section a.RuleLink+.RuleLinkSymbol {
margin-left:.5rem;
font-variation-settings: 'FILL' 0, 'wght' 600, 'GRAD' 0, 'opsz' 40;
font-size: 130%;
}

.Chapter a.RuleLink+.RuleLinkSymbol {
margin-left:.5rem;
font-variation-settings: 'FILL' 0, 'wght' 600, 'GRAD' 0, 'opsz' 40;
font-size: 130%;
}

/* Add another 1.5rem to account for link symbol width */
.ExamplesSubRule {
margin-left: -2.8em;
}

.ExamplesRule {
margin-left: -4.5em;
}

.ExamplesSubSection {
margin-left: -7.5em;
}
132 changes: 132 additions & 0 deletions data/templates/php/index.php
@@ -0,0 +1,132 @@
<?php
require './config.php';

// ----------------------------------------------------------------
// Set base OpenGraph information. May be overridden by later code.
// ----------------------------------------------------------------
$image_file = "logo.png";
$og_site_name = "NSG Comprehensive Rules";
$og_title = "NSG Comprehensive Rules";
$og_description = "Description";

function stripTrailingSlash(&$component) {
$component = rtrim($component, '/');
}
$parts = array($CONFIG['base_path'], $image_file);
array_walk_recursive($parts, 'stripTrailingSlash');
$og_image_url = implode('/', $parts);

// --------------------
// Load json rules file
// --------------------
$path = 'rules.json';

$jsonString = file_get_contents($path);
$jsonData = json_decode($jsonString, true);

function search_for_rule($jsonData, $key, $value) {
foreach($jsonData as $r){
if($r[$key] == $value) {
return $r;
}
}
}

// --------------------------
// Check for ?r=... GET query
// --------------------------
if(isset($_GET['r'])) {
$rule = $_GET['r'];
} else {
$rule = '';
}

// ---------------------------------------------------------------------------
// Search for rule given by GET query and set og_desc and og_title accordingly
// ---------------------------------------------------------------------------
$og_desc = "Access the complete rules framework that makes Netrunner click.";
$jsonRule = null;
if($rule) {
$jsonRule = search_for_rule($jsonData, 'id', $rule);
}

if($jsonRule) {
/* Three different scenarios:
$jsonRule is level 1 (i.e. chapter):
- og:title will be $jsonRule
- og:description will be empty
$jsonRule is level 2 (i.e. subchapter):
- og:title will be $jsonRule
- og:description will be its children
$jsonRule is level 3 or deeper (i.e. subrule):
- og:title will be the parent section
- og:description will be $jsonRule and its children
*/
$level = count(explode('.', $jsonRule['nr']));
if($level == 1) {
$og_title = $jsonRule['nr'].". ".$jsonRule['text'];
$og_desc = "";
} else {
if($level == 2) {
$og_title = $jsonRule['nr'].". ".$jsonRule['text'];
$skipSelf = true;
}
if($level > 2) {
// Look for parent section and display it as title
$sectionNR = join('.',array_slice(explode('.', $jsonRule['nr']), 0, -1));
$jsonSection = search_for_rule($jsonData, 'nr', $sectionNR);

$og_title = $jsonSection['nr'].". ".$jsonSection['text'];
$skipSelf = false;
}

// Fill text with this item and its children
$out = "";
function appendItem(&$out, &$jsonData, $jsonElem, $skipSelf){
if(strlen($out) > 500) {
return;
}
if(!$skipSelf){
$out = $out." ".$jsonElem['nr'].". ".$jsonElem['text'];
}
foreach($jsonElem['children'] as $id) {
$childElem = search_for_rule($jsonData, 'id', $id);
if($childElem) {
appendItem($out, $jsonData, $childElem, false);
}
}
}
appendItem($out, $jsonData, $jsonRule, $skipSelf);

$og_desc = trim($out);
}
}

// ---------------------------------------------------
// Load rules.html file and insert OpenGraph meta tags
// ---------------------------------------------------
$doc = new DOMDocument('1.0', 'UTF-8');
$internalErrors = libxml_use_internal_errors(true);
$doc->loadHTMLFile("rules.html");

$head = $doc->getElementsByTagName('head')[0];

$metaProps = array();
$metaProps[] = array("property" => "og:title", "content" => $og_title);
$metaProps[] = array("property" => "og:type", "content" => "website");
$metaProps[] = array("property" => "og:image", "content" => $og_image_url);
$metaProps[] = array("property" => "og:site_name", "content" => $og_site_name);
$metaProps[] = array("property" => "og:description", "content" => $og_desc);

foreach($metaProps as $entry) {
$el = $doc->createElement('meta');
$el->setAttribute('property', $entry['property']);
$el->setAttribute('content', $entry['content']);
$head->appendChild($el);
}

// -----------------
// Serve HTML output
// -----------------
echo $doc->saveHTML();
?>
Binary file added data/templates/php/logo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
35 changes: 35 additions & 0 deletions data/templates/php/rules.js
@@ -0,0 +1,35 @@
addEventListener("load", (event) => {
const urlParams = new URLSearchParams(window.location.search);
const ruleId = urlParams.get('r');
if(ruleId) {
document.getElementById(ruleId).scrollIntoView();
}
});

// Prevent reloading page while following rules links
// The rule link still appears in the user's navigation bar to copy and paste
const $elems = document.querySelectorAll('a.RuleLink')
const elems = Array.from($elems)
elems.forEach(a => {
url = new URL(a.href);
url.search = url.hash.replace('#', '?r=');
url.hash = "";
a.href = url.href;
a.onclick = (e) => {
// do not follow link
e.preventDefault();

// replace url in nav bar
history.replaceState(null, null, a.href);

// Replace link symbol with clipboard symbol
navigator.clipboard.writeText(a.href);
a.parentElement.getElementsByClassName('material-symbols-outlined')[0].innerText = "content_paste_go";

// Revert back to link symbol after 1s
setTimeout(() => {
a.parentElement.getElementsByClassName('material-symbols-outlined')[0].innerText = "link";

}, 1000);
}
})

0 comments on commit de0de65

Please sign in to comment.