@awesomeness-js/server 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -0
- package/build/build.js +7 -0
- package/build/postBuild.js +7 -0
- package/example-.awesomeness/componentLocations.js +15 -0
- package/example-.awesomeness/config.js +5 -21
- package/example-site/example.awesomenessjs.com/api/readme.md +1 -0
- package/example-site/example.awesomenessjs.com/components/example/index.js +3 -0
- package/example-site/example.awesomenessjs.com/components/notCommonExample/_.css +1 -0
- package/example-site/example.awesomenessjs.com/components/notCommonExample/index.js +3 -0
- package/example-site/example.awesomenessjs.com/pages/_md/_info.js +15 -0
- package/example-site/example.awesomenessjs.com/pages/_md/css/example.css +4 -0
- package/example-site/example.awesomenessjs.com/pages/_md/drafts/example.md +32 -0
- package/example-site/example.awesomenessjs.com/pages/_md/getBlogData.js +117 -0
- package/example-site/example.awesomenessjs.com/pages/_md/getData.js +36 -0
- package/example-site/example.awesomenessjs.com/pages/_md/js/callToAction.js +43 -0
- package/example-site/example.awesomenessjs.com/pages/_md/js/init.js +88 -0
- package/example-site/example.awesomenessjs.com/pages/_md/meta.template.md +33 -0
- package/example-site/example.awesomenessjs.com/pages/_md/pages/mission.md +345 -0
- package/example-site/example.awesomenessjs.com/pages/_template/_info.js +9 -0
- package/example-site/example.awesomenessjs.com/pages/_template/css/_.css +4 -0
- package/example-site/example.awesomenessjs.com/pages/_template/getData.js +6 -0
- package/example-site/example.awesomenessjs.com/pages/_template/js/init.js +3 -0
- package/example-site/example.awesomenessjs.com/pages/blog/_info.js +14 -0
- package/example-site/example.awesomenessjs.com/pages/blog/css/topics.css +17 -0
- package/example-site/example.awesomenessjs.com/pages/blog/getData.js +22 -0
- package/example-site/example.awesomenessjs.com/pages/blog/js/colors.js +159 -0
- package/example-site/example.awesomenessjs.com/pages/blog/js/init.js +242 -0
- package/example-site/example.awesomenessjs.com/pages/blog/js/tag.js +111 -0
- package/example-site/example.awesomenessjs.com/pages/examples/_info.js +9 -0
- package/example-site/example.awesomenessjs.com/pages/examples/css/footer.css +6 -0
- package/example-site/example.awesomenessjs.com/pages/examples/css/intro.css +67 -0
- package/example-site/example.awesomenessjs.com/pages/examples/css/nav.css +221 -0
- package/example-site/example.awesomenessjs.com/pages/examples/getData.js +8 -0
- package/example-site/example.awesomenessjs.com/pages/examples/js/init.js +15 -0
- package/example-site/example.awesomenessjs.com/pages/examples/js/updateState.js +14 -0
- package/example-site/example.awesomenessjs.com/pages/start/_info.js +11 -0
- package/example-site/example.awesomenessjs.com/pages/start/css/footer.css +6 -0
- package/example-site/example.awesomenessjs.com/pages/start/css/intro.css +67 -0
- package/example-site/example.awesomenessjs.com/pages/start/css/nav.css +221 -0
- package/example-site/example.awesomenessjs.com/pages/start/getData.js +8 -0
- package/example-site/example.awesomenessjs.com/pages/start/js/init.js +30 -0
- package/example-site/example.awesomenessjs.com/pages/start/js/updateState.js +14 -0
- package/example-site/example.awesomenessjs.com/public/images/awesomeness.webp +0 -0
- package/example-site/example.awesomenessjs.com/public/manifest.webmanifest.json +8 -0
- package/example-site/example.awesomenessjs.com/specialRoutes.js +14 -0
- package/index.js +105 -25
- package/package.json +5 -2
- package/{server → src}/applicationMap.js +1 -1
- package/{server → src}/awesomenessNormalizeRequest.js +24 -3
- package/{server → src}/componentDependencies.js +11 -5
- package/{config.js → src/config.js} +6 -1
- package/{server → src}/fetchPage.js +6 -6
- package/src/getConfig.js +3 -0
- package/src/init.js +3 -0
- package/{server → src}/koa/attachAwesomenessRequest.js +5 -6
- package/{server → src}/koa/cors.js +2 -4
- package/{server → src}/koa/errorHandler.js +3 -3
- package/{server → src}/koa/finalFormat.js +2 -5
- package/{server → src}/koa/jsonBodyParser.js +3 -3
- package/src/koa/routeRequest.js +334 -0
- package/{server → src}/koa/serverUp.js +2 -4
- package/{server → src}/koa/staticFiles.js +10 -11
- package/{server → src}/koa/timeout.js +3 -3
- package/{server → src}/pageInfo.js +6 -6
- package/{server → src}/reRoute.js +3 -2
- package/{server → src}/resolveRealCasePath.js +1 -1
- package/{start.js → src/start.js} +14 -13
- package/{server → src}/validateRequest.js +1 -1
- package/{server → src}/ws/handlers.js +4 -2
- package/{server → src}/ws/index.js +2 -2
- package/ui/README.md +438 -0
- package/ui/awesomeness-ui.instructions.md +156 -0
- package/ui/build.js +119 -0
- package/ui/commonComponent.test.js +27 -0
- package/ui/components/blogPost/all.css +371 -0
- package/ui/components/blogPost/headerImage.js +153 -0
- package/ui/components/blogPost/index.js +45 -0
- package/ui/components/blogPost/marked.js +74 -0
- package/ui/components/blogPost/md.js +73 -0
- package/ui/components/blogPost/parseAwesomeness.jQuery.js +47 -0
- package/ui/components/blogPost/readme.md +8 -0
- package/ui/components/colors/README.md +65 -0
- package/ui/components/colors/dynamic.css +1884 -0
- package/ui/components/colors/dynamic.css.js +135 -0
- package/ui/components/colors/dynamic.css.php +79 -0
- package/ui/components/colors/example.js +133 -0
- package/ui/components/colors/index.js +65 -0
- package/ui/components/colors/map.js +13 -0
- package/ui/components/colors/random.js +17 -0
- package/ui/components/colors/tailwind-colors.css +247 -0
- package/ui/components/insertIntoList/insertIntoList.jquery.js +150 -0
- package/ui/components/tag/category.js +8 -0
- package/ui/components/tag/index.js +33 -0
- package/ui/components/tag/list.js +42 -0
- package/ui/components/tag/map.js +64 -0
- package/ui/components/tag/tagMap.css +60 -0
- package/ui/components/tag/tags.css +20 -0
- package/ui/core/css/_normalize.css +57 -0
- package/ui/core/css/dynamic/flex.js +36 -0
- package/ui/core/css/dynamic/grid.js +185 -0
- package/ui/core/css/dynamic/shortcuts.js +48 -0
- package/ui/core/css/dynamic/spacing.js +61 -0
- package/ui/core/css/dynamic/text.js +80 -0
- package/ui/core/css/dynamic/width.js +75 -0
- package/ui/core/css/dynamic.js +40 -0
- package/ui/core/js/app/api.js +143 -0
- package/ui/core/js/app/init.js +88 -0
- package/ui/core/js/app/initDarkMode.js +26 -0
- package/ui/core/js/app/initialScroll.js +28 -0
- package/ui/core/js/app/meta/update/components.js +65 -0
- package/ui/core/js/app/meta/update/pages.js +86 -0
- package/ui/core/js/app/metaUpdates.js +10 -0
- package/ui/core/js/app/page.js +319 -0
- package/ui/core/js/app/resize.js +52 -0
- package/ui/core/js/app/state/back.js +16 -0
- package/ui/core/js/app/state/create.js +50 -0
- package/ui/core/js/app/state/get.js +5 -0
- package/ui/core/js/app/state/init.js +25 -0
- package/ui/core/js/app.js +41 -0
- package/ui/core/js/jquery-3.7.1.min.js +2 -0
- package/ui/core/public/app.css +1 -0
- package/ui/core/public/app.js +975 -0
- package/ui/core/public/favicon.ico +0 -0
- package/ui/core/public/index.html +40 -0
- package/server/koa/routeRequest.js +0 -286
- /package/{server/errors.js → errors.js} +0 -0
- /package/{server → src}/brotliJsonResponse.js +0 -0
- /package/{server → src}/checkAccess.js +0 -0
- /package/{server → src}/getMD.js +0 -0
- /package/{server → src}/specialPaths.js +0 -0
package/README.md
CHANGED
|
@@ -8,6 +8,67 @@ The `./awesomeness/` folder and you can customize the location by modifying the
|
|
|
8
8
|
|
|
9
9
|
[@awesomeness-js/app-template](https://github.com/awesomeness-js/app-template)
|
|
10
10
|
|
|
11
|
+
# Underlying Request Flow
|
|
12
|
+
|
|
13
|
+
## Pre Routing
|
|
14
|
+
- start
|
|
15
|
+
- attachAwesomenessRequest
|
|
16
|
+
- gets awesomenessRequest.awesomenessType [page, api, generic]
|
|
17
|
+
- [staticFiles](#Static-Files)
|
|
18
|
+
- beforeRouteMiddleware
|
|
19
|
+
- routeRequest
|
|
20
|
+
|
|
21
|
+
## Routing
|
|
22
|
+
- needs to be valid route for [page, api]
|
|
23
|
+
- /[^a-zA-Z0-9\/\_\-]/ -> make sure there is no dots in the path -> 404
|
|
24
|
+
|
|
25
|
+
## API Routing ( needs awesomenessType in post body `api`)
|
|
26
|
+
- try /api/routes/${awesomenessRequest.pageRoute}
|
|
27
|
+
- try site specific first
|
|
28
|
+
- then try common routes (needs to be edited uses URL and not passed config)
|
|
29
|
+
- validateRequest
|
|
30
|
+
- route index.js
|
|
31
|
+
|
|
32
|
+
## Page Routing ( needs awesomenessType in post body `page`)
|
|
33
|
+
- pageInfo
|
|
34
|
+
- validateRequest
|
|
35
|
+
- fetchPage
|
|
36
|
+
- finalFormat
|
|
37
|
+
|
|
38
|
+
## Generic Routing ( has no defined awesomenessType in post body)
|
|
39
|
+
- method must be get or 405 is thrown
|
|
40
|
+
- /test -> returns awesomeness is working
|
|
41
|
+
- routes to staticFiles
|
|
42
|
+
- awesomenessRequest.awesomenessType is not set in POST body
|
|
43
|
+
- request is treated as a static file request
|
|
44
|
+
- static file served is it exists in the public folder
|
|
45
|
+
- site specific first
|
|
46
|
+
- then common files
|
|
47
|
+
|
|
48
|
+
If a public static file is not found
|
|
49
|
+
- public/index.html is served
|
|
50
|
+
- You DO NOT need your own public/index.html file
|
|
51
|
+
- The awesomeness-ui framework provides a default one
|
|
52
|
+
- this calls app.init()
|
|
53
|
+
|
|
54
|
+
# From the Browser
|
|
55
|
+
|
|
56
|
+
## What happens
|
|
57
|
+
|
|
58
|
+
1. User visits a page
|
|
59
|
+
- Server receives request
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## Static Files
|
|
63
|
+
- site specific files first
|
|
64
|
+
- /${awesomenessConfig.siteDir__URL}/${awesomenessRequest.site}/public/index.html
|
|
65
|
+
- then common files
|
|
66
|
+
- [/${awesomenessConfig.commonPublicDir__URL}/${awesomenessRequest.site}/public/index.html](/example-site/example.awesomenessjs.com/public/index.html)
|
|
67
|
+
|
|
68
|
+
[index.html](/example-site/example.awesomenessjs.com/public/index.html)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
11
72
|
## License
|
|
12
73
|
|
|
13
74
|
This project is licensed under the Apache License 2.0.
|
package/build/build.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default (awesomenessRequest) => {
|
|
2
|
+
|
|
3
|
+
const siteSpecific__URL = new URL(`../sites/${awesomenessRequest.site}/`, import.meta.url);
|
|
4
|
+
|
|
5
|
+
return [
|
|
6
|
+
|
|
7
|
+
// first match wins (site first)
|
|
8
|
+
new URL('./components/', siteSpecific__URL),
|
|
9
|
+
|
|
10
|
+
// last item is the default
|
|
11
|
+
new URL('../awesomeness-ui/components/', import.meta.url),
|
|
12
|
+
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
};
|
|
@@ -5,31 +5,15 @@ import wsHandler from './wsHandler.js';
|
|
|
5
5
|
import beforeRouteMiddleware from './beforeRouteMiddleware.js';
|
|
6
6
|
import checkSession from './checkSession.js';
|
|
7
7
|
import applicationMap from './applicationMap.js';
|
|
8
|
+
import componentLocations from './componentLocations.js';
|
|
8
9
|
|
|
9
10
|
export default {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
componentLocations: (awesomenessRequest) => {
|
|
16
|
-
|
|
17
|
-
const siteURL = new URL(`../sites/${awesomenessRequest.site}/`, import.meta.url);
|
|
18
|
-
|
|
19
|
-
return [
|
|
20
|
-
|
|
21
|
-
// first match wins (site first)
|
|
22
|
-
new URL('./components/', siteURL),
|
|
23
|
-
|
|
24
|
-
// last item is the default
|
|
25
|
-
new URL('../awesomeness-ui/components/', import.meta.url),
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
|
|
11
|
+
siteDir__URL: new URL("../sites/", import.meta.url),
|
|
12
|
+
commonPublicDir__URL: new URL("../awesomeness-ui/core/public/", import.meta.url),
|
|
13
|
+
commonApiDir__URL: new URL("../api/", import.meta.url),
|
|
14
|
+
componentLocations,
|
|
30
15
|
debug: true,
|
|
31
16
|
byPassAccessRequirementsInDev: true,
|
|
32
|
-
|
|
33
17
|
applicationMap,
|
|
34
18
|
beforeRouteMiddleware,
|
|
35
19
|
checkSession,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
custom routes can be added here.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/* test */
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
version: 1,
|
|
3
|
+
name: '',
|
|
4
|
+
description: 'this allows for any url to be treated as a blog post, if the slug matches a blog post, all post are simple md format',
|
|
5
|
+
permissions: [ '*' ],
|
|
6
|
+
components: [
|
|
7
|
+
'blogPage',
|
|
8
|
+
'blogPost',
|
|
9
|
+
'prism',
|
|
10
|
+
'statusPage',
|
|
11
|
+
'video',
|
|
12
|
+
'riskDisclosure',
|
|
13
|
+
'bibleVerse'
|
|
14
|
+
]
|
|
15
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Example Post
|
|
3
|
+
subTitle: This post wont show up because its in drafts.
|
|
4
|
+
image: /tools/images/posts/investing-101.webp
|
|
5
|
+
published: 2025-10-10
|
|
6
|
+
priority: null
|
|
7
|
+
url: /test-post
|
|
8
|
+
tags: [ faith, life hacks, spiritual freedom, christianity ]
|
|
9
|
+
tagColor: bg-stone-900
|
|
10
|
+
author:
|
|
11
|
+
name: Scott Forte
|
|
12
|
+
title: Pilot, Investor, Realtor ®
|
|
13
|
+
image: /tools/images/scott-forte-america-small-40.webp
|
|
14
|
+
url: https://scottmforte.com
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<break-50>
|
|
18
|
+
|
|
19
|
+
<youTube>ek_RodYzUCI</youTube>
|
|
20
|
+
|
|
21
|
+

|
|
22
|
+
|
|
23
|
+
<div class="blogPost-next-reads">
|
|
24
|
+
|
|
25
|
+
### Next Reads
|
|
26
|
+
|
|
27
|
+
- [Investing 101: Planting a Money Tree](/investing-101)
|
|
28
|
+
- [🏔️ Moving to NH: The Freest State on Planet Earth](/moving-to-new-hampshire)
|
|
29
|
+
- [Alternative Investments: Beyond Stocks, bonds, 401ks and IRAs](/alternative-investments)
|
|
30
|
+
- [👨✈️ Spiritual Freedom: When Jesus Becomes Your Co-Pilot](/spiritual-freedom)
|
|
31
|
+
|
|
32
|
+
</div>
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import parseMd from '#utils/parseMd.js';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
function tagToFilename(tag) {
|
|
10
|
+
|
|
11
|
+
return tag.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, '');
|
|
12
|
+
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default async function getBlogData(folder = 'pages') {
|
|
16
|
+
|
|
17
|
+
const folderPath = path.resolve(__dirname, folder);
|
|
18
|
+
const files = fs.readdirSync(folderPath).filter((f) => f.endsWith('.md'));
|
|
19
|
+
|
|
20
|
+
const tagImagePath = path.resolve(__dirname, '../../public/tools/images/tags');
|
|
21
|
+
const tagImageFiles = fs.existsSync(tagImagePath) ? fs.readdirSync(tagImagePath) : [];
|
|
22
|
+
|
|
23
|
+
const tagMap = Object.create(null);
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
const result = {
|
|
27
|
+
tags: [], // now array of { name, count }
|
|
28
|
+
links: [], // array of urls
|
|
29
|
+
posts: [] // array of metadata objects
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const linkRegex = /\[([^\]]+)\]\(([^)\s]+)\)/g;
|
|
33
|
+
|
|
34
|
+
for (const file of files) {
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
|
|
38
|
+
const filePath = path.join(folderPath, file);
|
|
39
|
+
const mdContent = fs.readFileSync(filePath, 'utf-8');
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
metadata,
|
|
43
|
+
cleanedMDContent
|
|
44
|
+
} = await parseMd(mdContent);
|
|
45
|
+
|
|
46
|
+
if (!metadata || !metadata.published) continue;
|
|
47
|
+
|
|
48
|
+
// collect tags
|
|
49
|
+
if (Array.isArray(metadata.tags)) {
|
|
50
|
+
|
|
51
|
+
for (const tag of metadata.tags) {
|
|
52
|
+
|
|
53
|
+
if (!tag) continue;
|
|
54
|
+
tagMap[tag] = (tagMap[tag] || 0) + 1;
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// collect links
|
|
61
|
+
let match;
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
while ((match = linkRegex.exec(cleanedMDContent)) !== null) {
|
|
65
|
+
|
|
66
|
+
const url = match[2];
|
|
67
|
+
|
|
68
|
+
result.links.push(url);
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// collect post metadata
|
|
73
|
+
result.posts.push({
|
|
74
|
+
file,
|
|
75
|
+
...metadata
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
} catch(err){
|
|
79
|
+
|
|
80
|
+
console.error(`Error processing file ${file}:`, err);
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// build tag array
|
|
89
|
+
result.tags = Object.entries(tagMap)
|
|
90
|
+
.map(([ name, count ]) => ({
|
|
91
|
+
name,
|
|
92
|
+
count,
|
|
93
|
+
image: tagImageFiles.includes(tagToFilename(name) + '.webp') ? `/tools/images/tags/${tagToFilename(name)}.webp` : null
|
|
94
|
+
}))
|
|
95
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
96
|
+
|
|
97
|
+
// sort posts by priority (asc), then date (desc), fallback to title
|
|
98
|
+
result.posts.sort((a, b) => {
|
|
99
|
+
|
|
100
|
+
const ap = (typeof a.priority === 'number') ? a.priority : Infinity;
|
|
101
|
+
const bp = (typeof b.priority === 'number') ? b.priority : Infinity;
|
|
102
|
+
|
|
103
|
+
if (ap !== bp) return ap - bp;
|
|
104
|
+
|
|
105
|
+
const ad = new Date(a.published);
|
|
106
|
+
const bd = new Date(b.published);
|
|
107
|
+
|
|
108
|
+
if (!isNaN(ad) && !isNaN(bd)) return bd - ad;
|
|
109
|
+
|
|
110
|
+
return (a.title || '').localeCompare(b.title || '');
|
|
111
|
+
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
return result;
|
|
116
|
+
|
|
117
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import parseMd from '#utils/parseMd.js';
|
|
2
|
+
import checkAccess from '#server/checkAccess.js';
|
|
3
|
+
|
|
4
|
+
// @awesomenessRequest
|
|
5
|
+
export default async function getData(awesomenessRequest) {
|
|
6
|
+
|
|
7
|
+
let mdContent =
|
|
8
|
+
awesomenessRequest.mdContent
|
|
9
|
+
|| awesomenessRequest.data.mdContent
|
|
10
|
+
|| '## No Content Found\n\nSorry, no content could be found for this post.';
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
metadata,
|
|
14
|
+
cleanedMDContent,
|
|
15
|
+
} = await parseMd(mdContent);
|
|
16
|
+
|
|
17
|
+
mdContent = cleanedMDContent;
|
|
18
|
+
|
|
19
|
+
awesomenessRequest.pageInit = '_md';
|
|
20
|
+
|
|
21
|
+
// check permissions?
|
|
22
|
+
if(metadata?.permissions?.length){
|
|
23
|
+
|
|
24
|
+
await checkAccess({
|
|
25
|
+
permissionsAllowed: metadata.permissions,
|
|
26
|
+
awesomenessRequest,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
mdContent,
|
|
33
|
+
metadata,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import ui from '#ui';
|
|
2
|
+
|
|
3
|
+
app.pages._md.callToAction = async function(){
|
|
4
|
+
|
|
5
|
+
$(`.custom-callToAction-real-estate`).each(function(){
|
|
6
|
+
|
|
7
|
+
const $container = $(this);
|
|
8
|
+
|
|
9
|
+
$container.empty();
|
|
10
|
+
|
|
11
|
+
const $break = $(`<div class="break height-100"></div>`)
|
|
12
|
+
.appendTo($container);
|
|
13
|
+
|
|
14
|
+
const $h1 = $(`<h1 class="">Ready to start investing?</h1>`)
|
|
15
|
+
.appendTo($container);
|
|
16
|
+
|
|
17
|
+
const $break2 = $(`<div class="break height-25"></div>`)
|
|
18
|
+
.appendTo($container);
|
|
19
|
+
|
|
20
|
+
const $p = $(`<p class="lead">
|
|
21
|
+
I turn down about 200 deals a year <b>because I do the math</b>.
|
|
22
|
+
They simply don't pencil out.
|
|
23
|
+
I also have an extremely conservative investment criteria.
|
|
24
|
+
That said,
|
|
25
|
+
Ive flipped over 40 units
|
|
26
|
+
from single family homes,
|
|
27
|
+
to office condos and hotels.
|
|
28
|
+
I currently own over 150 units.
|
|
29
|
+
You need me, or someone like me, as your copilot.
|
|
30
|
+
If you are interested in learning more about investment real estate,
|
|
31
|
+
no matter where in the world you live,
|
|
32
|
+
text me
|
|
33
|
+
</p>`).appendTo($container);
|
|
34
|
+
|
|
35
|
+
const $link = $(`<a href="sms:855.SCOTT.40" class="">855.SCOTT.40</a>`)
|
|
36
|
+
.appendTo($p);
|
|
37
|
+
|
|
38
|
+
const $break3 = $(`<div class="break height-100"></div>`)
|
|
39
|
+
.appendTo($container);
|
|
40
|
+
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import ui from '#ui';
|
|
2
|
+
|
|
3
|
+
app.pages._md.init = async function(pageData){
|
|
4
|
+
|
|
5
|
+
ui.cleanMain();
|
|
6
|
+
|
|
7
|
+
if(
|
|
8
|
+
pageData.metadata.breadcrumbs
|
|
9
|
+
&& !pageData.metadata.image
|
|
10
|
+
){
|
|
11
|
+
|
|
12
|
+
ui.breadcrumbs({
|
|
13
|
+
data: pageData.metadata.breadcrumbs,
|
|
14
|
+
color: pageData.metadata.breadcrumbColor ?? 'stone'
|
|
15
|
+
}).appendTo(app.$main);
|
|
16
|
+
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const $page = ui.blogPost.md(pageData,{
|
|
20
|
+
tagBase: '/tag',
|
|
21
|
+
customRules: [
|
|
22
|
+
(mdContent)=>{
|
|
23
|
+
|
|
24
|
+
return mdContent.replace(/<cta-realEstate>/g, '<div class="custom-callToAction-real-estate"></div>');
|
|
25
|
+
|
|
26
|
+
},
|
|
27
|
+
(mdContent)=>{
|
|
28
|
+
|
|
29
|
+
// find <bibleVerse> tags and replace with divs
|
|
30
|
+
return mdContent.replace(/<bibleVerse>/g, '<div class="data-bible-verse">').replace(/<\/bibleVerse>/g, '</div>');
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}).appendTo(app.$main);
|
|
35
|
+
|
|
36
|
+
$page.find('a').click(function(event){
|
|
37
|
+
|
|
38
|
+
event.preventDefault();
|
|
39
|
+
event.stopPropagation();
|
|
40
|
+
|
|
41
|
+
const href = $(this).attr('href');
|
|
42
|
+
|
|
43
|
+
if(href && href.startsWith('http')){
|
|
44
|
+
|
|
45
|
+
// open target blank
|
|
46
|
+
window.open(href, '_blank');
|
|
47
|
+
|
|
48
|
+
} else {
|
|
49
|
+
|
|
50
|
+
// does this a tag have any awesomeness-* class
|
|
51
|
+
if($(this).attr('class') && $(this).attr('class').match(/awesomeness-/)){
|
|
52
|
+
|
|
53
|
+
// do nothing, let awesomeness-ui handle it
|
|
54
|
+
|
|
55
|
+
} else {
|
|
56
|
+
|
|
57
|
+
// internal links
|
|
58
|
+
app.page(href);
|
|
59
|
+
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// find code blocks that have only a string inside and add class .simple-code-string
|
|
67
|
+
$page.find('code').each(function(){
|
|
68
|
+
|
|
69
|
+
const $code = $(this);
|
|
70
|
+
const codeText = $code.text().trim();
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
if(codeText && !codeText.includes('\n')){
|
|
74
|
+
|
|
75
|
+
$code.addClass('simple-code-string');
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
Prism.highlightAll();
|
|
82
|
+
|
|
83
|
+
app.pages._md.callToAction();
|
|
84
|
+
|
|
85
|
+
ui.bibleVerse.init();
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Some Example Blog Post
|
|
3
|
+
subTitle: This is a subtitle of a blog post
|
|
4
|
+
readTime: null
|
|
5
|
+
image: /tools/images/posts/investing-101.png
|
|
6
|
+
published: 2025-10-10
|
|
7
|
+
priority: null
|
|
8
|
+
url: /test-post
|
|
9
|
+
tags: [ faith, life hacks, spiritual freedom, christianity ]
|
|
10
|
+
tagColor: bg-stone-900
|
|
11
|
+
author:
|
|
12
|
+
name: Scott Forte
|
|
13
|
+
title: Pilot, Investor, Realtor ®
|
|
14
|
+
image: /tools/images/scott-forte-america-small-40.webp
|
|
15
|
+
url: https://scottmforte.com
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
<break-50>
|
|
19
|
+
|
|
20
|
+
<youTube>5guXs-675KI</youTube>
|
|
21
|
+
|
|
22
|
+

|
|
23
|
+
|
|
24
|
+
<div class="blogPost-next-reads">
|
|
25
|
+
|
|
26
|
+
### Next Reads
|
|
27
|
+
|
|
28
|
+
- [👨✈️ Spiritual Freedom: When Jesus Becomes Your Co-Pilot](/spiritual-freedom)
|
|
29
|
+
- [💸 Financial Freedom: What does it mean?](/financial-freedom)
|
|
30
|
+
- [🗽 Political Freedom](/political-freedom)
|
|
31
|
+
- [🧠 Mental Freedom: The Basis of Freedom](/mental-freedom)
|
|
32
|
+
|
|
33
|
+
</div>
|