├── .npmignore ├── .gitignore ├── component.json ├── bower.json ├── test ├── test.css ├── main.css └── index.html ├── package.json ├── LICENSE.md ├── CONTRIBUTING.md ├── README.md ├── hanging-punctuation.js └── hanging-punctuation.min.js /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | node_modules/ 3 | components/ 4 | bower_components/ 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | .DS_Store 3 | 4 | # Logs 5 | logs 6 | *.log 7 | 8 | # Dependency directory 9 | node_modules/ 10 | components/ 11 | bower_components/ 12 | 13 | # Compile directories 14 | www/ 15 | _book/ 16 | build/ 17 | dist/ 18 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hanging-punctuation", 3 | "repo": "kennethormandy/hanging-punctuation", 4 | "description": "A polyfill for proper hanging punctuation in CSS.", 5 | "version": "0.2.3", 6 | "main": "hanging-punctuation.js", 7 | "scripts": [ 8 | "hanging-punctuation.js" 9 | ], 10 | "keywords": [ 11 | "type", 12 | "typography", 13 | "css", 14 | "stylefill", 15 | "punctuation" 16 | ], 17 | "dependencies": {}, 18 | "development": {}, 19 | "license": "MIT" 20 | } 21 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hanging-punctuation", 3 | "main": "hanging-punctuation.js", 4 | "version": "0.2.3", 5 | "homepage": "https://github.com/kennethormandy/hanging-punctuation", 6 | "authors": ["Kenneth Ormandy "], 7 | "description": "A polyfill for proper hanging punctuation in CSS.", 8 | "license": "MIT", 9 | "keywords": [ 10 | "type", 11 | "typography", 12 | "css", 13 | "stylefill", 14 | "punctuation" 15 | ], 16 | "ignore": [ 17 | "**/.*", 18 | "node_modules", 19 | "bower_components", 20 | "components", 21 | "app/bower_components", 22 | "test" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /test/test.css: -------------------------------------------------------------------------------- 1 | .issue-1 { 2 | width: 589px; 3 | } 4 | 5 | .hardcoded--inline q { 6 | hanging-punctuation: first; 7 | font-style: italic; 8 | } 9 | 10 | .hardcoded--right p { 11 | text-align: right; 12 | } 13 | 14 | .hardcoded--justify p { 15 | text-align: justify; 16 | } 17 | 18 | .pseudo q:before, 19 | .pseudo q:after { 20 | hanging-punctuation: first; 21 | } 22 | 23 | .first p { 24 | hanging-punctuation: first; 25 | } 26 | 27 | .last p { 28 | hanging-punctuation: last; 29 | } 30 | 31 | .both p { 32 | hanging-punctuation: both; 33 | } 34 | 35 | .allow-end p { 36 | text-align: justify; 37 | hanging-punctuation: allow-end; 38 | } 39 | 40 | .force-end p { 41 | text-align: justify; 42 | hanging-punctuation: force-end; 43 | } 44 | -------------------------------------------------------------------------------- /test/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Georgia, serif; 3 | font-size: 1.33em; 4 | font-weight: 400; 5 | line-height: 1.4; 6 | padding: 0 2em; 7 | margin: 0; 8 | color: #444; 9 | overflow-x: hidden; 10 | } 11 | 12 | @media (min-width: 40em) { 13 | body { 14 | padding: 0 5em; 15 | } 16 | } 17 | 18 | h1, h2, h3, h4, h5, h6 { 19 | font-family: "Avenir Next", "Avenir", "Verdana", sans-serif; 20 | } 21 | 22 | pre, 23 | code { 24 | font-family: "Source Code Pro", "Iconsolas", monospace; 25 | } 26 | 27 | article { 28 | max-width: 30em; 29 | margin: 0 auto; 30 | padding: 2em 0; 31 | border-left: 1px solid tomato; 32 | border-right: 1px solid tomato; 33 | } 34 | 35 | q { 36 | quotes: "\201C" "\201D" "\2018" "\2019"; 37 | } 38 | 39 | q:before { 40 | content: open-quote; 41 | background: tomato; 42 | } 43 | 44 | q:after { 45 | content: close-quote; 46 | background: tomato; 47 | } 48 | 49 | .hardcoded q { 50 | quotes: "" "" "" ""; 51 | } 52 | 53 | .hardcoded q:before, 54 | .hardcoded q:after { 55 | content: ""; 56 | content: none; 57 | } 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hanging-punctuation", 3 | "version": "0.2.3", 4 | "description": "A polyfill for proper hanging punctuation in CSS.", 5 | "homepage": "https://github.com/kennethormandy/hanging-punctuation", 6 | "author": "Kenneth Ormandy (http://kennethormandy.com)", 7 | "license": "MIT", 8 | "private": true, 9 | "repository": "git@github.com:kennethormandy/hanging-punctuation.git", 10 | "keywords": [ 11 | "type", 12 | "typography", 13 | "css", 14 | "stylefill", 15 | "punctuation" 16 | ], 17 | "scripts": { 18 | "test": "static -H '{\"Cache-Control\": \"no-cache, must-revalidate\"}'", 19 | "concat": "cat node_modules/stylefill/js/stylefill.js hanging-punctuation.js >> hanging-punctuation.min.js", 20 | "minify": "minify hanging-punctuation.min.js", 21 | "build": "npm run concat && npm run minify" 22 | }, 23 | "dependencies": { 24 | "stylefill": "git://github.com/kennethormandy/stylefill" 25 | }, 26 | "devDependencies": { 27 | "minify": "~0.6.4", 28 | "node-static": "~0.7.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2011 [Manuel Strehl](http://www.manuel-strehl.de) 4 | Copyright © 2014 [Kenneth Ormandy](http://kennethormandy.com) & [Chloi Inc.](http://chloi.io) 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy of 7 | this software and associated documentation files (the “Software”), to deal in 8 | the Software without restriction, including without limitation the rights to 9 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software is furnished to do so, 11 | subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 18 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 19 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 20 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for considering contributing to the `hanging-punctuation` Stylefill. 4 | 5 | If you’re new to all this GitHub, Open Source, JavaScript, Node.js, testing, wow all this stuff seems really difficult I just want to make my sites better stuff, I get it. I’m still there, too. 6 | 7 | Feel free to [send me an email](kenneth@chloi.io) or [open an issue here](http://github.com/kennethormandy/hanging-punctuation/issues) and I’ll do my best to share some resources that have helped me out. No promises—I’m still learning, too—but I can say it would be great to have you involved in any capacity. 8 | 9 | ## Opening issues 10 | 11 | If you find a bug, please feel free to [open an issue](https://github.com/kennethormandy/hanging-punctuation/issues). 12 | 13 | If you taking the time to mention a problem, even a seemingly minor one, it is greatly appreciated, and a totally valid contribution to this project. Thank you! 14 | 15 | ## Fixing bugs 16 | 17 | We love pull requests. Here’s a quick guide: 18 | 19 | 1. [Fork this repository](https://github.com/kennethormandy/hanging-punctuation/fork) and then clone it locally: 20 | 21 | ```bash 22 | git clone https://github.com/kennethormandy/hanging-punctuation 23 | ``` 24 | 25 | 2. Create a topic branch for your changes: 26 | 27 | ```bash 28 | git checkout -b fix-for-that-thing 29 | ``` 30 | 3. Commit a failing test for the bug: 31 | 32 | ```bash 33 | git commit -am "Adds a failing test to demonstrate that thing" 34 | ``` 35 | 36 | 4. Commit a fix that makes the test pass: 37 | 38 | ```bash 39 | git commit -am "Adds a fix for that thing!" 40 | ``` 41 | 42 | 5. Run the tests: 43 | 44 | ```bash 45 | npm test 46 | ``` 47 | 48 | 6. If everything looks good, push to your fork: 49 | 50 | ```bash 51 | git push origin fix-for-that-thing 52 | ``` 53 | 54 | 7. [Submit a pull request.](https://help.github.com/articles/creating-a-pull-request) 55 | 56 | 8. Enjoy being the wonderful person you are 57 | 58 | After you’ve opened your pull request, [you should email me](mailto:kenneth@chloi.io) your mailing address so I can mail you a personal thank you note. Seriously! 59 | 60 | ## Adding new features 61 | 62 | Thinking of adding a new feature? Cool! [Open an issue](https://github.com/kennethormandy/hanging-punctuation/issues) and let’s design it together. 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > _Hanging punctuation_ prevents quotations and other marks from taking a bite out of the crisp left edge of a text block. Make a clean edge by pushing the quotation marks into the margin.
2 | >     —Ellen Lupton, _Thinking with Type, 2nd Revised and Expanded Edition_ 3 | 4 | # Hanging Punctuation 5 | 6 | A polyfill for proper [hanging punctuation](http://www.w3.org/TR/css-text-3/#hanging-punctuation) in CSS. 7 | 8 | The `hanging-punctuation` property should be part of CSS in the future, but no browser supports in yet. In the meantime, this [Stylefill](https://github.com/nathanford/stylefill/) polyfills the missing features through the exact same CSS: 9 | 10 | ```css 11 | p { 12 | hanging-punctuation: first; 13 | /* Dynamically hang punctuation 14 | on the left margin. */ 15 | } 16 | 17 | html[lang="zh-Hans"] { 18 | text-align: justify; 19 | hanging-punctuation: allow-end; 20 | /* Allow ending punctuation 21 | to hang even when justified. */ 22 | } 23 | ``` 24 | 25 | Now, the first glyph only hangs if it is at the beginning of a line. This corrects 26 | 27 | ``` 28 | “Lorem ipsum dolor sit amet, consectetuer 29 | adipiscing elit. Ut a sapien alt,” they said, 30 | “Purus molestie dolor. Integer quis eros ut 31 | erat posuere dictum.” 32 | ``` 33 | 34 | to this: 35 | 36 | ``` 37 | “Lorem ipsum dolor sit amet, consectetuer 38 | adipiscing elit. Ut a sapien alt,” they said, 39 | “Purus molestie dolor. Integer quis eros ut 40 | erat posuere dictum.” 41 | ``` 42 | 43 | ## Getting started 44 | 45 | Hanging Punctuation is a [Stylefill](https://github.com/nathanford/stylefill/), meaning you only need to interact with this library through your CSS, and browsers that already support the `hanging-punctuation` property won’t use this polyfill. 46 | 47 | To install Hanging Punctuation, add it to your project or use the package manager and build tool of your choice. 48 | 49 | 54 | 55 | ##### With npm 56 | 57 | ```bash 58 | npm install kennethormandy/hanging-punctuation 59 | ``` 60 | 61 | Now it’s ready to include through your task runner or build tool, or you may just reference the files in your HTML: 62 | 63 | ```html 64 | 65 | ``` 66 | 67 | ##### With Component 68 | 69 | ```bash 70 | component install kennethormandy/hanging-punctuation 71 | ``` 72 | 73 | ##### With Bower 74 | 75 | ```bash 76 | bower install hanging-punctuation 77 | ``` 78 | 79 | ## Running locally 80 | 81 | To run the tests locally: 82 | 83 | ```bash 84 | git clone https://github.com/kennethormandy/hanging-punctuation 85 | npm install 86 | npm test 87 | # Now visit http://127.0.0.1:8080/test 88 | ``` 89 | 90 | ## Contributing 91 | 92 | Thanks for considering contributing! There’s information about how to [get started with Hanging Punctuation here](CONTRIBUTING.md). 93 | 94 | ## License 95 | 96 | [The MIT License (MIT)](LICENSE.md) 97 | 98 | Copyright © 2014 [Kenneth Ormandy](http://kennethormandy.com) & [Chloi Inc.](http://chloi.io) 99 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hanging Punctuation 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |

Hanging Punctuation tests

15 |

The following are examples and tests for working toward v1.0.0 of the hanging-punctuation polyfill.

16 |
17 |

Without hanging punctuation

18 |

“Lorem ipsum dolor sit amet, consectetur adipiscing elit. 19 | Cras sagittis mauris ut nisi cursus eleifend. Quisque consectetur 20 | gravida urna id euismod. Sed consectetur adipiscing vehicula. 21 | Fusce arcu velit, scelerisque et iaculis eu, posuere quis lacus. 22 | Sed non est eu augue vestibulum malesuada ut non purus. Suspendisse 23 | vel feugiat turpis. Sed consectetur nibh ut ligula dignissim 24 | tincidunt. Donec luctus feugiat enim ac dapibus.”

25 |
26 |
27 |

With hanging punctuation applied to q inline

28 |

“Lorem ipsum dolor sit amet,” consectetur adipiscing elit. 29 | Cras sagittis mauris ut nisi cursus eleifend. Quisque consectetur 30 | gravida urna id euismod. Sed consectetur adipiscing vehicula. 31 | Fusce arcu velit, scelerisque et iaculis eu, posuere quis lacus. 32 | “This works also in the middle of the text.” Try to resize the 33 | window to bring this quote to the left border. 34 | Sed non est eu augue vestibulum malesuada ut non purus. Suspendisse 35 | vel feugiat turpis. Sed consectetur nibh ut ligula dignissim 36 | tincidunt. Donec luctus feugiat enim ac dapibus.

