mirror of
https://github.com/caddyserver/website.git
synced 2025-04-20 12:15:08 -04:00
New Website: Phase I (#357)
* Initial commit; starting new design Dropdown menu * Begin docs layout of new design * Get themes under control; button hover splash * Some basic responsiveness * Finish responsive layout; several bug fixes * Avoid flash during color scheme change * Begin building top of homepage * docs: Start building quick-assist feature * Work on homepage a little more * Keep working on homepage * More homepage progress * Some sponsor SVGs * Add sponsor features * Implement basic Sponsor Experience box * Reorganize some styles * WIP sponsors page * Start features page WIP * Minor improvements * Fix headings; work on features page * WIP features page * Continue work on marketing pages * Continue work on features page * More features WIP * Continue features page... * More work on features page * Keeping going :) * Continue home and features pages * More homepage/features content, screenshots, tweaks * Minor fixes to features page * Minor tweaks * Work on testimonials * Work on homepage more * More homepage work * Continue work on homepage * Add some sponsor logos * Some citation screenshots * Add citations * Start making homepage responsive * Re-add cache busting Fix docs * Use markdown syntax highlighting on frontpage * Rework AJQuery to $_ to not interfere with jQuery * Rewrite quick assist with AlpineJS, use markdown for contents * More work on marketing pages * Rebase and fix code displays * Syntax highlight on-demand example, fix rollover * Adjust on-demand demo * Work on responsiveness * Keep working on responsiveness * Mainly finish making design responsive * Thiccer favicon * More work on marketing pages * Keep on going * Fix link * Move new site into src folder * Add open graph image * Add recorded demo for homepage * Tweak caption * Fix Poppins font for now * Minor tweaks * Trim demo ending * Remove unfinished pages Also update Framer logo --------- Co-authored-by: Francis Lavoie <lavofr@gmail.com>
This commit is contained in:
parent
5bb6d92c63
commit
07c51663ab
191 changed files with 13008 additions and 4970 deletions
|
@ -1,393 +0,0 @@
|
|||
// TODO: sanitize all HTML renderings, especially markdown: https://github.com/cure53/DOMPurify
|
||||
|
||||
var pageDocs = {};
|
||||
var pageData = {};
|
||||
var $hovercard;
|
||||
|
||||
const nonStandardFlag = '<span class="nonstandard-flag" title="This module does not come with official Caddy distributions by default; it needs to be added to custom Caddy builds.">Non-standard</span>';
|
||||
const standardFlag = '<span class="standard-flag" title="This module comes with official Caddy distributions by default.">Standard</span>';
|
||||
|
||||
|
||||
$(function() {
|
||||
$hovercard = $('#hovercard');
|
||||
|
||||
var hoverTimeout;
|
||||
$hovercard.hover(function() {
|
||||
clearTimeout(hoverTimeout);
|
||||
}, function() {
|
||||
clearTimeout(hoverTimeout);
|
||||
$hovercard.removeClass('popup');
|
||||
});
|
||||
|
||||
// toggle an object as expanded or collapsed
|
||||
$('body').on('click', '.renderbox .toggle-obj', function() {
|
||||
if ($(this).hasClass('expanded')) {
|
||||
// collapse
|
||||
$(this).html('▸');
|
||||
} else {
|
||||
// expand
|
||||
$(this).html('▾');
|
||||
}
|
||||
$(this).nextUntil('.end-obj').toggleClass('collapsed');
|
||||
$(this).toggleClass('expanded');
|
||||
});
|
||||
|
||||
$('body').on({
|
||||
mouseenter: function() {
|
||||
// don't allow hoverbox to close anymore, we're re-opening it
|
||||
clearTimeout(hoverTimeout);
|
||||
|
||||
var pos = $(this).offset();
|
||||
var moduleID = $(this).closest('.module-repo-container').data('module-id') || '';
|
||||
var moduleData = pageData[moduleID];
|
||||
|
||||
// there is a gap between the hoverbox and the link that originated it;
|
||||
// there may be a different link in this gap; if the hover box is visible,
|
||||
// then we should ignore the hover on this link to allow cursor to visit
|
||||
// the hoverbox if that is where it is going; this makes it possible to
|
||||
// visit the hoverbox while it is over a list of links that are tightly
|
||||
// stacked vertically; if user wants to visit hoverbox for link in this
|
||||
// gap, they can just move the cursor slow enough to fire the timeout
|
||||
if ($hovercard.is(':visible') && $hovercard.offset().top - 10 < pos.top) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fill out hovercard
|
||||
|
||||
var elemPath = $(this).data('path');
|
||||
var modNamespace = $(this).data('namespace');
|
||||
|
||||
$('.hovercard-elem').hide();
|
||||
|
||||
if ($(this).hasClass('module')) {
|
||||
// module
|
||||
var $list =$('<div/>');
|
||||
if (moduleData.namespaces && moduleData.namespaces[modNamespace]) {
|
||||
for (var i = 0; i < moduleData.namespaces[modNamespace].length; i++) {
|
||||
var modInfo = moduleData.namespaces[modNamespace][i];
|
||||
var href = canTraverse(moduleData) ? '.'+elemPath+'/'+modInfo.name+'/' : './'+modNamespace+'.'+modInfo.name;
|
||||
var content = '<a href="'+href+'" class="module-link"> '+modInfo.name;
|
||||
if (!isStandard(modInfo.package)) {
|
||||
content += nonStandardFlag;
|
||||
}
|
||||
content += '<span class="module-link-description">'+truncate(modInfo.docs, 115)+'</span></a>';
|
||||
$list.append(content);
|
||||
}
|
||||
}
|
||||
$('#hovercard-module-list').html($list);
|
||||
$('#hovercard-namespace').text(modNamespace)
|
||||
$('#hovercard-module').show();
|
||||
|
||||
} else if ($(this).hasClass('module-inline-key')) {
|
||||
// inline key
|
||||
$('#hovercard-inline-key').show();
|
||||
|
||||
} else if ($(this).hasClass('breadcrumb')) {
|
||||
// breadcrumb siblings
|
||||
|
||||
var siblingPath = $(this).data('sibling-path');
|
||||
var bcVal = moduleData.breadcrumb[siblingPath];
|
||||
var bcSiblings = [];
|
||||
|
||||
// drill down to the true underlying type
|
||||
while (bcVal.elems) {
|
||||
bcVal = bcVal.elems;
|
||||
}
|
||||
|
||||
switch (bcVal.type) {
|
||||
case "struct":
|
||||
for (var j = 0; j < bcVal.struct_fields.length; j++) {
|
||||
var sf = bcVal.struct_fields[j];
|
||||
bcSiblings.push({name: sf.key, path: siblingPath, isStandard: isStandard(bcVal.type_name)})
|
||||
}
|
||||
break;
|
||||
|
||||
case "module":
|
||||
case "module_map":
|
||||
for (var j = 0; j < moduleData.namespaces[bcVal.module_namespace].length; j++) {
|
||||
var mod = moduleData.namespaces[bcVal.module_namespace][j];
|
||||
bcSiblings.push({name: mod.name, path: siblingPath, isStandard: isStandard(mod.package)})
|
||||
}
|
||||
}
|
||||
|
||||
var $siblings = $('<div class="breadcrumb-siblings"/>').append('<div class="breadcrumb-siblings-title">Siblings:</div>');
|
||||
for (var j = 0; j < bcSiblings.length; j++) {
|
||||
var sib = bcSiblings[j];
|
||||
var sibPath = sib.path;
|
||||
if (sibPath) {
|
||||
sibPath += "/";
|
||||
}
|
||||
sibPath += sib.name+"/";
|
||||
var aTag = '<a href="'+jsonDocsPathPrefix+sibPath+'"';
|
||||
if (!sib.isStandard) {
|
||||
aTag += ' class="nonstandard" title="Non-standard module"';
|
||||
}
|
||||
aTag += '>'+sib.name+'</a>';
|
||||
$siblings.append(aTag);
|
||||
}
|
||||
$('#hovercard-breadcrumb-siblings').html($siblings).show();
|
||||
|
||||
} else if ($(this).hasClass('documented')) {
|
||||
// docs
|
||||
var elemDocs = truncate(pageDocs[elemPath], 500);
|
||||
if (!elemDocs) {
|
||||
elemDocs = '<p class="explain">There are no docs for this property.</p>';
|
||||
return;
|
||||
}
|
||||
$('#hovercard-docs').html(markdown(elemDocs)).show();
|
||||
$('#hovercard-inline-link').html('<a href="#'+elemPath.substr(1)+'">View docs below ↓</a>').show();
|
||||
}
|
||||
|
||||
// show hoverbox for this link
|
||||
var height = $(this).height();
|
||||
var linkWidth = $(this).width();
|
||||
var boxWidth = $hovercard.width();
|
||||
$hovercard.css({
|
||||
'top': pos.top + height*1.5 + 10, // '+10' counters 'translateY(-10px)'
|
||||
'left': pos.left + (linkWidth/2) - (boxWidth/2)
|
||||
}).addClass('popup');
|
||||
},
|
||||
mouseleave: function() {
|
||||
// don't hide the hoverbox right away; user might need a
|
||||
// few milliseconds to get the cursor over the hovercard
|
||||
hoverTimeout = setTimeout(function() {
|
||||
$hovercard.removeClass('popup');
|
||||
}, 200);
|
||||
}
|
||||
}, '.has-popup');
|
||||
});
|
||||
|
||||
function beginRenderingInto($tpl, moduleID, module) {
|
||||
console.log("RENDERING:", moduleID, module);
|
||||
$tpl.data('module-id', moduleID);
|
||||
pageData[moduleID] = module;
|
||||
|
||||
// show notice if module is non-standard
|
||||
if (module.repo) {
|
||||
if (!isStandard(module.structure.type_name)) {
|
||||
let { pkg, _ } = splitTypeName(module.structure.type_name);
|
||||
$('.nonstandard-project-link', $tpl).attr('href', module.repo).text(module.repo);
|
||||
$('.nonstandard-package-path', $tpl).text(pkg);
|
||||
$('.nonstandard-notice', $tpl).css('display', 'block').prepend(nonStandardFlag); // for some reason show() dosen't work
|
||||
}
|
||||
|
||||
var $repoName = $('<span/>').text(stripScheme(module.repo));
|
||||
$('.module-repo-selector', $tpl).html('<span class="module-repo-selector-arrow">▸</span>').append($repoName);
|
||||
}
|
||||
|
||||
// for most types, just render their docs; but for maps or arrays, fall through to underlying type for docs
|
||||
let rawDocs = module.structure.doc ?? module.structure.elems;
|
||||
|
||||
$('.top-doc', $tpl).html(markdown(replaceGoTypeNameWithCaddyModuleName(rawDocs, module, moduleID)));
|
||||
$('.top-doc', $tpl).append(makeSubmoduleList(module, "", module.structure));
|
||||
|
||||
let $group = newGroup();
|
||||
renderData($tpl, module, module.structure, 0, "", $group);
|
||||
$('.renderbox', $tpl).append($group);
|
||||
|
||||
if ($('.field-list-contents', $tpl).text().trim()) {
|
||||
$('.field-list-header', $tpl).show();
|
||||
}
|
||||
|
||||
// TODO: see about fixing this for module and JSON docs pages
|
||||
// // if the browser tried to navigate directly to an element
|
||||
// // on the page when it loaded, it would have failed since
|
||||
// // we hadn't rendered it yet; but now we can scroll to it
|
||||
// // directly since rendering has finished
|
||||
// if (window.location.hash.length > 1) {
|
||||
// document.getElementById(window.location.hash.substr(1)).scrollIntoView();
|
||||
// }
|
||||
}
|
||||
|
||||
function renderData($tpl, module, data, nesting, path, $group) {
|
||||
switch (data.type) {
|
||||
case "struct":
|
||||
$group.append('{<a href="javascript:" class="toggle-obj expanded" title="Collapse/expand">▾</a>');
|
||||
nesting++;
|
||||
|
||||
var $fieldGroup = newGroup();
|
||||
renderModuleInlineKey($tpl, module, data, nesting, $fieldGroup);
|
||||
$group.append($fieldGroup);
|
||||
if (data.struct_fields) {
|
||||
// TODO: Not sure if sorting the struct fields is a good idea...
|
||||
// data.struct_fields.sort(function(a, b) {
|
||||
// if (a.key > b.key) return 1;
|
||||
// if (b.key > a.key) return -1;
|
||||
// return 0;
|
||||
// });
|
||||
for (var i = 0; i < data.struct_fields.length; i++) {
|
||||
var field = data.struct_fields[i];
|
||||
var fieldPath = path+"/"+field.key;
|
||||
var cleanFieldPath = fieldPath.slice(1); // trim leading slash
|
||||
|
||||
// store the docs for this path
|
||||
let linkClass = "documented";
|
||||
if (field.doc) {
|
||||
pageDocs[fieldPath] = field.doc;
|
||||
linkClass += " has-popup";
|
||||
}
|
||||
|
||||
// render the docs to the page
|
||||
var fieldDoc = markdown(field.doc) || '<p class="explain">There are no docs for this property.</p>';
|
||||
fieldDoc += makeSubmoduleList(module, fieldPath, field.value);
|
||||
appendToFieldDocs($tpl, module, cleanFieldPath, fieldDoc);
|
||||
|
||||
// render the field to the JSON box
|
||||
var $fieldGroup = newGroup();
|
||||
indent(nesting, $fieldGroup);
|
||||
var keyATag = '<a ';
|
||||
if (canTraverse(module)) {
|
||||
keyATag += 'href=".'+fieldPath+'/" ';
|
||||
}
|
||||
keyATag += 'data-path="'+fieldPath+'" class="'+linkClass+'">'+field.key+'</a>';
|
||||
$fieldGroup.append('<span class="qu">"</span><span class="key">'+keyATag+'</span><span class="qu">"</span>: ');
|
||||
renderData($tpl, module, field.value, nesting, fieldPath, $fieldGroup);
|
||||
if (i < data.struct_fields.length-1) {
|
||||
$fieldGroup.append(',');
|
||||
}
|
||||
$group.append($fieldGroup);
|
||||
}
|
||||
}
|
||||
nesting--;
|
||||
indent(nesting, $group);
|
||||
$group.append('<span class="end-obj">}</span>');
|
||||
break;
|
||||
|
||||
case "bool":
|
||||
$group.append('<span class="bool">false</span>'); // TODO: default value?
|
||||
break;
|
||||
|
||||
case "int":
|
||||
case "uint":
|
||||
case "float":
|
||||
case "complex":
|
||||
$group.append('<span class="num">0</span>'); // TODO: default value?
|
||||
break;
|
||||
|
||||
case "string":
|
||||
$group.append('<span class="qu">"</span><span class="str"></span><span class="qu">"</span>'); // TODO: default value?
|
||||
break;
|
||||
|
||||
case "array":
|
||||
$group.append('[');
|
||||
if (data.elems.type == "module_map") {
|
||||
$group.append('{<a href=".'+path+'/" class="module has-popup" data-namespace="'+(data.elems.module_namespace || '')+'" data-path="'+path+'">•••</a>}');
|
||||
} else {
|
||||
renderData($tpl, module, data.elems, nesting, path, $group);
|
||||
}
|
||||
$group.append(']');
|
||||
break;
|
||||
|
||||
case "map":
|
||||
$group.append('{\n')
|
||||
nesting++;
|
||||
renderModuleInlineKey($tpl, module, data, nesting, $group);
|
||||
indent(nesting, $group);
|
||||
renderData($tpl, module, data.map_keys, nesting, path, $group);
|
||||
$group.append(': ');
|
||||
renderData($tpl, module, data.elems, nesting, path, $group);
|
||||
$group.append('\n');
|
||||
nesting--;
|
||||
indent(nesting, $group);
|
||||
$group.append('}');
|
||||
break;
|
||||
|
||||
case "module":
|
||||
case "module_map":
|
||||
var aTag = '<a';
|
||||
if (canTraverse(module)) {
|
||||
aTag += ' href=".'+path+'/"';
|
||||
}
|
||||
aTag += ' class="module has-popup" data-namespace="'+(data.module_namespace || '')+'" data-path="'+path+'">•••</a>';
|
||||
$group.append('{'+aTag+'}');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function renderModuleInlineKey($tpl, module, data, nesting, $group) {
|
||||
if (!data.module_inline_key) {
|
||||
return
|
||||
}
|
||||
var moduleName = pathComponents[pathComponents.length-2];
|
||||
indent(nesting, $group);
|
||||
$group.append('<span class="qu">"</span><span class="key module-inline-key has-popup">'+data.module_inline_key+'</span><span class="qu">"</span>: <span class="qu">"</span><span class="str"><b>'+moduleName+'</b></span><span class="qu">"</span>');
|
||||
if (data.struct_fields && data.struct_fields.length > 0) {
|
||||
$group.append(',');
|
||||
}
|
||||
$group.append('\n');
|
||||
|
||||
appendToFieldDocs($tpl, module, data.module_inline_key, $('#hovercard-inline-key').html());
|
||||
}
|
||||
|
||||
function appendToFieldDocs($tpl, module, cleanFieldPath, fieldDoc) {
|
||||
var dt = cleanFieldPath;
|
||||
if (canTraverse(module)) {
|
||||
dt = '<a href="./'+cleanFieldPath+'/">'+dt+'</a>';
|
||||
}
|
||||
$('.field-list-contents', $tpl).append('<dt class="field-name" id="'+cleanFieldPath+'"><a href="#'+cleanFieldPath+'" class="inline-link">🔗</a>'+dt+'</dt> <dd>'+fieldDoc+'</dd>');
|
||||
}
|
||||
|
||||
function indent(nesting, $group) {
|
||||
var $span = $('<span class="indent"></span>');
|
||||
$span.append('\t'.repeat(nesting));
|
||||
$group.append($span);
|
||||
}
|
||||
|
||||
function makeSubmoduleList(module, path, value) {
|
||||
while (value.elems) {
|
||||
value = value.elems;
|
||||
}
|
||||
if (value.type != "module" && value.type != "module_map") {
|
||||
return '';
|
||||
}
|
||||
var submodList = '<ul>';
|
||||
if (module.namespaces && module.namespaces[value.module_namespace]) {
|
||||
for (var j = 0; j < module.namespaces[value.module_namespace].length; j++) {
|
||||
var submod = module.namespaces[value.module_namespace][j];
|
||||
var href = canTraverse(module) ? '.'+path+'/'+submod.name+'/' : './'+value.module_namespace+'.'+submod.name;
|
||||
var submodLink = '<a href="'+href+'">'+submod.name+'</a>';
|
||||
if (!isStandard(submod.package)) {
|
||||
submodLink += ' '+nonStandardFlag;
|
||||
}
|
||||
submodList += '<li>'+submodLink+'</li>';
|
||||
}
|
||||
}
|
||||
submodList += '</ul>';
|
||||
return '<div><p>Fulfilled by modules in namespace: <b>'+value.module_namespace+'</b></p>'+submodList+'</div>';
|
||||
}
|
||||
|
||||
// canTraverse returns true if the page data
|
||||
// includes breadcrumbs; i.e. we are on a page
|
||||
// that can traverse the JSON structure, not
|
||||
// only render part of it in isolation.
|
||||
function canTraverse(data) {
|
||||
return data.breadcrumb != null;
|
||||
}
|
||||
|
||||
function newGroup() {
|
||||
return $('<div class="group"/>');
|
||||
}
|
||||
|
||||
function replaceGoTypeNameWithCaddyModuleName(docs, module, moduleID) {
|
||||
if (!docs || !moduleID) return docs;
|
||||
|
||||
// fully qualified type name
|
||||
let fqtn = module.structure.type_name;
|
||||
|
||||
// extract just the local type name
|
||||
let {_, typeName} = splitTypeName(fqtn);
|
||||
|
||||
// replace the type name with the Caddy module ID if it starts the docs.
|
||||
if (docs.indexOf(typeName) === 0) {
|
||||
docs = moduleID + docs.substr(typeName.length);
|
||||
}
|
||||
|
||||
return docs;
|
||||
}
|
||||
|
||||
function markdown(input) {
|
||||
if (!input) {
|
||||
return "";
|
||||
}
|
||||
return marked(input);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue