├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── dist ├── stickyfill.es6.js ├── stickyfill.js └── stickyfill.min.js ├── package.json ├── src └── stickyfill.js ├── test ├── index.html └── js │ └── jquery-3.1.1.min.js ├── types └── index.d.ts └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 4 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://www.paypal.me/wilddeer/3usd 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 'use strict'; 3 | 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | banner: 7 | `/*! 8 | * Stickyfill – \`position: sticky\` polyfill 9 | * v. <%= pkg.version %> | <%= pkg.homepage %> 10 | * MIT License 11 | */ 12 | `, 13 | 14 | babel: { 15 | options: { 16 | presets: ['es2015'] 17 | }, 18 | dist: { 19 | files: { 20 | 'dist/stickyfill.js': 'src/stickyfill.js' 21 | } 22 | } 23 | }, 24 | 25 | wrap: { 26 | es5: { 27 | options: { 28 | wrapper: [ 29 | '<%= banner %>\n;(function(window, document) {', 30 | '})(window, document);' 31 | ], 32 | indent: ' ' 33 | }, 34 | files: { 35 | 'dist/stickyfill.js': ['dist/stickyfill.js'] 36 | } 37 | }, 38 | es6: { 39 | options: { 40 | wrapper: [ 41 | '<%= banner %>', 42 | '' 43 | ] 44 | }, 45 | files: { 46 | 'dist/stickyfill.es6.js': ['src/stickyfill.js'] 47 | } 48 | } 49 | }, 50 | 51 | uglify: { 52 | options: { 53 | banner: '<%= banner %>', 54 | mangle: true 55 | }, 56 | dist: { 57 | files: { 58 | 'dist/stickyfill.min.js': ['dist/stickyfill.js'] 59 | } 60 | } 61 | }, 62 | 63 | bump: { 64 | options: { 65 | files: ['package.json'], 66 | updateConfigs: ['pkg'], 67 | commit: true, 68 | commitMessage: 'v %VERSION%', 69 | commitFiles: ['.'], 70 | createTag: true, 71 | tagName: '%VERSION%', 72 | tagMessage: 'v %VERSION%', 73 | push: false 74 | } 75 | }, 76 | 77 | shell: { 78 | push: { 79 | command: 'git push' 80 | }, 81 | 82 | pushTags: { 83 | command: 'git push --tags' 84 | }, 85 | 86 | publishToNpm: { 87 | command: 'npm publish' 88 | } 89 | }, 90 | 91 | watch: { 92 | files: ['src/**/*.js'], 93 | tasks: ['build'] 94 | } 95 | }); 96 | 97 | grunt.loadNpmTasks('grunt-wrap'); 98 | grunt.loadNpmTasks('grunt-babel'); 99 | grunt.loadNpmTasks('grunt-contrib-uglify'); 100 | grunt.loadNpmTasks('grunt-contrib-watch'); 101 | grunt.loadNpmTasks('grunt-bump'); 102 | grunt.loadNpmTasks('grunt-shell'); 103 | grunt.registerTask('build', ['babel', 'wrap', 'uglify']); 104 | grunt.registerTask('release', ['bump-only:patch', 'build', 'bump-commit', 'shell:push', 'shell:pushTags', 'shell:publishToNpm']); 105 | grunt.registerTask('default', ['build', 'watch']); 106 | }; 107 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Oleg Korsunsky 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ---- 2 | # ⚠ Unmaintained! 3 | 4 | Stickyfill did a good job while the browsers were implementing `position: sticky` support. You can now safely use stickies without a polyfill, all modern browsers [support them natively](https://caniuse.com/?search=position%3Asticky). 5 | 6 | ---- 7 |

8 | 9 | # Polyfill for CSS `position: sticky` 10 | 11 | The most accurate sticky polyfill out in the wild. 12 | 13 | Check out [the demo](http://wd.dizaina.net/en/scripts/stickyfill/) and [use cases test page](http://wilddeer.github.io/stickyfill/test/). 14 | 15 | ## What it does 16 | 17 | - supports top-positioned stickies, 18 | - works in IE9+, 19 | - disables itself in older IEs and in browsers with native `position: sticky` support, 20 | - mimics original `position: sticky` behavior: 21 | 22 | - uses parent node as a boundary box, 23 | - behaves nicely with horizontal page scrolling, 24 | - only works on elements with specified `top`, 25 | - mimics native `top` and `margin-bottom` behavior, 26 | - ~~works with table cells~~ removed for consistency until Firefox [makes a native implementation](https://bugzilla.mozilla.org/show_bug.cgi?id=975644) 27 | 28 | ## What it doesn't 29 | 30 | - doesn't support left, right, bottom or combined stickies, 31 | - doesn't work in overflowed blocks, 32 | - doesn't parse your CSS! Launch it manually. 33 | 34 | ---- 35 | 36 |

37 | Installation   38 | Usage   39 | Pro tips   40 | API   41 | Feature requests   42 | Bug reports   43 | Contributing   44 | Buy me a beer 45 |

