als-layout 5.1.0 → 6.0.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/docs/#.md +19 -0
- package/docs/0-change-log.md +10 -0
- package/docs/1-basic-usage.md +37 -0
- package/docs/2-cloning.md +19 -0
- package/docs/3-advanced-usage.md +48 -0
- package/docs/4-res-and-status.md +10 -0
- package/docs/5-api.md +97 -0
- package/lib/elements/index.js +38 -0
- package/lib/elements/keywords.js +19 -0
- package/lib/elements/link.js +13 -0
- package/lib/elements/meta.js +12 -0
- package/lib/elements/url.js +18 -0
- package/lib/layout.js +25 -94
- package/package.json +4 -3
- package/readme.md +38 -51
- package/{build-readme.js → scripts/build-readme.js} +2 -2
- package/tests/integrative.test.js +1 -7
- package/lib/onload.js +0 -11
- /package/lib/{script.js → elements/script.js} +0 -0
- /package/lib/{style.js → elements/style.js} +0 -0
package/docs/#.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# als-layout Documentation
|
|
2
|
+
|
|
3
|
+
`als-layout` is a JavaScript library designed to simplify and enhance the process of constructing and managing web page layouts. It provides a comprehensive API for modifying HTML documents dynamically, allowing developers to add, update, and manipulate various elements such as meta tags, styles, scripts, and more.
|
|
4
|
+
|
|
5
|
+
The `als-layout` library is versatile, suitable for:
|
|
6
|
+
- Building dynamic web pages that require frequent updates to their metadata, styles, or scripts.
|
|
7
|
+
- Creating templating systems where multiple page layouts share similar structures but differ in content or styling.
|
|
8
|
+
- Developing web applications that need to dynamically adjust their UI based on user interactions or data changes.
|
|
9
|
+
|
|
10
|
+
## Installation and Adding
|
|
11
|
+
To use the `als-layout` library in your project, you can install it via npm and then include it in your JavaScript files:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm i als-layout
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```js
|
|
18
|
+
const Layout = require('als-layout')
|
|
19
|
+
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
## Basic Usage
|
|
2
|
+
|
|
3
|
+
### Initialization
|
|
4
|
+
To start using `als-layout`, you first need to create a new instance of `Layout`. This instance will serve as the foundation for building and modifying your web page.
|
|
5
|
+
|
|
6
|
+
```js
|
|
7
|
+
const Layout = require('als-layout');
|
|
8
|
+
const layout = new Layout();
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Adding Different Elements
|
|
12
|
+
Once you have your `Layout` instance, you can easily add or modify various elements of your web page. Here are some examples of how you can use the library to customize your layout:
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
const layout = new Layout()
|
|
16
|
+
.viewport() // default width=device-width, initial-scale=1.0
|
|
17
|
+
.title('Test title') // adding/updating title and meta[og:title]
|
|
18
|
+
.favicon('/favicon.png') // adding/updating link[rel=icon][type=image/x-icon] with new href
|
|
19
|
+
.keywords(['some', 'keyword']) // adding/updating meta[name=keywords]. not adding existing keywords
|
|
20
|
+
.image('/main-image.png', '1.5') // adding/updating meta - og:image, twitter:image, twitter:card
|
|
21
|
+
.description('Cool site') // adding/updating meta og:description, twitter:description, and description tag
|
|
22
|
+
.url('/some', 'http://site.com') // adding/updating meta[og:url] and link[rel="canonical"]
|
|
23
|
+
.style('body {margin:0; background-color:whitesmoke;}', true) // adding css styles to existing/new style tag. Second parameter is minified (default=false).
|
|
24
|
+
.link('/styles.css') // adding link[rel=stylesheet] if such href not exists
|
|
25
|
+
.script({src:'/app.js'}, '', true) // set script with src to head if such src not exists
|
|
26
|
+
.script({}, 'console.log("hello world")', false) // set script with script code to footer
|
|
27
|
+
|
|
28
|
+
// Accessors for document parts
|
|
29
|
+
layout.body // getter for body element (if not exists, created)
|
|
30
|
+
layout.head // getter for head element (if not exists, created)
|
|
31
|
+
layout.html // getter for html Element (if not exists, created)
|
|
32
|
+
|
|
33
|
+
// Outputs
|
|
34
|
+
layout.rawHtml // raw HTML of the document
|
|
35
|
+
layout.clone // creates a new layout object clone for current object
|
|
36
|
+
```
|
|
37
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## Cloning Functionality
|
|
2
|
+
|
|
3
|
+
### What is Cloning and Why is it Necessary?
|
|
4
|
+
Cloning in the `als-layout` library refers to creating a complete, independent copy of the existing `Layout` instance. This functionality is crucial when you need to generate multiple pages or versions of a page from a single base layout without affecting the original setup.
|
|
5
|
+
|
|
6
|
+
### How to Use Cloning
|
|
7
|
+
To clone a `Layout` instance, simply use the `clone` method. This method creates a new `Layout` instance with the same properties and settings as the original, allowing for independent modifications without interference.
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
const newLayout = layout.clone;
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
### Benefits of Cloning
|
|
14
|
+
- **Efficiency:** Cloning is highly efficient, especially for creating pages with similar structures but different content or styles. It avoids the overhead of reinitializing and reconfiguring a new `Layout` instance from scratch.
|
|
15
|
+
- **Speed:** Cloning is fast, typically taking less than 20ms even for large pages. This makes it ideal for high-performance web applications that need to dynamically generate content.
|
|
16
|
+
- **Isolation:** Changes made to a cloned `Layout` do not affect the original, ensuring that each instance can be modified independently based on specific requirements.
|
|
17
|
+
|
|
18
|
+
Cloning is particularly useful in scenarios where templates or base layouts are used repeatedly with slight variations, providing a robust and scalable solution for web page generation.
|
|
19
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
## Advanced Usage
|
|
2
|
+
|
|
3
|
+
The `als-layout` library allows for sophisticated manipulation of web page layouts, providing robust tools for creating dynamic and complex web pages. Below is an advanced example demonstrating various capabilities of the library:
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
const Layout = require('als-layout')
|
|
7
|
+
|
|
8
|
+
// Starting with a basic HTML template and specifying the host for URL methods
|
|
9
|
+
const raw = /*html*/`<html></html>`
|
|
10
|
+
const host = 'http://example.com';
|
|
11
|
+
const options = {
|
|
12
|
+
logger,
|
|
13
|
+
host,
|
|
14
|
+
minified=false
|
|
15
|
+
}
|
|
16
|
+
const layout = new Layout(raw, options).lang('fr')
|
|
17
|
+
console.log(layout.rawHtml)
|
|
18
|
+
// <!DOCTYPE html><html lang="fr"><head></p></head><body></body></html>
|
|
19
|
+
|
|
20
|
+
// Cloning the initial layout to create a specialized page
|
|
21
|
+
const homePage = layout.clone
|
|
22
|
+
homeAutoReload = layout.clone
|
|
23
|
+
homePage.title('Home page')
|
|
24
|
+
homePage.body.innerHTML = /*html*/`<h1>Home page</h1>`
|
|
25
|
+
console.log(homePage.rawHtml)
|
|
26
|
+
// <!DOCTYPE html><html lang="fr"><head><title>Home page</title><meta property="og:title" content="Home page"></head><body><h1>Home page</h1></body></html>
|
|
27
|
+
|
|
28
|
+
// Adding script that reloads the page every minute
|
|
29
|
+
homeAutoReload.script({}, 'setTimeout(function() { window.location.reload(); }, 60000);', false)
|
|
30
|
+
console.log(homeAutoReload.rawHtml)
|
|
31
|
+
// <!DOCTYPE html><html lang="fr"><head><title>Automatic Reload Page</title><meta property="og:title" content="Automatic Reload Page"></head><body><script>setTimeout(function() { window.location.reload(); }, 60000);</script></body></html>
|
|
32
|
+
|
|
33
|
+
// Demonstrating dynamic stylesheet linkage with versioning
|
|
34
|
+
homePage.link('/css/main.css')
|
|
35
|
+
console.log(homePage.rawHtml)
|
|
36
|
+
// Includes link to the stylesheet with version parameter to ensure fresh cache
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
In this example:
|
|
40
|
+
|
|
41
|
+
- We start with a basic HTML template and use the `lang` method to set the language.
|
|
42
|
+
- We use the `clone` method to create two versions of the base layout: one for the home page and another that automatically reloads every minute.
|
|
43
|
+
- We manipulate the `body` of the `homePage` to include custom HTML.
|
|
44
|
+
- We add a script to `homeAutoReload` that sets up an automatic page reload, showcasing how to insert JavaScript dynamically.
|
|
45
|
+
- We dynamically add a versioned link to a stylesheet in the `homePage`, demonstrating control over caching and resource management.
|
|
46
|
+
|
|
47
|
+
This advanced example illustrates how `als-layout` can be used to handle complex scenarios and requirements in web development, enhancing the flexibility and power at your disposal.
|
|
48
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
## Response with status
|
|
2
|
+
|
|
3
|
+
In version 6, added two methods:
|
|
4
|
+
1. `status(statusCode)` - adds the statusCode to `__status` property
|
|
5
|
+
2. `end(res)`
|
|
6
|
+
1. writes head with `__status, { 'Content-Type': 'text/html' }`
|
|
7
|
+
2. runs `res.end(this.rawHtml)`
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
The main idea, is to add quick way to response with layout.
|
package/docs/5-api.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
## API
|
|
2
|
+
|
|
3
|
+
### Constructor
|
|
4
|
+
|
|
5
|
+
#### `new Layout(html: string, options: object)`
|
|
6
|
+
|
|
7
|
+
Creates a new `Layout` instance.
|
|
8
|
+
|
|
9
|
+
- **html**: The initial HTML document string.
|
|
10
|
+
- **options**: Configuration options for the layout instance.
|
|
11
|
+
- **minified**: Boolean (default: `false`). Determines if inline CSS and JavaScript should be minified.
|
|
12
|
+
- **logger**: Function (default: `console`). Function for errors in minifiying and url method.
|
|
13
|
+
- **host**: String (defult:`undefined`). String for url method.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Properties
|
|
17
|
+
|
|
18
|
+
#### `layout.rawHtml`
|
|
19
|
+
|
|
20
|
+
Returns the inner HTML of the document.
|
|
21
|
+
|
|
22
|
+
#### `layout.clone`
|
|
23
|
+
|
|
24
|
+
Creates a clone of the current layout instance, preserving `options`.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
#### `__status`
|
|
28
|
+
|
|
29
|
+
Stores the statusCode for `end` method.
|
|
30
|
+
|
|
31
|
+
### Methods
|
|
32
|
+
|
|
33
|
+
#### `lang(lang: string): this`
|
|
34
|
+
|
|
35
|
+
Sets the `lang` attribute on the `<html>` element.
|
|
36
|
+
|
|
37
|
+
#### `title(title: string): this`
|
|
38
|
+
|
|
39
|
+
Sets the document title and creates an Open Graph title meta tag.
|
|
40
|
+
|
|
41
|
+
#### `description(description: string): this`
|
|
42
|
+
|
|
43
|
+
Adds description meta tags for SEO and social platforms.
|
|
44
|
+
|
|
45
|
+
#### `favicon(href: string): this`
|
|
46
|
+
|
|
47
|
+
Sets or updates the favicon URL.
|
|
48
|
+
|
|
49
|
+
#### `meta(props: object): this`
|
|
50
|
+
|
|
51
|
+
Adds or updates a `<meta>` tag with specified attributes.
|
|
52
|
+
|
|
53
|
+
- **props**: An object where each key-value pair corresponds to a meta attribute.
|
|
54
|
+
|
|
55
|
+
#### `keywords(keywords: array): this`
|
|
56
|
+
|
|
57
|
+
Sets or appends keywords in the `content` attribute of a `<meta name="keywords">` tag.
|
|
58
|
+
|
|
59
|
+
#### `viewport(viewport: string): this`
|
|
60
|
+
|
|
61
|
+
Sets the viewport meta tag. Default: `width=device-width, initial-scale=1.0`.
|
|
62
|
+
|
|
63
|
+
#### `image(image: string): this`
|
|
64
|
+
|
|
65
|
+
Sets meta tags for Open Graph and Twitter cards for an image.
|
|
66
|
+
|
|
67
|
+
#### `style(styles: string, minified: boolean = this.options.minified): this`
|
|
68
|
+
|
|
69
|
+
Adds inline CSS to the document. If `minified` is true, the CSS is minified.
|
|
70
|
+
|
|
71
|
+
- **styles**: CSS string.
|
|
72
|
+
- **minified**: Boolean (optional).
|
|
73
|
+
|
|
74
|
+
#### `url(url: string, host: string = this.URL): this`
|
|
75
|
+
|
|
76
|
+
Sets the canonical URL and Open Graph URL meta tags.
|
|
77
|
+
|
|
78
|
+
#### `script(attrs: object = {}, innerHTML: string = '', head: boolean = true, minified: boolean = this.options.minified): this`
|
|
79
|
+
|
|
80
|
+
Adds a `<script>` tag to the document.
|
|
81
|
+
|
|
82
|
+
- **attrs**: Attributes for the `<script>` tag.
|
|
83
|
+
- **innerHTML**: Inline JavaScript (optional).
|
|
84
|
+
- **head**: Boolean (default: `true`). If `false`, the script is added to `<body>`.
|
|
85
|
+
- **minified**: Boolean. If true, inline JavaScript is minified.
|
|
86
|
+
|
|
87
|
+
#### `link(href: string, attributes: object = { rel: "stylesheet", type: "text/css" }): this`
|
|
88
|
+
|
|
89
|
+
Adds a `<link>` tag for external stylesheets.
|
|
90
|
+
|
|
91
|
+
#### `status(statusCode:number): this`
|
|
92
|
+
|
|
93
|
+
Adds the statusCode to `__status` property
|
|
94
|
+
|
|
95
|
+
#### `end(res):undefined`
|
|
96
|
+
|
|
97
|
+
Response with res(rawHtml)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const meta = require('./meta')
|
|
2
|
+
const script = require('./script')
|
|
3
|
+
const style = require('./style')
|
|
4
|
+
const addKeywords = require('./keywords')
|
|
5
|
+
const addLink = require('./link')
|
|
6
|
+
const addUrl = require('./url')
|
|
7
|
+
|
|
8
|
+
const { SingleNode } = require('als-document');
|
|
9
|
+
|
|
10
|
+
function getDescription(description,layout) {
|
|
11
|
+
meta({ name: 'description', content: description },layout)
|
|
12
|
+
meta({ property: 'og:description', content: description },layout)
|
|
13
|
+
meta({ property: 'twitter:description', content: description },layout)
|
|
14
|
+
return layout
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getFavicon(href,layout) {
|
|
18
|
+
const faviconElement = layout.root.$('link[rel=icon][type=image/x-icon]')
|
|
19
|
+
if (faviconElement) faviconElement.setAttribute('href', href)
|
|
20
|
+
else layout.head.insert(2, new SingleNode('link', { rel: 'icon', href, type: 'image/x-icon' }))
|
|
21
|
+
return layout
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getViewport(viewport = 'width=device-width, initial-scale=1.0',layout) {
|
|
25
|
+
const element = layout.root.$('meta[name="viewport"]')
|
|
26
|
+
if (element) element.setAttribute('content', viewport)
|
|
27
|
+
else layout.head.insert(2, new SingleNode('meta', { name: 'viewport', content: viewport }))
|
|
28
|
+
return layout
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getImage(image,layout) {
|
|
32
|
+
meta({ property: 'og:image', content: image },layout)
|
|
33
|
+
meta({ name: 'twitter:image', content: image },layout)
|
|
34
|
+
meta({ name: 'twitter:card', content: 'summary_large_image' },layout)
|
|
35
|
+
return layout
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = { meta, getDescription, getFavicon, getViewport, getImage, script, style, addKeywords, addLink, addUrl }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const { SingleNode } = require('als-document');
|
|
2
|
+
|
|
3
|
+
function keywords(keywords = [],layout) {
|
|
4
|
+
let keywordsElement = layout.root.$('meta[name=keywords]')
|
|
5
|
+
if (!keywordsElement) keywordsElement = new SingleNode('meta', { name: 'keywords' })
|
|
6
|
+
const content = keywordsElement.getAttribute('content')
|
|
7
|
+
const existingKeywords = content ? content.split(',') : []
|
|
8
|
+
keywords.forEach(keyword => {
|
|
9
|
+
keyword = keyword.trim()
|
|
10
|
+
if (!existingKeywords.includes(keyword)) existingKeywords.push(keyword)
|
|
11
|
+
});
|
|
12
|
+
if (existingKeywords.length) {
|
|
13
|
+
keywordsElement.setAttribute('content', existingKeywords.join())
|
|
14
|
+
layout.head.insert(2, keywordsElement)
|
|
15
|
+
}
|
|
16
|
+
return layout
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = keywords
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { SingleNode } = require('als-document');
|
|
2
|
+
|
|
3
|
+
function link(href, attributes = { rel: "stylesheet", type: "text/css" },layout) {
|
|
4
|
+
if (!href || typeof href !== 'string') return layout
|
|
5
|
+
const selector = `link[rel=stylesheet][href^="${href}"]`
|
|
6
|
+
let linkElement = layout.root.$(selector)
|
|
7
|
+
if (linkElement) return
|
|
8
|
+
linkElement = new SingleNode('link', { href, ...attributes })
|
|
9
|
+
layout.head.insert(2, linkElement)
|
|
10
|
+
return layout
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = link
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { SingleNode } = require('als-document');
|
|
2
|
+
|
|
3
|
+
function meta(props,layout) {
|
|
4
|
+
const entries = Object.entries(props)
|
|
5
|
+
const [name, value] = entries[0]
|
|
6
|
+
const selector = `meta[${name}="${value}"]`
|
|
7
|
+
const metaElement = layout.root.$(selector)
|
|
8
|
+
if (metaElement) entries.forEach(([name, v]) => metaElement.setAttribute(name, props[name]))
|
|
9
|
+
else layout.head.insert(2, new SingleNode('meta', props))
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = meta
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const meta = require('./meta');
|
|
2
|
+
const { SingleNode } = require('als-document');
|
|
3
|
+
|
|
4
|
+
function addUrl(url, host,layout) {
|
|
5
|
+
try {
|
|
6
|
+
url = (host ? new URL(url, host) : new URL(url)).href.replace(/\/$/, '')
|
|
7
|
+
meta({ property: 'og:url', content: url },layout)
|
|
8
|
+
const canonicalElement = layout.root.$('link[rel="canonical"]')
|
|
9
|
+
if (canonicalElement) canonicalElement.setAttribute('href', url)
|
|
10
|
+
else layout.head.insert(2, new SingleNode('link', { rel: 'canonical', href: url }))
|
|
11
|
+
} catch (error) {
|
|
12
|
+
console.log(error)
|
|
13
|
+
layout.logger.log(`url "${url}" with host "${host}" is not valid url`)
|
|
14
|
+
}
|
|
15
|
+
return layout
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = addUrl
|
package/lib/layout.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
const { Document
|
|
2
|
-
const script = require('./
|
|
3
|
-
const style = require('./style');
|
|
4
|
-
const onloadScript = require('./onload');
|
|
1
|
+
const { Document } = require('als-document');
|
|
2
|
+
const { meta, getDescription, getFavicon, getViewport, getImage, script, style, addKeywords, addLink, addUrl } = require('./elements/index')
|
|
5
3
|
|
|
6
4
|
class Layout extends Document {
|
|
5
|
+
static propsToClone = [];
|
|
6
|
+
static options = { logger: console, host: '', minified: false }
|
|
7
|
+
|
|
7
8
|
constructor(html, options = {}) {
|
|
9
|
+
options = { ...Layout.options, ...options }
|
|
8
10
|
super(html, options.host);
|
|
9
11
|
this.options = options
|
|
10
12
|
this.logger = options.logger || console
|
|
@@ -13,13 +15,26 @@ class Layout extends Document {
|
|
|
13
15
|
|
|
14
16
|
get rawHtml() { return this.innerHTML }
|
|
15
17
|
get clone() { return new this.constructor(new Document(this, this.URL), this.options) }
|
|
16
|
-
lang(lang) { this.html.setAttribute('lang', lang); return this }
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.
|
|
22
|
-
|
|
19
|
+
__status = 200;
|
|
20
|
+
status(status) { this.__status = status; return this }
|
|
21
|
+
end(res) {
|
|
22
|
+
res.writeHead(this.__status, { 'Content-Type': 'text/html' })
|
|
23
|
+
res.end(this.rawHtml)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
lang(lang) { this.html.setAttribute('lang', lang); return this }
|
|
27
|
+
link(href, attributes) { return addLink(href, attributes, this) }
|
|
28
|
+
keywords(keywords = []) { return addKeywords(keywords, this) }
|
|
29
|
+
style(styles, minified = this.options.minified) { return style(styles, minified, this) }
|
|
30
|
+
url(url, host = this.URL) { return addUrl(url, host, this) }
|
|
31
|
+
meta(props) { return meta(props, this) }
|
|
32
|
+
description(description) { return getDescription(description,this) }
|
|
33
|
+
favicon(href) { return getFavicon(href,this) }
|
|
34
|
+
viewport(viewport) { return getViewport(viewport,this) }
|
|
35
|
+
image(image) { return getImage(image,this) }
|
|
36
|
+
script(attrs = {}, innerHTML = '', head = true, minified = this.options.minified) {
|
|
37
|
+
return script(attrs, innerHTML, head, minified, this)
|
|
23
38
|
}
|
|
24
39
|
|
|
25
40
|
title(title) {
|
|
@@ -28,90 +43,6 @@ class Layout extends Document {
|
|
|
28
43
|
this.meta({ property: 'og:title', content: title })
|
|
29
44
|
return this
|
|
30
45
|
}
|
|
31
|
-
|
|
32
|
-
description(description) {
|
|
33
|
-
this.meta({ name: 'description', content: description })
|
|
34
|
-
this.meta({ property: 'og:description', content: description })
|
|
35
|
-
this.meta({ property: 'twitter:description', content: description })
|
|
36
|
-
return this
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
favicon(href) {
|
|
40
|
-
const faviconElement = this.root.$('link[rel=icon][type=image/x-icon]')
|
|
41
|
-
if (faviconElement) faviconElement.setAttribute('href', href)
|
|
42
|
-
else this.head.insert(2, new SingleNode('link', { rel: 'icon', href, type: 'image/x-icon' }))
|
|
43
|
-
return this
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
meta(props) {
|
|
47
|
-
const entries = Object.entries(props)
|
|
48
|
-
const [name, value] = entries[0]
|
|
49
|
-
const selector = `meta[${name}="${value}"]`
|
|
50
|
-
const metaElement = this.root.$(selector)
|
|
51
|
-
if (metaElement) entries.forEach(([name, v]) => metaElement.setAttribute(name, props[name]))
|
|
52
|
-
else this.head.insert(2, new SingleNode('meta', props))
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
keywords(keywords = []) {
|
|
56
|
-
let keywordsElement = this.root.$('meta[name=keywords]')
|
|
57
|
-
if (!keywordsElement) keywordsElement = new SingleNode('meta', { name: 'keywords' })
|
|
58
|
-
const content = keywordsElement.getAttribute('content')
|
|
59
|
-
const existingKeywords = content ? content.split(',') : []
|
|
60
|
-
keywords.forEach(keyword => {
|
|
61
|
-
keyword = keyword.trim()
|
|
62
|
-
if (!existingKeywords.includes(keyword)) existingKeywords.push(keyword)
|
|
63
|
-
});
|
|
64
|
-
if (existingKeywords.length) {
|
|
65
|
-
keywordsElement.setAttribute('content', existingKeywords.join())
|
|
66
|
-
this.head.insert(2, keywordsElement)
|
|
67
|
-
}
|
|
68
|
-
return this
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
viewport(viewport = 'width=device-width, initial-scale=1.0') {
|
|
72
|
-
const element = this.root.$('meta[name="viewport"]')
|
|
73
|
-
if (element) element.setAttribute('content', viewport)
|
|
74
|
-
else this.head.insert(2, new SingleNode('meta', { name: 'viewport', content: viewport }))
|
|
75
|
-
return this
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
image(image) {
|
|
79
|
-
this.meta({ property: 'og:image', content: image })
|
|
80
|
-
this.meta({ name: 'twitter:image', content: image })
|
|
81
|
-
this.meta({ name: 'twitter:card', content: 'summary_large_image' })
|
|
82
|
-
return this
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
style(styles, minified = this.options.minified) {
|
|
86
|
-
return style(styles,minified,this)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
url(url, host = this.URL) {
|
|
90
|
-
try {
|
|
91
|
-
url = new URL(url, host).href.replace(/\/$/, '')
|
|
92
|
-
this.meta({ property: 'og:url', content: url })
|
|
93
|
-
const canonicalElement = this.root.$('link[rel="canonical"]')
|
|
94
|
-
if (canonicalElement) canonicalElement.setAttribute('href', url)
|
|
95
|
-
else this.head.insert(2, new SingleNode('link', { rel: 'canonical', href: url }))
|
|
96
|
-
} catch (error) {
|
|
97
|
-
this.logger.log(`url ${url} with host ${host} is not valid url`)
|
|
98
|
-
}
|
|
99
|
-
return this
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
script(attrs = {}, innerHTML = '', head = true, minified = this.options.minified) {
|
|
103
|
-
return script(attrs,innerHTML,head,minified,this)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
link(href, attributes = { rel: "stylesheet", type: "text/css" }) {
|
|
107
|
-
if (!href || typeof href !== 'string') return this
|
|
108
|
-
const selector = `link[rel=stylesheet][href^="${href}"]`
|
|
109
|
-
let linkElement = this.root.$(selector)
|
|
110
|
-
if (linkElement) return
|
|
111
|
-
linkElement = new SingleNode('link', { href, ...attributes })
|
|
112
|
-
this.head.insert(2, linkElement)
|
|
113
|
-
return this
|
|
114
|
-
}
|
|
115
46
|
}
|
|
116
47
|
|
|
117
48
|
module.exports = Layout
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "als-layout",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"test": "node --test --experimental-test-coverage",
|
|
7
|
-
"report": "node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=lcov.info"
|
|
6
|
+
"test": "node --test --experimental-test-coverage ./tests/**.*",
|
|
7
|
+
"report": "node --test --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=lcov.info",
|
|
8
|
+
"docs":"node ./scripts/build-readme.js"
|
|
8
9
|
},
|
|
9
10
|
"keywords": [],
|
|
10
11
|
"author": "Alex Sorkin",
|
package/readme.md
CHANGED
|
@@ -17,43 +17,18 @@ npm i als-layout
|
|
|
17
17
|
```js
|
|
18
18
|
const Layout = require('als-layout')
|
|
19
19
|
```
|
|
20
|
-
|
|
21
|
-
## Change Log
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
*
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
|
|
32
|
-
* status and end methods added
|
|
33
|
-
* V4.2.0
|
|
34
|
-
* link method changed
|
|
35
|
-
* `link(href, attributes = { rel: "stylesheet", type: "text/css" })`
|
|
36
|
-
* no version method
|
|
37
|
-
* no version parameter in image, link,script
|
|
38
|
-
* script
|
|
39
|
-
* script in footer added one after other in right order
|
|
40
|
-
|
|
41
|
-
* V4.1.0
|
|
42
|
-
* Render removed
|
|
43
|
-
|
|
44
|
-
* V4.0.0
|
|
45
|
-
* All code rebuilded and refactored
|
|
46
|
-
* No als-simple-css for style
|
|
47
|
-
* No charset method
|
|
48
|
-
* minifying for style and inner scripts
|
|
49
|
-
* updated render version
|
|
50
|
-
* render as element's method instead layout's method
|
|
51
|
-
|
|
52
|
-
* V3.0.0
|
|
53
|
-
* render switched to als-render
|
|
54
|
-
* updated bug with meta tags after body
|
|
55
|
-
|
|
56
|
-
|
|
20
|
+
|
|
21
|
+
## Change Log for V6
|
|
22
|
+
|
|
23
|
+
The code refactored.
|
|
24
|
+
|
|
25
|
+
### Removed
|
|
26
|
+
* no deepclone for options, using { ...Layout.options, ...options } instead
|
|
27
|
+
* no onload method
|
|
28
|
+
### Added
|
|
29
|
+
* status
|
|
30
|
+
* end(res)
|
|
31
|
+
|
|
57
32
|
## Basic Usage
|
|
58
33
|
|
|
59
34
|
### Initialization
|
|
@@ -91,15 +66,7 @@ layout.rawHtml // raw HTML of the document
|
|
|
91
66
|
layout.clone // creates a new layout object clone for current object
|
|
92
67
|
```
|
|
93
68
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
By adding onload attribute, you can run scripts for each element after dom content has loaded.
|
|
97
|
-
|
|
98
|
-
Example how it works:
|
|
99
|
-
```js
|
|
100
|
-
const layout = new Layout().viewport().title('On load').onload()
|
|
101
|
-
layout.body.innerHTML = /*html*/`<div onload="this.innerHTML = 'new content'">original content</div>`
|
|
102
|
-
```
|
|
69
|
+
|
|
103
70
|
## Cloning Functionality
|
|
104
71
|
|
|
105
72
|
### What is Cloning and Why is it Necessary?
|
|
@@ -119,7 +86,7 @@ const newLayout = layout.clone;
|
|
|
119
86
|
|
|
120
87
|
Cloning is particularly useful in scenarios where templates or base layouts are used repeatedly with slight variations, providing a robust and scalable solution for web page generation.
|
|
121
88
|
|
|
122
|
-
|
|
89
|
+
|
|
123
90
|
## Advanced Usage
|
|
124
91
|
|
|
125
92
|
The `als-layout` library allows for sophisticated manipulation of web page layouts, providing robust tools for creating dynamic and complex web pages. Below is an advanced example demonstrating various capabilities of the library:
|
|
@@ -168,6 +135,17 @@ In this example:
|
|
|
168
135
|
|
|
169
136
|
This advanced example illustrates how `als-layout` can be used to handle complex scenarios and requirements in web development, enhancing the flexibility and power at your disposal.
|
|
170
137
|
|
|
138
|
+
|
|
139
|
+
## Response with status
|
|
140
|
+
|
|
141
|
+
In version 6, added two methods:
|
|
142
|
+
1. `status(statusCode)` - adds the statusCode to `__status` property
|
|
143
|
+
2. `end(res)`
|
|
144
|
+
1. writes head with `__status, { 'Content-Type': 'text/html' }`
|
|
145
|
+
2. runs `res.end(this.rawHtml)`
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
The main idea, is to add quick way to response with layout.
|
|
171
149
|
## API
|
|
172
150
|
|
|
173
151
|
### Constructor
|
|
@@ -193,16 +171,17 @@ Returns the inner HTML of the document.
|
|
|
193
171
|
|
|
194
172
|
Creates a clone of the current layout instance, preserving `options`.
|
|
195
173
|
|
|
174
|
+
|
|
175
|
+
#### `__status`
|
|
176
|
+
|
|
177
|
+
Stores the statusCode for `end` method.
|
|
178
|
+
|
|
196
179
|
### Methods
|
|
197
180
|
|
|
198
181
|
#### `lang(lang: string): this`
|
|
199
182
|
|
|
200
183
|
Sets the `lang` attribute on the `<html>` element.
|
|
201
184
|
|
|
202
|
-
#### `onload(): this`
|
|
203
|
-
|
|
204
|
-
Adds an `onload` script to the document. If called multiple times, the script is only added once.
|
|
205
|
-
|
|
206
185
|
#### `title(title: string): this`
|
|
207
186
|
|
|
208
187
|
Sets the document title and creates an Open Graph title meta tag.
|
|
@@ -256,3 +235,11 @@ Adds a `<script>` tag to the document.
|
|
|
256
235
|
#### `link(href: string, attributes: object = { rel: "stylesheet", type: "text/css" }): this`
|
|
257
236
|
|
|
258
237
|
Adds a `<link>` tag for external stylesheets.
|
|
238
|
+
|
|
239
|
+
#### `status(statusCode:number): this`
|
|
240
|
+
|
|
241
|
+
Adds the statusCode to `__status` property
|
|
242
|
+
|
|
243
|
+
#### `end(res):undefined`
|
|
244
|
+
|
|
245
|
+
Response with res(rawHtml)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const { readFileSync, writeFileSync, readdirSync } = require('fs')
|
|
2
2
|
const { join } = require('path')
|
|
3
3
|
|
|
4
|
-
const docsDir = join(__dirname, 'docs')
|
|
4
|
+
const docsDir = join(__dirname, '..','docs')
|
|
5
5
|
const files = readdirSync(docsDir)
|
|
6
6
|
const content = files.map(file => readFileSync(join(docsDir, file), 'utf-8')).join('\n');
|
|
7
|
-
writeFileSync(join(__dirname, 'readme.md'), content, 'utf-8')
|
|
7
|
+
writeFileSync(join(__dirname, '..','readme.md'), content, 'utf-8')
|
|
8
8
|
|
|
@@ -30,12 +30,6 @@ describe('Layout Integrative tests', () => {
|
|
|
30
30
|
assert(layout.root.$('title') === null, 'Title element was not removed correctly');
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
it('onload', () => {
|
|
34
|
-
layout.onload()
|
|
35
|
-
const script = layout.root.$('script')
|
|
36
|
-
assert(script.innerHTML.includes(`document.addEventListener('DOMContentLoaded'`))
|
|
37
|
-
})
|
|
38
|
-
|
|
39
33
|
});
|
|
40
34
|
|
|
41
35
|
describe('HTML Structure Initialization', () => {
|
|
@@ -81,7 +75,7 @@ describe('Clone Testing', () => {
|
|
|
81
75
|
|
|
82
76
|
it('should clone the layout correctly', () => {
|
|
83
77
|
const clone = layout.clone;
|
|
84
|
-
console.log(clone.constructor.name)
|
|
78
|
+
// console.log(clone.constructor.name)
|
|
85
79
|
assert(clone instanceof Layout, 'Clone should be an instance of Layout');
|
|
86
80
|
assert.notStrictEqual(clone, layout, 'Clone should not be the same instance as the original');
|
|
87
81
|
});
|
package/lib/onload.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
const onloadScript = /*js*/`document.addEventListener('DOMContentLoaded', function() {
|
|
2
|
-
const elements = document.querySelectorAll('[onload]');
|
|
3
|
-
elements.forEach(element => {
|
|
4
|
-
const onloadCode = element.getAttribute('onload');
|
|
5
|
-
const func = Function('"use strict"; return function() { ' + onloadCode + ' }');
|
|
6
|
-
func().call(element);
|
|
7
|
-
element.removeAttribute('onload');
|
|
8
|
-
});
|
|
9
|
-
});`;
|
|
10
|
-
|
|
11
|
-
module.exports = onloadScript
|
|
File without changes
|
|
File without changes
|