37 |
38 |
39 |

With hanging punctuation to a p block

40 |

“Lorem ipsum dolor sit amet,” consectetur adipiscing elit. 41 | Cras sagittis mauris ut nisi cursus eleifend. Quisque consectetur 42 | gravida urna id euismod. Sed consectetur adipiscing vehicula. 43 | Fusce arcu velit, scelerisque et iaculis eu, posuere quis lacus. 44 | “This works also in the middle of the text.” Try to resize the 45 | window to bring this quote to the left border. 46 | Sed non est eu augue vestibulum malesuada ut non purus. Suspendisse 47 | vel feugiat turpis. Sed consectetur nibh ut ligula dignissim 48 | tincidunt. Donec luctus feugiat enim ac dapibus.

49 |
50 |
51 |

With hanging punctuation set to last

52 |

“Lorem ipsum dolor sit amet consectetur adipiscing elit. 53 | Cras sagittis mauris ut nisi cursus eleifend. Quisque consectetur,” 54 | gravida urna id euismod. Sed consectetur adipiscing vehicula. 55 | Fusce arcu velit, scelerisque et iaculis eu, posuere quis lacus. 56 | “This works also in the middle of the text.” Try to resize the 57 | window to bring this quote to the left border. 58 |

59 |
60 |

With hanging punctuation set to both

61 |

“Lorem ipsum dolor sit amet consectetur adipiscing elit. 62 | Cras sagittis mauris ut nisi cursus eleifend. Quisque consectetur,” 63 | gravida urna id euismod. Sed consectetur adipiscing vehicula. 64 | Fusce arcu velit, scelerisque et iaculis eu, posuere quis lacus. 65 | “This works also in the middle of the text.” Try to resize the 66 | window to bring this quote to the left border. 67 |

68 | 69 |
70 |

With hanging punctuation on the wrong character (should fail)

71 |

@Lorem ipsum dolor sit amet.@

72 |
73 |
74 |

Allow End (failing)

75 | 76 |

77 | 知彼知己,百戰不殆;不知彼而知己,
78 | 一勝一負;不知彼,不知己,每戰必殆。 79 |

80 |
81 |
82 |

Force End (failing)

83 | 84 |

85 | 知彼知己,百戰不殆;不知彼而知己,
86 | 一勝一負;不知彼,不知己,每戰必殆。 87 |

88 |
89 |
90 |

Issue #1 (failing)

91 |

“Lorem ipsum dolor sit amet,” consectetur adipiscing elit. 92 | Cras sagittis mauris ut nisi cursus eleifend. Quisque consectetur 93 | gravida urna id euismod. Sed consectetur adipiscing vehicula. 94 | Fusce arcu velit, scelerisque et iaculis eu, posuere quis lacus. 95 | “This works also in the middle of the text.” Try to resize the 96 | window to bring this quote to the left border. 97 | Sed non est eu augue vestibulum malesuada ut non purus. Suspendisse 98 | vel feugiat turpis. Sed consectetur nibh ut ligula dignissim 99 | tincidunt. Donec luctus feugiat enim ac dapibus.

100 |
101 |
102 |

With hanging punctuation in a q pseudo-element (failing)

103 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. 104 | Cras sagittis mauris ut nisi cursus eleifend. Quisque consectetur 105 | gravida urna id euismod. Sed consectetur adipiscing vehicula. 106 | Fusce arcu velit, scelerisque et iaculis eu, posuere quis lacus. 107 | This works also in the middle of the text. Try to resize the 108 | window to bring this quote to the left border. 109 | Sed non est eu augue vestibulum malesuada ut non purus. Suspendisse 110 | vel feugiat turpis. Sed consectetur nibh ut ligula dignissim 111 | tincidunt. Donec luctus feugiat enim ac dapibus.

