├── CNAME
├── _config.yml
├── .travis.yml
├── test
├── qualcomm.test.js
├── twitter.test.js
├── test.js
├── fixture-qualcomm.html
└── fixture-twitter.html
├── LICENSE
├── CHANGELOG.md
├── .gitignore
├── hypertag.js
├── benchmark.js
├── package.json
└── README.md
/CNAME:
--------------------------------------------------------------------------------
1 | hypertag.js.org
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "node"
4 | - "lts/*"
5 | after_success:
6 | - bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports"
7 | - nyc report --reporter=text-lcov | coveralls
8 |
--------------------------------------------------------------------------------
/test/qualcomm.test.js:
--------------------------------------------------------------------------------
1 | const {readFileSync} = require('node:fs')
2 | const {resolve} = require('node:path')
3 | const test = require('ava')
4 | const parse = require('../hypertag.js')
5 | const {stripComments} = require('../hypertag.js')
6 |
7 | const html = readFileSync(resolve(__dirname, './fixture-qualcomm.html'), 'utf-8')
8 |
9 | test('it can strip comments from developer.qualcomm.com', t => {
10 | t.notThrows(() => stripComments(html))
11 | })
12 |
13 | test('it can parse developer.qualcomm.com', t => {
14 | t.notThrows(() => parse(html))
15 | })
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Andreas Pizsa
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ### [0.0.6](https://github.com/AndreasPizsa/hypertag/compare/v0.0.5...v0.0.6) (2022-03-17)
6 |
7 |
8 | ### Bug Fixes
9 |
10 | * return empty string for empty attributes (“”, ‘’) ([44288f3](https://github.com/AndreasPizsa/hypertag/commit/44288f3b4a47ffd3d560eae99827f3c25c6e430b))
11 |
12 | ### [0.0.5](https://github.com/AndreasPizsa/hypertag/compare/v0.0.4...v0.0.5) (2022-03-08)
13 |
14 |
15 | ### Bug Fixes
16 |
17 | * stripComments bug that caused infinite loop ([dbd9819](https://github.com/AndreasPizsa/hypertag/commit/dbd98192908542ad1f71f05c438b052fe4ba7197))
18 |
19 | ### [0.0.4](https://github.com/AndreasPizsa/hypertag/compare/v0.0.3...v0.0.4) (2022-02-08)
20 |
21 |
22 | ### Bug Fixes
23 |
24 | * ensure dist contains current code ([112c75d](https://github.com/AndreasPizsa/hypertag/commit/112c75dd71cfb94d29babfa2ce699ee01bb3bd44))
25 |
26 |
27 | ## [0.0.3](https://github.com/AndreasPizsa/hypertag/compare/v0.0.2...v0.0.3) (2019-05-28)
28 |
29 |
30 |
31 |
32 | ## 0.0.1 (2018-12-07)
33 |
--------------------------------------------------------------------------------
/test/twitter.test.js:
--------------------------------------------------------------------------------
1 | const {readFile} = require('node:fs/promises')
2 | const {resolve} = require('node:path')
3 | const test = require('ava')
4 | const parse = require('../hypertag.js')
5 |
6 | test('correctly parses relevant tags from twitter.com', async t => {
7 | const text = await readFile(resolve(__dirname, './fixture-twitter.html'), 'utf-8')
8 | const tags = parse(text, 'link')
9 |
10 | const hreflangs = 'x-default en ar ar-x-fm bg bn ca cs da de el en-GB es eu fa fi fr ga gl gu he hi hr hu id it ja kn ko mr ms nb nl pl pt ro ro sk sr sv ta th tr uk ur vi zh zh-Hant'.split(/\s+/)
11 | const languageLinks = tags.filter(({rel}) => rel === 'alternate')
12 | t.is(hreflangs.length, languageLinks.length)
13 |
14 | const iconLinks = tags.filter(({rel}) => /\bicon\b/i.test(rel))
15 | t.deepEqual(iconLinks, [
16 | {
17 | '<' : 'link',
18 | rel : 'mask-icon',
19 | sizes: 'any',
20 | href : 'https://abs.twimg.com/responsive-web/client-web-legacy/icon-svg.168b89d5.svg',
21 | color: '#1D9BF0'
22 | },
23 | {
24 | '<' : 'link',
25 | rel : 'shortcut icon',
26 | href: '//abs.twimg.com/favicons/twitter.2.ico'
27 | },
28 | {
29 | '<' : 'link',
30 | rel : 'apple-touch-icon',
31 | sizes: '192x192',
32 | href : 'https://abs.twimg.com/responsive-web/client-web-legacy/icon-ios.b1fc7275.png'
33 | }
34 | ])
35 | })
36 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # Bower dependency directory (https://bower.io/)
27 | bower_components
28 |
29 | # node-waf configuration
30 | .lock-wscript
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # Optional npm cache directory
43 | .npm
44 |
45 | # Optional eslint cache
46 | .eslintcache
47 |
48 | # Optional REPL history
49 | .node_repl_history
50 |
51 | # Output of 'npm pack'
52 | *.tgz
53 |
54 | # Yarn Integrity file
55 | .yarn-integrity
56 |
57 | # dotenv environment variables file
58 | .env
59 |
60 | # parcel-bundler cache (https://parceljs.org/)
61 | .cache
62 |
63 | # next.js build output
64 | .next
65 |
66 | # nuxt.js build output
67 | .nuxt
68 |
69 | # vuepress build output
70 | .vuepress/dist
71 |
72 | # Serverless directories
73 | .serverless/
74 |
75 | # FuseBox cache
76 | .fusebox/
77 |
78 | #DynamoDB Local files
79 | .dynamodb/
80 |
81 | dist/
82 |
--------------------------------------------------------------------------------
/hypertag.js:
--------------------------------------------------------------------------------
1 | const attrPattern = /([\w\-_]+)\s*(:?=\s*((?:(['"])(.*?)\4)|[^\s>]+))?/ms
2 |
3 | module.exports = parse
4 | Object.assign(module.exports, {
5 | parse,
6 | parseAttrs,
7 | stripComments,
8 | extend,
9 | })
10 |
11 | function extend(tags, options) {
12 | options = {...options}
13 | return source => parse(source, tags, options)
14 | }
15 |
16 | function parse(source, tags, options) {
17 | tags = Array.isArray(tags) ? tags : [tags]
18 | tags = tags.map(tag => tag === '*' ? '[^/\\s>]+' : tag)
19 | options = {
20 | tagKey: '<',
21 | ...options,
22 | }
23 |
24 | const pattern = new RegExp(`<(?:${tags.join('|')})(?:\\s+(.*?))?>`, 'igms')
25 | return (stripComments(source).match(pattern) || [])
26 | .map(tag => parseAttrs(tag, options.tagKey))
27 | .filter(x => x)
28 | }
29 |
30 | function parseAttrs(htmlTagText, tagKey = '<') {
31 | const attrs = {}
32 |
33 | const matchPattern = new RegExp(attrPattern, 'gims')
34 | let match = matchPattern.exec(htmlTagText)
35 | if (!match) {
36 | return
37 | }
38 | attrs[tagKey] = match[1]
39 |
40 | while ((match = matchPattern.exec(htmlTagText)) !== null) {
41 | const key = match[1]
42 | attrs[key]
43 | = match[5] !== undefined
44 | ? match[5]
45 | : (match[3] !== undefined
46 | ? match[3]
47 | : true)
48 | }
49 | return attrs
50 | }
51 |
52 | function stripComments(html) {
53 | // eslint-disable-next-line unicorn/better-regex
54 | return html.replace(/)/gms, '')
55 | }
56 |
--------------------------------------------------------------------------------
/benchmark.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 |
3 | let html = `
4 |
5 |
6 |
7 |
8 |
Hello, world!
9 |
10 | `
11 |
12 | const Benchmark = require('benchmark')
13 | const got = require('got')
14 | const htmlTagParser = require('html-tag-parser')
15 | const cheerio = require('cheerio')
16 | const htmlparser2 = require('htmlparser2')
17 | const htmlParseStringify2 = require('html-parse-stringify2')
18 | const fastHtml = require('fast-html')
19 | const parse5 = require('parse5')
20 | const hypertag = require('./hypertag.js')
21 |
22 | const suite = (new Benchmark.Suite())
23 | .add('hypertag', () => {
24 | hypertag(html, 'meta')
25 | })
26 | .add('fast-html', () => {
27 | fastHtml({parseAttributes: true}).parse(html)
28 | })
29 | .add('parse5', () => parse5.parse(html))
30 | .add('htmlparser2', () => {
31 | const parser = new htmlparser2.Parser()
32 | parser.parseComplete(html)
33 | })
34 | .add('html-tag-parser', () => {
35 | htmlTagParser(html, 'meta')
36 | })
37 | .add('cheerio', () => {
38 | cheerio.load(html)('meta')
39 | })
40 | .add('html-parse-stringify2', () => {
41 | htmlParseStringify2.parse(html)
42 | })
43 | .on('cycle', event => {
44 | console.log(String(event.target))
45 | })
46 | .on('complete', function () {
47 | console.log('Fastest is ' + this.filter('fastest').map('name'))
48 | })
49 |
50 | got('https://apple.com/').then(result => {
51 | html = result.body
52 | suite.run({async: true})
53 | })
54 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hypertag",
3 | "version": "0.0.6",
4 | "main": "dist/index.js",
5 | "umd:main": "dist/index.umd.js",
6 | "module": "dist/index.mjs",
7 | "source": "hypertag.js",
8 | "scripts": {
9 | "build": "rimraf dist && microbundle",
10 | "test": "nyc ava",
11 | "prepublish": "npm test && npm run build",
12 | "pretest": "xo",
13 | "release": "standard-version"
14 | },
15 | "keywords": [],
16 | "author": "Andreas Pizsa (https://github.com/AndreasPizsa)",
17 | "repository": "AndreasPizsa/hypertag",
18 | "license": "MIT",
19 | "devDependencies": {
20 | "@andreaspizsa/eslint-config-xo": "^0.2.0",
21 | "@commitlint/cli": "^9.1.2",
22 | "@commitlint/config-conventional": "^7.1.2",
23 | "ava": "^4.0.1",
24 | "benchmark": "^2.1.4",
25 | "cheerio": "^1.0.0-rc.2",
26 | "coveralls": "^3.0.2",
27 | "fast-html": "^0.1.2",
28 | "got": "^9.3.2",
29 | "html-parse-stringify2": "^2.0.1",
30 | "html-tag-parser": "^1.0.0",
31 | "htmlparser2": "^3.10.0",
32 | "husky": "^1.2.0",
33 | "microbundle": "^0.14.2",
34 | "microtime": "^3.0.0",
35 | "nyc": "^15.1.0",
36 | "parse5": "^5.1.0",
37 | "rimraf": "^2.6.2",
38 | "standard-version": "^9.3.2",
39 | "xo": "^0.47.0"
40 | },
41 | "xo": {
42 | "semicolon": false,
43 | "space": true,
44 | "rules": {
45 | "unicorn/prefer-module": 0,
46 | "padding-line-between-statements": 0,
47 | "comma-dangle": 0
48 | },
49 | "extends": [
50 | "@andreaspizsa/eslint-config-xo"
51 | ]
52 | },
53 | "description": "",
54 | "commitlint": {
55 | "extends": [
56 | "@commitlint/config-conventional"
57 | ]
58 | },
59 | "husky": {
60 | "hooks": {
61 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
62 | }
63 | },
64 | "standard-version": {
65 | "scripts": {
66 | "prerelease": "npm test && npm run build"
67 | }
68 | },
69 | "postinstaller": {
70 | "add your config here": true,
71 | "see": "https://npmjs.com/postinstaller#readme"
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # hypertag> [![npm-version-badge][]]() [![npm-license-badge][]]()
2 |
3 | > The fastest HTML tag and attributes parser.
4 |
5 | **hypertag** is an HTML tag parser built for speed. Use it to find specific HTML tags and their attributes in HTML documents. It’s like a superfast `getElementsByTagName` without the DOM.
6 |
7 | ## ✨ Features
8 | + ✅ **Hyperfast.** 50 × faster than cheerio, 30 × parse5, 10 × htmlparser2.
9 | + ✅ **Tiny.** < 500 bytes gzipped.
10 | + ✅ **Complete** Zero dependencies.
11 | + ✅ **Robust.** 100% Code Coverage. [![coveralls-badge][]]() [![travis-build-badge][]]()
12 |
13 | ## 💻 Use
14 | ```js
15 | const html = `
16 |
17 |
18 |
19 |
20 |
Hello, world!
21 |
22 | `
23 |
24 | const result = parseHtmlTags(html, 'meta')
25 | console.log(result)
26 |
27 | [
28 | {
29 | '<' : 'meta',
30 | name: 'hello',
31 | content: 'world'
32 | },
33 | {
34 | '<' : 'meta',
35 | name: 'hello',
36 | content: 'moon'
37 | }
38 | ]
39 | ```
40 |
41 | ### Examples
42 |
43 | #### Getting Favicons
44 |
45 | ```js
46 | const result = parseHtmlTags(html, 'link')
47 | .filter(({rel}) => /^(shortcut\s+)?icon/i.test(rel))
48 |
49 | [
50 | {
51 | '<': 'link',
52 | rel: 'icon',
53 | href: 'favicon.png',
54 | sizes: '16x16'
55 | type: 'image/png'
56 | }
57 | ]
58 | ```
59 |
60 | #### Getting OpenGraph Images
61 | ```js
62 | const result = parseHtmlTags(html, 'meta')
63 | .filter(({property}) => property.toLowerCase() === 'og:image')
64 |
65 | [
66 | {
67 | '<': 'meta',
68 | property: 'og:image',
69 | content: 'http://static01.nyt.com/images/2015/02/19/arts/international/19iht-btnumbers19A/19iht-btnumbers19A-facebookJumbo-v2.jpg'
70 | }
71 | ]
72 | ```
73 |
74 | # Benchmarks 🍏🍊
75 | Run benchmarks with
76 | ```sh
77 | $ ./benchmark.js
78 | ```
79 | #### Benchmark Design
80 |
81 | The tested packages all do different things and have their strengths in different areas, so the benchmark by design compares apples to oranges.
82 |
83 | The question this benchmark aims to answer is
84 |
85 | > How fast can I find tags of interest in an HTML string?
86 |
87 | Most of the tested parsers come with many more features and allow you to do more complex queries than hypertag; for example, parse5 and cheerio create a whole DOM, and similarly html-parse-stringify2 creates an AST. html-tag-parser parses tags but not attributes.
88 |
89 | One objection could be that this is an unfair test, since the parsers are just too different. This can be rebutted by the fact that one ought to pick the right tool for the job: a sports car is faster than a truck, but the truck can load more freight. Do you need a fast and simple parser to find a few tags or do you want to manipulate a DOM?
90 |
91 | For this benchmark, we load a pretty "standard" web page (specifically, apple.com) and the let each of the parsers parse the HTML.
92 |
93 | #### Results
94 | ```sh
95 | hypertag x 10,248 ops/sec ±0.78% (88 runs sampled)
96 | fast-html x 980 ops/sec ±1.36% (87 runs sampled)
97 | parse5 x 323 ops/sec ±1.68% (83 runs sampled)
98 | htmlparser2 x 1,079 ops/sec ±0.87% (88 runs sampled)
99 | html-tag-parser x 1,482 ops/sec ±0.71% (91 runs sampled)
100 | cheerio x 182 ops/sec ±5.20% (70 runs sampled)
101 | html-parse-stringify2 x 499 ops/sec ±1.07% (87 runs sampled)
102 | Fastest is hypertag
103 | ```
104 |
105 | [npm-version-badge]: https://flat.badgen.net/npm/v/hypertag
106 | [npm-license-badge]: https://flat.badgen.net/npm/license/hypertag
107 | [travis-build-badge]: https://flat.badgen.net/travis/AndreasPizsa/hypertag
108 | [coveralls-badge]: https://flat.badgen.net/coveralls/c/github/AndreasPizsa/hypertag
109 | [bundlepohobia-badge]: https://flat.badgen.net/bundlepohobia/minzip/hypertag
110 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | const test = require('ava')
2 | const parseTags = require('../hypertag.js')
3 | const {stripComments, parseAttrs} = require('../hypertag.js')
4 |
5 | test('full HTML', t => {
6 | const result = parseTags(`
7 |
10 |
11 |
12 | <>
13 |
14 | `, 'hello')
15 | t.deepEqual(result, [
16 | {
17 | '<' : 'hello',
18 | world: 'yes'
19 | },
20 | {
21 | '<': 'hello'
22 | }
23 | ])
24 | })
25 |
26 | test('extend', t => {
27 | const randomTagName = randomString()
28 | const randomAttr = randomString()
29 | const randomValue = randomString()
30 | const tagKey = randomString()
31 |
32 | const input = `<${randomTagName} ${randomAttr}="${randomValue}">`
33 | const expect = {
34 | [tagKey] : randomTagName,
35 | [randomAttr]: randomValue
36 | }
37 | const parse = parseTags.extend(randomTagName, {tagKey})
38 | const result = parse(input)
39 | t.deepEqual(result, [expect])
40 | })
41 |
42 | test('string and array arguments', t => {
43 | const input = ''
44 | const expected = [{'<': 'hello', who: 'world'}]
45 |
46 | t.deepEqual(parseTags(input, 'hello'), expected)
47 | t.deepEqual(parseTags(input, ['hello']), expected)
48 | })
49 |
50 | test('stripComments', t => {
51 | t.is(
52 | stripComments('Hello, world!'),
53 | 'Hello, world!'
54 | )
55 | })
56 |
57 | test('attributes', t => {
58 | const randomTagName = randomString()
59 | const randomAttr = randomString()
60 | const randomValue = randomString()
61 | const tagKey = randomString()
62 |
63 | const input = `
64 | <${randomTagName} ${randomAttr}>
65 | <${randomTagName} ${randomAttr}= "${randomValue}">
66 | <${randomTagName} ${randomAttr} ='${randomValue}'>
67 | <${randomTagName} ${randomAttr} = ${randomValue}>
68 | <${randomTagName} emptyTag = "">
69 | <${randomTagName} emptyTag = ''>
70 | <${randomTagName} emptyTag = >
71 | <${randomTagName}
72 | ${randomAttr} = ${randomValue}>
73 | <${randomTagName} ${randomAttr}
74 | =${randomValue}>
75 | `
76 | const parse = parseTags.extend(randomTagName, {tagKey})
77 | const result = parse(input)
78 | t.deepEqual(result, [
79 | {[tagKey]: randomTagName, [randomAttr]: true},
80 | {[tagKey]: randomTagName, [randomAttr]: randomValue},
81 | {[tagKey]: randomTagName, [randomAttr]: randomValue},
82 | {[tagKey]: randomTagName, [randomAttr]: randomValue},
83 | {[tagKey]: randomTagName, emptyTag: ''},
84 | {[tagKey]: randomTagName, emptyTag: ''},
85 | {[tagKey]: randomTagName, emptyTag: true},
86 | {[tagKey]: randomTagName, [randomAttr]: randomValue},
87 | {[tagKey]: randomTagName, [randomAttr]: randomValue}
88 | ])
89 | })
90 |
91 | test('getTags with dash in tag', t => {
92 | const randomTag = [randomString(), randomString()].join('-')
93 | t.deepEqual(
94 | parseTags(`<${randomTag}>`, randomTag),
95 | [{
96 | '<': randomTag
97 | }])
98 | })
99 |
100 | test('getTags with non-tag string', t => {
101 | t.deepEqual(parseTags(''), [])
102 | })
103 |
104 | test('parseAttrs with non-tag string', t => {
105 | t.is(parseAttrs(''), undefined)
106 | })
107 |
108 | test('match all tags', t => {
109 | const generatedTags = []
110 | for (let i = 0; i < 10; i++) {
111 | generatedTags.push(randomString())
112 | }
113 | const html = generatedTags
114 | .map(tag => `<${tag}>${tag}>`)
115 | .join('\n')
116 | + ''
117 | const expected = generatedTags
118 | .map(tagname => ({'<': tagname}))
119 | .concat([{'<': 'title'}])
120 |
121 | const result = parseTags(html, '*')
122 | t.deepEqual(result, expected)
123 | })
124 |
125 | function randomString() {
126 | return Math.random().toString(36).slice(2)
127 | }
128 |
--------------------------------------------------------------------------------
/test/fixture-qualcomm.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Mobile Development - Qualcomm Developer Network
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
50 |
53 |
65 |
75 |
80 |
83 |
86 |
90 |
94 |
97 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
141 |
142 |
143 |
144 |
145 |
148 |
5G
Integrate enhanced mobile broadband and mission critical control into your applications designed for 5G and industrial and massive IoT applications.
Get Started with 5G Artificial Intelligence (AI)
Developers interested in incorporating on-device AI can utilize our HW engine and related SW tools to design your own AI applications.
AI Resources for Developers Mobile Gaming
Our mobile gaming platforms with Elite Gaming features provide developers with visuals, performance, and audio to power next-level gaming experiences.
Gaming Developer Resources Snapdragon Tools
This suite of tools can tap into our mobile platforms to optimize the power and performance of your mobile applications running on Snapdragon.
Optimize your apps for Snapdragon 5G Artificial Intelligence Mobile Gaming Snapdragon Tools
Developing for the Invention Age
Qualcomm Developer Network (QDN) is a comprehensive program designed to equip the next generation of mobile pioneers to develop what’s next. Our collection of software and hardware tools and resources is designed so you can build upon our foundational technologies in new and ways, creating the power to transform products, enrich lives and even transform entire industries.
At Qualcomm Developer Network, our aim is to help you kick-start your development by being the catalyst for your vision, today, tomorrow, and in the future. Welcome to the Age of Invention.
Powerful Solutions for Your Platform
200 |
201 |
202 |
--------------------------------------------------------------------------------
/test/fixture-twitter.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
48 |
49 |
110 |
111 |
112 |
JavaScript is not available.
113 |
We’ve detected that JavaScript is disabled in this browser. Please enable JavaScript or switch to a supported browser to continue using twitter.com. You can see a list of supported browsers in our Help Center.
114 |
Help Center
115 |
123 |
124 |
125 |
--------------------------------------------------------------------------------