@tkeron/html-parser 0.1.3 → 0.1.5
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 +5 -5
- package/bun.lock +4 -4
- package/index.ts +0 -5
- package/package.json +1 -1
- package/src/css-selector.ts +0 -5
- package/src/dom-simulator.ts +122 -45
- package/src/tokenizer.ts +0 -20
- package/tests/advanced.test.ts +2 -2
- package/tests/cloneNode.test.ts +587 -0
- package/tests/custom-elements.test.ts +8 -8
- package/tests/official/acid/acid-tests.test.ts +6 -6
- package/tests/official/final-output/final-output.test.ts +15 -15
- package/tests/official/html5lib/tokenizer-utils.ts +19 -31
- package/tests/official/html5lib/tokenizer.test.ts +4 -4
- package/tests/official/html5lib/tree-construction-utils.ts +20 -34
- package/tests/official/html5lib/tree-construction.test.ts +5 -5
- package/tests/official/validator/validator-tests.test.ts +11 -11
- package/tests/official/wpt/wpt-tests.test.ts +5 -5
- package/tests/outerHTML-replacement.test.ts +208 -0
- package/tests/parser.test.ts +1 -1
- package/tests/test-page-0.txt +12 -355
- package/tests/api-integration.test.ts +0 -114
- package/tests/dom-adoption.test.ts +0 -363
- package/tests/dom-synchronization.test.ts +0 -675
- package/tests/setAttribute-outerHTML.test.ts +0 -102
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { describe, it, expect } from "bun:test";
|
|
2
|
+
import { parseHTML } from "../index";
|
|
3
|
+
|
|
4
|
+
describe("outerHTML replacement - Browser behavior", () => {
|
|
5
|
+
it("should replace element with its innerHTML when setting outerHTML = innerHTML", () => {
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const doc = parseHTML(`
|
|
11
|
+
<html>
|
|
12
|
+
<body>
|
|
13
|
+
<div id="mi-prueba" style="border: 2px solid red; padding: 10px;">
|
|
14
|
+
<strong>Lorem ipsum!</strong> Dolor sit amet consectetur.
|
|
15
|
+
</div>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
18
|
+
`);
|
|
19
|
+
|
|
20
|
+
const elem = doc.querySelector("#mi-prueba");
|
|
21
|
+
expect(elem).not.toBeNull();
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
const innerHTML = elem!.innerHTML;
|
|
25
|
+
expect(innerHTML).toContain("<strong>Lorem ipsum!</strong>");
|
|
26
|
+
expect(innerHTML).toContain("Dolor sit amet consectetur.");
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
const parent = elem!.parentNode;
|
|
30
|
+
expect(parent).not.toBeNull();
|
|
31
|
+
expect(parent!.childNodes).toContain(elem);
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
elem!.outerHTML = innerHTML;
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
const elemAfter = doc.querySelector("#mi-prueba");
|
|
38
|
+
expect(elemAfter).toBeNull();
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
const body = doc.querySelector("body");
|
|
42
|
+
expect(body!.innerHTML).toContain("<strong>Lorem ipsum!</strong>");
|
|
43
|
+
expect(body!.innerHTML).toContain("Dolor sit amet consectetur.");
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
expect(body!.innerHTML).not.toContain('id="mi-prueba"');
|
|
47
|
+
expect(body!.innerHTML).not.toContain('style=');
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should replace element with simple text content", () => {
|
|
51
|
+
const doc = parseHTML(`
|
|
52
|
+
<div>
|
|
53
|
+
<p id="paragraph" class="styled">Simple text</p>
|
|
54
|
+
</div>
|
|
55
|
+
`);
|
|
56
|
+
|
|
57
|
+
const paragraph = doc.querySelector("#paragraph");
|
|
58
|
+
expect(paragraph).not.toBeNull();
|
|
59
|
+
|
|
60
|
+
const parent = paragraph!.parentNode;
|
|
61
|
+
const innerHTML = paragraph!.innerHTML;
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
paragraph!.outerHTML = innerHTML;
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
expect(doc.querySelector("#paragraph")).toBeNull();
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
expect(parent!.textContent).toContain("Simple text");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should replace element with multiple child nodes", () => {
|
|
74
|
+
const doc = parseHTML(`
|
|
75
|
+
<ul>
|
|
76
|
+
<li id="item-container">
|
|
77
|
+
<span>Item 1</span>
|
|
78
|
+
<span>Item 2</span>
|
|
79
|
+
</li>
|
|
80
|
+
</ul>
|
|
81
|
+
`);
|
|
82
|
+
|
|
83
|
+
const container = doc.querySelector("#item-container");
|
|
84
|
+
expect(container).not.toBeNull();
|
|
85
|
+
|
|
86
|
+
const ul = doc.querySelector("ul");
|
|
87
|
+
const innerHTML = container!.innerHTML;
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
container!.outerHTML = innerHTML;
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
expect(doc.querySelector("#item-container")).toBeNull();
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
const spans = ul!.querySelectorAll("span");
|
|
97
|
+
expect(spans.length).toBe(2);
|
|
98
|
+
expect(spans[0]?.textContent).toBe("Item 1");
|
|
99
|
+
expect(spans[1]?.textContent).toBe("Item 2");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("should replace element with empty string", () => {
|
|
103
|
+
const doc = parseHTML(`
|
|
104
|
+
<div>
|
|
105
|
+
<span id="to-remove"></span>
|
|
106
|
+
</div>
|
|
107
|
+
`);
|
|
108
|
+
|
|
109
|
+
const span = doc.querySelector("#to-remove");
|
|
110
|
+
expect(span).not.toBeNull();
|
|
111
|
+
|
|
112
|
+
const parent = span!.parentNode;
|
|
113
|
+
const childCountBefore = parent!.childNodes.length;
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
span!.outerHTML = "";
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
expect(doc.querySelector("#to-remove")).toBeNull();
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
expect(parent!.childNodes.length).toBe(childCountBefore - 1);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("should replace element with new HTML structure", () => {
|
|
126
|
+
const doc = parseHTML(`
|
|
127
|
+
<div>
|
|
128
|
+
<p id="old">Old content</p>
|
|
129
|
+
</div>
|
|
130
|
+
`);
|
|
131
|
+
|
|
132
|
+
const oldParagraph = doc.querySelector("#old");
|
|
133
|
+
expect(oldParagraph).not.toBeNull();
|
|
134
|
+
|
|
135
|
+
const parent = oldParagraph!.parentNode;
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
oldParagraph!.outerHTML = '<div id="new">New content</div>';
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
expect(doc.querySelector("#old")).toBeNull();
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
const newDiv = doc.querySelector("#new");
|
|
145
|
+
expect(newDiv).not.toBeNull();
|
|
146
|
+
expect(newDiv!.textContent).toBe("New content");
|
|
147
|
+
expect(newDiv!.parentNode).toBe(parent);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("should maintain sibling relationships after outerHTML replacement", () => {
|
|
151
|
+
const doc = parseHTML(`
|
|
152
|
+
<div>
|
|
153
|
+
<span>First</span>
|
|
154
|
+
<p id="middle">Middle</p>
|
|
155
|
+
<span>Last</span>
|
|
156
|
+
</div>
|
|
157
|
+
`);
|
|
158
|
+
|
|
159
|
+
const middle = doc.querySelector("#middle");
|
|
160
|
+
const firstSpan = doc.querySelectorAll("span")[0];
|
|
161
|
+
const lastSpan = doc.querySelectorAll("span")[1];
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
middle!.outerHTML = middle!.innerHTML;
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
expect(firstSpan!.nextSibling).not.toBe(middle);
|
|
168
|
+
expect(lastSpan!.previousSibling).not.toBe(middle);
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
const parent = firstSpan!.parentNode;
|
|
172
|
+
expect(parent!.textContent).toContain("Middle");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("should handle complex nested HTML replacement", () => {
|
|
176
|
+
const doc = parseHTML(`
|
|
177
|
+
<article>
|
|
178
|
+
<section id="wrapper" class="container" data-id="123">
|
|
179
|
+
<h2>Title</h2>
|
|
180
|
+
<p>Paragraph <strong>bold</strong> text</p>
|
|
181
|
+
<ul>
|
|
182
|
+
<li>Item 1</li>
|
|
183
|
+
<li>Item 2</li>
|
|
184
|
+
</ul>
|
|
185
|
+
</section>
|
|
186
|
+
</article>
|
|
187
|
+
`);
|
|
188
|
+
|
|
189
|
+
const wrapper = doc.querySelector("#wrapper");
|
|
190
|
+
expect(wrapper).not.toBeNull();
|
|
191
|
+
|
|
192
|
+
const article = doc.querySelector("article");
|
|
193
|
+
const innerHTML = wrapper!.innerHTML;
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
wrapper!.outerHTML = innerHTML;
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
expect(doc.querySelector("#wrapper")).toBeNull();
|
|
200
|
+
expect(doc.querySelector("section")).toBeNull();
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
expect(article!.querySelector("h2")).not.toBeNull();
|
|
204
|
+
expect(article!.querySelector("h2")!.textContent).toBe("Title");
|
|
205
|
+
expect(article!.querySelector("strong")).not.toBeNull();
|
|
206
|
+
expect(article!.querySelectorAll("li").length).toBe(2);
|
|
207
|
+
});
|
|
208
|
+
});
|
package/tests/parser.test.ts
CHANGED
|
@@ -326,7 +326,7 @@ describe('HTML Parser', () => {
|
|
|
326
326
|
)!;
|
|
327
327
|
expect(htmlElement.type).toBe(ASTNodeType.ELEMENT);
|
|
328
328
|
expect(htmlElement.tagName).toBe('html');
|
|
329
|
-
expect(htmlElement.attributes!.lang).toBe('
|
|
329
|
+
expect(htmlElement.attributes!.lang).toBe('en');
|
|
330
330
|
const headElement = htmlElement.children!.find(
|
|
331
331
|
child => child.type === ASTNodeType.ELEMENT && child.tagName === 'head'
|
|
332
332
|
)!;
|
package/tests/test-page-0.txt
CHANGED
|
@@ -1,362 +1,19 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
2
|
<!DOCTYPE html>
|
|
4
|
-
<html lang="
|
|
3
|
+
<html lang="en"><head>
|
|
5
4
|
<meta charset="UTF-8">
|
|
6
|
-
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
7
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
8
|
-
<title>
|
|
9
|
-
<style
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
background-size: cover;
|
|
19
|
-
|
|
20
|
-
position: relative;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
.container .content {
|
|
24
|
-
max-width: 1280px;
|
|
25
|
-
width: 100%;
|
|
26
|
-
z-index: 1;
|
|
27
|
-
padding: 0px 15px;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
.container .bg_color {
|
|
31
|
-
position: absolute;
|
|
32
|
-
|
|
33
|
-
top: 0px;
|
|
34
|
-
left: 0px;
|
|
35
|
-
|
|
36
|
-
height: 100%;
|
|
37
|
-
width: 100%;
|
|
38
|
-
|
|
39
|
-
z-index: 0;
|
|
40
|
-
}
|
|
41
|
-
</style><style>a.a {
|
|
42
|
-
text-decoration: none;
|
|
43
|
-
}
|
|
44
|
-
</style><style>.logo {
|
|
45
|
-
height: 62%;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
a.logo {
|
|
49
|
-
height: 100%;
|
|
50
|
-
}
|
|
51
|
-
</style><style>.footer {
|
|
52
|
-
padding-top: 80px;
|
|
53
|
-
width: 100%;
|
|
54
|
-
background: #060303;
|
|
55
|
-
color: #eed;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.footer a {
|
|
59
|
-
text-decoration: none;
|
|
60
|
-
color: #eed;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.footer a:visited {
|
|
64
|
-
color: inherit;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.footer a {
|
|
68
|
-
grid-area: a;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.footer .copyright {
|
|
72
|
-
grid-area: c;
|
|
73
|
-
justify-self: center;
|
|
74
|
-
margin-top: 40px;
|
|
75
|
-
font-size: 12px;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
.footer .content {
|
|
79
|
-
display: grid;
|
|
80
|
-
grid-template:
|
|
81
|
-
"l a1 a4"
|
|
82
|
-
"l a2 a5"
|
|
83
|
-
"l a3 a6"
|
|
84
|
-
"l a7 a8"
|
|
85
|
-
"c c c";
|
|
86
|
-
justify-content: space-between;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
.footer .logo {
|
|
90
|
-
max-width: 250px;
|
|
91
|
-
height: auto;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.footer a.logo {
|
|
95
|
-
grid-area: l;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
@media screen and (max-width: 800px) {
|
|
99
|
-
.footer .content {
|
|
100
|
-
display: grid;
|
|
101
|
-
grid-template:
|
|
102
|
-
"a1 a4"
|
|
103
|
-
"a2 a5"
|
|
104
|
-
"a3 a6"
|
|
105
|
-
"a7 a8"
|
|
106
|
-
"l l"
|
|
107
|
-
"c c";
|
|
108
|
-
justify-content: space-evenly;
|
|
109
|
-
}
|
|
110
|
-
.footer .logo {
|
|
111
|
-
margin-top: 20px;
|
|
112
|
-
justify-self: center;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
</style><style>.top {
|
|
116
|
-
background: linear-gradient(0deg, #060303, #6c6868);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.container.top .content {
|
|
120
|
-
display: flex;
|
|
121
|
-
flex-direction: row;
|
|
122
|
-
justify-content: space-between;
|
|
123
|
-
align-items: center;
|
|
124
|
-
|
|
125
|
-
height: 90px;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
.container.top .content a {
|
|
129
|
-
display: flex;
|
|
130
|
-
|
|
131
|
-
align-items: center;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
@media screen and (max-width: 600px) {
|
|
135
|
-
.container.top .content {
|
|
136
|
-
height: 60px;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
</style><style>
|
|
140
|
-
.sectionMenu .content {
|
|
141
|
-
width: 100%;
|
|
142
|
-
display: flex;
|
|
143
|
-
justify-content: space-between;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
padding-top: 10px;
|
|
147
|
-
padding-bottom: 10px;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
font-weight: bold;
|
|
151
|
-
font-size: 20px;
|
|
152
|
-
color: #fff;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
.sectionMenu {
|
|
156
|
-
background: linear-gradient(0deg, #f2be11, #87722d);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.sectionMenu a.a {
|
|
160
|
-
color: white;
|
|
161
|
-
}
|
|
162
|
-
</style><style></style><style>.button,
|
|
163
|
-
button {
|
|
164
|
-
border: none;
|
|
165
|
-
padding: 20px 35px;
|
|
166
|
-
font-size: 30px;
|
|
167
|
-
font-weight: bold;
|
|
168
|
-
border-radius: 15px;
|
|
169
|
-
cursor: pointer;
|
|
170
|
-
|
|
171
|
-
color: #1c1c1c;
|
|
172
|
-
background: #f2be11;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
a.button {
|
|
176
|
-
display: block;
|
|
177
|
-
width: fit-content;
|
|
178
|
-
margin: 20px;
|
|
179
|
-
text-align: center;
|
|
180
|
-
}
|
|
181
|
-
</style><style>.container.header .content {
|
|
182
|
-
display: grid;
|
|
183
|
-
grid-template-areas:
|
|
184
|
-
"h1 h1"
|
|
185
|
-
"im h2";
|
|
186
|
-
grid-template-columns: 1fr 720px;
|
|
187
|
-
gap: 30px;
|
|
188
|
-
margin: 50px auto;
|
|
189
|
-
color: #eed;
|
|
190
|
-
}
|
|
191
|
-
.service .content {
|
|
192
|
-
display: grid;
|
|
193
|
-
grid-template-areas:
|
|
194
|
-
"h3 h3"
|
|
195
|
-
"h4 h4"
|
|
196
|
-
"a p";
|
|
197
|
-
grid-template-columns: 1fr 720px;
|
|
198
|
-
gap: 30px;
|
|
199
|
-
margin: 100px auto;
|
|
200
|
-
color: #4c4c4c;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
.header .content h1 {
|
|
204
|
-
grid-area: h1;
|
|
205
|
-
}
|
|
206
|
-
.header .content h2 {
|
|
207
|
-
grid-area: h2;
|
|
208
|
-
}
|
|
209
|
-
.header .content img {
|
|
210
|
-
grid-area: im;
|
|
211
|
-
width: 100%;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
.service .content a {
|
|
215
|
-
display: flex;
|
|
216
|
-
grid-area: a;
|
|
217
|
-
justify-content: center;
|
|
218
|
-
align-items: center;
|
|
219
|
-
margin-left: 0;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
.service .content h3 {
|
|
223
|
-
grid-area: h3;
|
|
224
|
-
max-width: 720px;
|
|
225
|
-
}
|
|
226
|
-
.service .content h4 {
|
|
227
|
-
grid-area: h4;
|
|
228
|
-
max-width: 720px;
|
|
229
|
-
}
|
|
230
|
-
.service .content p {
|
|
231
|
-
grid-area: p;
|
|
232
|
-
max-width: 720px;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
.service.service4,
|
|
236
|
-
.service.service2 {
|
|
237
|
-
background: #4c4c4c;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
.service.service4 .content,
|
|
241
|
-
.service.service2 .content {
|
|
242
|
-
color: #eed;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
.container.cta .content {
|
|
246
|
-
display: flex;
|
|
247
|
-
justify-content: center;
|
|
248
|
-
align-items: center;
|
|
249
|
-
flex-direction: column;
|
|
250
|
-
margin: 100px auto;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
@media screen and (max-width: 1280px) {
|
|
254
|
-
.service .content {
|
|
255
|
-
grid-template-areas:
|
|
256
|
-
"h3 h3"
|
|
257
|
-
"h4 h4"
|
|
258
|
-
". p"
|
|
259
|
-
"a a";
|
|
260
|
-
grid-template-columns: 1fr;
|
|
261
|
-
gap: 30px;
|
|
262
|
-
}
|
|
263
|
-
.service .content a {
|
|
264
|
-
justify-content: center;
|
|
265
|
-
align-items: center;
|
|
266
|
-
margin-left: 0;
|
|
267
|
-
justify-self: end;
|
|
268
|
-
margin-right: 0;
|
|
269
|
-
}
|
|
270
|
-
.container.header .content {
|
|
271
|
-
display: grid;
|
|
272
|
-
grid-template-areas:
|
|
273
|
-
"h1 h1"
|
|
274
|
-
"im h2";
|
|
275
|
-
grid-template-columns: 1fr 2fr;
|
|
276
|
-
gap: 30px;
|
|
277
|
-
margin: 50px auto;
|
|
278
|
-
color: #eed;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
@media screen and (max-width: 700px) {
|
|
282
|
-
.container.header .content {
|
|
283
|
-
grid-template-areas:
|
|
284
|
-
"h1"
|
|
285
|
-
"h2";
|
|
286
|
-
grid-template-columns: 1fr;
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
</style><style>* {
|
|
290
|
-
margin: 0;
|
|
291
|
-
padding: 0;
|
|
292
|
-
box-sizing: border-box;
|
|
293
|
-
transition: all 0.3s ease;
|
|
294
|
-
font-family: inherit;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
body,
|
|
298
|
-
button {
|
|
299
|
-
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
|
300
|
-
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
|
301
|
-
|
|
302
|
-
line-height: 1.6;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
h1 {
|
|
306
|
-
font-size: 60px;
|
|
307
|
-
}
|
|
308
|
-
h2 {
|
|
309
|
-
font-size: 42px;
|
|
310
|
-
}
|
|
311
|
-
h3 {
|
|
312
|
-
font-size: 32px;
|
|
313
|
-
}
|
|
314
|
-
h4 {
|
|
315
|
-
font-size: 20px;
|
|
316
|
-
}
|
|
317
|
-
p {
|
|
318
|
-
font-size: 18px;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
@media screen and (max-width: 1200px) {
|
|
322
|
-
h1 {
|
|
323
|
-
font-size: 42px;
|
|
324
|
-
}
|
|
325
|
-
h2 {
|
|
326
|
-
font-size: 32px;
|
|
327
|
-
}
|
|
328
|
-
h3 {
|
|
329
|
-
font-size: 20px;
|
|
330
|
-
}
|
|
331
|
-
h4 {
|
|
332
|
-
font-size: 18px;
|
|
333
|
-
}
|
|
334
|
-
p {
|
|
335
|
-
font-size: 16px;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
@media screen and (max-width: 800px) {
|
|
340
|
-
h1 {
|
|
341
|
-
font-size: 32px;
|
|
342
|
-
}
|
|
343
|
-
h2 {
|
|
344
|
-
font-size: 20px;
|
|
345
|
-
}
|
|
346
|
-
h3 {
|
|
347
|
-
font-size: 18px;
|
|
348
|
-
}
|
|
349
|
-
h4{
|
|
350
|
-
font-size: 16px;
|
|
351
|
-
}
|
|
352
|
-
p {
|
|
353
|
-
font-size: 14px;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
</style><script id='tkeron_page_js'>(()=>{})();
|
|
357
|
-
</script></head>
|
|
6
|
+
<title>Lorem Ipsum Services</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
+
body { font-family: Arial, sans-serif; line-height: 1.6; }
|
|
10
|
+
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
|
|
11
|
+
.header { background: #333; color: #fff; text-align: center; padding: 60px 20px; }
|
|
12
|
+
.service { margin: 40px 0; padding: 20px; border: 1px solid #ddd; }
|
|
13
|
+
.footer { background: #f4f4f4; padding: 20px; text-align: center; margin-top: 40px; }
|
|
14
|
+
</style>
|
|
15
|
+
</head>
|
|
358
16
|
|
|
359
17
|
<body>
|
|
360
18
|
|
|
361
|
-
<div class="container top"><div class="content"><a class="a logo" href="/"><img class="logo" src="/./
|
|
362
|
-
</body></html>
|
|
19
|
+
<div class="container top"><div class="content"><a class="a logo" href="/"><img class="logo" src="/./logo.png"></a></div></div><div class="container sectionMenu"><div class="content"><a class="a" href="/">HOME</a><a class="a" href="/services">SERVICES</a><a class="a" href="/contact">CONTACT</a></div></div><div class="container header" style="background-image: url("/./bg.png");"><div class="bg_color" style="background: rgba(0, 0, 0, 0.4);"></div><div class="content"><h1>Our Services</h1><h2>Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor.</h2></div></div><div class="container service service1"><div class="content"><h3>Custom Lorem Applications</h3><h4>Lorem ipsum dolor sit amet consectetur adipiscing elit.</h4><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p><a class="a button" href="/contact/custom-apps">Request custom app</a></div></div><div class="container service service2"><div class="content"><h3>Process Automation</h3><h4>Ut enim ad minim veniam quis nostrud exercitation ullamco.</h4><p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p><a class="a button" href="/contact/automation">Automate processes</a></div></div><div class="container service service3"><div class="content"><h3>Custom Widgets</h3><h4>Excepteur sint occaecat cupidatat non proident sunt in culpa.</h4><p>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium.</p><a class="a button" href="/contact/widgets">Add custom widget</a></div></div><div class="container service service4"><div class="content"><h3>API Integrations</h3><h4>Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut.</h4><p>At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium.</p><a class="a button" href="/contact/api">Integrate APIs</a></div></div><div class="container service service5"><div class="content"><h3>Other Custom Needs</h3><h4>Quis autem vel eum iure reprehenderit qui in ea voluptate.</h4><p>Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates.</p><a class="a button" href="/contact/other">Propose your idea</a></div></div><div class="container footer"><div class="content"><a class="a logo" href="/"><img class="logo" src="/./logo.png"></a><div class="copyright">© 2025 LOREM IPSUM TECH. All rights reserved.</div><a class="a" href="/" style="grid-area: a1;">Home</a><a class="a" href="/services" style="grid-area: a2;">Services</a><a class="a" href="/contact" style="grid-area: a3;">Contact</a><a class="a" href="/privacy-policy" style="grid-area: a4;">Privacy Policy</a><a class="a" href="/terms" style="grid-area: a5;">Terms</a><a class="a" href="/cookies" style="grid-area: a6;">Cookies Policy</a></div></div><script defer src="https://static.cloudflareinsights.com/beacon.min.js/vcd15cbe7772f49c399c6a5babf22c1241717689176015" integrity="sha512-ZpsOmlRQV6y907TI0dKBHq9Md29nnaEIPlkf84rnaERnq6zvWvPUqr2ft8M1aS28oN72PdrCzSjY4U6VaAw1EQ==" data-cf-beacon='{"rayId":"955eb8668eaacdc9","version":"2025.6.2","r":1,"token":"09161630e3ae4c94992e51777c351c88","serverTiming":{"name":{"cfExtPri":true,"cfEdge":true,"cfOrigin":true,"cfL4":true,"cfSpeedBrain":true,"cfCacheStatus":true}}}' crossorigin="anonymous"></script>
|