112 |
113 |
114 | 115 | 116 | -------------------------------------------------------------------------------- /hanging-punctuation.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Hanging Punctuation v0.2.3 3 | * https://github.com/kennethormandy/hanging-punctuation 4 | * MIT License 5 | */ 6 | 7 | (function(document, window) { 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * Debounce Event – http://davidwalsh.name/function-debounce 13 | */ 14 | var debounce = function(func, wait, immediate) { 15 | var timeout; 16 | return function() { 17 | var context = this, args = arguments; 18 | var later = function() { 19 | timeout = null; 20 | if (!immediate) func.apply(context, args); 21 | }; 22 | var callNow = immediate && !timeout; 23 | clearTimeout(timeout); 24 | timeout = setTimeout(later, wait); 25 | if (callNow) func.apply(context, args); 26 | }; 27 | }; 28 | 29 | /** 30 | * Hanging Punctuation 31 | */ 32 | var hangingPunctuation = function(rule) { 33 | 34 | var els 35 | , elsCount 36 | , widthCache = {}; 37 | 38 | // Adding pseudo support 39 | // if(typeof rule.selector.split('::')[1] !== 'undefined' || typeof rule.selector.split(':')[1] !== 'undefined') { 40 | // console.log(rule.selector); 41 | // } 42 | els = document.querySelectorAll(rule.selector); 43 | elsCount = els.length; 44 | 45 | // '\u201C' 46 | 47 | // Opening 48 | // ,Ps:[0x28,0x29,0x5B,0x5C,0x7B,0x7C,0xF3A,0xF3B,0xF3C,0xF3D,0x169B,0x169C,0x201A,0x201B,0x201E,0x201F,0x2045,0x2046,0x207D,0x207E,0x208D,0x208E,0x2329,0x232A,0x2768,0x2769,0x276A,0x276B,0x276C,0x276D,0x276E,0x276F,0x2770,0x2771,0x2772,0x2773,0x2774,0x2775,0x27C5,0x27C6,0x27E6,0x27E7,0x27E8,0x27E9,0x27EA,0x27EB,0x27EC,0x27ED,0x27EE,0x27EF,0x2983,0x2984,0x2985,0x2986,0x2987,0x2988,0x2989,0x298A,0x298B,0x298C,0x298D,0x298E,0x298F,0x2990,0x2991,0x2992,0x2993,0x2994,0x2995,0x2996,0x2997,0x2998,0x29D8,0x29D9,0x29DA,0x29DB,0x29FC,0x29FD,0x2E22,0x2E23,0x2E24,0x2E25,0x2E26,0x2E27,0x2E28,0x2E29,0x3008,0x3009,0x300A,0x300B,0x300C,0x300D,0x300E,0x300F,0x3010,0x3011,0x3014,0x3015,0x3016,0x3017,0x3018,0x3019,0x301A,0x301B,0x301D,0x301E,0xFD3E,0xFD3F,0xFE17,0xFE18,0xFE35,0xFE36,0xFE37,0xFE38,0xFE39,0xFE3A,0xFE3B,0xFE3C,0xFE3D,0xFE3E,0xFE3F,0xFE40,0xFE41,0xFE42,0xFE43,0xFE44,0xFE47,0xFE48,0xFE59,0xFE5A,0xFE5B,0xFE5C,0xFE5D,0xFE5E,0xFF08,0xFF09,0xFF3B,0xFF3C,0xFF5B,0xFF5C,0xFF5F,0xFF60,0xFF62,0xFF63] 49 | 50 | // Closing 51 | //,Pe:[0x29,0x2A,0x5D,0x5E,0x7D,0x7E,0xF3B,0xF3C,0xF3D,0xF3E,0x169C,0x169D,0x2046,0x2047,0x207E,0x207F,0x208E,0x208F,0x232A,0x232B,0x2769,0x276A,0x276B,0x276C,0x276D,0x276E,0x276F,0x2770,0x2771,0x2772,0x2773,0x2774,0x2775,0x2776,0x27C6,0x27C7,0x27E7,0x27E8,0x27E9,0x27EA,0x27EB,0x27EC,0x27ED,0x27EE,0x27EF,0x27F0,0x2984,0x2985,0x2986,0x2987,0x2988,0x2989,0x298A,0x298B,0x298C,0x298D,0x298E,0x298F,0x2990,0x2991,0x2992,0x2993,0x2994,0x2995,0x2996,0x2997,0x2998,0x2999,0x29D9,0x29DA,0x29DB,0x29DC,0x29FD,0x29FE,0x2E23,0x2E24,0x2E25,0x2E26,0x2E27,0x2E28,0x2E29,0x2E2A,0x3009,0x300A,0x300B,0x300C,0x300D,0x300E,0x300F,0x3010,0x3011,0x3012,0x3015,0x3016,0x3017,0x3018,0x3019,0x301A,0x301B,0x301C,0x301E,0x3020,0xFD3F,0xFD40,0xFE18,0xFE19,0xFE36,0xFE37,0xFE38,0xFE39,0xFE3A,0xFE3B,0xFE3C,0xFE3D,0xFE3E,0xFE3F,0xFE40,0xFE41,0xFE42,0xFE43,0xFE44,0xFE45,0xFE48,0xFE49,0xFE5A,0xFE5B,0xFE5C,0xFE5D,0xFE5E,0xFE5F,0xFF09,0xFF0A,0xFF3D,0xFF3E,0xFF5D,0xFF5E,0xFF60,0xFF61,0xFF63,0xFF64] 52 | 53 | // Opening & Closing 54 | // ,Pi:[0xAB,0xAC,0x2018,0x2019,0x201B,0x201D,0x201F,0x2020,0x2039,0x203A,0x2E02,0x2E03,0x2E04,0x2E05,0x2E09,0x2E0A,0x2E0C,0x2E0D,0x2E1C,0x2E1D,0x2E20,0x2E21] 55 | // ,Pf:[0xBB,0xBC,0x2019,0x201A,0x201D,0x201E,0x203A,0x203B,0x2E03,0x2E04,0x2E05,0x2E06,0x2E0A,0x2E0B,0x2E0D,0x2E0E,0x2E1D,0x2E1E,0x2E21,0x2E22] 56 | 57 | // Force-End or Allow-End 58 | // http://www.w3.org/TR/css-text-3/#stops-and-commas 59 | // ,stopsAndCommas: [0x2C, 0x2E, 0x060C, 0xAB, 0x3001, 0x3002, 0xFF0C, 0xFF0E, 0xFE50, 0xFE51, 0xFE52, 0xFF61, 0xFF64] 60 | 61 | while(elsCount-- > 0) { 62 | var cacheKey 63 | , el 64 | , elParent 65 | , elContent 66 | , elFirstChar 67 | , elLastChar 68 | , elStyle 69 | , elSpan 70 | , tmp 71 | , left = 0 72 | , top 73 | , width; 74 | 75 | el = els[elsCount]; 76 | elParent = el.parentElement; 77 | elContent = el.textContent; 78 | elFirstChar = elContent[0]; 79 | elLastChar = elContent[elContent.length - 1] 80 | elStyle = window.getComputedStyle(el, null); 81 | 82 | // 1. Replace every character that matches first with a span (defering and using first glyph for now) 83 | // 2. Iterate over the spans 84 | // 3. Record and cache their width 85 | // 4. Remove previous sibling if it is a line break that was inserted 86 | // 5. Record their position from the top 87 | // 6. Apply negative margin equal to their width if necessary 88 | // 7. If the position from the top has changed, insert a line break 89 | 90 | if(elFirstChar.match(/\“/) !== null || elLastChar.match(/\”/) !== null) { // Need to replace this with appropriate Unicode range 91 | cacheKey = elStyle.fontWeight + ' ' + 92 | elStyle.fontSize + ' ' + 93 | elStyle.fontFamily + ' ' + 94 | elStyle.fontStretch + ' ' + 95 | elStyle.fontStyle; 96 | 97 | // Detect the desired width upfront 98 | if(!(cacheKey in widthCache)) { 99 | tmp = document.createElement('span') 100 | tmp.innerHTML = elFirstChar; 101 | el.appendChild(tmp); 102 | widthCache[cacheKey] = tmp.offsetWidth; 103 | el.removeChild(tmp); 104 | } 105 | width = widthCache[cacheKey]; 106 | tmp = null; 107 | 108 | if(rule.value === 'none') { 109 | 110 | } else if(rule.value === 'first') { 111 | 112 | // Might need to expose a more fogiving mode under `all` 113 | // that works on any quotations, but otherwise follows 114 | // the specification under `first`, etc. 115 | 116 | if(el.previousElementSibling && el.previousElementSibling.hasAttribute('data-hangPunctHelper')) { 117 | elParent.removeChild(el.previousElementSibling); 118 | } 119 | 120 | if(typeof el.children[0] !== 'undefined' && el.children[0].hasAttribute('data-hangPunctFirst')) { 121 | elSpan = el.children[0]; 122 | } else { 123 | el.innerHTML = el.innerHTML.replace(elFirstChar, ''); 124 | elSpan = document.createElement('span'); 125 | elSpan.setAttribute('data-hangPunctFirst', true); 126 | elSpan.textContent = elFirstChar; 127 | el.insertBefore(elSpan, el.firstChild); 128 | } 129 | 130 | // This is the “true” offset in case of multiline elements 131 | // see http://stackoverflow.com/q/995838/113195 132 | // http://stackoverflow.com/a/18953277/864799 133 | left = elSpan.getBoundingClientRect().left; 134 | 135 | // console.log(Math.ceil(left - width), Math.floor(el.getBoundingClientRect().left)); 136 | if(left - width - 1 <= el.getBoundingClientRect().left) { 137 | top = el.getBoundingClientRect().top; 138 | elSpan.style.marginLeft = -width + 'px'; 139 | 140 | if(top !== el.getBoundingClientRect().top) { 141 | var br = document.createElement('br'); 142 | br.setAttribute('data-hangPunctHelper', true); 143 | elParent.insertBefore(br, el); 144 | } 145 | 146 | } else { 147 | elSpan.style.marginLeft = 0; 148 | stylefill.runFills; 149 | } 150 | 151 | } 152 | } else if(rule.value === 'last') { 153 | console.log(el.innerHTML); 154 | 155 | } else if((rule.value === 'force-end') || (rule.value === 'allow-end')) { 156 | console.warn('Not implemented yet.'); 157 | } 158 | 159 | else { 160 | // First or last char doesn’t match hanging-punctuation’s rules 161 | } 162 | 163 | } 164 | 165 | } 166 | 167 | if(!window.onresize) { 168 | window.onresize = debounce(stylefill.runFills, 100); 169 | } 170 | 171 | stylefill.init({ 172 | 'hanging-punctuation' : hangingPunctuation 173 | }); 174 | 175 | }(document, window)); 176 | -------------------------------------------------------------------------------- /hanging-punctuation.min.js: -------------------------------------------------------------------------------- 1 | var stylefill={allRules:new Object,allFills:new Object,init:function(e){this.allFills=e,this.getStyleSheet(e)},objSize:function(e){var t,l=0;for(t in e)e.hasOwnProperty(t)&&l++;return l},arraySliceShim:function(){"use strict";var e=Array.prototype.slice;try{e.call(document.documentElement)}catch(t){Array.prototype.slice=function(t,l){var n,i=this.length,r=[];if(this.charAt)for(n=0;i>n;n++)r.push(this.charAt(n));else for(n=0;n0;){var n=t[l];n.innerHTML?this.findRules(e,n.innerHTML,l):n.href.match(document.domain)&&this.loadFile(e,n.href,l)}},checkRule:function(e){var t=e.replace(/(^|-)([a-z])/g,function(e,t,l){return l.toUpperCase()});return document.body&&("Webkit"+t in document.body.style||"Moz"+t in document.body.style||"O"+t in document.body.style||e in document.body.style)?!0:!1},findRules:function(e,t,l){if(t){this.objSize(e);for(property in e)for(var n,i=new RegExp("([^}{]+){([^}]+)?"+property.replace("-","\\-")+"[\\s\\t]*:[\\s\\t]*([^;]+)","gi"),r=stylefill.checkRule(property);n=i.exec(t);){var o=n[1].replace(/^([\s\n\r\t]+|\/\*.*?\*\/)+/,"").replace(/[\s\n\r\t]+$/,""),s=n[3];o=o.split(",");for(sel in o){var a=o[sel];stylefill.allRules[a]||(stylefill.allRules[a]=new Object),stylefill.allRules[a][property]={support:r,value:s}}}1==l&&this.runFills()}},runFills:function(){var e=stylefill.allRules,t=stylefill.allFills;for(i in e){var l=e[i];for(j in l){var n=l[j],r=t[j],o={support:n.support,selector:i,property:j,value:n.value};r(o)}}},binder:function(e,t,l){for(var t=t.split(","),n=t.length;n-->0;)e.attachEvent?e.attachEvent("on"+t[n].trim(),l):e.addEventListener(t[n].trim(),l,!1)}};!function(e,t){"use strict";var l=function(e,t,l){var n;return function(){var i=this,r=arguments,o=function(){n=null,l||e.apply(i,r)},s=l&&!n;clearTimeout(n),n=setTimeout(o,t),s&&e.apply(i,r)}},n=function(l){var n,i,r={};for(n=e.querySelectorAll(l.selector),i=n.length;i-->0;){var o,s,a,u,c,f,p,h,y,d=0;if(s=n[i],a=s.textContent,u=a[0],c=a[a.length-1],f=t.getComputedStyle(s,null),"none"===l.value);else if("first"===l.value){if(null!==u.match(/\“|\”/)){o=f.fontWeight+" "+f.fontSize+" "+f.fontFamily+" "+f.fontStretch+" "+f.fontStyle,o in r||(p=e.createElement("span"),p.innerHTML=u,s.appendChild(p),r[o]=p.offsetWidth,s.removeChild(p)),y=r[o],p=null,p=e.createElement("span"),p.innerHTML=" ",p.style.display="inline",s.insertBefore(p,s.firstChild);var d=p.getBoundingClientRect().left;if(s.removeChild(p),p=null,s.previousElementSibling&&s.previousElementSibling.hasAttribute("data-hangPunctHelper")&&s.parentElement.removeChild(s.previousElementSibling),d-y-1<=s.parentElement.getBoundingClientRect().left){if(h=s.getBoundingClientRect().top,s.style.marginLeft=-y+"px",h!==s.getBoundingClientRect().top){var m=e.createElement("br");m.setAttribute("data-hangPunctHelper"),s.insertBefore(m,s.firstChild)}}else s.style.marginLeft=0}}else("force-end"===l.value||"allow-end"===l.value||"last"===l.value)&&console.warn("Not implemented yet.")}};t.onresize||(t.onresize=l(stylefill.runFills,75)),stylefill.init({"hanging-punctuation":n})}(document,window);var stylefill={allRules:new Object,allFills:new Object,init:function(e){this.allFills=e,this.getStyleSheet(e)},objSize:function(e){var t,l=0;for(t in e)e.hasOwnProperty(t)&&l++;return l},arraySliceShim:function(){"use strict";var e=Array.prototype.slice;try{e.call(document.documentElement)}catch(t){Array.prototype.slice=function(t,l){var n,i=this.length,r=[];if(this.charAt)for(n=0;i>n;n++)r.push(this.charAt(n));else for(n=0;n0;){var n=t[l];n.innerHTML?this.findRules(e,n.innerHTML,l):n.href.match(document.domain)&&this.loadFile(e,n.href,l)}},checkRule:function(e){var t=e.replace(/(^|-)([a-z])/g,function(e,t,l){return l.toUpperCase()});return document.body&&("Webkit"+t in document.body.style||"Moz"+t in document.body.style||"O"+t in document.body.style||e in document.body.style)?!0:!1},findRules:function(e,t,l){if(t){this.objSize(e);for(property in e)for(var n,i=new RegExp("([^}{]+){([^}]+)?"+property.replace("-","\\-")+"[\\s\\t]*:[\\s\\t]*([^;]+)","gi"),r=stylefill.checkRule(property);n=i.exec(t);){var o=n[1].replace(/^([\s\n\r\t]+|\/\*.*?\*\/)+/,"").replace(/[\s\n\r\t]+$/,""),s=n[3];o=o.split(",");for(sel in o){var a=o[sel];stylefill.allRules[a]||(stylefill.allRules[a]=new Object),stylefill.allRules[a][property]={support:r,value:s}}}1==l&&this.runFills()}},runFills:function(){var e=stylefill.allRules,t=stylefill.allFills;for(i in e){var l=e[i];for(j in l){var n=l[j],r=t[j],o={support:n.support,selector:i,property:j,value:n.value};r(o)}}},binder:function(e,t,l){for(var t=t.split(","),n=t.length;n-->0;)e.attachEvent?e.attachEvent("on"+t[n].trim(),l):e.addEventListener(t[n].trim(),l,!1)}};!function(e,t){"use strict";var l=function(e,t,l){var n;return function(){var i=this,r=arguments,o=function(){n=null,l||e.apply(i,r)},s=l&&!n;clearTimeout(n),n=setTimeout(o,t),s&&e.apply(i,r)}},n=function(l){var n,i,r={};for(n=e.querySelectorAll(l.selector),i=n.length;i-->0;){var o,s,a,u,c,f,p,h,y,d=0;if(s=n[i],a=s.textContent,u=a[0],c=a[a.length-1],f=t.getComputedStyle(s,null),"none"===l.value);else if("first"===l.value){if(null!==u.match(/\“|\”/)){o=f.fontWeight+" "+f.fontSize+" "+f.fontFamily+" "+f.fontStretch+" "+f.fontStyle,o in r||(p=e.createElement("span"),p.innerHTML=u,s.appendChild(p),r[o]=p.offsetWidth,s.removeChild(p)),y=r[o],p=null,p=e.createElement("span"),p.innerHTML=" ",p.style.display="inline",s.insertBefore(p,s.firstChild);var d=p.getBoundingClientRect().left;if(s.removeChild(p),p=null,s.previousElementSibling&&s.previousElementSibling.hasAttribute("data-hangPunctHelper")&&s.parentElement.removeChild(s.previousElementSibling),d-y-1<=s.parentElement.getBoundingClientRect().left){if(h=s.getBoundingClientRect().top,s.style.marginLeft=-y+"px",h!==s.getBoundingClientRect().top){var m=e.createElement("br");m.setAttribute("data-hangPunctHelper"),s.insertBefore(m,s.firstChild)}}else s.style.marginLeft=0}}else("force-end"===l.value||"allow-end"===l.value||"last"===l.value)&&console.warn("Not implemented yet.")}};t.onresize||(t.onresize=l(stylefill.runFills,75)),stylefill.init({"hanging-punctuation":n})}(document,window);var stylefill={allRules:new Object,allFills:new Object,init:function(e){this.allFills=e,this.getStyleSheet(e)},objSize:function(e){var t,l=0;for(t in e)e.hasOwnProperty(t)&&l++;return l},arraySliceShim:function(){"use strict";var e=Array.prototype.slice;try{e.call(document.documentElement)}catch(t){Array.prototype.slice=function(t,l){var n,i=this.length,r=[];if(this.charAt)for(n=0;i>n;n++)r.push(this.charAt(n));else for(n=0;n0;){var n=t[l];n.innerHTML?this.findRules(e,n.innerHTML,l):n.href.match(document.domain)&&this.loadFile(e,n.href,l)}},checkRule:function(e){var t=e.replace(/(^|-)([a-z])/g,function(e,t,l){return l.toUpperCase()});return document.body&&("Webkit"+t in document.body.style||"Moz"+t in document.body.style||"O"+t in document.body.style||e in document.body.style)?!0:!1},findRules:function(e,t,l){if(t){this.objSize(e);for(property in e)for(var n,i=new RegExp("([^}{]+){([^}]+)?"+property.replace("-","\\-")+"[\\s\\t]*:[\\s\\t]*([^;]+)","gi"),r=stylefill.checkRule(property);n=i.exec(t);){var o=n[1].replace(/^([\s\n\r\t]+|\/\*.*?\*\/)+/,"").replace(/[\s\n\r\t]+$/,""),s=n[3];o=o.split(",");for(sel in o){var a=o[sel];stylefill.allRules[a]||(stylefill.allRules[a]=new Object),stylefill.allRules[a][property]={support:r,value:s}}}1==l&&this.runFills()}},runFills:function(){var e=stylefill.allRules,t=stylefill.allFills;for(i in e){var l=e[i];for(j in l){var n=l[j],r=t[j],o={support:n.support,selector:i,property:j,value:n.value};r(o)}}},binder:function(e,t,l){for(var t=t.split(","),n=t.length;n-->0;)e.attachEvent?e.attachEvent("on"+t[n].trim(),l):e.addEventListener(t[n].trim(),l,!1)}};!function(e,t){"use strict";var l=function(e,t,l){var n;return function(){var i=this,r=arguments,o=function(){n=null,l||e.apply(i,r)},s=l&&!n;clearTimeout(n),n=setTimeout(o,t),s&&e.apply(i,r)}},n=function(l){var n,i,r={};for(n=e.querySelectorAll(l.selector),i=n.length;i-->0;){var o,s,a,u,c,f,p,h,y,d=0;if(s=n[i],a=s.textContent,u=a[0],c=a[a.length-1],f=t.getComputedStyle(s,null),"none"===l.value);else if("first"===l.value){if(null!==u.match(/\“|\”/)){o=f.fontWeight+" "+f.fontSize+" "+f.fontFamily+" "+f.fontStretch+" "+f.fontStyle,o in r||(p=e.createElement("span"),p.innerHTML=u,s.appendChild(p),r[o]=p.offsetWidth,s.removeChild(p)),y=r[o],p=null,p=e.createElement("span"),p.innerHTML=" ",p.style.display="inline",s.insertBefore(p,s.firstChild);var d=p.getBoundingClientRect().left;if(s.removeChild(p),p=null,s.previousElementSibling&&s.previousElementSibling.hasAttribute("data-hangPunctHelper")&&s.parentElement.removeChild(s.previousElementSibling),d-y-1<=s.parentElement.getBoundingClientRect().left){if(h=s.getBoundingClientRect().top,s.style.marginLeft=-y+"px",h!==s.getBoundingClientRect().top){var m=e.createElement("br");m.setAttribute("data-hangPunctHelper"),s.insertBefore(m,s.firstChild)}}else s.style.marginLeft=0}}else("force-end"===l.value||"allow-end"===l.value||"last"===l.value)&&console.warn("Not implemented yet.")}};t.onresize||(t.onresize=l(stylefill.runFills,75)),stylefill.init({"hanging-punctuation":n})}(document,window);var stylefill={allRules:new Object,allFills:new Object,init:function(e){this.allFills=e,this.getStyleSheet(e)},objSize:function(e){var t,l=0;for(t in e)e.hasOwnProperty(t)&&l++;return l},arraySliceShim:function(){"use strict";var e=Array.prototype.slice;try{e.call(document.documentElement)}catch(t){Array.prototype.slice=function(t,l){var n,i=this.length,r=[];if(this.charAt)for(n=0;i>n;n++)r.push(this.charAt(n));else for(n=0;n0;)if("undefined"!=typeof t[l]){var n=t[l];n.innerHTML?this.findRules(e,n.innerHTML,l):n.href.match(document.domain)&&this.loadFile(e,n.href,l)}},checkRule:function(e){var t=e.replace(/(^|-)([a-z])/g,function(e,t,l){return l.toUpperCase()});return document.body&&("Webkit"+t in document.body.style||"Moz"+t in document.body.style||"O"+t in document.body.style||e in document.body.style)?!0:!1},findRules:function(e,t,l){if(t){this.objSize(e);for(property in e)for(var n,i=new RegExp("([^}{]+){([^}]+)?"+property.replace("-","\\-")+"[\\s\\t]*:[\\s\\t]*([^;]+)","gi"),r=stylefill.checkRule(property);n=i.exec(t);){var o=n[1].replace(/^([\s\n\r\t]+|\/\*.*?\*\/)+/,"").replace(/[\s\n\r\t]+$/,""),s=n[3];o=o.split(",");for(sel in o){var a=o[sel];stylefill.allRules[a]||(stylefill.allRules[a]=new Object),stylefill.allRules[a][property]={support:r,value:s}}}1==l&&this.runFills()}},runFills:function(){var e=stylefill.allRules,t=stylefill.allFills;for(i in e){var l=e[i];for(j in l){var n=l[j],r=t[j],o={support:n.support,selector:i,property:j,value:n.value};r(o)}}},binder:function(e,t,l){for(var t=t.split(","),n=t.length;n-->0;)e.attachEvent?e.attachEvent("on"+t[n].trim(),l):e.addEventListener(t[n].trim(),l,!1)}};!function(e,t){"use strict";var l=function(e,t,l){var n;return function(){var i=this,r=arguments,o=function(){n=null,l||e.apply(i,r)},s=l&&!n;clearTimeout(n),n=setTimeout(o,t),s&&e.apply(i,r)}},n=function(l){var n,i,r={};for(n=e.querySelectorAll(l.selector),i=n.length;i-->0;){var o,s,a,u,c,f,p,h,y,d,m=0;if(s=n[i],a=s.parentElement,u=s.textContent,c=u[0],f=u[u.length-1],p=t.getComputedStyle(s,null),"none"===l.value);else if("first"===l.value){if(null!==c.match(/\“/))if(o=p.fontWeight+" "+p.fontSize+" "+p.fontFamily+" "+p.fontStretch+" "+p.fontStyle,o in r||(h=e.createElement("span"),h.innerHTML=c,s.appendChild(h),r[o]=h.offsetWidth,s.removeChild(h)),d=r[o],h=null,h=e.createElement("span"),h.innerHTML=c,h.style.display="inline",a.insertBefore(h,s),m=h.getBoundingClientRect().left,a.removeChild(h),h=null,s.previousElementSibling&&s.previousElementSibling.hasAttribute("data-hangPunctHelper","")&&s.parentElement.removeChild(s.previousElementSibling),console.log(m-d-1,a.getBoundingClientRect().left),m-d-1<=a.getBoundingClientRect().left){if(y=s.getBoundingClientRect().top,s.style.marginLeft=-d+"px",y!==s.getBoundingClientRect().top){var v=e.createElement("br");v.setAttribute("data-hangPunctHelper",""),s.insertBefore(v,s.firstChild)}}else s.style.marginLeft=0}else("force-end"===l.value||"allow-end"===l.value||"last"===l.value)&&console.warn("Not implemented yet.")}};t.onresize||(t.onresize=l(stylefill.runFills,75)),stylefill.init({"hanging-punctuation":n})}(document,window);var stylefill={allRules:new Object,allFills:new Object,init:function(e){this.allFills=e,this.getStyleSheet(e)},objSize:function(e){var t,l=0;for(t in e)e.hasOwnProperty(t)&&l++;return l},arraySliceShim:function(){"use strict";var e=Array.prototype.slice;try{e.call(document.documentElement)}catch(t){Array.prototype.slice=function(t,l){var n,i=this.length,r=[];if(this.charAt)for(n=0;i>n;n++)r.push(this.charAt(n));else for(n=0;n0;)if("undefined"!=typeof t[l]){var n=t[l];n.innerHTML?this.findRules(e,n.innerHTML,l):n.href.match(document.domain)&&this.loadFile(e,n.href,l)}},checkRule:function(e){var t=e.replace(/(^|-)([a-z])/g,function(e,t,l){return l.toUpperCase()});return document.body&&("Webkit"+t in document.body.style||"Moz"+t in document.body.style||"O"+t in document.body.style||e in document.body.style)?!0:!1},findRules:function(e,t,l){if(t){this.objSize(e);for(property in e)for(var n,i=new RegExp("([^}{]+){([^}]+)?"+property.replace("-","\\-")+"[\\s\\t]*:[\\s\\t]*([^;]+)","gi"),r=stylefill.checkRule(property);n=i.exec(t);){var o=n[1].replace(/^([\s\n\r\t]+|\/\*.*?\*\/)+/,"").replace(/[\s\n\r\t]+$/,""),s=n[3];o=o.split(",");for(sel in o){var a=o[sel];stylefill.allRules[a]||(stylefill.allRules[a]=new Object),stylefill.allRules[a][property]={support:r,value:s}}}1==l&&this.runFills()}},runFills:function(){var e=stylefill.allRules,t=stylefill.allFills;for(i in e){var l=e[i];for(j in l){var n=l[j],r=t[j],o={support:n.support,selector:i,property:j,value:n.value};r(o)}}},binder:function(e,t,l){for(var t=t.split(","),n=t.length;n-->0;)e.attachEvent?e.attachEvent("on"+t[n].trim(),l):e.addEventListener(t[n].trim(),l,!1)}};!function(e,t){"use strict";var l=function(e,t,l){var n;return function(){var i=this,r=arguments,o=function(){n=null,l||e.apply(i,r)},s=l&&!n;clearTimeout(n),n=setTimeout(o,t),s&&e.apply(i,r)}},n=function(l){var n,i,r={};for(n=e.querySelectorAll(l.selector),i=n.length;i-->0;){var o,s,a,u,c,f,p,h,y,d,m,v=0;s=n[i],a=s.parentElement,u=s.textContent,c=u[0],f=u[u.length-1],p=t.getComputedStyle(s,null),"none"===l.value||("first"===l.value?null!==c.match(/\“/)&&(o=p.fontWeight+" "+p.fontSize+" "+p.fontFamily+" "+p.fontStretch+" "+p.fontStyle,o in r||(y=e.createElement("span"),y.innerHTML=c,s.appendChild(y),r[o]=y.offsetWidth,s.removeChild(y)),m=r[o],y=null,s.previousElementSibling&&s.previousElementSibling.hasAttribute("data-hangPunctHelper")&&a.removeChild(s.previousElementSibling),"undefined"!=typeof s.children[0]&&s.children[0].hasAttribute("data-hangPunctFirst")?h=s.children[0]:(s.innerHTML=s.innerHTML.replace(c,""),h=e.createElement("span"),h.setAttribute("data-hangPunctFirst",!0),h.textContent=c,s.insertBefore(h,s.firstChild)),v=h.getBoundingClientRect().left,console.log(Math.ceil(v-m),Math.floor(s.getBoundingClientRect().left)),v-m-1<=s.getBoundingClientRect().left?(d=s.getBoundingClientRect().top,h.style.outline="1px solid green",h.style.marginLeft=-m+"px"):(console.log("hi"),h.style.outline="1px solid blue",h.style.marginLeft=0,stylefill.runFills)):("force-end"===l.value||"allow-end"===l.value||"last"===l.value)&&console.warn("Not implemented yet."))}};t.onresize||(t.onresize=l(stylefill.runFills,100)),stylefill.init({"hanging-punctuation":n})}(document,window);var stylefill={allRules:new Object,allFills:new Object,init:function(e){this.allFills=e,this.getStyleSheet(e)},objSize:function(e){var t,l=0;for(t in e)e.hasOwnProperty(t)&&l++;return l},arraySliceShim:function(){"use strict";var e=Array.prototype.slice;try{e.call(document.documentElement)}catch(t){Array.prototype.slice=function(t,l){var n,i=this.length,r=[];if(this.charAt)for(n=0;i>n;n++)r.push(this.charAt(n));else for(n=0;n0;)if("undefined"!=typeof t[l]){var n=t[l];n.innerHTML?this.findRules(e,n.innerHTML,l):n.href.match(document.domain)&&this.loadFile(e,n.href,l)}},checkRule:function(e){var t=e.replace(/(^|-)([a-z])/g,function(e,t,l){return l.toUpperCase()});return document.body&&("Webkit"+t in document.body.style||"Moz"+t in document.body.style||"O"+t in document.body.style||e in document.body.style)?!0:!1},findRules:function(e,t,l){if(t){{this.objSize(e)}for(property in e)for(var n,i=new RegExp("([^}{]+){([^}]+)?"+property.replace("-","\\-")+"[\\s\\t]*:[\\s\\t]*([^;]+)","gi"),r=stylefill.checkRule(property);n=i.exec(t);){var o=n[1].replace(/^([\s\n\r\t]+|\/\*.*?\*\/)+/,"").replace(/[\s\n\r\t]+$/,""),s=n[3];o=o.split(",");for(sel in o){var a=o[sel];stylefill.allRules[a]||(stylefill.allRules[a]=new Object),stylefill.allRules[a][property]={support:r,value:s}}}1==l&&this.runFills()}},runFills:function(){var e=stylefill.allRules,t=stylefill.allFills;for(i in e){var l=e[i];for(j in l){var n=l[j],r=t[j],o={support:n.support,selector:i,property:j,value:n.value};r(o)}}},binder:function(e,t,l){for(var t=t.split(","),n=t.length;n-->0;)e.attachEvent?e.attachEvent("on"+t[n].trim(),l):e.addEventListener(t[n].trim(),l,!1)}};!function(e,t){"use strict";var l=function(e,t,l){var n;return function(){var i=this,r=arguments,o=function(){n=null,l||e.apply(i,r)},s=l&&!n;clearTimeout(n),n=setTimeout(o,t),s&&e.apply(i,r)}},n=function(l){var n,i,r={};for(n=e.querySelectorAll(l.selector),i=n.length;i-->0;){var o,s,a,u,c,f,p,h,y,d,m,v=0;if(s=n[i],a=s.parentElement,u=s.textContent,c=u[0],f=u[u.length-1],p=t.getComputedStyle(s,null),"none"===l.value);else if("first"===l.value){if(null!==c.match(/\“/))if(o=p.fontWeight+" "+p.fontSize+" "+p.fontFamily+" "+p.fontStretch+" "+p.fontStyle,o in r||(y=e.createElement("span"),y.innerHTML=c,s.appendChild(y),r[o]=y.offsetWidth,s.removeChild(y)),m=r[o],y=null,s.previousElementSibling&&s.previousElementSibling.hasAttribute("data-hangPunctHelper")&&a.removeChild(s.previousElementSibling),"undefined"!=typeof s.children[0]&&s.children[0].hasAttribute("data-hangPunctFirst")?h=s.children[0]:(s.innerHTML=s.innerHTML.replace(c,""),h=e.createElement("span"),h.setAttribute("data-hangPunctFirst",!0),h.textContent=c,s.insertBefore(h,s.firstChild)),v=h.getBoundingClientRect().left,console.log(Math.ceil(v-m),Math.floor(s.getBoundingClientRect().left)),v-m-1<=s.getBoundingClientRect().left){if(d=s.getBoundingClientRect().top,h.style.marginLeft=-m+"px",d!==s.getBoundingClientRect().top){var g=e.createElement("br");g.setAttribute("data-hangPunctHelper",!0),a.insertBefore(g,s)}}else h.style.marginLeft=0,stylefill.runFills}else("force-end"===l.value||"allow-end"===l.value||"last"===l.value)&&console.warn("Not implemented yet.")}};t.onresize||(t.onresize=l(stylefill.runFills,100)),stylefill.init({"hanging-punctuation":n})}(document,window); 2 | --------------------------------------------------------------------------------