├── .editorconfig ├── .eslintrc ├── .gitignore ├── .jscsrc ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── Gruntfile.js ├── LICENSE.md ├── README.md ├── bower.json ├── dist ├── svg4everybody.js ├── svg4everybody.legacy.js ├── svg4everybody.legacy.min.js └── svg4everybody.min.js ├── lib └── svg4everybody.js ├── package.json └── test ├── 1138.png ├── demo.svg ├── demo.svg.1138.png ├── demo.svg.camera.png ├── demo.svg.eye.png ├── demo.svg.pencil.png ├── eye.png └── index.html /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = tab 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.{json,yml}] 14 | indent_size = 2 15 | indent_style = space 16 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "node": true 5 | }, 6 | "rules": { 7 | "accessor-pairs": [2], 8 | "block-scoped-var": [2], 9 | "callback-return": [2], 10 | "complexity": [2, 20], 11 | "consistent-return": [2], 12 | "consistent-this": [2, "self"], 13 | "constructor-super": [2], 14 | "default-case": [2], 15 | "eqeqeq": [2], 16 | "func-style": [0], 17 | "global-require": [2], 18 | "guard-for-in": [0], 19 | "handle-callback-err": [2, "^err(or)?$"], 20 | "id-length": [0], 21 | "id-match": [0], 22 | "init-declarations": [0], 23 | "max-depth": [2, 6], 24 | "max-nested-callbacks": [2, 3], 25 | "max-params": [2, 4], 26 | "max-statements": [0], 27 | "new-parens": [0], 28 | "no-alert": [2], 29 | "no-array-constructor": [0], 30 | "no-bitwise": [0], 31 | "no-caller": [2], 32 | "no-case-declarations": [2], 33 | "no-catch-shadow": [2], 34 | "no-class-assign": [2], 35 | "no-cond-assign": [2], 36 | "no-console": [0], 37 | "no-const-assign": [2], 38 | "no-constant-condition": [0], 39 | "no-continue": [0], 40 | "no-control-regex": [2], 41 | "no-debugger": [2], 42 | "no-delete-var": [2], 43 | "no-div-regex": [0], 44 | "no-dupe-args": [2], 45 | "no-dupe-class-members": [2], 46 | "no-dupe-keys": [2], 47 | "no-duplicate-case": [2], 48 | "no-else-return": [0], 49 | "no-empty-character-class": [2], 50 | "no-empty-label": [2], 51 | "no-empty-pattern": [2], 52 | "no-empty": [2], 53 | "no-eq-null": [2], 54 | "no-eval": [2], 55 | "no-ex-assign": [2], 56 | "no-extend-native": [0], 57 | "no-extra-bind": [2], 58 | "no-extra-boolean-cast": [2], 59 | "no-extra-parens": [2], 60 | "no-extra-semi": [2], 61 | "no-fallthrough": [2], 62 | "no-floating-decimal": [2], 63 | "no-func-assign": [2], 64 | "no-implicit-coercion": [2], 65 | "no-implicit-globals": [0], 66 | "no-implied-eval": [2], 67 | "no-inline-comments": [0], 68 | "no-inner-declarations": [2], 69 | "no-invalid-regexp": [2], 70 | "no-invalid-this": [0], 71 | "no-irregular-whitespace": [2], 72 | "no-iterator": [2], 73 | "no-label-var": [2], 74 | "no-labels": [0], 75 | "no-lone-blocks": [2], 76 | "no-lonely-if": [2], 77 | "no-loop-func": [2], 78 | "no-magic-numbers": [2], 79 | "no-mixed-requires": [0], 80 | "no-multi-str": [2], 81 | "no-native-reassign": [2], 82 | "no-negated-condition": [0], 83 | "no-negated-in-lhs": [2], 84 | "no-nested-ternary": [0], 85 | "no-new-func": [0], 86 | "no-new-object": [2], 87 | "no-new-require": [0], 88 | "no-new-wrappers": [2], 89 | "no-new": [2], 90 | "no-obj-calls": [2], 91 | "no-octal-escape": [2], 92 | "no-octal": [2], 93 | "no-param-reassign": [2], 94 | "no-path-concat": [2], 95 | "no-plusplus": [0], 96 | "no-process-env": [2], 97 | "no-process-exit": [0], 98 | "no-proto": [2], 99 | "no-redeclare": [2], 100 | "no-regex-spaces": [0], 101 | "no-restricted-imports": [0], 102 | "no-restricted-syntax": [0], 103 | "no-return-assign": [2], 104 | "no-script-url": [0], 105 | "no-self-compare": [2], 106 | "no-sequences": [2], 107 | "no-shadow-restricted-names": [2], 108 | "no-shadow": [2], 109 | "no-sparse-arrays": [2], 110 | "no-sync": [0], 111 | "no-ternary": [0], 112 | "no-this-before-super": [2], 113 | "no-throw-literal": [2], 114 | "no-undef-init": [0], 115 | "no-undef": [2], 116 | "no-undefined": [0], 117 | "no-unneeded-ternary": [2], 118 | "no-unreachable": [2], 119 | "no-unused-expressions": [2], 120 | "no-unused-vars": [2], 121 | "no-use-before-define": [0], 122 | "no-useless-call": [2], 123 | "no-useless-concat": [2], 124 | "no-var": [0], 125 | "no-void": [0], 126 | "no-with": [2], 127 | "object-shorthand": [0], 128 | "one-var": [0], 129 | "operator-assignment": [2, "always"], 130 | "prefer-arrow-callback": [0], 131 | "prefer-const": [0], 132 | "prefer-reflect": [0], 133 | "prefer-rest-params": [0], 134 | "prefer-spread": [0], 135 | "prefer-template": [0], 136 | "quotes": [0], 137 | "radix": [2, "as-needed"], 138 | "require-yield": [0], 139 | "sort-imports": [0], 140 | "sort-vars": [0], 141 | "strict": [0], 142 | "use-isnan": [2], 143 | "valid-typeof": [2], 144 | "vars-on-top": [0], 145 | "wrap-iife": [2, "inside"], 146 | "wrap-regex": [0], 147 | "yoda": [0], 148 | 149 | "camelcase": [0], 150 | "key-spacing": [0], 151 | "spaced-comment": [0] 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "disallowKeywords": [ 3 | "with" 4 | ], 5 | "disallowKeywordsOnNewLine": [ 6 | "else" 7 | ], 8 | "disallowMultipleSpaces": false, 9 | "disallowMixedSpacesAndTabs": true, 10 | "disallowMultipleLineBreaks": true, 11 | "disallowMultipleLineStrings": true, 12 | "disallowMultipleVarDecl": true, 13 | "disallowNewlineBeforeBlockStatements": true, 14 | "disallowSpaceAfterObjectKeys": true, 15 | "disallowSpaceAfterPrefixUnaryOperators": true, 16 | "disallowSpacesInCallExpression": true, 17 | "disallowSpacesInFunctionDeclaration": { 18 | "beforeOpeningRoundBrace": true 19 | }, 20 | "disallowSpacesInsideArrayBrackets": "all", 21 | "disallowSpacesInsideObjectBrackets": "all", 22 | "disallowSpacesInsideParentheses": true, 23 | "disallowTrailingWhitespace": true, 24 | "maximumLineLength": null, 25 | "requireCamelCaseOrUpperCaseIdentifiers": true, 26 | "requireCapitalizedConstructors": true, 27 | "requireCurlyBraces": [ 28 | "if", 29 | "else", 30 | "for", 31 | "while", 32 | "do", 33 | "try", 34 | "catch" 35 | ], 36 | "requireDotNotation": true, 37 | "requireLineFeedAtFileEnd": true, 38 | "requireOperatorBeforeLineBreak": true, 39 | "requireSemicolons": true, 40 | "requireSpaceAfterBinaryOperators": true, 41 | "requireSpaceAfterKeywords": [ 42 | "if", 43 | "else", 44 | "for", 45 | "while", 46 | "do", 47 | "switch", 48 | "return", 49 | "try", 50 | "catch" 51 | ], 52 | "requireSpaceBeforeBinaryOperators": [ 53 | "=", 54 | "+=", 55 | "-=", 56 | "*=", 57 | "/=", 58 | "%=", 59 | "<<=", 60 | ">>=", 61 | ">>>=", 62 | "&=", 63 | "|=", 64 | "^=", 65 | "+=", 66 | "+", 67 | "-", 68 | "*", 69 | "/", 70 | "%", 71 | "<<", 72 | ">>", 73 | ">>>", 74 | "&", 75 | "|", 76 | "^", 77 | "&&", 78 | "||", 79 | "===", 80 | "==", 81 | ">=", 82 | "<=", 83 | "<", 84 | ">", 85 | "!=", 86 | "!==" 87 | ], 88 | "requireSpaceBeforeBlockStatements": true, 89 | "requireSpaceBeforeObjectValues": true, 90 | "requireSpacesInAnonymousFunctionExpression": { 91 | "beforeOpeningCurlyBrace": true, 92 | "beforeOpeningRoundBrace": true 93 | }, 94 | "requireSpacesInConditionalExpression": true, 95 | "requireSpacesInForStatement": true, 96 | "requireSpacesInFunctionDeclaration": { 97 | "beforeOpeningCurlyBrace": true 98 | }, 99 | "validateIndentation": "\t", 100 | "validateParameterSeparator": ", ", 101 | "validateQuoteMarks": "'" 102 | } 103 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - "0.12" 5 | before_script: 6 | - npm install grunt-cli -g 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.1.8 (2017-04-18) 2 | 3 | - Added: Support for [custom attribute on `use` elements](https://github.com/jonathantneal/svg4everybody/pull/155) 4 | 5 | ## 2.1.7 (2017-03-03) 6 | 7 | - Updated: Fixing internal use tag in polyfill 8 | 9 | ## 2.1.6 (2017-03-01) 10 | 11 | - Updated: Ensure the interval begins to run 12 | 13 | ## 2.1.5 (2017-02-28) 14 | 15 | - Updated: Fix for infinite loop in validation with polyfill 16 | - Updated: Fix for IFrame svg use issue in Edge 17 | 18 | ## 2.1.4 (2016-06-15) 19 | 20 | - Added: Support for [nested `use` elements](https://github.com/jonathantneal/svg4everybody/pull/117) 21 | - Updated: Test document 22 | 23 | ## 2.0.3 (2016-02-02) 24 | 25 | - Added: No polyfilling of Edge 12.10547+ 26 | - Updated: Source with documentation 27 | - Updated: Caching for fetched SVG elements 28 | - Updated: Test document 29 | 30 | ## 2.0.2 (2016-02-02) 31 | 32 | - Added: Use non-minified non-legacy version as main file 33 | - Added: caching for elements 34 | - Updated: project configuration and stricter linting 35 | - Added: Test document 36 | - Updated documentation 37 | 38 | ## 2.0.1 (2015-11-24) 39 | 40 | - Fix infinite loop issue with invalid tags 41 | - Update documentation 42 | 43 | ## 2.0.0 (2015-08-01) 44 | 45 | - Added: UMD pattern and `svg4everybody` method to activate shim 46 | - Added: Grunt build process 47 | - Added: Option to remove legacy code from Grunt 48 | - Added: Option to manually shim SVG or External Content from the browser 49 | - Added: Option to customize PNG fallback from the browser 50 | - Updated: SVG External Content feature-deficient browser detection 51 | - Updated: Exclusive activation on `` children of `` 52 | - Updated: Preservation of existing `viewbox` attribute for SVGs 53 | - Updated: Preservation of existing `width` and `height` attributes for PNG fallbacks 54 | - Updated: Many performance improvements for caching, AJAX, and polling 55 | 56 | ## 1.0.0 (2015-07-28) 57 | 58 | - Initial release 59 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | You want to help? You rock! Now, take a moment to be sure your contributions make sense to everyone else. 2 | 3 | ## Reporting Issues 4 | 5 | Found a problem? Want a new feature? 6 | 7 | - See if your issue or idea has [already been reported]. 8 | - Provide a [reduced test case] or a [live example]. 9 | 10 | Remember, a bug is a _demonstrable problem_ caused by _our_ code. 11 | 12 | ## Submitting Pull Requests 13 | 14 | Pull requests are the greatest contributions, so be sure they are focused in scope, and do avoid unrelated commits. 15 | 16 | 1. To begin, [fork this project], clone your fork, and add our upstream. 17 | ```bash 18 | # Clone your fork of the repo into the current directory 19 | git clone https://github.com//svg4everybody 20 | # Navigate to the newly cloned directory 21 | cd svg4everybody 22 | # Assign the original repo to a remote called "upstream" 23 | git remote add upstream https://github.com/jonathantenal/svg4everybody 24 | # Install the tools necessary for development 25 | npm install 26 | ``` 27 | 28 | 2. Create a branch for your feature or fix: 29 | ```bash 30 | # Move into a new branch for a feature 31 | git checkout -b feature/thing 32 | ``` 33 | ```bash 34 | # Move into a new branch for a fix 35 | git checkout -b fix/something 36 | ``` 37 | 38 | 3. Be sure your code follows our practices. 39 | ```bash 40 | # Compile the source and watch for changes 41 | npm run watch 42 | ``` 43 | 44 | 4. Undo changes made to compiled files and rebase all of your commits into one. 45 | ```bash 46 | # Undo changes to the compiled files 47 | npm run clean 48 | # Combine commits in your branch 49 | git rebase -i master 50 | ``` 51 | 52 | 5. Push your branch up to your fork: 53 | ```bash 54 | # Push a feature branch 55 | git push origin feature/thing 56 | ``` 57 | ```bash 58 | # Push a fix branch 59 | git push origin fix/something 60 | ``` 61 | 62 | 6. Now [open a pull request] with a clear title and description. 63 | 64 | Once your pull is accepted, be sure we add you to the list of [contributors]. 65 | 66 | [already been reported]: issues 67 | [contributors]: blob/master/README.md 68 | [fork]: fork 69 | [live example]: http://codepen.io/pen 70 | [open a pull request]: https://help.github.com/articles/using-pull-requests/ 71 | [reduced test case]: https://css-tricks.com/reduced-test-cases/ 72 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | You might not have enjoyed SVG for Everybody, had it not been for the effort by these individuals. 4 | 5 | - [Adam Ryan Merrifield](https://github.com/AdamMerrifield) 6 | - The option to cleanup built files from a feature branch 7 | - [Camilo Nova](https://github.com/camilonova) 8 | - Added bower package 9 | - [Jake Rayson](https://github.com/growdigital) 10 | - Added npm package 11 | - [Jonathan Neal](https://github.com/jonathantneal) 12 | - Author and maintainer 13 | - [Luis Merino](https://github.com/Rendez) 14 | - Overseer of the 2.0 refactor 15 | - [Travis Stone](https://github.com/travstone) 16 | - The option to validate whether an `` will be shimmed 17 | 18 |
19 | 20 | > “Things that are impossible just take longer.” 21 | > — Ian Hickson 22 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | grunt.initConfig({ 3 | pkg: grunt.file.readJSON('package.json'), 4 | eslint: { 5 | gruntfile: { 6 | options: { 7 | rules: { 8 | camelcase: [0], 9 | 'global-require': [0] 10 | } 11 | }, 12 | files: { 13 | src: ['Gruntfile.js'] 14 | } 15 | }, 16 | buildfile: { 17 | options: { 18 | globals: ['LEGACY_SUPPORT', 'svg4everybody'], 19 | rules: { 20 | 'no-magic-numbers': [0], 21 | 'no-unused-vars': [0] 22 | } 23 | }, 24 | files: { 25 | src: ['lib/svg4everybody.js'] 26 | } 27 | } 28 | }, 29 | jscs: { 30 | gruntfile: { 31 | options: { 32 | requireCamelCaseOrUpperCaseIdentifiers: null 33 | }, 34 | files: { 35 | src: ['Gruntfile.js'] 36 | } 37 | }, 38 | buildfile: { 39 | files: { 40 | src: ['lib/svg4everybody.js'] 41 | } 42 | } 43 | }, 44 | uglify: { 45 | build: { 46 | files: { 47 | 'dist/svg4everybody.js': ['dist/svg4everybody.legacy.js'] 48 | }, 49 | options: { 50 | beautify: { 51 | beautify: true, 52 | bracketize: true 53 | }, 54 | compress: { 55 | global_defs: { 56 | LEGACY_SUPPORT: false 57 | } 58 | }, 59 | mangle: false, 60 | preserveComments: 'some' 61 | } 62 | }, 63 | buildmin: { 64 | files: { 65 | 'dist/svg4everybody.min.js': ['dist/svg4everybody.legacy.js'] 66 | }, 67 | options: { 68 | compress: { 69 | global_defs: { 70 | LEGACY_SUPPORT: false 71 | } 72 | }, 73 | mangle: true, 74 | preserveComments: false 75 | } 76 | }, 77 | legacy: { 78 | files: { 79 | 'dist/svg4everybody.legacy.js': ['dist/svg4everybody.legacy.js'] 80 | }, 81 | options: { 82 | beautify: { 83 | beautify: true, 84 | bracketize: true 85 | }, 86 | compress: { 87 | global_defs: { 88 | LEGACY_SUPPORT: true 89 | } 90 | }, 91 | mangle: false, 92 | preserveComments: 'some' 93 | } 94 | }, 95 | legacymin: { 96 | files: { 97 | 'dist/svg4everybody.legacy.min.js': ['dist/svg4everybody.legacy.js'] 98 | }, 99 | options: { 100 | compress: { 101 | global_defs: { 102 | LEGACY_SUPPORT: true 103 | } 104 | }, 105 | mangle: true, 106 | preserveComments: false 107 | } 108 | } 109 | }, 110 | umd: { 111 | build: { 112 | options: { 113 | src: 'lib/svg4everybody.js', 114 | dest: 'dist/svg4everybody.legacy.js', 115 | globalAlias: 'svg4everybody', 116 | objectToExport: 'svg4everybody' 117 | } 118 | } 119 | }, 120 | watch: { 121 | files: ['lib/svg4everybody.js'], 122 | tasks: ['test', 'build'] 123 | } 124 | }); 125 | 126 | require('load-grunt-tasks')(grunt); 127 | 128 | // npm run test 129 | grunt.registerTask('test', ['eslint', 'jscs']); 130 | 131 | // npm run build, grunt build 132 | grunt.registerTask('build', ['test', 'umd', 'uglify']); 133 | 134 | // npm run watch, grunt build:watch 135 | grunt.registerTask('build:watch', ['build', 'watch']); 136 | 137 | // grunt 138 | grunt.registerTask('default', ['build']); 139 | }; 140 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # CC0 1.0 Universal License 2 | 3 | Public Domain Dedication 4 | 5 | The person(s) who associated a work with this deed has dedicated the work to the public domain by waiving all of his or her rights to the work worldwide under copyright law, including all related and neighboring rights, to the extent allowed by law. 6 | 7 | You can copy, modify, distribute and perform the work, even for commercial purposes, all without asking permission. 8 | 9 | In no way are the patent or trademark rights of any person affected by CC0, nor are the rights that other persons may have in the work or in how the work is used, such as publicity or privacy rights. 10 | 11 | Unless expressly stated otherwise, the person(s) who associated a work with this deed makes no warranties about the work, and disclaims liability for all uses of the work, to the fullest extent permitted by applicable law. 12 | 13 | When using or citing the work, you should not imply endorsement by the author or the affirmer. 14 | 15 | This is a [human-readable summary of the Legal Code](https://creativecommons.org/publicdomain/zero/1.0/) ([read the full text](https://creativecommons.org/publicdomain/zero/1.0/legalcode)). 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SVG for Everybody 2 | 3 | [![NPM Version][npm-img]][npm] [![Build Status][ci-img]][ci] 4 | 5 | [SVG for Everybody] adds [SVG External Content] support to [all browsers]. 6 | 7 | To use it now, include the script in your document. 8 | 9 | ```html 10 | 11 | 12 | ``` 13 | 14 | To support Internet Explorer 6-8, include the legacy script instead. 15 | 16 | ```html 17 | 18 | 19 | ``` 20 | 21 | _As of v2.0.0, you must manually call `svg4everybody()`. If you are using an AMD/CommonJS dependency loader then you may call it within the callback closure._ 22 | 23 | IE 6-8 require you to put the script in the `` in order to shiv `` and `` elements. For best results in IE, set [X-UA-Compatible] to `ie=edge`. This can be done with a response header from the server or the following HTML in the ``. 24 | 25 | ```html 26 | 27 | ``` 28 | 29 | ## Usage 30 | 31 | Create an SVG image. 32 | 33 | **map.svg:** 34 | ```html 35 | 36 | 37 | CodePen 38 | 39 | 40 | 41 | YouTube 42 | 43 | 44 | 45 | Twitter 46 | 47 | 48 | 49 | ``` 50 | 51 | This spritemap displays fine in **Chrome**, **Safari 7.1+**, **Firefox**, **Edge 13+**, **Opera**. [SVG for Everybody] polyfills the experience in **Safari 6**, **IE 6+**, and **Edge 12**. 52 | 53 | ```html 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | ``` 64 | 65 | *IE 6-8 requires a trailing slash `/` when using a self-closing `` element.* 66 | 67 | ![3 SVG logos](http://i.imgur.com/87Npdzn.png) 68 | 69 | Browsers not supporting SVG fallback to images. 70 | 71 | ```html 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | ``` 82 | 83 | By default, fallback images point to a PNG file in the same location as the SVG, only with the `#` hash replaced by a `.` dot and then appended with a `.png` extension. If you want to change this behavior, you can define your own fallback. 84 | 85 | ```js 86 | svg4everybody({ 87 | fallback: function (src, svg, use) { 88 | // src: current href String 89 | // svg: current SVG Element 90 | // use: current USE Element 91 | 92 | return 'fallback.png'; // ok, always return fallback.png 93 | } 94 | }); 95 | ``` 96 | 97 | All `` elements that are descendants of an `` are checked for external content. If you want to change this behavior, you can define your own validator. 98 | 99 | ```js 100 | svg4everybody({ 101 | validate: function (src, svg, use) { 102 | // src: current href String 103 | // svg: current SVG Element 104 | // use: current USE Element 105 | 106 | return true; // ok, everything is valid 107 | } 108 | }); 109 | ``` 110 | 111 | You can override whether the script polyfills External Content at all (`polyfill`), or whether SVG should even be used over fallback images (`nosvg`). 112 | 113 | ```js 114 | svg4everybody({ 115 | nosvg: true, // shiv and elements and use image fallbacks 116 | polyfill: true // polyfill elements for External Content 117 | }); 118 | ``` 119 | 120 | *Use of the `nosvg` option will requires you to use the legacy version of SVG for Everybody.* 121 | 122 | ## Implementation status 123 | 124 | Modern browsers support external content in SVGs, *except Edge*. No frets; we can shim it. 125 | 126 | | OS | Browser | SVG | External Content | Shimmed | 127 | |:---:|-------------------|:---:|:----------------:|:-------:| 128 | | * | Chrome 21+ | ✔ | ✔ | — | 129 | | * | Chrome 14-20 | ✔ | ✖ | ✔ | 130 | | * | Firefox 4+ | ✔ | ✔ | — | 131 | | * | Opera 23+ | ✔ | ✔ | — | 132 | | * | Opera Mini 8+ | ✔ | ✔ | — | 133 | | And | And. Browser 40+ | ✔ | ✔ | — | 134 | | And | And. Browser 4.1+ | ✔ | ✖ | ✔ | 135 | | iOS | iOS 8.1+ | ✔ | ✔ | — | 136 | | iOS | iOS 6-7 | ✔ | ✖ | ✔ | 137 | | OSX | Saf 7.1+ | ✔ | ✔ | — | 138 | | OSX | Saf 6 | ✔ | ✖ | ✔ | 139 | | Win | Edge 13+ | ✔ | ✔ | — | 140 | | Win | Edge 12 | ✔ | ✖ | ✔ | 141 | | Win | IE 9 - 11 | ✔ | ✖ | ✔ | 142 | | Win | IE 6 - 8 | ✖ | ✖ | ✔ | 143 | 144 | As you can see, **all major browsers support external content**. 145 | 146 | We had been waiting on Edge, previously, but [David Storey], Edge’s project manager assured us that native support for external content in SVGs was high on their checklist. We would [track progress] and [vote for attention] to this issue. Then, just as I predicted... 147 | 148 | > I have complete faith in the Microsoft Edge team and absolutely expect support to arrive within the next few months. 149 | > 150 | > — Jon Neal (August, 2015) 151 | 152 | All of our [dreams came true]. 153 | 154 | ## Readability and accessibility 155 | 156 | SVGs are compelling to use for many reasons, and one of them is their ease of accessibility. 157 | 158 | Within your spritemap, have each sprite use a `` element to identify itself. 159 | 160 | ```html 161 | 162 | CodePen 163 | 164 | 165 | ``` 166 | 167 | When this sprite is used, its title will be read aloud in [JAWS](http://www.freedomscientific.com/products/fs/JAWS-product-page.asp) and [NVDA](http://www.nvaccess.org/). Then, within your document, each sprite may use a `title` attribute to identify itself. 168 | 169 | ```html 170 | 171 | 172 | 173 | ``` 174 | 175 | That title will be read aloud in [VoiceOver](http://www.apple.com/accessibility/osx/voiceover/) and [NVDA](http://www.nvaccess.org/). At present, the `title` attribute is the only way to properly read aloud an SVG in VoiceOver. I’ll let you know if this changes. 176 | 177 | All together, **use the `title` attribute in your document and the `title` element in your SVG**. 178 | 179 | ARIA roles may also be used to provide even more information to assistive technology. 180 | 181 | When a sprite is merely decoration, use `role="presentation"`. 182 | 183 | ```html 184 | 185 | 186 | Find me on Twitter 187 | ``` 188 | 189 | Otherwise, use `role="img"` and remember to add a description. 190 | 191 | ```html 192 | 193 | 194 | 195 | ``` 196 | 197 | ### Further reading 198 | 199 | - [Tips for creating accessible SVG](https://www.sitepoint.com/tips-accessible-svg/) 200 | - [Using ARIA to enhance SVG accessibility](http://blog.paciellogroup.com/2013/12/using-aria-enhance-svg-accessibility/) 201 | - [SVG symbol a good choice for icons](http://css-tricks.com/svg-symbol-good-choice-icons/) 202 | - [Implementing inline SVG Icons](https://kartikprabhu.com/article/inline-svg-icons) 203 | 204 | ## Optimized SVGs 205 | 206 | SVG files, especially exported from vector tools, often contain tons of unnecessary data such as editor metadata, comments, hidden elements, and other stuff that can be safely removed without affecting SVG rendering result. 207 | 208 | Use a tool like [SVGO] to optimize SVG spritemaps. 209 | 210 | ```bash 211 | $ [sudo] npm install -g svgo 212 | $ svgo spritemap.svg 213 | ``` 214 | 215 | [ci]: https://travis-ci.org/jonathantneal/svg4everybody 216 | [ci-img]: https://img.shields.io/travis/jonathantneal/svg4everybody.svg 217 | [npm]: https://www.npmjs.com/package/svg4everybody 218 | [npm-img]: https://img.shields.io/npm/v/svg4everybody.svg 219 | 220 | [all browsers]: http://caniuse.com/svg 221 | [David Storey]: https://twitter.com/dstorey/status/626514631884804096 222 | [dreams came true]: https://dev.windows.com/en-us/microsoft-edge/platform/changelog/desktop/10586/?compareWith=10240 223 | [SVG External Content]: http://css-tricks.com/svg-sprites-use-better-icon-fonts/##Browser+Support 224 | [SVG for Everybody]: https://github.com/jonathantneal/svg4everybody 225 | [SVGO]: https://github.com/svg/svgo 226 | [track progress]: http://dev.modern.ie/platform/status/svgexternalcontent/?filter=f3e0000bf&search=svg 227 | [vote for attention]: https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/6263916-svg-external-content 228 | [X-UA-Compatible]: http://www.modern.ie/en-us/performance/how-to-use-x-ua-compatible 229 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svg4everybody", 3 | "description": "Use external SVG spritemaps in any browser", 4 | "authors": ["Jonathan Neal (http://jonathantneal.com)"], 5 | "homepage": "https://github.com/jonathantneal/svg4everybody", 6 | "license": "CC0 1.0", 7 | "keywords": ["svgs", "sprites", "spritemaps", "symbols", "defs", "uses", "oldies", "ie8s", "ie9s", "safaris", "externals", "icons", "fallbacks"], 8 | "repository": { 9 | "type": "git", 10 | "url": "git@github.com:jonathantneal/svg4everybody.git" 11 | }, 12 | "main": [ 13 | "dist/svg4everybody.js" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /dist/svg4everybody.js: -------------------------------------------------------------------------------- 1 | !function(root, factory) { 2 | "function" == typeof define && define.amd ? // AMD. Register as an anonymous module unless amdModuleId is set 3 | define([], function() { 4 | return root.svg4everybody = factory(); 5 | }) : "object" == typeof module && module.exports ? // Node. Does not work with strict CommonJS, but 6 | // only CommonJS-like environments that support module.exports, 7 | // like Node. 8 | module.exports = factory() : root.svg4everybody = factory(); 9 | }(this, function() { 10 | /*! svg4everybody v2.1.9 | github.com/jonathantneal/svg4everybody */ 11 | function embed(parent, svg, target, use) { 12 | // if the target exists 13 | if (target) { 14 | // create a document fragment to hold the contents of the target 15 | var fragment = document.createDocumentFragment(), viewBox = !svg.hasAttribute("viewBox") && target.getAttribute("viewBox"); 16 | // conditionally set the viewBox on the svg 17 | viewBox && svg.setAttribute("viewBox", viewBox); 18 | // copy the contents of the clone into the fragment 19 | for (// clone the target 20 | var clone = document.importNode ? document.importNode(target, !0) : target.cloneNode(!0), g = document.createElementNS(svg.namespaceURI || "http://www.w3.org/2000/svg", "g"); clone.childNodes.length; ) { 21 | g.appendChild(clone.firstChild); 22 | } 23 | if (use) { 24 | for (var i = 0; use.attributes.length > i; i++) { 25 | var attr = use.attributes[i]; 26 | "xlink:href" !== attr.name && "href" !== attr.name && g.setAttribute(attr.name, attr.value); 27 | } 28 | } 29 | fragment.appendChild(g), // append the fragment into the svg 30 | parent.appendChild(fragment); 31 | } 32 | } 33 | function loadreadystatechange(xhr, use) { 34 | // listen to changes in the request 35 | xhr.onreadystatechange = function() { 36 | // if the request is ready 37 | if (4 === xhr.readyState) { 38 | // get the cached html document 39 | var cachedDocument = xhr._cachedDocument; 40 | // ensure the cached html document based on the xhr response 41 | cachedDocument || (cachedDocument = xhr._cachedDocument = document.implementation.createHTMLDocument(""), 42 | cachedDocument.body.innerHTML = xhr.responseText, // ensure domains are the same, otherwise we'll have issues appending the 43 | // element in IE 11 44 | cachedDocument.domain !== document.domain && (cachedDocument.domain = document.domain), 45 | xhr._cachedTarget = {}), // clear the xhr embeds list and embed each item 46 | xhr._embeds.splice(0).map(function(item) { 47 | // get the cached target 48 | var target = xhr._cachedTarget[item.id]; 49 | // ensure the cached target 50 | target || (target = xhr._cachedTarget[item.id] = cachedDocument.getElementById(item.id)), 51 | // embed the target into the svg 52 | embed(item.parent, item.svg, target, use); 53 | }); 54 | } 55 | }, // test the ready state change immediately 56 | xhr.onreadystatechange(); 57 | } 58 | function svg4everybody(rawopts) { 59 | function oninterval() { 60 | // if all s in the array are being bypassed, don't proceed. 61 | if (numberOfSvgUseElementsToBypass && uses.length - numberOfSvgUseElementsToBypass <= 0) { 62 | return void requestAnimationFrame(oninterval, 67); 63 | } 64 | // if there are s to process, proceed. 65 | // reset the bypass counter, since the counter will be incremented for every bypassed element, 66 | // even ones that were counted before. 67 | numberOfSvgUseElementsToBypass = 0; 68 | // while the index exists in the live collection 69 | for (// get the cached index 70 | var index = 0; index < uses.length; ) { 71 | // get the current 72 | var use = uses[index], parent = use.parentNode, svg = getSVGAncestor(parent), src = use.getAttribute("xlink:href") || use.getAttribute("href"); 73 | if (!src && opts.attributeName && (src = use.getAttribute(opts.attributeName)), 74 | svg && src) { 75 | if (polyfill) { 76 | if (!opts.validate || opts.validate(src, svg, use)) { 77 | // remove the element 78 | parent.removeChild(use); 79 | // parse the src and get the url and id 80 | var srcSplit = src.split("#"), url = srcSplit.shift(), id = srcSplit.join("#"); 81 | // if the link is external 82 | if (url.length) { 83 | // get the cached xhr request 84 | var xhr = requests[url]; 85 | // ensure the xhr request exists 86 | xhr || (xhr = requests[url] = new XMLHttpRequest(), xhr.open("GET", url), xhr.send(), 87 | xhr._embeds = []), // add the svg and id as an item to the xhr embeds list 88 | xhr._embeds.push({ 89 | parent: parent, 90 | svg: svg, 91 | id: id 92 | }), // prepare the xhr ready state change event 93 | loadreadystatechange(xhr, use); 94 | } else { 95 | // embed the local id into the svg 96 | embed(parent, svg, document.getElementById(id), use); 97 | } 98 | } else { 99 | // increase the index when the previous value was not "valid" 100 | ++index, ++numberOfSvgUseElementsToBypass; 101 | } 102 | } 103 | } else { 104 | // increase the index when the previous value was not "valid" 105 | ++index; 106 | } 107 | } 108 | // continue the interval 109 | requestAnimationFrame(oninterval, 67); 110 | } 111 | var polyfill, opts = Object(rawopts), newerIEUA = /\bTrident\/[567]\b|\bMSIE (?:9|10)\.0\b/, webkitUA = /\bAppleWebKit\/(\d+)\b/, olderEdgeUA = /\bEdge\/12\.(\d+)\b/, edgeUA = /\bEdge\/.(\d+)\b/, inIframe = window.top !== window.self; 112 | polyfill = "polyfill" in opts ? opts.polyfill : newerIEUA.test(navigator.userAgent) || (navigator.userAgent.match(olderEdgeUA) || [])[1] < 10547 || (navigator.userAgent.match(webkitUA) || [])[1] < 537 || edgeUA.test(navigator.userAgent) && inIframe; 113 | // create xhr requests object 114 | var requests = {}, requestAnimationFrame = window.requestAnimationFrame || setTimeout, uses = document.getElementsByTagName("use"), numberOfSvgUseElementsToBypass = 0; 115 | // conditionally start the interval if the polyfill is active 116 | polyfill && oninterval(); 117 | } 118 | function getSVGAncestor(node) { 119 | for (var svg = node; "svg" !== svg.nodeName.toLowerCase() && (svg = svg.parentNode); ) {} 120 | return svg; 121 | } 122 | return svg4everybody; 123 | }); -------------------------------------------------------------------------------- /dist/svg4everybody.legacy.js: -------------------------------------------------------------------------------- 1 | !function(root, factory) { 2 | "function" == typeof define && define.amd ? // AMD. Register as an anonymous module unless amdModuleId is set 3 | define([], function() { 4 | return root.svg4everybody = factory(); 5 | }) : "object" == typeof module && module.exports ? // Node. Does not work with strict CommonJS, but 6 | // only CommonJS-like environments that support module.exports, 7 | // like Node. 8 | module.exports = factory() : root.svg4everybody = factory(); 9 | }(this, function() { 10 | /*! svg4everybody v2.1.9 | github.com/jonathantneal/svg4everybody */ 11 | function embed(parent, svg, target, use) { 12 | // if the target exists 13 | if (target) { 14 | // create a document fragment to hold the contents of the target 15 | var fragment = document.createDocumentFragment(), viewBox = !svg.hasAttribute("viewBox") && target.getAttribute("viewBox"); 16 | // conditionally set the viewBox on the svg 17 | viewBox && svg.setAttribute("viewBox", viewBox); 18 | // copy the contents of the clone into the fragment 19 | for (// clone the target 20 | var clone = document.importNode ? document.importNode(target, !0) : target.cloneNode(!0), g = document.createElementNS(svg.namespaceURI || "http://www.w3.org/2000/svg", "g"); clone.childNodes.length; ) { 21 | g.appendChild(clone.firstChild); 22 | } 23 | if (use) { 24 | for (var i = 0; use.attributes.length > i; i++) { 25 | var attr = use.attributes[i]; 26 | "xlink:href" !== attr.name && "href" !== attr.name && g.setAttribute(attr.name, attr.value); 27 | } 28 | } 29 | fragment.appendChild(g), // append the fragment into the svg 30 | parent.appendChild(fragment); 31 | } 32 | } 33 | function loadreadystatechange(xhr, use) { 34 | // listen to changes in the request 35 | xhr.onreadystatechange = function() { 36 | // if the request is ready 37 | if (4 === xhr.readyState) { 38 | // get the cached html document 39 | var cachedDocument = xhr._cachedDocument; 40 | // ensure the cached html document based on the xhr response 41 | cachedDocument || (cachedDocument = xhr._cachedDocument = document.implementation.createHTMLDocument(""), 42 | cachedDocument.body.innerHTML = xhr.responseText, // ensure domains are the same, otherwise we'll have issues appending the 43 | // element in IE 11 44 | cachedDocument.domain !== document.domain && (cachedDocument.domain = document.domain), 45 | xhr._cachedTarget = {}), // clear the xhr embeds list and embed each item 46 | xhr._embeds.splice(0).map(function(item) { 47 | // get the cached target 48 | var target = xhr._cachedTarget[item.id]; 49 | // ensure the cached target 50 | target || (target = xhr._cachedTarget[item.id] = cachedDocument.getElementById(item.id)), 51 | // embed the target into the svg 52 | embed(item.parent, item.svg, target, use); 53 | }); 54 | } 55 | }, // test the ready state change immediately 56 | xhr.onreadystatechange(); 57 | } 58 | function svg4everybody(rawopts) { 59 | function oninterval() { 60 | // if all s in the array are being bypassed, don't proceed. 61 | if (numberOfSvgUseElementsToBypass && uses.length - numberOfSvgUseElementsToBypass <= 0) { 62 | return void requestAnimationFrame(oninterval, 67); 63 | } 64 | // if there are s to process, proceed. 65 | // reset the bypass counter, since the counter will be incremented for every bypassed element, 66 | // even ones that were counted before. 67 | numberOfSvgUseElementsToBypass = 0; 68 | // while the index exists in the live collection 69 | for (// get the cached index 70 | var index = 0; index < uses.length; ) { 71 | // get the current 72 | var use = uses[index], parent = use.parentNode, svg = getSVGAncestor(parent), src = use.getAttribute("xlink:href") || use.getAttribute("href"); 73 | if (!src && opts.attributeName && (src = use.getAttribute(opts.attributeName)), 74 | svg && src) { 75 | // if running with legacy support 76 | if (nosvg) { 77 | // create a new fallback image 78 | var img = document.createElement("img"); 79 | // force display in older IE 80 | img.style.cssText = "display:inline-block;height:100%;width:100%", // set the fallback size using the svg size 81 | img.setAttribute("width", svg.getAttribute("width") || svg.clientWidth), img.setAttribute("height", svg.getAttribute("height") || svg.clientHeight), 82 | // set the fallback src 83 | img.src = fallback(src, svg, use), // replace the with the fallback image 84 | parent.replaceChild(img, use); 85 | } else { 86 | if (polyfill) { 87 | if (!opts.validate || opts.validate(src, svg, use)) { 88 | // remove the element 89 | parent.removeChild(use); 90 | // parse the src and get the url and id 91 | var srcSplit = src.split("#"), url = srcSplit.shift(), id = srcSplit.join("#"); 92 | // if the link is external 93 | if (url.length) { 94 | // get the cached xhr request 95 | var xhr = requests[url]; 96 | // ensure the xhr request exists 97 | xhr || (xhr = requests[url] = new XMLHttpRequest(), xhr.open("GET", url), xhr.send(), 98 | xhr._embeds = []), // add the svg and id as an item to the xhr embeds list 99 | xhr._embeds.push({ 100 | parent: parent, 101 | svg: svg, 102 | id: id 103 | }), // prepare the xhr ready state change event 104 | loadreadystatechange(xhr, use); 105 | } else { 106 | // embed the local id into the svg 107 | embed(parent, svg, document.getElementById(id), use); 108 | } 109 | } else { 110 | // increase the index when the previous value was not "valid" 111 | ++index, ++numberOfSvgUseElementsToBypass; 112 | } 113 | } 114 | } 115 | } else { 116 | // increase the index when the previous value was not "valid" 117 | ++index; 118 | } 119 | } 120 | // continue the interval 121 | requestAnimationFrame(oninterval, 67); 122 | } 123 | var nosvg, fallback, opts = Object(rawopts); 124 | // configure the fallback method 125 | fallback = opts.fallback || function(src) { 126 | return src.replace(/\?[^#]+/, "").replace("#", ".").replace(/^\./, "") + ".png" + (/\?[^#]+/.exec(src) || [ "" ])[0]; 127 | }, // set whether to shiv and elements and use image fallbacks 128 | nosvg = "nosvg" in opts ? opts.nosvg : /\bMSIE [1-8]\b/.test(navigator.userAgent), 129 | // conditionally shiv and 130 | nosvg && (document.createElement("svg"), document.createElement("use")); 131 | // set whether the polyfill will be activated or not 132 | var polyfill, olderIEUA = /\bMSIE [1-8]\.0\b/, newerIEUA = /\bTrident\/[567]\b|\bMSIE (?:9|10)\.0\b/, webkitUA = /\bAppleWebKit\/(\d+)\b/, olderEdgeUA = /\bEdge\/12\.(\d+)\b/, edgeUA = /\bEdge\/.(\d+)\b/, inIframe = window.top !== window.self; 133 | polyfill = "polyfill" in opts ? opts.polyfill : olderIEUA.test(navigator.userAgent) || newerIEUA.test(navigator.userAgent) || (navigator.userAgent.match(olderEdgeUA) || [])[1] < 10547 || (navigator.userAgent.match(webkitUA) || [])[1] < 537 || edgeUA.test(navigator.userAgent) && inIframe; 134 | // create xhr requests object 135 | var requests = {}, requestAnimationFrame = window.requestAnimationFrame || setTimeout, uses = document.getElementsByTagName("use"), numberOfSvgUseElementsToBypass = 0; 136 | // conditionally start the interval if the polyfill is active 137 | polyfill && oninterval(); 138 | } 139 | function getSVGAncestor(node) { 140 | for (var svg = node; "svg" !== svg.nodeName.toLowerCase() && (svg = svg.parentNode); ) {} 141 | return svg; 142 | } 143 | return svg4everybody; 144 | }); -------------------------------------------------------------------------------- /dist/svg4everybody.legacy.min.js: -------------------------------------------------------------------------------- 1 | !function(a,b){"function"==typeof define&&define.amd?define([],function(){return a.svg4everybody=b()}):"object"==typeof module&&module.exports?module.exports=b():a.svg4everybody=b()}(this,function(){function a(a,b,c,d){if(c){var e=document.createDocumentFragment(),f=!b.hasAttribute("viewBox")&&c.getAttribute("viewBox");f&&b.setAttribute("viewBox",f);for(var g=document.importNode?document.importNode(c,!0):c.cloneNode(!0),h=document.createElementNS(b.namespaceURI||"http://www.w3.org/2000/svg","g");g.childNodes.length;)h.appendChild(g.firstChild);if(d)for(var i=0;d.attributes.length>i;i++){var j=d.attributes[i];"xlink:href"!==j.name&&"href"!==j.name&&h.setAttribute(j.name,j.value)}e.appendChild(h),a.appendChild(e)}}function b(b,c){b.onreadystatechange=function(){if(4===b.readyState){var d=b._cachedDocument;d||(d=b._cachedDocument=document.implementation.createHTMLDocument(""),d.body.innerHTML=b.responseText,b._cachedTarget={}),b._embeds.splice(0).map(function(e){var f=b._cachedTarget[e.id];f||(f=b._cachedTarget[e.id]=d.getElementById(e.id)),a(e.parent,e.svg,f,c)})}},b.onreadystatechange()}function c(c){function e(){for(var c=0;c0)&&q(e,67)}var f,g,h=Object(c);g=h.fallback||function(a){return a.replace(/\?[^#]+/,"").replace("#",".").replace(/^\./,"")+".png"+(/\?[^#]+/.exec(a)||[""])[0]},f="nosvg"in h?h.nosvg:/\bMSIE [1-8]\b/.test(navigator.userAgent),f&&(document.createElement("svg"),document.createElement("use"));var i,j=/\bMSIE [1-8]\.0\b/,k=/\bTrident\/[567]\b|\bMSIE (?:9|10)\.0\b/,l=/\bAppleWebKit\/(\d+)\b/,m=/\bEdge\/12\.(\d+)\b/,n=/\bEdge\/.(\d+)\b/,o=window.top!==window.self;i="polyfill"in h?h.polyfill:j.test(navigator.userAgent)||k.test(navigator.userAgent)||(navigator.userAgent.match(m)||[])[1]<10547||(navigator.userAgent.match(l)||[])[1]<537||n.test(navigator.userAgent)&&o;var p={},q=window.requestAnimationFrame||setTimeout,r=document.getElementsByTagName("use"),s=0;i&&e()}function d(a){for(var b=a;"svg"!==b.nodeName.toLowerCase()&&(b=b.parentNode););return b}return c}); 2 | -------------------------------------------------------------------------------- /dist/svg4everybody.min.js: -------------------------------------------------------------------------------- 1 | !function(a,b){"function"==typeof define&&define.amd?define([],function(){return a.svg4everybody=b()}):"object"==typeof module&&module.exports?module.exports=b():a.svg4everybody=b()}(this,function(){function a(a,b,c,d){if(c){var e=document.createDocumentFragment(),f=!b.hasAttribute("viewBox")&&c.getAttribute("viewBox");f&&b.setAttribute("viewBox",f);for(var g=document.importNode?document.importNode(c,!0):c.cloneNode(!0),h=document.createElementNS(b.namespaceURI||"http://www.w3.org/2000/svg","g");g.childNodes.length;)h.appendChild(g.firstChild);if(d)for(var i=0;d.attributes.length>i;i++){var j=d.attributes[i];"xlink:href"!==j.name&&"href"!==j.name&&h.setAttribute(j.name,j.value)}e.appendChild(h),a.appendChild(e)}}function b(b,c){b.onreadystatechange=function(){if(4===b.readyState){var d=b._cachedDocument;d||(d=b._cachedDocument=document.implementation.createHTMLDocument(""),d.body.innerHTML=b.responseText,b._cachedTarget={}),b._embeds.splice(0).map(function(e){var f=b._cachedTarget[e.id];f||(f=b._cachedTarget[e.id]=d.getElementById(e.id)),a(e.parent,e.svg,f,c)})}},b.onreadystatechange()}function c(c){function e(){for(var c=0;c0)&&n(e,67)}var f,g=Object(c),h=/\bTrident\/[567]\b|\bMSIE (?:9|10)\.0\b/,i=/\bAppleWebKit\/(\d+)\b/,j=/\bEdge\/12\.(\d+)\b/,k=/\bEdge\/.(\d+)\b/,l=window.top!==window.self;f="polyfill"in g?g.polyfill:h.test(navigator.userAgent)||(navigator.userAgent.match(j)||[])[1]<10547||(navigator.userAgent.match(i)||[])[1]<537||k.test(navigator.userAgent)&&l;var m={},n=window.requestAnimationFrame||setTimeout,o=document.getElementsByTagName("use"),p=0;f&&e()}function d(a){for(var b=a;"svg"!==b.nodeName.toLowerCase()&&(b=b.parentNode););return b}return c}); 2 | -------------------------------------------------------------------------------- /lib/svg4everybody.js: -------------------------------------------------------------------------------- 1 | /*! svg4everybody v2.1.9 | github.com/jonathantneal/svg4everybody */ 2 | 3 | function embed(parent, svg, target, use) { 4 | // if the target exists 5 | if (target) { 6 | // create a document fragment to hold the contents of the target 7 | var fragment = document.createDocumentFragment(); 8 | 9 | // cache the closest matching viewBox 10 | var viewBox = !svg.hasAttribute('viewBox') && target.getAttribute('viewBox'); 11 | 12 | // conditionally set the viewBox on the svg 13 | if (viewBox) { 14 | svg.setAttribute('viewBox', viewBox); 15 | } 16 | 17 | // clone the target 18 | var clone = document.importNode ? document.importNode(target, true) : target.cloneNode(true); 19 | 20 | var g = document.createElementNS(svg.namespaceURI || 'http://www.w3.org/2000/svg', 'g'); 21 | 22 | // copy the contents of the clone into the fragment 23 | while (clone.childNodes.length) { 24 | g.appendChild(clone.firstChild); 25 | } 26 | 27 | if (use) { 28 | for (var i = 0; use.attributes.length > i; i++) { 29 | var attr = use.attributes[i]; 30 | if (attr.name === 'xlink:href' || attr.name === 'href') { 31 | continue; 32 | } 33 | g.setAttribute(attr.name, attr.value); 34 | } 35 | } 36 | 37 | fragment.appendChild(g); 38 | 39 | // append the fragment into the svg 40 | parent.appendChild(fragment); 41 | } 42 | } 43 | 44 | function loadreadystatechange(xhr, use) { 45 | // listen to changes in the request 46 | xhr.onreadystatechange = function () { 47 | // if the request is ready 48 | if (xhr.readyState === 4) { 49 | // get the cached html document 50 | var cachedDocument = xhr._cachedDocument; 51 | 52 | // ensure the cached html document based on the xhr response 53 | if (!cachedDocument) { 54 | cachedDocument = xhr._cachedDocument = document.implementation.createHTMLDocument(''); 55 | 56 | cachedDocument.body.innerHTML = xhr.responseText; 57 | 58 | // ensure domains are the same, otherwise we'll have issues appending the 59 | // element in IE 11 60 | if (cachedDocument.domain !== document.domain) { 61 | cachedDocument.domain = document.domain; 62 | } 63 | 64 | xhr._cachedTarget = {}; 65 | } 66 | 67 | // clear the xhr embeds list and embed each item 68 | xhr._embeds.splice(0).map(function (item) { 69 | // get the cached target 70 | var target = xhr._cachedTarget[item.id]; 71 | 72 | // ensure the cached target 73 | if (!target) { 74 | target = xhr._cachedTarget[item.id] = cachedDocument.getElementById(item.id); 75 | } 76 | 77 | // embed the target into the svg 78 | embed(item.parent, item.svg, target, use); 79 | }); 80 | } 81 | }; 82 | 83 | // test the ready state change immediately 84 | xhr.onreadystatechange(); 85 | } 86 | 87 | function svg4everybody(rawopts) { 88 | var opts = Object(rawopts); 89 | 90 | // create legacy support variables 91 | var nosvg; 92 | var fallback; 93 | 94 | // if running with legacy support 95 | if (LEGACY_SUPPORT) { 96 | // configure the fallback method 97 | fallback = opts.fallback || function (src) { 98 | return src.replace(/\?[^#]+/, '').replace('#', '.').replace(/^\./, '') + '.png' + (/\?[^#]+/.exec(src) || [''])[0]; 99 | }; 100 | 101 | // set whether to shiv and elements and use image fallbacks 102 | nosvg = 'nosvg' in opts ? opts.nosvg : /\bMSIE [1-8]\b/.test(navigator.userAgent); 103 | 104 | // conditionally shiv and 105 | if (nosvg) { 106 | document.createElement('svg'); 107 | document.createElement('use'); 108 | } 109 | } 110 | 111 | // set whether the polyfill will be activated or not 112 | var polyfill; 113 | var olderIEUA = /\bMSIE [1-8]\.0\b/; 114 | var newerIEUA = /\bTrident\/[567]\b|\bMSIE (?:9|10)\.0\b/; 115 | var webkitUA = /\bAppleWebKit\/(\d+)\b/; 116 | var olderEdgeUA = /\bEdge\/12\.(\d+)\b/; 117 | var edgeUA = /\bEdge\/.(\d+)\b/; 118 | //Checks whether iframed 119 | var inIframe = window.top !== window.self; 120 | 121 | if ('polyfill' in opts) { 122 | polyfill = opts.polyfill; 123 | } else if (LEGACY_SUPPORT) { 124 | polyfill = olderIEUA.test(navigator.userAgent) || newerIEUA.test(navigator.userAgent) || (navigator.userAgent.match(olderEdgeUA) || [])[1] < 10547 || (navigator.userAgent.match(webkitUA) || [])[1] < 537 || edgeUA.test(navigator.userAgent) && inIframe; 125 | } else { 126 | polyfill = newerIEUA.test(navigator.userAgent) || (navigator.userAgent.match(olderEdgeUA) || [])[1] < 10547 || (navigator.userAgent.match(webkitUA) || [])[1] < 537 || edgeUA.test(navigator.userAgent) && inIframe; 127 | } 128 | 129 | // create xhr requests object 130 | var requests = {}; 131 | 132 | // use request animation frame or a timeout to search the dom for svgs 133 | var requestAnimationFrame = window.requestAnimationFrame || setTimeout; 134 | 135 | // get a live collection of use elements on the page 136 | var uses = document.getElementsByTagName('use'); 137 | var numberOfSvgUseElementsToBypass = 0; 138 | 139 | function oninterval() { 140 | // if all s in the array are being bypassed, don't proceed. 141 | if (numberOfSvgUseElementsToBypass && uses.length - numberOfSvgUseElementsToBypass <= 0) { 142 | return void requestAnimationFrame(oninterval, 67); 143 | } 144 | 145 | // if there are s to process, proceed. 146 | 147 | // reset the bypass counter, since the counter will be incremented for every bypassed element, 148 | // even ones that were counted before. 149 | numberOfSvgUseElementsToBypass = 0; 150 | 151 | // get the cached index 152 | var index = 0; 153 | 154 | // while the index exists in the live collection 155 | while (index < uses.length) { 156 | // get the current 157 | var use = uses[index]; 158 | 159 | // get the current 160 | var parent = use.parentNode; 161 | var svg = getSVGAncestor(parent); 162 | var src = use.getAttribute('xlink:href') || use.getAttribute('href'); 163 | 164 | if (!src && opts.attributeName) { 165 | src = use.getAttribute(opts.attributeName); 166 | } 167 | 168 | if (svg && src) { 169 | 170 | // if running with legacy support 171 | if (LEGACY_SUPPORT && nosvg) { 172 | // create a new fallback image 173 | var img = document.createElement('img'); 174 | 175 | // force display in older IE 176 | img.style.cssText = 'display:inline-block;height:100%;width:100%'; 177 | 178 | // set the fallback size using the svg size 179 | img.setAttribute('width', svg.getAttribute('width') || svg.clientWidth); 180 | img.setAttribute('height', svg.getAttribute('height') || svg.clientHeight); 181 | 182 | // set the fallback src 183 | img.src = fallback(src, svg, use); 184 | 185 | // replace the with the fallback image 186 | parent.replaceChild(img, use); 187 | } else if (polyfill) { 188 | if (!opts.validate || opts.validate(src, svg, use)) { 189 | // remove the element 190 | parent.removeChild(use); 191 | 192 | // parse the src and get the url and id 193 | var srcSplit = src.split('#'); 194 | var url = srcSplit.shift(); 195 | var id = srcSplit.join('#'); 196 | 197 | // if the link is external 198 | if (url.length) { 199 | // get the cached xhr request 200 | var xhr = requests[url]; 201 | 202 | // ensure the xhr request exists 203 | if (!xhr) { 204 | xhr = requests[url] = new XMLHttpRequest(); 205 | 206 | xhr.open('GET', url); 207 | 208 | xhr.send(); 209 | 210 | xhr._embeds = []; 211 | } 212 | 213 | // add the svg and id as an item to the xhr embeds list 214 | xhr._embeds.push({ 215 | parent: parent, 216 | svg: svg, 217 | id: id 218 | }); 219 | 220 | // prepare the xhr ready state change event 221 | loadreadystatechange(xhr, use); 222 | } else { 223 | // embed the local id into the svg 224 | embed(parent, svg, document.getElementById(id), use); 225 | } 226 | } else { 227 | // increase the index when the previous value was not "valid" 228 | ++index; 229 | ++numberOfSvgUseElementsToBypass; 230 | } 231 | } 232 | } else { 233 | // increase the index when the previous value was not "valid" 234 | ++index; 235 | } 236 | } 237 | 238 | // continue the interval 239 | requestAnimationFrame(oninterval, 67); 240 | } 241 | 242 | // conditionally start the interval if the polyfill is active 243 | if (polyfill) { 244 | oninterval(); 245 | } 246 | } 247 | 248 | function getSVGAncestor(node) { 249 | var svg = node; 250 | while (svg.nodeName.toLowerCase() !== 'svg') { 251 | svg = svg.parentNode; 252 | if (!svg) { 253 | break; 254 | } 255 | } 256 | return svg; 257 | } 258 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svg4everybody", 3 | "version": "2.1.9", 4 | "description": "Use external SVG spritemaps in any browser", 5 | "main": "dist/svg4everybody.js", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "grunt": "^0.4.5", 9 | "grunt-contrib-jshint": "^0.12.0", 10 | "grunt-contrib-uglify": "^0.11.0", 11 | "grunt-contrib-watch": "^0.6.1", 12 | "grunt-eslint": "^17.3.1", 13 | "grunt-jscs": "^2.7.0", 14 | "grunt-umd": "^2.4.0", 15 | "load-grunt-tasks": "^3.4.0" 16 | }, 17 | "engines": { 18 | "node": ">=0.8.0" 19 | }, 20 | "scripts": { 21 | "build": "grunt build", 22 | "clean": "git checkout dist/svg4everybody.js && git checkout dist/svg4everybody.min.js && git checkout dist/svg4everybody.legacy.js && git checkout dist/svg4everybody.legacy.min.js", 23 | "test": "grunt test", 24 | "watch": "grunt build:watch" 25 | }, 26 | "repository": "jonathantneal/svg4everybody", 27 | "keywords": [ 28 | "contents", 29 | "defs", 30 | "externals", 31 | "fallbacks", 32 | "icons", 33 | "ie8s", 34 | "ie9s", 35 | "oldies", 36 | "safaris", 37 | "sprites", 38 | "spritemaps", 39 | "svgs", 40 | "symbols", 41 | "uses" 42 | ], 43 | "author": "Jonathan Neal (http://jonathantneal.com)", 44 | "contributors": [ 45 | "Federico Brigante (https://github.com/bfred-it)", 46 | "Matija Marohnić (https://github.com/silvenon)", 47 | "Michał Rumanek (https://github.com/michal-rumanek)", 48 | "Oleg Andreyev (https://github.com/oleg-andreyev)", 49 | "Shawn Allen (https://github.com/shawnbot)" 50 | ], 51 | "license": "CC0-1.0", 52 | "bugs": "https://github.com/jonathantneal/svg4everybody/issues", 53 | "homepage": "https://github.com/jonathantneal/svg4everybody#readme" 54 | } 55 | -------------------------------------------------------------------------------- /test/1138.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathantneal/svg4everybody/8bd711049ba3faefb8285ca70c63a67efc35636c/test/1138.png -------------------------------------------------------------------------------- /test/demo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/demo.svg.1138.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathantneal/svg4everybody/8bd711049ba3faefb8285ca70c63a67efc35636c/test/demo.svg.1138.png -------------------------------------------------------------------------------- /test/demo.svg.camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathantneal/svg4everybody/8bd711049ba3faefb8285ca70c63a67efc35636c/test/demo.svg.camera.png -------------------------------------------------------------------------------- /test/demo.svg.eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathantneal/svg4everybody/8bd711049ba3faefb8285ca70c63a67efc35636c/test/demo.svg.eye.png -------------------------------------------------------------------------------- /test/demo.svg.pencil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathantneal/svg4everybody/8bd711049ba3faefb8285ca70c63a67efc35636c/test/demo.svg.pencil.png -------------------------------------------------------------------------------- /test/eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jonathantneal/svg4everybody/8bd711049ba3faefb8285ca70c63a67efc35636c/test/eye.png -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | SVG for Everybody 3 | 4 | 5 | 6 | 25 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | --------------------------------------------------------------------------------