46 | 47 | ---- 48 | 49 | ## Installation 50 | 51 | ### NPM 52 | 53 | ``` 54 | npm install stickyfilljs --save 55 | ``` 56 | 57 | ### Yarn 58 | 59 | ``` 60 | yarn add stickyfilljs 61 | ``` 62 | 63 | ### Raw ES6 module 64 | 65 | [stickyfill.es6.js](https://raw.github.com/wilddeer/stickyfill/master/dist/stickyfill.es6.js) 66 | 67 | ### Old fashioned 68 | 69 | Download minified production ES5 script: 70 | 71 | [stickyfill.min.js](https://raw.github.com/wilddeer/stickyfill/master/dist/stickyfill.min.js) 72 | 73 | Include it on your page: 74 | 75 | ```html 76 | 77 | ``` 78 | 79 | ## Usage 80 | 81 | First things first, make sure your stickies work in the [browsers that support them natively](http://caniuse.com/#feat=css-sticky), e.g.: 82 | 83 | ```html 84 |
85 | ... 86 |
87 | ``` 88 | 89 | ```css 90 | .sticky { 91 | position: -webkit-sticky; 92 | position: sticky; 93 | top: 0; 94 | } 95 | ``` 96 | 97 | Then apply the polyfill: 98 | 99 | JS: 100 | 101 | ```js 102 | var elements = document.querySelectorAll('.sticky'); 103 | Stickyfill.add(elements); 104 | ``` 105 | 106 | or JS + jQuery: 107 | 108 | ```js 109 | var elements = $('.sticky'); 110 | Stickyfill.add(elements); 111 | ``` 112 | 113 | Also worth having a clearfix: 114 | 115 | ```css 116 | .sticky:before, 117 | .sticky:after { 118 | content: ''; 119 | display: table; 120 | } 121 | ``` 122 | 123 | ## Pro tips 124 | 125 | - `top` specifies sticky’s position relatively to the top edge of the viewport. It accepts negative values, too. 126 | - You can push sticky’s bottom limit up or down by specifying positive or negative `margin-bottom`. 127 | - Any non-default value (not `visible`) for `overflow`, `overflow-x`, or `overflow-y` on any of the ancestor elements anchors the sticky to the overflow context of that ancestor. Simply put, scrolling the ancestor will cause the sticky to stick, scrolling the window will not. This is expected with `overflow: auto` and `overflow: scroll`, but often causes confusion with `overflow: hidden`. Keep this in mind, folks! 128 | 129 | Check out [the test page](http://wilddeer.github.io/stickyfill/test/) to understand stickies better. 130 | 131 | ## API 132 | 133 | ### `Stickyfill` 134 | 135 | #### `Stickyfill.addOne(element)` 136 | 137 | `element` – `HTMLElement` or iterable element list ([`NodeList`](https://developer.mozilla.org/en/docs/Web/API/NodeList), jQuery collection, etc.). First element of the list is used. 138 | 139 | Adds the element as a sticky. Returns new [Sticky](#stickyfillsticky) instance associated with the element. 140 | 141 | If there’s a sticky associated with the element, returns existing [Sticky](#stickyfillsticky) instance instead. 142 | 143 | #### `Stickyfill.add(elementList)` 144 | 145 | `elementList` – iterable element list ([`NodeList`](https://developer.mozilla.org/en/docs/Web/API/NodeList), jQuery collection, etc.) or single `HTMLElement`. 146 | 147 | Adds the elements as stickies. Skips the elements that have stickies associated with them. 148 | 149 | Returns an array of [Sticky](#stickyfillsticky) instances associated with the elements (both existing and new ones). 150 | 151 | #### `Stickyfill.refreshAll()` 152 | 153 | Refreshes all existing stickies, updates their parameters and positions. 154 | 155 | All stickies are automatically refreshed after window resizes and device orientations changes. 156 | 157 | There’s also a fast but not very accurate layout change detection that triggers this method. Call this method manually in case automatic detection fails. 158 | 159 | #### `Stickyfill.removeOne(element)` 160 | 161 | `element` – `HTMLElement` or iterable element list ([`NodeList`](https://developer.mozilla.org/en/docs/Web/API/NodeList), jQuery collection, etc.). First element of the list is used. 162 | 163 | Removes sticky associated with the element. 164 | 165 | #### `Stickyfill.remove(elementList)` 166 | 167 | `elementList` – iterable element list ([`NodeList`](https://developer.mozilla.org/en/docs/Web/API/NodeList), jQuery collection, etc.) or single `HTMLElement`. 168 | 169 | Removes stickies associated with the elements in the list. 170 | 171 | #### `Stickyfill.removeAll()` 172 | 173 | Removes all existing stickies. 174 | 175 | #### `Stickyfill.forceSticky()` 176 | 177 | Force-enable the polyfill, even if the browser supports `position: sticky` natively. 178 | 179 | #### `Stickyfill.stickies` 180 | 181 | Array of existing [Sticky](#Stickyfill.Sticky) instances. 182 | 183 | ### `Stickyfill.Sticky` 184 | 185 | Sticky class. You can use it directly if you want: 186 | 187 | ```js 188 | const sticky = new Stickyfill.Sticky(element); 189 | ``` 190 | 191 | Throws an error if there’s a sticky already bound to the element. 192 | 193 | #### `Sticky.refresh()` 194 | 195 | Refreshes the sticky, updates its parameters and position. 196 | 197 | #### `Sticky.remove()` 198 | 199 | Removes the sticky. Restores the element to its original state. 200 | 201 | ## Feature requests 202 | 203 | ### TL;DR 204 | 205 | These features will never be implemented in Stickyfill: 206 | 207 | - Callbacks for sticky state changes 208 | - Switching classes between different sticky states 209 | - Other features that add non-standard functionality 210 | 211 | If your request isn’t about one of these, you are welcome to [create an issue](https://github.com/wilddeer/stickyfill/issues/new). Please check [existing issues](https://github.com/wilddeer/stickyfill/issues) before creating new one. 212 | 213 | ### Some reasoning 214 | 215 | Stickyfill is a [polyfill](https://en.wikipedia.org/wiki/Polyfill). This means that it implements a feature (sticky positioning in this case) that already exists in some browsers natively, and allows to use this feature in the browsers that don’t support it yet and older versions of the browsers that didn’t support it at the time. This is its only purpose. 216 | 217 | This also means that Stickyfill does nothing in the browsers that _do_ support sticky positioning. Which, in turn, means that those browsers won’t support any additional non-standard features. 218 | 219 | ## Bug reports 220 | 221 | Check [existing issues](https://github.com/wilddeer/stickyfill/issues) before creating new one. **Please provide a live reproduction of a bug.** 222 | 223 | ## Contributing 224 | 225 | ### Prerequisites 226 | 227 | - Install Git 😱 228 | - Install [node](https://nodejs.org/en/) 229 | - Install [grunt-cli](http://gruntjs.com/getting-started#installing-the-cli) 230 | - Clone the repo, `cd` into the repo folder, run `npm install` (or `yarn` if you are fancy). 231 | 232 | Ok, you are all set. 233 | 234 | ### Building and testing 235 | 236 | `cd` into the repo folder and run `grunt`. It will build the project from `/src/stickyfill.js` into `/dist` and run the watcher that will rebuild the project every time you change something in the source file. 237 | 238 | Make changes to the source file. Stick to ES6 syntax. 239 | 240 | Open `/test/index.html` in a browser that [doesn’t support](http://caniuse.com/#feat=css-sticky) `position: sticky` to check that everything works as expected. Compare the results to the same page in a browser that supports `position: sticky`. 241 | 242 | Commit the changes. **DO NOT** commit the files in the `/dist` folder. **DO NOT** change the version in `package.json`. 243 | 244 | Make a pull request 👍 245 | 246 | ### Adding / removing / updating npm packages 247 | 248 | Use [Yarn](https://yarnpkg.com/), dont’t forget to commit `yarn.lock`. 249 | 250 | ## Using Stickyfill? 251 | 252 | 🍻 [Buy me a beer](https://www.paypal.me/wilddeer/3usd) 253 | 254 | ## License 255 | 256 | [MIT license](LICENSE). 257 | -------------------------------------------------------------------------------- /dist/stickyfill.es6.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Stickyfill – `position: sticky` polyfill 3 | * v. 2.1.0 | https://github.com/wilddeer/stickyfill 4 | * MIT License 5 | */ 6 | 7 | 'use strict'; 8 | 9 | /* 10 | * 1. Check if the browser supports `position: sticky` natively or is too old to run the polyfill. 11 | * If either of these is the case set `seppuku` flag. It will be checked later to disable key features 12 | * of the polyfill, but the API will remain functional to avoid breaking things. 13 | */ 14 | let seppuku = false; 15 | 16 | const isWindowDefined = typeof window !== 'undefined'; 17 | 18 | // The polyfill can’t function properly without `window` or `window.getComputedStyle`. 19 | if (!isWindowDefined || !window.getComputedStyle) seppuku = true; 20 | // Dont’t get in a way if the browser supports `position: sticky` natively. 21 | else { 22 | const testNode = document.createElement('div'); 23 | 24 | if ( 25 | ['', '-webkit-', '-moz-', '-ms-'].some(prefix => { 26 | try { 27 | testNode.style.position = prefix + 'sticky'; 28 | } 29 | catch(e) {} 30 | 31 | return testNode.style.position != ''; 32 | }) 33 | ) seppuku = true; 34 | } 35 | 36 | 37 | /* 38 | * 2. “Global” vars used across the polyfill 39 | */ 40 | let isInitialized = false; 41 | 42 | // Check if Shadow Root constructor exists to make further checks simpler 43 | const shadowRootExists = typeof ShadowRoot !== 'undefined'; 44 | 45 | // Last saved scroll position 46 | const scroll = { 47 | top: null, 48 | left: null 49 | }; 50 | 51 | // Array of created Sticky instances 52 | const stickies = []; 53 | 54 | 55 | /* 56 | * 3. Utility functions 57 | */ 58 | function extend (targetObj, sourceObject) { 59 | for (var key in sourceObject) { 60 | if (sourceObject.hasOwnProperty(key)) { 61 | targetObj[key] = sourceObject[key]; 62 | } 63 | } 64 | } 65 | 66 | function parseNumeric (val) { 67 | return parseFloat(val) || 0; 68 | } 69 | 70 | function getDocOffsetTop (node) { 71 | let docOffsetTop = 0; 72 | 73 | while (node) { 74 | docOffsetTop += node.offsetTop; 75 | node = node.offsetParent; 76 | } 77 | 78 | return docOffsetTop; 79 | } 80 | 81 | 82 | /* 83 | * 4. Sticky class 84 | */ 85 | class Sticky { 86 | constructor (node) { 87 | if (!(node instanceof HTMLElement)) 88 | throw new Error('First argument must be HTMLElement'); 89 | if (stickies.some(sticky => sticky._node === node)) 90 | throw new Error('Stickyfill is already applied to this node'); 91 | 92 | this._node = node; 93 | this._stickyMode = null; 94 | this._active = false; 95 | 96 | stickies.push(this); 97 | 98 | this.refresh(); 99 | } 100 | 101 | refresh () { 102 | if (seppuku || this._removed) return; 103 | if (this._active) this._deactivate(); 104 | 105 | const node = this._node; 106 | 107 | /* 108 | * 1. Save node computed props 109 | */ 110 | const nodeComputedStyle = getComputedStyle(node); 111 | const nodeComputedProps = { 112 | position: nodeComputedStyle.position, 113 | top: nodeComputedStyle.top, 114 | display: nodeComputedStyle.display, 115 | marginTop: nodeComputedStyle.marginTop, 116 | marginBottom: nodeComputedStyle.marginBottom, 117 | marginLeft: nodeComputedStyle.marginLeft, 118 | marginRight: nodeComputedStyle.marginRight, 119 | cssFloat: nodeComputedStyle.cssFloat 120 | }; 121 | 122 | /* 123 | * 2. Check if the node can be activated 124 | */ 125 | if ( 126 | isNaN(parseFloat(nodeComputedProps.top)) || 127 | nodeComputedProps.display == 'table-cell' || 128 | nodeComputedProps.display == 'none' 129 | ) return; 130 | 131 | this._active = true; 132 | 133 | /* 134 | * 3. Check if the current node position is `sticky`. If it is, it means that the browser supports sticky positioning, 135 | * but the polyfill was force-enabled. We set the node’s position to `static` before continuing, so that the node 136 | * is in it’s initial position when we gather its params. 137 | */ 138 | const originalPosition = node.style.position; 139 | if (nodeComputedStyle.position == 'sticky' || nodeComputedStyle.position == '-webkit-sticky') 140 | node.style.position = 'static'; 141 | 142 | /* 143 | * 4. Get necessary node parameters 144 | */ 145 | const referenceNode = node.parentNode; 146 | const parentNode = shadowRootExists && referenceNode instanceof ShadowRoot? referenceNode.host: referenceNode; 147 | const nodeWinOffset = node.getBoundingClientRect(); 148 | const parentWinOffset = parentNode.getBoundingClientRect(); 149 | const parentComputedStyle = getComputedStyle(parentNode); 150 | 151 | this._parent = { 152 | node: parentNode, 153 | styles: { 154 | position: parentNode.style.position 155 | }, 156 | offsetHeight: parentNode.offsetHeight 157 | }; 158 | this._offsetToWindow = { 159 | left: nodeWinOffset.left, 160 | right: document.documentElement.clientWidth - nodeWinOffset.right 161 | }; 162 | this._offsetToParent = { 163 | top: nodeWinOffset.top - parentWinOffset.top - parseNumeric(parentComputedStyle.borderTopWidth), 164 | left: nodeWinOffset.left - parentWinOffset.left - parseNumeric(parentComputedStyle.borderLeftWidth), 165 | right: -nodeWinOffset.right + parentWinOffset.right - parseNumeric(parentComputedStyle.borderRightWidth) 166 | }; 167 | this._styles = { 168 | position: originalPosition, 169 | top: node.style.top, 170 | bottom: node.style.bottom, 171 | left: node.style.left, 172 | right: node.style.right, 173 | width: node.style.width, 174 | marginTop: node.style.marginTop, 175 | marginLeft: node.style.marginLeft, 176 | marginRight: node.style.marginRight 177 | }; 178 | 179 | const nodeTopValue = parseNumeric(nodeComputedProps.top); 180 | this._limits = { 181 | start: nodeWinOffset.top + window.pageYOffset - nodeTopValue, 182 | end: parentWinOffset.top + window.pageYOffset + parentNode.offsetHeight - 183 | parseNumeric(parentComputedStyle.borderBottomWidth) - node.offsetHeight - 184 | nodeTopValue - parseNumeric(nodeComputedProps.marginBottom) 185 | }; 186 | 187 | /* 188 | * 5. Ensure that the node will be positioned relatively to the parent node 189 | */ 190 | const parentPosition = parentComputedStyle.position; 191 | 192 | if ( 193 | parentPosition != 'absolute' && 194 | parentPosition != 'relative' 195 | ) { 196 | parentNode.style.position = 'relative'; 197 | } 198 | 199 | /* 200 | * 6. Recalc node position. 201 | * It’s important to do this before clone injection to avoid scrolling bug in Chrome. 202 | */ 203 | this._recalcPosition(); 204 | 205 | /* 206 | * 7. Create a clone 207 | */ 208 | const clone = this._clone = {}; 209 | clone.node = document.createElement('div'); 210 | 211 | // Apply styles to the clone 212 | extend(clone.node.style, { 213 | width: nodeWinOffset.right - nodeWinOffset.left + 'px', 214 | height: nodeWinOffset.bottom - nodeWinOffset.top + 'px', 215 | marginTop: nodeComputedProps.marginTop, 216 | marginBottom: nodeComputedProps.marginBottom, 217 | marginLeft: nodeComputedProps.marginLeft, 218 | marginRight: nodeComputedProps.marginRight, 219 | cssFloat: nodeComputedProps.cssFloat, 220 | padding: 0, 221 | border: 0, 222 | borderSpacing: 0, 223 | fontSize: '1em', 224 | position: 'static' 225 | }); 226 | 227 | referenceNode.insertBefore(clone.node, node); 228 | clone.docOffsetTop = getDocOffsetTop(clone.node); 229 | } 230 | 231 | _recalcPosition () { 232 | if (!this._active || this._removed) return; 233 | 234 | const stickyMode = scroll.top <= this._limits.start? 'start': scroll.top >= this._limits.end? 'end': 'middle'; 235 | 236 | if (this._stickyMode == stickyMode) return; 237 | 238 | switch (stickyMode) { 239 | case 'start': 240 | extend(this._node.style, { 241 | position: 'absolute', 242 | left: this._offsetToParent.left + 'px', 243 | right: this._offsetToParent.right + 'px', 244 | top: this._offsetToParent.top + 'px', 245 | bottom: 'auto', 246 | width: 'auto', 247 | marginLeft: 0, 248 | marginRight: 0, 249 | marginTop: 0 250 | }); 251 | break; 252 | 253 | case 'middle': 254 | extend(this._node.style, { 255 | position: 'fixed', 256 | left: this._offsetToWindow.left + 'px', 257 | right: this._offsetToWindow.right + 'px', 258 | top: this._styles.top, 259 | bottom: 'auto', 260 | width: 'auto', 261 | marginLeft: 0, 262 | marginRight: 0, 263 | marginTop: 0 264 | }); 265 | break; 266 | 267 | case 'end': 268 | extend(this._node.style, { 269 | position: 'absolute', 270 | left: this._offsetToParent.left + 'px', 271 | right: this._offsetToParent.right + 'px', 272 | top: 'auto', 273 | bottom: 0, 274 | width: 'auto', 275 | marginLeft: 0, 276 | marginRight: 0 277 | }); 278 | break; 279 | } 280 | 281 | this._stickyMode = stickyMode; 282 | } 283 | 284 | _fastCheck () { 285 | if (!this._active || this._removed) return; 286 | 287 | if ( 288 | Math.abs(getDocOffsetTop(this._clone.node) - this._clone.docOffsetTop) > 1 || 289 | Math.abs(this._parent.node.offsetHeight - this._parent.offsetHeight) > 1 290 | ) this.refresh(); 291 | } 292 | 293 | _deactivate () { 294 | if (!this._active || this._removed) return; 295 | 296 | this._clone.node.parentNode.removeChild(this._clone.node); 297 | delete this._clone; 298 | 299 | extend(this._node.style, this._styles); 300 | delete this._styles; 301 | 302 | // Check whether element’s parent node is used by other stickies. 303 | // If not, restore parent node’s styles. 304 | if (!stickies.some(sticky => sticky !== this && sticky._parent && sticky._parent.node === this._parent.node)) { 305 | extend(this._parent.node.style, this._parent.styles); 306 | } 307 | delete this._parent; 308 | 309 | this._stickyMode = null; 310 | this._active = false; 311 | 312 | delete this._offsetToWindow; 313 | delete this._offsetToParent; 314 | delete this._limits; 315 | } 316 | 317 | remove () { 318 | this._deactivate(); 319 | 320 | stickies.some((sticky, index) => { 321 | if (sticky._node === this._node) { 322 | stickies.splice(index, 1); 323 | return true; 324 | } 325 | }); 326 | 327 | this._removed = true; 328 | } 329 | } 330 | 331 | 332 | /* 333 | * 5. Stickyfill API 334 | */ 335 | const Stickyfill = { 336 | stickies, 337 | Sticky, 338 | 339 | forceSticky () { 340 | seppuku = false; 341 | init(); 342 | 343 | this.refreshAll(); 344 | }, 345 | 346 | addOne (node) { 347 | // Check whether it’s a node 348 | if (!(node instanceof HTMLElement)) { 349 | // Maybe it’s a node list of some sort? 350 | // Take first node from the list then 351 | if (node.length && node[0]) node = node[0]; 352 | else return; 353 | } 354 | 355 | // Check if Stickyfill is already applied to the node 356 | // and return existing sticky 357 | for (var i = 0; i < stickies.length; i++) { 358 | if (stickies[i]._node === node) return stickies[i]; 359 | } 360 | 361 | // Create and return new sticky 362 | return new Sticky(node); 363 | }, 364 | 365 | add (nodeList) { 366 | // If it’s a node make an array of one node 367 | if (nodeList instanceof HTMLElement) nodeList = [nodeList]; 368 | // Check if the argument is an iterable of some sort 369 | if (!nodeList.length) return; 370 | 371 | // Add every element as a sticky and return an array of created Sticky instances 372 | const addedStickies = []; 373 | 374 | for (let i = 0; i < nodeList.length; i++) { 375 | const node = nodeList[i]; 376 | 377 | // If it’s not an HTMLElement – create an empty element to preserve 1-to-1 378 | // correlation with input list 379 | if (!(node instanceof HTMLElement)) { 380 | addedStickies.push(void 0); 381 | continue; 382 | } 383 | 384 | // If Stickyfill is already applied to the node 385 | // add existing sticky 386 | if (stickies.some(sticky => { 387 | if (sticky._node === node) { 388 | addedStickies.push(sticky); 389 | return true; 390 | } 391 | })) continue; 392 | 393 | // Create and add new sticky 394 | addedStickies.push(new Sticky(node)); 395 | } 396 | 397 | return addedStickies; 398 | }, 399 | 400 | refreshAll () { 401 | stickies.forEach(sticky => sticky.refresh()); 402 | }, 403 | 404 | removeOne (node) { 405 | // Check whether it’s a node 406 | if (!(node instanceof HTMLElement)) { 407 | // Maybe it’s a node list of some sort? 408 | // Take first node from the list then 409 | if (node.length && node[0]) node = node[0]; 410 | else return; 411 | } 412 | 413 | // Remove the stickies bound to the nodes in the list 414 | stickies.some(sticky => { 415 | if (sticky._node === node) { 416 | sticky.remove(); 417 | return true; 418 | } 419 | }); 420 | }, 421 | 422 | remove (nodeList) { 423 | // If it’s a node make an array of one node 424 | if (nodeList instanceof HTMLElement) nodeList = [nodeList]; 425 | // Check if the argument is an iterable of some sort 426 | if (!nodeList.length) return; 427 | 428 | // Remove the stickies bound to the nodes in the list 429 | for (let i = 0; i < nodeList.length; i++) { 430 | const node = nodeList[i]; 431 | 432 | stickies.some(sticky => { 433 | if (sticky._node === node) { 434 | sticky.remove(); 435 | return true; 436 | } 437 | }); 438 | } 439 | }, 440 | 441 | removeAll () { 442 | while (stickies.length) stickies[0].remove(); 443 | } 444 | }; 445 | 446 | 447 | /* 448 | * 6. Setup events (unless the polyfill was disabled) 449 | */ 450 | function init () { 451 | if (isInitialized) { 452 | return; 453 | } 454 | 455 | isInitialized = true; 456 | 457 | // Watch for scroll position changes and trigger recalc/refresh if needed 458 | function checkScroll () { 459 | if (window.pageXOffset != scroll.left) { 460 | scroll.top = window.pageYOffset; 461 | scroll.left = window.pageXOffset; 462 | 463 | Stickyfill.refreshAll(); 464 | } 465 | else if (window.pageYOffset != scroll.top) { 466 | scroll.top = window.pageYOffset; 467 | scroll.left = window.pageXOffset; 468 | 469 | // recalc position for all stickies 470 | stickies.forEach(sticky => sticky._recalcPosition()); 471 | } 472 | } 473 | 474 | checkScroll(); 475 | window.addEventListener('scroll', checkScroll); 476 | 477 | // Watch for window resizes and device orientation changes and trigger refresh 478 | window.addEventListener('resize', Stickyfill.refreshAll); 479 | window.addEventListener('orientationchange', Stickyfill.refreshAll); 480 | 481 | //Fast dirty check for layout changes every 500ms 482 | let fastCheckTimer; 483 | 484 | function startFastCheckTimer () { 485 | fastCheckTimer = setInterval(function () { 486 | stickies.forEach(sticky => sticky._fastCheck()); 487 | }, 500); 488 | } 489 | 490 | function stopFastCheckTimer () { 491 | clearInterval(fastCheckTimer); 492 | } 493 | 494 | let docHiddenKey; 495 | let visibilityChangeEventName; 496 | 497 | if ('hidden' in document) { 498 | docHiddenKey = 'hidden'; 499 | visibilityChangeEventName = 'visibilitychange'; 500 | } 501 | else if ('webkitHidden' in document) { 502 | docHiddenKey = 'webkitHidden'; 503 | visibilityChangeEventName = 'webkitvisibilitychange'; 504 | } 505 | 506 | if (visibilityChangeEventName) { 507 | if (!document[docHiddenKey]) startFastCheckTimer(); 508 | 509 | document.addEventListener(visibilityChangeEventName, () => { 510 | if (document[docHiddenKey]) { 511 | stopFastCheckTimer(); 512 | } 513 | else { 514 | startFastCheckTimer(); 515 | } 516 | }); 517 | } 518 | else startFastCheckTimer(); 519 | } 520 | 521 | if (!seppuku) init(); 522 | 523 | 524 | /* 525 | * 7. Expose Stickyfill 526 | */ 527 | if (typeof module != 'undefined' && module.exports) { 528 | module.exports = Stickyfill; 529 | } 530 | else if (isWindowDefined) { 531 | window.Stickyfill = Stickyfill; 532 | } 533 | 534 | -------------------------------------------------------------------------------- /dist/stickyfill.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Stickyfill – `position: sticky` polyfill 3 | * v. 2.1.0 | https://github.com/wilddeer/stickyfill 4 | * MIT License 5 | */ 6 | 7 | ;(function(window, document) { 8 | 'use strict'; 9 | 10 | /* 11 | * 1. Check if the browser supports `position: sticky` natively or is too old to run the polyfill. 12 | * If either of these is the case set `seppuku` flag. It will be checked later to disable key features 13 | * of the polyfill, but the API will remain functional to avoid breaking things. 14 | */ 15 | 16 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 17 | 18 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 19 | 20 | var seppuku = false; 21 | 22 | var isWindowDefined = typeof window !== 'undefined'; 23 | 24 | // The polyfill can’t function properly without `window` or `window.getComputedStyle`. 25 | if (!isWindowDefined || !window.getComputedStyle) seppuku = true; 26 | // Dont’t get in a way if the browser supports `position: sticky` natively. 27 | else { 28 | (function () { 29 | var testNode = document.createElement('div'); 30 | 31 | if (['', '-webkit-', '-moz-', '-ms-'].some(function (prefix) { 32 | try { 33 | testNode.style.position = prefix + 'sticky'; 34 | } catch (e) {} 35 | 36 | return testNode.style.position != ''; 37 | })) seppuku = true; 38 | })(); 39 | } 40 | 41 | /* 42 | * 2. “Global” vars used across the polyfill 43 | */ 44 | var isInitialized = false; 45 | 46 | // Check if Shadow Root constructor exists to make further checks simpler 47 | var shadowRootExists = typeof ShadowRoot !== 'undefined'; 48 | 49 | // Last saved scroll position 50 | var scroll = { 51 | top: null, 52 | left: null 53 | }; 54 | 55 | // Array of created Sticky instances 56 | var stickies = []; 57 | 58 | /* 59 | * 3. Utility functions 60 | */ 61 | function extend(targetObj, sourceObject) { 62 | for (var key in sourceObject) { 63 | if (sourceObject.hasOwnProperty(key)) { 64 | targetObj[key] = sourceObject[key]; 65 | } 66 | } 67 | } 68 | 69 | function parseNumeric(val) { 70 | return parseFloat(val) || 0; 71 | } 72 | 73 | function getDocOffsetTop(node) { 74 | var docOffsetTop = 0; 75 | 76 | while (node) { 77 | docOffsetTop += node.offsetTop; 78 | node = node.offsetParent; 79 | } 80 | 81 | return docOffsetTop; 82 | } 83 | 84 | /* 85 | * 4. Sticky class 86 | */ 87 | 88 | var Sticky = function () { 89 | function Sticky(node) { 90 | _classCallCheck(this, Sticky); 91 | 92 | if (!(node instanceof HTMLElement)) throw new Error('First argument must be HTMLElement'); 93 | if (stickies.some(function (sticky) { 94 | return sticky._node === node; 95 | })) throw new Error('Stickyfill is already applied to this node'); 96 | 97 | this._node = node; 98 | this._stickyMode = null; 99 | this._active = false; 100 | 101 | stickies.push(this); 102 | 103 | this.refresh(); 104 | } 105 | 106 | _createClass(Sticky, [{ 107 | key: 'refresh', 108 | value: function refresh() { 109 | if (seppuku || this._removed) return; 110 | if (this._active) this._deactivate(); 111 | 112 | var node = this._node; 113 | 114 | /* 115 | * 1. Save node computed props 116 | */ 117 | var nodeComputedStyle = getComputedStyle(node); 118 | var nodeComputedProps = { 119 | position: nodeComputedStyle.position, 120 | top: nodeComputedStyle.top, 121 | display: nodeComputedStyle.display, 122 | marginTop: nodeComputedStyle.marginTop, 123 | marginBottom: nodeComputedStyle.marginBottom, 124 | marginLeft: nodeComputedStyle.marginLeft, 125 | marginRight: nodeComputedStyle.marginRight, 126 | cssFloat: nodeComputedStyle.cssFloat 127 | }; 128 | 129 | /* 130 | * 2. Check if the node can be activated 131 | */ 132 | if (isNaN(parseFloat(nodeComputedProps.top)) || nodeComputedProps.display == 'table-cell' || nodeComputedProps.display == 'none') return; 133 | 134 | this._active = true; 135 | 136 | /* 137 | * 3. Check if the current node position is `sticky`. If it is, it means that the browser supports sticky positioning, 138 | * but the polyfill was force-enabled. We set the node’s position to `static` before continuing, so that the node 139 | * is in it’s initial position when we gather its params. 140 | */ 141 | var originalPosition = node.style.position; 142 | if (nodeComputedStyle.position == 'sticky' || nodeComputedStyle.position == '-webkit-sticky') node.style.position = 'static'; 143 | 144 | /* 145 | * 4. Get necessary node parameters 146 | */ 147 | var referenceNode = node.parentNode; 148 | var parentNode = shadowRootExists && referenceNode instanceof ShadowRoot ? referenceNode.host : referenceNode; 149 | var nodeWinOffset = node.getBoundingClientRect(); 150 | var parentWinOffset = parentNode.getBoundingClientRect(); 151 | var parentComputedStyle = getComputedStyle(parentNode); 152 | 153 | this._parent = { 154 | node: parentNode, 155 | styles: { 156 | position: parentNode.style.position 157 | }, 158 | offsetHeight: parentNode.offsetHeight 159 | }; 160 | this._offsetToWindow = { 161 | left: nodeWinOffset.left, 162 | right: document.documentElement.clientWidth - nodeWinOffset.right 163 | }; 164 | this._offsetToParent = { 165 | top: nodeWinOffset.top - parentWinOffset.top - parseNumeric(parentComputedStyle.borderTopWidth), 166 | left: nodeWinOffset.left - parentWinOffset.left - parseNumeric(parentComputedStyle.borderLeftWidth), 167 | right: -nodeWinOffset.right + parentWinOffset.right - parseNumeric(parentComputedStyle.borderRightWidth) 168 | }; 169 | this._styles = { 170 | position: originalPosition, 171 | top: node.style.top, 172 | bottom: node.style.bottom, 173 | left: node.style.left, 174 | right: node.style.right, 175 | width: node.style.width, 176 | marginTop: node.style.marginTop, 177 | marginLeft: node.style.marginLeft, 178 | marginRight: node.style.marginRight 179 | }; 180 | 181 | var nodeTopValue = parseNumeric(nodeComputedProps.top); 182 | this._limits = { 183 | start: nodeWinOffset.top + window.pageYOffset - nodeTopValue, 184 | end: parentWinOffset.top + window.pageYOffset + parentNode.offsetHeight - parseNumeric(parentComputedStyle.borderBottomWidth) - node.offsetHeight - nodeTopValue - parseNumeric(nodeComputedProps.marginBottom) 185 | }; 186 | 187 | /* 188 | * 5. Ensure that the node will be positioned relatively to the parent node 189 | */ 190 | var parentPosition = parentComputedStyle.position; 191 | 192 | if (parentPosition != 'absolute' && parentPosition != 'relative') { 193 | parentNode.style.position = 'relative'; 194 | } 195 | 196 | /* 197 | * 6. Recalc node position. 198 | * It’s important to do this before clone injection to avoid scrolling bug in Chrome. 199 | */ 200 | this._recalcPosition(); 201 | 202 | /* 203 | * 7. Create a clone 204 | */ 205 | var clone = this._clone = {}; 206 | clone.node = document.createElement('div'); 207 | 208 | // Apply styles to the clone 209 | extend(clone.node.style, { 210 | width: nodeWinOffset.right - nodeWinOffset.left + 'px', 211 | height: nodeWinOffset.bottom - nodeWinOffset.top + 'px', 212 | marginTop: nodeComputedProps.marginTop, 213 | marginBottom: nodeComputedProps.marginBottom, 214 | marginLeft: nodeComputedProps.marginLeft, 215 | marginRight: nodeComputedProps.marginRight, 216 | cssFloat: nodeComputedProps.cssFloat, 217 | padding: 0, 218 | border: 0, 219 | borderSpacing: 0, 220 | fontSize: '1em', 221 | position: 'static' 222 | }); 223 | 224 | referenceNode.insertBefore(clone.node, node); 225 | clone.docOffsetTop = getDocOffsetTop(clone.node); 226 | } 227 | }, { 228 | key: '_recalcPosition', 229 | value: function _recalcPosition() { 230 | if (!this._active || this._removed) return; 231 | 232 | var stickyMode = scroll.top <= this._limits.start ? 'start' : scroll.top >= this._limits.end ? 'end' : 'middle'; 233 | 234 | if (this._stickyMode == stickyMode) return; 235 | 236 | switch (stickyMode) { 237 | case 'start': 238 | extend(this._node.style, { 239 | position: 'absolute', 240 | left: this._offsetToParent.left + 'px', 241 | right: this._offsetToParent.right + 'px', 242 | top: this._offsetToParent.top + 'px', 243 | bottom: 'auto', 244 | width: 'auto', 245 | marginLeft: 0, 246 | marginRight: 0, 247 | marginTop: 0 248 | }); 249 | break; 250 | 251 | case 'middle': 252 | extend(this._node.style, { 253 | position: 'fixed', 254 | left: this._offsetToWindow.left + 'px', 255 | right: this._offsetToWindow.right + 'px', 256 | top: this._styles.top, 257 | bottom: 'auto', 258 | width: 'auto', 259 | marginLeft: 0, 260 | marginRight: 0, 261 | marginTop: 0 262 | }); 263 | break; 264 | 265 | case 'end': 266 | extend(this._node.style, { 267 | position: 'absolute', 268 | left: this._offsetToParent.left + 'px', 269 | right: this._offsetToParent.right + 'px', 270 | top: 'auto', 271 | bottom: 0, 272 | width: 'auto', 273 | marginLeft: 0, 274 | marginRight: 0 275 | }); 276 | break; 277 | } 278 | 279 | this._stickyMode = stickyMode; 280 | } 281 | }, { 282 | key: '_fastCheck', 283 | value: function _fastCheck() { 284 | if (!this._active || this._removed) return; 285 | 286 | if (Math.abs(getDocOffsetTop(this._clone.node) - this._clone.docOffsetTop) > 1 || Math.abs(this._parent.node.offsetHeight - this._parent.offsetHeight) > 1) this.refresh(); 287 | } 288 | }, { 289 | key: '_deactivate', 290 | value: function _deactivate() { 291 | var _this = this; 292 | 293 | if (!this._active || this._removed) return; 294 | 295 | this._clone.node.parentNode.removeChild(this._clone.node); 296 | delete this._clone; 297 | 298 | extend(this._node.style, this._styles); 299 | delete this._styles; 300 | 301 | // Check whether element’s parent node is used by other stickies. 302 | // If not, restore parent node’s styles. 303 | if (!stickies.some(function (sticky) { 304 | return sticky !== _this && sticky._parent && sticky._parent.node === _this._parent.node; 305 | })) { 306 | extend(this._parent.node.style, this._parent.styles); 307 | } 308 | delete this._parent; 309 | 310 | this._stickyMode = null; 311 | this._active = false; 312 | 313 | delete this._offsetToWindow; 314 | delete this._offsetToParent; 315 | delete this._limits; 316 | } 317 | }, { 318 | key: 'remove', 319 | value: function remove() { 320 | var _this2 = this; 321 | 322 | this._deactivate(); 323 | 324 | stickies.some(function (sticky, index) { 325 | if (sticky._node === _this2._node) { 326 | stickies.splice(index, 1); 327 | return true; 328 | } 329 | }); 330 | 331 | this._removed = true; 332 | } 333 | }]); 334 | 335 | return Sticky; 336 | }(); 337 | 338 | /* 339 | * 5. Stickyfill API 340 | */ 341 | 342 | 343 | var Stickyfill = { 344 | stickies: stickies, 345 | Sticky: Sticky, 346 | 347 | forceSticky: function forceSticky() { 348 | seppuku = false; 349 | init(); 350 | 351 | this.refreshAll(); 352 | }, 353 | addOne: function addOne(node) { 354 | // Check whether it’s a node 355 | if (!(node instanceof HTMLElement)) { 356 | // Maybe it’s a node list of some sort? 357 | // Take first node from the list then 358 | if (node.length && node[0]) node = node[0];else return; 359 | } 360 | 361 | // Check if Stickyfill is already applied to the node 362 | // and return existing sticky 363 | for (var i = 0; i < stickies.length; i++) { 364 | if (stickies[i]._node === node) return stickies[i]; 365 | } 366 | 367 | // Create and return new sticky 368 | return new Sticky(node); 369 | }, 370 | add: function add(nodeList) { 371 | // If it’s a node make an array of one node 372 | if (nodeList instanceof HTMLElement) nodeList = [nodeList]; 373 | // Check if the argument is an iterable of some sort 374 | if (!nodeList.length) return; 375 | 376 | // Add every element as a sticky and return an array of created Sticky instances 377 | var addedStickies = []; 378 | 379 | var _loop = function _loop(i) { 380 | var node = nodeList[i]; 381 | 382 | // If it’s not an HTMLElement – create an empty element to preserve 1-to-1 383 | // correlation with input list 384 | if (!(node instanceof HTMLElement)) { 385 | addedStickies.push(void 0); 386 | return 'continue'; 387 | } 388 | 389 | // If Stickyfill is already applied to the node 390 | // add existing sticky 391 | if (stickies.some(function (sticky) { 392 | if (sticky._node === node) { 393 | addedStickies.push(sticky); 394 | return true; 395 | } 396 | })) return 'continue'; 397 | 398 | // Create and add new sticky 399 | addedStickies.push(new Sticky(node)); 400 | }; 401 | 402 | for (var i = 0; i < nodeList.length; i++) { 403 | var _ret2 = _loop(i); 404 | 405 | if (_ret2 === 'continue') continue; 406 | } 407 | 408 | return addedStickies; 409 | }, 410 | refreshAll: function refreshAll() { 411 | stickies.forEach(function (sticky) { 412 | return sticky.refresh(); 413 | }); 414 | }, 415 | removeOne: function removeOne(node) { 416 | // Check whether it’s a node 417 | if (!(node instanceof HTMLElement)) { 418 | // Maybe it’s a node list of some sort? 419 | // Take first node from the list then 420 | if (node.length && node[0]) node = node[0];else return; 421 | } 422 | 423 | // Remove the stickies bound to the nodes in the list 424 | stickies.some(function (sticky) { 425 | if (sticky._node === node) { 426 | sticky.remove(); 427 | return true; 428 | } 429 | }); 430 | }, 431 | remove: function remove(nodeList) { 432 | // If it’s a node make an array of one node 433 | if (nodeList instanceof HTMLElement) nodeList = [nodeList]; 434 | // Check if the argument is an iterable of some sort 435 | if (!nodeList.length) return; 436 | 437 | // Remove the stickies bound to the nodes in the list 438 | 439 | var _loop2 = function _loop2(i) { 440 | var node = nodeList[i]; 441 | 442 | stickies.some(function (sticky) { 443 | if (sticky._node === node) { 444 | sticky.remove(); 445 | return true; 446 | } 447 | }); 448 | }; 449 | 450 | for (var i = 0; i < nodeList.length; i++) { 451 | _loop2(i); 452 | } 453 | }, 454 | removeAll: function removeAll() { 455 | while (stickies.length) { 456 | stickies[0].remove(); 457 | } 458 | } 459 | }; 460 | 461 | /* 462 | * 6. Setup events (unless the polyfill was disabled) 463 | */ 464 | function init() { 465 | if (isInitialized) { 466 | return; 467 | } 468 | 469 | isInitialized = true; 470 | 471 | // Watch for scroll position changes and trigger recalc/refresh if needed 472 | function checkScroll() { 473 | if (window.pageXOffset != scroll.left) { 474 | scroll.top = window.pageYOffset; 475 | scroll.left = window.pageXOffset; 476 | 477 | Stickyfill.refreshAll(); 478 | } else if (window.pageYOffset != scroll.top) { 479 | scroll.top = window.pageYOffset; 480 | scroll.left = window.pageXOffset; 481 | 482 | // recalc position for all stickies 483 | stickies.forEach(function (sticky) { 484 | return sticky._recalcPosition(); 485 | }); 486 | } 487 | } 488 | 489 | checkScroll(); 490 | window.addEventListener('scroll', checkScroll); 491 | 492 | // Watch for window resizes and device orientation changes and trigger refresh 493 | window.addEventListener('resize', Stickyfill.refreshAll); 494 | window.addEventListener('orientationchange', Stickyfill.refreshAll); 495 | 496 | //Fast dirty check for layout changes every 500ms 497 | var fastCheckTimer = void 0; 498 | 499 | function startFastCheckTimer() { 500 | fastCheckTimer = setInterval(function () { 501 | stickies.forEach(function (sticky) { 502 | return sticky._fastCheck(); 503 | }); 504 | }, 500); 505 | } 506 | 507 | function stopFastCheckTimer() { 508 | clearInterval(fastCheckTimer); 509 | } 510 | 511 | var docHiddenKey = void 0; 512 | var visibilityChangeEventName = void 0; 513 | 514 | if ('hidden' in document) { 515 | docHiddenKey = 'hidden'; 516 | visibilityChangeEventName = 'visibilitychange'; 517 | } else if ('webkitHidden' in document) { 518 | docHiddenKey = 'webkitHidden'; 519 | visibilityChangeEventName = 'webkitvisibilitychange'; 520 | } 521 | 522 | if (visibilityChangeEventName) { 523 | if (!document[docHiddenKey]) startFastCheckTimer(); 524 | 525 | document.addEventListener(visibilityChangeEventName, function () { 526 | if (document[docHiddenKey]) { 527 | stopFastCheckTimer(); 528 | } else { 529 | startFastCheckTimer(); 530 | } 531 | }); 532 | } else startFastCheckTimer(); 533 | } 534 | 535 | if (!seppuku) init(); 536 | 537 | /* 538 | * 7. Expose Stickyfill 539 | */ 540 | if (typeof module != 'undefined' && module.exports) { 541 | module.exports = Stickyfill; 542 | } else if (isWindowDefined) { 543 | window.Stickyfill = Stickyfill; 544 | } 545 | 546 | })(window, document); -------------------------------------------------------------------------------- /dist/stickyfill.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Stickyfill – `position: sticky` polyfill 3 | * v. 2.1.0 | https://github.com/wilddeer/stickyfill 4 | * MIT License 5 | */ 6 | !function(a,b){"use strict";function c(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}function d(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c])}function e(a){return parseFloat(a)||0}function f(a){for(var b=0;a;)b+=a.offsetTop,a=a.offsetParent;return b}function g(){function c(){a.pageXOffset!=m.left?(m.top=a.pageYOffset,m.left=a.pageXOffset,p.refreshAll()):a.pageYOffset!=m.top&&(m.top=a.pageYOffset,m.left=a.pageXOffset,n.forEach(function(a){return a._recalcPosition()}))}function d(){f=setInterval(function(){n.forEach(function(a){return a._fastCheck()})},500)}function e(){clearInterval(f)}if(!k){k=!0,c(),a.addEventListener("scroll",c),a.addEventListener("resize",p.refreshAll),a.addEventListener("orientationchange",p.refreshAll);var f=void 0,g=void 0,h=void 0;"hidden"in b?(g="hidden",h="visibilitychange"):"webkitHidden"in b&&(g="webkitHidden",h="webkitvisibilitychange"),h?(b[g]||d(),b.addEventListener(h,function(){b[g]?e():d()})):d()}}var h=function(){function a(a,b){for(var c=0;c=this._limits.end?"end":"middle";if(this._stickyMode!=a){switch(a){case"start":d(this._node.style,{position:"absolute",left:this._offsetToParent.left+"px",right:this._offsetToParent.right+"px",top:this._offsetToParent.top+"px",bottom:"auto",width:"auto",marginLeft:0,marginRight:0,marginTop:0});break;case"middle":d(this._node.style,{position:"fixed",left:this._offsetToWindow.left+"px",right:this._offsetToWindow.right+"px",top:this._styles.top,bottom:"auto",width:"auto",marginLeft:0,marginRight:0,marginTop:0});break;case"end":d(this._node.style,{position:"absolute",left:this._offsetToParent.left+"px",right:this._offsetToParent.right+"px",top:"auto",bottom:0,width:"auto",marginLeft:0,marginRight:0})}this._stickyMode=a}}}},{key:"_fastCheck",value:function(){this._active&&!this._removed&&(Math.abs(f(this._clone.node)-this._clone.docOffsetTop)>1||Math.abs(this._parent.node.offsetHeight-this._parent.offsetHeight)>1)&&this.refresh()}},{key:"_deactivate",value:function(){var a=this;this._active&&!this._removed&&(this._clone.node.parentNode.removeChild(this._clone.node),delete this._clone,d(this._node.style,this._styles),delete this._styles,n.some(function(b){return b!==a&&b._parent&&b._parent.node===a._parent.node})||d(this._parent.node.style,this._parent.styles),delete this._parent,this._stickyMode=null,this._active=!1,delete this._offsetToWindow,delete this._offsetToParent,delete this._limits)}},{key:"remove",value:function(){var a=this;this._deactivate(),n.some(function(b,c){if(b._node===a._node)return n.splice(c,1),!0}),this._removed=!0}}]),g}(),p={stickies:n,Sticky:o,forceSticky:function(){i=!1,g(),this.refreshAll()},addOne:function(a){if(!(a instanceof HTMLElement)){if(!a.length||!a[0])return;a=a[0]}for(var b=0;b { 20 | try { 21 | testNode.style.position = prefix + 'sticky'; 22 | } 23 | catch(e) {} 24 | 25 | return testNode.style.position != ''; 26 | }) 27 | ) seppuku = true; 28 | } 29 | 30 | 31 | /* 32 | * 2. “Global” vars used across the polyfill 33 | */ 34 | let isInitialized = false; 35 | 36 | // Check if Shadow Root constructor exists to make further checks simpler 37 | const shadowRootExists = typeof ShadowRoot !== 'undefined'; 38 | 39 | // Last saved scroll position 40 | const scroll = { 41 | top: null, 42 | left: null 43 | }; 44 | 45 | // Array of created Sticky instances 46 | const stickies = []; 47 | 48 | 49 | /* 50 | * 3. Utility functions 51 | */ 52 | function extend (targetObj, sourceObject) { 53 | for (var key in sourceObject) { 54 | if (sourceObject.hasOwnProperty(key)) { 55 | targetObj[key] = sourceObject[key]; 56 | } 57 | } 58 | } 59 | 60 | function parseNumeric (val) { 61 | return parseFloat(val) || 0; 62 | } 63 | 64 | function getDocOffsetTop (node) { 65 | let docOffsetTop = 0; 66 | 67 | while (node) { 68 | docOffsetTop += node.offsetTop; 69 | node = node.offsetParent; 70 | } 71 | 72 | return docOffsetTop; 73 | } 74 | 75 | 76 | /* 77 | * 4. Sticky class 78 | */ 79 | class Sticky { 80 | constructor (node) { 81 | if (!(node instanceof HTMLElement)) 82 | throw new Error('First argument must be HTMLElement'); 83 | if (stickies.some(sticky => sticky._node === node)) 84 | throw new Error('Stickyfill is already applied to this node'); 85 | 86 | this._node = node; 87 | this._stickyMode = null; 88 | this._active = false; 89 | 90 | stickies.push(this); 91 | 92 | this.refresh(); 93 | } 94 | 95 | refresh () { 96 | if (seppuku || this._removed) return; 97 | if (this._active) this._deactivate(); 98 | 99 | const node = this._node; 100 | 101 | /* 102 | * 1. Save node computed props 103 | */ 104 | const nodeComputedStyle = getComputedStyle(node); 105 | const nodeComputedProps = { 106 | position: nodeComputedStyle.position, 107 | top: nodeComputedStyle.top, 108 | display: nodeComputedStyle.display, 109 | marginTop: nodeComputedStyle.marginTop, 110 | marginBottom: nodeComputedStyle.marginBottom, 111 | marginLeft: nodeComputedStyle.marginLeft, 112 | marginRight: nodeComputedStyle.marginRight, 113 | cssFloat: nodeComputedStyle.cssFloat 114 | }; 115 | 116 | /* 117 | * 2. Check if the node can be activated 118 | */ 119 | if ( 120 | isNaN(parseFloat(nodeComputedProps.top)) || 121 | nodeComputedProps.display == 'table-cell' || 122 | nodeComputedProps.display == 'none' 123 | ) return; 124 | 125 | this._active = true; 126 | 127 | /* 128 | * 3. Check if the current node position is `sticky`. If it is, it means that the browser supports sticky positioning, 129 | * but the polyfill was force-enabled. We set the node’s position to `static` before continuing, so that the node 130 | * is in it’s initial position when we gather its params. 131 | */ 132 | const originalPosition = node.style.position; 133 | if (nodeComputedStyle.position == 'sticky' || nodeComputedStyle.position == '-webkit-sticky') 134 | node.style.position = 'static'; 135 | 136 | /* 137 | * 4. Get necessary node parameters 138 | */ 139 | const referenceNode = node.parentNode; 140 | const parentNode = shadowRootExists && referenceNode instanceof ShadowRoot? referenceNode.host: referenceNode; 141 | const nodeWinOffset = node.getBoundingClientRect(); 142 | const parentWinOffset = parentNode.getBoundingClientRect(); 143 | const parentComputedStyle = getComputedStyle(parentNode); 144 | 145 | this._parent = { 146 | node: parentNode, 147 | styles: { 148 | position: parentNode.style.position 149 | }, 150 | offsetHeight: parentNode.offsetHeight 151 | }; 152 | this._offsetToWindow = { 153 | left: nodeWinOffset.left, 154 | right: document.documentElement.clientWidth - nodeWinOffset.right 155 | }; 156 | this._offsetToParent = { 157 | top: nodeWinOffset.top - parentWinOffset.top - parseNumeric(parentComputedStyle.borderTopWidth), 158 | left: nodeWinOffset.left - parentWinOffset.left - parseNumeric(parentComputedStyle.borderLeftWidth), 159 | right: -nodeWinOffset.right + parentWinOffset.right - parseNumeric(parentComputedStyle.borderRightWidth) 160 | }; 161 | this._styles = { 162 | position: originalPosition, 163 | top: node.style.top, 164 | bottom: node.style.bottom, 165 | left: node.style.left, 166 | right: node.style.right, 167 | width: node.style.width, 168 | marginTop: node.style.marginTop, 169 | marginLeft: node.style.marginLeft, 170 | marginRight: node.style.marginRight 171 | }; 172 | 173 | const nodeTopValue = parseNumeric(nodeComputedProps.top); 174 | this._limits = { 175 | start: nodeWinOffset.top + window.pageYOffset - nodeTopValue, 176 | end: parentWinOffset.top + window.pageYOffset + parentNode.offsetHeight - 177 | parseNumeric(parentComputedStyle.borderBottomWidth) - node.offsetHeight - 178 | nodeTopValue - parseNumeric(nodeComputedProps.marginBottom) 179 | }; 180 | 181 | /* 182 | * 5. Ensure that the node will be positioned relatively to the parent node 183 | */ 184 | const parentPosition = parentComputedStyle.position; 185 | 186 | if ( 187 | parentPosition != 'absolute' && 188 | parentPosition != 'relative' 189 | ) { 190 | parentNode.style.position = 'relative'; 191 | } 192 | 193 | /* 194 | * 6. Recalc node position. 195 | * It’s important to do this before clone injection to avoid scrolling bug in Chrome. 196 | */ 197 | this._recalcPosition(); 198 | 199 | /* 200 | * 7. Create a clone 201 | */ 202 | const clone = this._clone = {}; 203 | clone.node = document.createElement('div'); 204 | 205 | // Apply styles to the clone 206 | extend(clone.node.style, { 207 | width: nodeWinOffset.right - nodeWinOffset.left + 'px', 208 | height: nodeWinOffset.bottom - nodeWinOffset.top + 'px', 209 | marginTop: nodeComputedProps.marginTop, 210 | marginBottom: nodeComputedProps.marginBottom, 211 | marginLeft: nodeComputedProps.marginLeft, 212 | marginRight: nodeComputedProps.marginRight, 213 | cssFloat: nodeComputedProps.cssFloat, 214 | padding: 0, 215 | border: 0, 216 | borderSpacing: 0, 217 | fontSize: '1em', 218 | position: 'static' 219 | }); 220 | 221 | referenceNode.insertBefore(clone.node, node); 222 | clone.docOffsetTop = getDocOffsetTop(clone.node); 223 | } 224 | 225 | _recalcPosition () { 226 | if (!this._active || this._removed) return; 227 | 228 | const stickyMode = scroll.top <= this._limits.start? 'start': scroll.top >= this._limits.end? 'end': 'middle'; 229 | 230 | if (this._stickyMode == stickyMode) return; 231 | 232 | switch (stickyMode) { 233 | case 'start': 234 | extend(this._node.style, { 235 | position: 'absolute', 236 | left: this._offsetToParent.left + 'px', 237 | right: this._offsetToParent.right + 'px', 238 | top: this._offsetToParent.top + 'px', 239 | bottom: 'auto', 240 | width: 'auto', 241 | marginLeft: 0, 242 | marginRight: 0, 243 | marginTop: 0 244 | }); 245 | break; 246 | 247 | case 'middle': 248 | extend(this._node.style, { 249 | position: 'fixed', 250 | left: this._offsetToWindow.left + 'px', 251 | right: this._offsetToWindow.right + 'px', 252 | top: this._styles.top, 253 | bottom: 'auto', 254 | width: 'auto', 255 | marginLeft: 0, 256 | marginRight: 0, 257 | marginTop: 0 258 | }); 259 | break; 260 | 261 | case 'end': 262 | extend(this._node.style, { 263 | position: 'absolute', 264 | left: this._offsetToParent.left + 'px', 265 | right: this._offsetToParent.right + 'px', 266 | top: 'auto', 267 | bottom: 0, 268 | width: 'auto', 269 | marginLeft: 0, 270 | marginRight: 0 271 | }); 272 | break; 273 | } 274 | 275 | this._stickyMode = stickyMode; 276 | } 277 | 278 | _fastCheck () { 279 | if (!this._active || this._removed) return; 280 | 281 | if ( 282 | Math.abs(getDocOffsetTop(this._clone.node) - this._clone.docOffsetTop) > 1 || 283 | Math.abs(this._parent.node.offsetHeight - this._parent.offsetHeight) > 1 284 | ) this.refresh(); 285 | } 286 | 287 | _deactivate () { 288 | if (!this._active || this._removed) return; 289 | 290 | this._clone.node.parentNode.removeChild(this._clone.node); 291 | delete this._clone; 292 | 293 | extend(this._node.style, this._styles); 294 | delete this._styles; 295 | 296 | // Check whether element’s parent node is used by other stickies. 297 | // If not, restore parent node’s styles. 298 | if (!stickies.some(sticky => sticky !== this && sticky._parent && sticky._parent.node === this._parent.node)) { 299 | extend(this._parent.node.style, this._parent.styles); 300 | } 301 | delete this._parent; 302 | 303 | this._stickyMode = null; 304 | this._active = false; 305 | 306 | delete this._offsetToWindow; 307 | delete this._offsetToParent; 308 | delete this._limits; 309 | } 310 | 311 | remove () { 312 | this._deactivate(); 313 | 314 | stickies.some((sticky, index) => { 315 | if (sticky._node === this._node) { 316 | stickies.splice(index, 1); 317 | return true; 318 | } 319 | }); 320 | 321 | this._removed = true; 322 | } 323 | } 324 | 325 | 326 | /* 327 | * 5. Stickyfill API 328 | */ 329 | const Stickyfill = { 330 | stickies, 331 | Sticky, 332 | 333 | forceSticky () { 334 | seppuku = false; 335 | init(); 336 | 337 | this.refreshAll(); 338 | }, 339 | 340 | addOne (node) { 341 | // Check whether it’s a node 342 | if (!(node instanceof HTMLElement)) { 343 | // Maybe it’s a node list of some sort? 344 | // Take first node from the list then 345 | if (node.length && node[0]) node = node[0]; 346 | else return; 347 | } 348 | 349 | // Check if Stickyfill is already applied to the node 350 | // and return existing sticky 351 | for (var i = 0; i < stickies.length; i++) { 352 | if (stickies[i]._node === node) return stickies[i]; 353 | } 354 | 355 | // Create and return new sticky 356 | return new Sticky(node); 357 | }, 358 | 359 | add (nodeList) { 360 | // If it’s a node make an array of one node 361 | if (nodeList instanceof HTMLElement) nodeList = [nodeList]; 362 | // Check if the argument is an iterable of some sort 363 | if (!nodeList.length) return; 364 | 365 | // Add every element as a sticky and return an array of created Sticky instances 366 | const addedStickies = []; 367 | 368 | for (let i = 0; i < nodeList.length; i++) { 369 | const node = nodeList[i]; 370 | 371 | // If it’s not an HTMLElement – create an empty element to preserve 1-to-1 372 | // correlation with input list 373 | if (!(node instanceof HTMLElement)) { 374 | addedStickies.push(void 0); 375 | continue; 376 | } 377 | 378 | // If Stickyfill is already applied to the node 379 | // add existing sticky 380 | if (stickies.some(sticky => { 381 | if (sticky._node === node) { 382 | addedStickies.push(sticky); 383 | return true; 384 | } 385 | })) continue; 386 | 387 | // Create and add new sticky 388 | addedStickies.push(new Sticky(node)); 389 | } 390 | 391 | return addedStickies; 392 | }, 393 | 394 | refreshAll () { 395 | stickies.forEach(sticky => sticky.refresh()); 396 | }, 397 | 398 | removeOne (node) { 399 | // Check whether it’s a node 400 | if (!(node instanceof HTMLElement)) { 401 | // Maybe it’s a node list of some sort? 402 | // Take first node from the list then 403 | if (node.length && node[0]) node = node[0]; 404 | else return; 405 | } 406 | 407 | // Remove the stickies bound to the nodes in the list 408 | stickies.some(sticky => { 409 | if (sticky._node === node) { 410 | sticky.remove(); 411 | return true; 412 | } 413 | }); 414 | }, 415 | 416 | remove (nodeList) { 417 | // If it’s a node make an array of one node 418 | if (nodeList instanceof HTMLElement) nodeList = [nodeList]; 419 | // Check if the argument is an iterable of some sort 420 | if (!nodeList.length) return; 421 | 422 | // Remove the stickies bound to the nodes in the list 423 | for (let i = 0; i < nodeList.length; i++) { 424 | const node = nodeList[i]; 425 | 426 | stickies.some(sticky => { 427 | if (sticky._node === node) { 428 | sticky.remove(); 429 | return true; 430 | } 431 | }); 432 | } 433 | }, 434 | 435 | removeAll () { 436 | while (stickies.length) stickies[0].remove(); 437 | } 438 | }; 439 | 440 | 441 | /* 442 | * 6. Setup events (unless the polyfill was disabled) 443 | */ 444 | function init () { 445 | if (isInitialized) { 446 | return; 447 | } 448 | 449 | isInitialized = true; 450 | 451 | // Watch for scroll position changes and trigger recalc/refresh if needed 452 | function checkScroll () { 453 | if (window.pageXOffset != scroll.left) { 454 | scroll.top = window.pageYOffset; 455 | scroll.left = window.pageXOffset; 456 | 457 | Stickyfill.refreshAll(); 458 | } 459 | else if (window.pageYOffset != scroll.top) { 460 | scroll.top = window.pageYOffset; 461 | scroll.left = window.pageXOffset; 462 | 463 | // recalc position for all stickies 464 | stickies.forEach(sticky => sticky._recalcPosition()); 465 | } 466 | } 467 | 468 | checkScroll(); 469 | window.addEventListener('scroll', checkScroll); 470 | 471 | // Watch for window resizes and device orientation changes and trigger refresh 472 | window.addEventListener('resize', Stickyfill.refreshAll); 473 | window.addEventListener('orientationchange', Stickyfill.refreshAll); 474 | 475 | //Fast dirty check for layout changes every 500ms 476 | let fastCheckTimer; 477 | 478 | function startFastCheckTimer () { 479 | fastCheckTimer = setInterval(function () { 480 | stickies.forEach(sticky => sticky._fastCheck()); 481 | }, 500); 482 | } 483 | 484 | function stopFastCheckTimer () { 485 | clearInterval(fastCheckTimer); 486 | } 487 | 488 | let docHiddenKey; 489 | let visibilityChangeEventName; 490 | 491 | if ('hidden' in document) { 492 | docHiddenKey = 'hidden'; 493 | visibilityChangeEventName = 'visibilitychange'; 494 | } 495 | else if ('webkitHidden' in document) { 496 | docHiddenKey = 'webkitHidden'; 497 | visibilityChangeEventName = 'webkitvisibilitychange'; 498 | } 499 | 500 | if (visibilityChangeEventName) { 501 | if (!document[docHiddenKey]) startFastCheckTimer(); 502 | 503 | document.addEventListener(visibilityChangeEventName, () => { 504 | if (document[docHiddenKey]) { 505 | stopFastCheckTimer(); 506 | } 507 | else { 508 | startFastCheckTimer(); 509 | } 510 | }); 511 | } 512 | else startFastCheckTimer(); 513 | } 514 | 515 | if (!seppuku) init(); 516 | 517 | 518 | /* 519 | * 7. Expose Stickyfill 520 | */ 521 | if (typeof module != 'undefined' && module.exports) { 522 | module.exports = Stickyfill; 523 | } 524 | else if (isWindowDefined) { 525 | window.Stickyfill = Stickyfill; 526 | } 527 | -------------------------------------------------------------------------------- /test/js/jquery-3.1.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */ 2 | !function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), 3 | a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), 4 | void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("