├── .c8rc.json ├── .editorconfig ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── parsing-bug-report.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── release-drafter.yml └── workflows │ ├── build_and_test.yaml │ ├── dependabot.yml │ ├── publish.yaml │ └── release-drafter.yml ├── .gitignore ├── .husky └── pre-commit ├── .mocharc.json ├── .nvmrc ├── .prettierignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── demo ├── demo.css ├── demo.js └── index.tpl.html ├── package.json ├── rollup.config.js ├── src ├── backcompat │ ├── adr.ts │ ├── geo.ts │ ├── hentry.ts │ ├── hfeed.ts │ ├── hnews.ts │ ├── hproduct.ts │ ├── hresume.ts │ ├── hreview-aggregate.ts │ ├── hreview.ts │ ├── index.ts │ ├── item.ts │ ├── vcard.ts │ └── vevent.ts ├── helpers │ ├── array.ts │ ├── attributes.ts │ ├── documentSetup.ts │ ├── experimental.ts │ ├── findChildren.ts │ ├── images.ts │ ├── includes.ts │ ├── metaformats.ts │ ├── nodeMatchers.ts │ ├── textContent.ts │ ├── url.ts │ └── valueClassPattern.ts ├── implied │ ├── name.ts │ ├── photo.ts │ └── url.ts ├── index.ts ├── microformats │ ├── parse.ts │ ├── properties.ts │ └── property.ts ├── parser.ts ├── rels │ └── rels.ts ├── types.ts └── validator.ts ├── test ├── package.cjs.spec.js ├── package.mjs.spec.js ├── scenarios.spec.ts ├── suites │ ├── README.md │ ├── experimental │ │ ├── lang-feed.html │ │ ├── lang-feed.json │ │ ├── lang-meta.html │ │ ├── lang-meta.json │ │ ├── lang.html │ │ ├── lang.json │ │ ├── metaformats-missing-head.html │ │ ├── metaformats-missing-head.json │ │ ├── metaformats-og-article.html │ │ ├── metaformats-og-article.json │ │ ├── metaformats-og-audio-soundcloud.html │ │ ├── metaformats-og-audio-soundcloud.json │ │ ├── metaformats-og-profile-linkedin.html │ │ ├── metaformats-og-profile-linkedin.json │ │ ├── metaformats-og-video-vimeo.html │ │ ├── metaformats-og-video-vimeo.json │ │ ├── metaformats-prefer-mf.html │ │ ├── metaformats-prefer-mf.json │ │ ├── metaformats-standard.html │ │ ├── metaformats-standard.json │ │ ├── metaformats-twitter-article.html │ │ ├── metaformats-twitter-article.json │ │ ├── text-content.html │ │ └── text-content.json │ └── local │ │ ├── microformats-v1 │ │ ├── includes.html │ │ └── includes.json │ │ └── microformats-v2 │ │ ├── dates.html │ │ ├── dates.json │ │ ├── empty-property.html │ │ ├── empty-property.json │ │ ├── lang.html │ │ ├── lang.json │ │ ├── nested.html │ │ ├── nested.json │ │ ├── rel-urls.html │ │ ├── rel-urls.json │ │ ├── urls.html │ │ └── urls.json ├── utils │ ├── dirname.d.ts │ ├── dirname.js │ ├── loadScenarios.d.ts │ └── loadScenarios.js └── validation.spec.ts ├── tsconfig.json └── yarn.lock /.c8rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "all": true, 3 | "check-coverage": true, 4 | "include": ["src/**"], 5 | "exclude": ["src/types.ts"], 6 | "statements": 99, 7 | "branches": 98.5, 8 | "functions": 100, 9 | "lines": 99 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | charset = utf-8 -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "extends": [ 4 | "plugin:@typescript-eslint/recommended", 5 | "plugin:import/errors", 6 | "plugin:import/typescript", 7 | "prettier" 8 | ], 9 | "ignorePatterns": ["node_modules/", "dist/", "public/", "**/*.html"], 10 | "settings": { 11 | "import/resolver": { 12 | "node": { "extensions": [".ts"] } 13 | } 14 | }, 15 | "rules": { 16 | "arrow-body-style": ["error", "as-needed"], 17 | "import/order": [ 18 | "error", 19 | { 20 | "groups": [["builtin", "external", "internal"]], 21 | "newlines-between": "always-and-inside-groups" 22 | } 23 | ] 24 | }, 25 | "overrides": [ 26 | { 27 | "files": ["./demo/**/*.js", "./rollup.config.js"], 28 | "rules": { 29 | "@typescript-eslint/no-var-requires": "off", 30 | "@typescript-eslint/explicit-function-return-type": "off", 31 | "@typescript-eslint/explicit-module-boundary-types": "off" 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for new microformats support 4 | title: "" 5 | labels: enhancement 6 | assignees: "" 7 | --- 8 | 9 | **What type of feature is it?** 10 | 11 | Is it an experimental feature or a new addition to the specification? 12 | 13 | **Describe the feature** 14 | 15 | Please provide a couple of sentences describing what will change with this feature. 16 | 17 | **Example of input** 18 | 19 | Provide clear examples of input HTML that covers the proposed feature. 20 | 21 | ```html 22 | 23 | ``` 24 | 25 | **Example of output** 26 | 27 | Please provide the expected JSON output for the provided HTML. 28 | 29 | ```json 30 | 31 | ``` 32 | 33 | **Additional context** 34 | 35 | Add any other context or information about the feature request here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/parsing-bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Parsing bug report 3 | about: Create a bug report for when microformats are incorrectly parsed 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | 11 | A clear and concise description of what the bug is. Please include a reference to the specification, other discussions or other parser behaviour. 12 | 13 | **To Reproduce** 14 | 15 | HTML input: 16 | 17 | ```html 18 | 19 | ``` 20 | 21 | **Expected behavior** 22 | 23 | Correct JSON output: 24 | 25 | ```json 26 | 27 | ``` 28 | 29 | **Additional context** 30 | 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # . 2 | 3 | **Checklist** 4 | 5 | 6 | 7 | - [ ] Added validaton to any changes in the parser API. 8 | - [ ] Added tests covering the parsing behaviour changes. 9 | - [ ] Linked to any relevant issues this will close. 10 | - [ ] Tested the output using the [demo](../CONTRIBUTING.md#testing-your-changes). 11 | 12 | **Changes to parsing behaviour** 13 | 14 | 15 | 16 | A brief summary of any changes to the parser behaviour. 17 | 18 | **Example input covered by new behaviour** 19 | 20 | 21 | 22 | ```html 23 | 24 | ``` 25 | 26 | **Example output from new behaviour** 27 | 28 | 29 | 30 | ```json 31 | 32 | ``` 33 | 34 | **Other changes** 35 | 36 | 37 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: npm 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | versioning-strategy: increase-if-necessary 13 | commit-message: 14 | prefix: "chore(deps): " 15 | - package-ecosystem: "github-actions" 16 | directory: "/" 17 | schedule: 18 | interval: "weekly" 19 | commit-message: 20 | prefix: "chore(deps): " 21 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: "v$RESOLVED_VERSION" 2 | tag-template: "v$RESOLVED_VERSION" 3 | template: | 4 | ## What's Changed 5 | 6 | $CHANGES 7 | categories: 8 | - title: Breaking changes 9 | labels: 10 | - major 11 | - breaking change 12 | - title: Features 13 | labels: 14 | - enhancement 15 | - minor 16 | - title: Bug fixes 17 | labels: 18 | - bug 19 | - title: Maintenance 20 | labels: 21 | - maintenance 22 | - documentation 23 | version-resolver: 24 | major: 25 | labels: 26 | - major 27 | - breaking change 28 | minor: 29 | labels: 30 | - minor 31 | - enhancement 32 | patch: 33 | labels: 34 | - bug 35 | - maintenance 36 | default: patch 37 | autolabeler: 38 | - label: bug 39 | title: 40 | - "/fix/i" 41 | - label: maintenance 42 | title: 43 | - "/perf/i" 44 | - "/refactor/i" 45 | - "/style/i" 46 | - "/test/i" 47 | - "/build/i" 48 | - "/chore/i" 49 | - "/ci/i" 50 | - label: enhancement 51 | title: 52 | - "/feat/i" 53 | - label: breaking change 54 | title: 55 | - "/breaking change/i" 56 | - label: documentation 57 | title: 58 | - "/docs/i" 59 | -------------------------------------------------------------------------------- /.github/workflows/build_and_test.yaml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | env: 10 | TZ: Europe/London 11 | 12 | jobs: 13 | build_and_test: 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 5 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-node@v4 19 | with: 20 | node-version-file: ".nvmrc" 21 | cache: "npm" 22 | - name: Install dependencies 23 | run: yarn 24 | - name: Lint code 25 | run: yarn lint 26 | - name: Run prettier list 27 | run: yarn prettier:list 28 | - name: Test code 29 | run: yarn test 30 | - name: Build package 31 | run: yarn build 32 | - name: Upload build artifacts 33 | uses: actions/upload-artifact@v4 34 | with: 35 | name: dist 36 | path: dist 37 | test_dist: 38 | runs-on: ubuntu-latest 39 | timeout-minutes: 5 40 | needs: [build_and_test] 41 | strategy: 42 | matrix: 43 | node: [18, 20, 22, 24] 44 | steps: 45 | - uses: actions/checkout@v4 46 | - uses: actions/setup-node@v4 47 | with: 48 | node-version: ${{ matrix.node }} 49 | - name: Install dependencies 50 | run: yarn 51 | - name: Download build artifacts 52 | uses: actions/download-artifact@v4 53 | with: 54 | name: dist 55 | path: dist 56 | - name: Test package 57 | run: yarn test:package 58 | -------------------------------------------------------------------------------- /.github/workflows/dependabot.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-approve 2 | on: pull_request 3 | 4 | permissions: 5 | contents: write 6 | pull-requests: write 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: ubuntu-latest 11 | if: ${{ github.actor == 'dependabot[bot]' }} 12 | steps: 13 | - name: Dependabot metadata 14 | id: metadata 15 | uses: dependabot/fetch-metadata@v2 16 | with: 17 | github-token: "${{ secrets.GITHUB_TOKEN }}" 18 | - name: Approve PR 19 | run: gh pr review --approve "$PR_URL" 20 | env: 21 | PR_URL: ${{github.event.pull_request.html_url}} 22 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 23 | - name: Auto-merge PR 24 | if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} 25 | run: | 26 | gh pr review --approve "$PR_URL" | 27 | gh pr merge --auto --squash "$PR_URL" 28 | env: 29 | PR_URL: ${{github.event.pull_request.html_url}} 30 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 31 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish NPM package 2 | 3 | on: 4 | release: 5 | types: 6 | - released 7 | 8 | jobs: 9 | publish: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: actions/setup-node@v4 14 | with: 15 | node-version-file: ".nvmrc" 16 | registry-url: https://registry.npmjs.org/ 17 | - name: Install dependencies 18 | run: yarn 19 | - name: Set correct version 20 | run: npm --no-git-tag-version version $VERSION 21 | env: 22 | VERSION: ${{ github.event.release.tag_name }} 23 | - name: Build package 24 | run: yarn build 25 | - name: Publish to NPM 26 | run: yarn publish --non-interactive 27 | env: 28 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 29 | - name: Deploy pages 30 | uses: peaceiris/actions-gh-pages@v4 31 | with: 32 | github_token: ${{ secrets.GITHUB_TOKEN }} 33 | publish_dir: ./public 34 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release drafter 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | types: [opened, reopened, synchronize] 7 | 8 | jobs: 9 | update_release_draft: 10 | permissions: 11 | contents: write 12 | pull-requests: write 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: release-drafter/release-drafter@v6 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | public 107 | demo/dist 108 | 109 | .DS_Store 110 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | yarn lint-staged 2 | -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": ["ts", "mjs", "cjs", "js"], 3 | "node-option": [ 4 | "experimental-specifier-resolution=node", 5 | "loader=ts-node/esm" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | .nyc_output/ 3 | .cache 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 |
` tags are treated as line breaks. 113 | 114 | #### `metaformats` 115 | 116 | Enables fallback to [metaformats](https://microformats.org/wiki/metaformats) parsing which looks at `` tags to infer content. 117 | 118 | ## Contributing 119 | 120 | See our [contributing guidelines](./CONTRIBUTING.md) for more information. 121 | -------------------------------------------------------------------------------- /demo/demo.css: -------------------------------------------------------------------------------- 1 | *, 2 | *:before, 3 | *:after { 4 | box-sizing: border-box; 5 | } 6 | 7 | h1, 8 | p, 9 | ul, 10 | li, 11 | body, 12 | html { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | 17 | html, 18 | body { 19 | font-size: 16px; 20 | font-family: "Source Sans Pro", sans-serif; 21 | } 22 | 23 | a { 24 | color: #177e89; 25 | text-decoration: none; 26 | } 27 | 28 | a:hover { 29 | color: #2b3238; 30 | } 31 | 32 | nav { 33 | background: #2b3238; 34 | } 35 | 36 | nav ul { 37 | margin: 0 -1rem; 38 | } 39 | 40 | nav ul li { 41 | display: inline-block; 42 | padding: 0.5rem; 43 | } 44 | 45 | nav a { 46 | color: #fff; 47 | } 48 | 49 | nav a:hover { 50 | color: #177e89; 51 | } 52 | 53 | header { 54 | background: #e9ecef; 55 | text-align: center; 56 | padding: 2rem 1rem; 57 | } 58 | 59 | header h1 { 60 | margin-bottom: 2rem; 61 | } 62 | 63 | footer { 64 | margin-top: 4rem; 65 | margin-bottom: 2rem; 66 | text-align: center; 67 | } 68 | 69 | .description { 70 | margin-bottom: 2rem; 71 | } 72 | 73 | .container { 74 | max-width: 750px; 75 | margin: 0 auto; 76 | padding: 0 1rem; 77 | } 78 | 79 | .documentation, 80 | button[type="submit"] { 81 | background: #08605f; 82 | color: #fff; 83 | display: inline-block; 84 | border-radius: 0.25rem; 85 | border: none; 86 | padding: 0.5rem 1rem; 87 | } 88 | 89 | .submit { 90 | text-align: center; 91 | } 92 | 93 | .documentation { 94 | padding: 0.75rem 1.5rem; 95 | font-size: 1.25rem; 96 | } 97 | 98 | .documentation:hover, 99 | button[type="submit"]:hover { 100 | background: #2b3238; 101 | color: #fff; 102 | cursor: pointer; 103 | } 104 | 105 | #result { 106 | border-radius: 0.25rem; 107 | border: 1px solid #ccc; 108 | background: #f4f4f4; 109 | min-height: 10rem; 110 | overflow: scroll; 111 | padding: 0.5rem; 112 | font-size: 0.8rem; 113 | } 114 | 115 | form label { 116 | display: block; 117 | padding: 1rem 0; 118 | } 119 | 120 | form input[type="text"], 121 | form textarea { 122 | width: 100%; 123 | display: block; 124 | border: 1px solid #177e89; 125 | background: #fff; 126 | border-radius: 0.25rem; 127 | padding: 0.5rem; 128 | } 129 | 130 | form textarea { 131 | min-height: 10rem; 132 | min-width: 100%; 133 | max-width: 100%; 134 | } 135 | 136 | .error { 137 | border: 1px solid #df3b57; 138 | border-radius: 0.25rem; 139 | padding: 0.5rem 1rem; 140 | color: #df3b57; 141 | margin: 2rem 0; 142 | } 143 | 144 | .hide { 145 | display: none; 146 | } 147 | 148 | .experimental label { 149 | display: inline-block; 150 | } 151 | 152 | h3 { 153 | font-size: 1rem; 154 | margin: 0; 155 | } 156 | -------------------------------------------------------------------------------- /demo/demo.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-unresolved 2 | import { mf2 } from "../dist/index.mjs"; 3 | import "./demo.css"; 4 | 5 | const setResult = (result) => { 6 | const escaped = JSON.stringify(result, null, 2) 7 | .replace(//g, ">"); 9 | document.getElementById("result").innerHTML = escaped; 10 | }; 11 | 12 | const setError = (error) => { 13 | const el = document.getElementById("error"); 14 | el.innerHTML = `Error: ${error}`; 15 | el.classList.remove("hide"); 16 | }; 17 | 18 | const parse = (html, options) => { 19 | document.getElementById("error").classList.add("hide"); 20 | 21 | try { 22 | const result = mf2(html, options); 23 | setResult(result); 24 | } catch (err) { 25 | setError(err.message); 26 | } 27 | 28 | return false; 29 | }; 30 | 31 | window.parseHtml = () => { 32 | const html = document.getElementById("html").value; 33 | const baseUrl = document.getElementById("base-url").value; 34 | const lang = document.getElementById("lang").checked; 35 | const textContent = document.getElementById("textContent").checked; 36 | const metaformats = document.getElementById("metaformats").checked; 37 | 38 | return parse(html, { 39 | baseUrl, 40 | experimental: { lang, textContent, metaformats }, 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /demo/index.tpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |Shouldn't return any items if properties are not found.
6 |OGP tags are read if no microformats are found
33 |True microformats should prevent metaformats
22 |13 | 16 | 21 | Bob Smith 22 |
23 |
26 | General
27 |
28 | Waste
29 |
35 |
36 |
Jane Doe
4 |Jane Doe
", 13 | "value": "Jane Doe" 14 | } 15 | ] 16 | } 17 | }, 18 | { 19 | "type": ["h-entry"], 20 | "properties": { 21 | "photo": [ 22 | { 23 | "type": ["h-card"], 24 | "properties": { 25 | "name": ["My name"], 26 | "photo": [ 27 | { "alt": "My name", "value": "http://example.com/photo.jpg" } 28 | ] 29 | }, 30 | "value": "http://example.com/photo.jpg" 31 | } 32 | ] 33 | } 34 | }, 35 | { 36 | "type": ["h-entry"], 37 | "properties": { 38 | "photo": [ 39 | { 40 | "type": ["h-card"], 41 | "properties": { 42 | "name": [""], 43 | "photo": ["http://example.com/photo.jpg"] 44 | }, 45 | "value": "http://example.com/photo.jpg" 46 | } 47 | ] 48 | } 49 | }, 50 | { 51 | "type": ["h-entry"], 52 | "properties": { 53 | "photo": [ 54 | { 55 | "type": ["h-card"], 56 | "properties": { 57 | "name": [""] 58 | }, 59 | "value": "" 60 | } 61 | ] 62 | } 63 | }, 64 | { 65 | "type": ["h-card"], 66 | "properties": { 67 | "bday": [ 68 | { 69 | "type": ["h-event"], 70 | "properties": { 71 | "name": ["2nd May"], 72 | "start": ["2010-05-02"], 73 | "end": ["2010-05-02"] 74 | }, 75 | "value": "2nd May" 76 | } 77 | ] 78 | } 79 | } 80 | ], 81 | "rels": {}, 82 | "rel-urls": {} 83 | } 84 | -------------------------------------------------------------------------------- /test/suites/local/microformats-v2/rel-urls.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | My name 4 | 5 | Go back home 6 | 7 | This URL should be trimmed 8 | 9 | This should be ignored 10 | -------------------------------------------------------------------------------- /test/suites/local/microformats-v2/rel-urls.json: -------------------------------------------------------------------------------- 1 | { 2 | "rels": { 3 | "me": ["http://example.com"], 4 | "home": ["http://example.com"], 5 | "example": ["http://example.com"] 6 | }, 7 | "rel-urls": { 8 | "http://example.com": { 9 | "rels": ["example", "home", "me"], 10 | "text": "My name", 11 | "type": "text/html" 12 | } 13 | }, 14 | "items": [] 15 | } 16 | -------------------------------------------------------------------------------- /test/suites/local/microformats-v2/urls.html: -------------------------------------------------------------------------------- 1 |