Permalink
Browse files

Initial commit

  • Loading branch information...
0 parents commit f048a688a3bc9bd28582703067e52c67802f2f00 @zippy1978 committed Aug 15, 2012
278 LICENSE-GPL
@@ -0,0 +1,278 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
22 LICENSE-MIT
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Gilles Grousset
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
67 README.md
@@ -0,0 +1,67 @@
+# jQuery Scrollz
+
+Modern scrolling for jQuery.
+
+Primarly designed to work on touch devices, the plugin works as well on desktop browsers.
+At the moment only vertical scroll is supported.
+
+## Getting Started
+Download the [production version][min] or the [development version][max].
+
+[min]: https://raw.github.com/zippy1978/jquery.scrollz/master/dist/jquery.scrollz.min.js
+[max]: https://raw.github.com/zippy1978/jquery.scrollz/master/dist/jquery.scrollz.js
+
+In your web page:
+
+```html
+<script src="jquery.js"></script>
+<script src="dist/jquery.scrollz.min.js"></script>
+<script>
+jQuery(function($) {
+ $.scrollz();
+});
+</script>
+```
+
+## Documentation
+_(Coming soon)_
+
+## Examples
+_(Coming soon)_
+
+## Release History
+_(Nothing yet)_
+
+## License
+Copyright (c) 2012 Gilles Grousset
+Licensed under the MIT, GPL licenses.
+
+## Contributing
+In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [grunt](https://github.com/cowboy/grunt).
+
+### Important notes
+Please don't edit files in the `dist` subdirectory as they are generated via grunt. You'll find source code in the `src` subdirectory!
+
+While grunt can run the included unit tests via PhantomJS, this shouldn't be considered a substitute for the real thing. Please be sure to test the `test/*.html` unit test file(s) in _actual_ browsers.
+
+### Installing grunt
+_This assumes you have [node.js](http://nodejs.org/) and [npm](http://npmjs.org/) installed already._
+
+1. Test that grunt is installed globally by running `grunt --version` at the command-line.
+1. If grunt isn't installed globally, run `npm install -g grunt` to install the latest version. _You may need to run `sudo npm install -g grunt`._
+1. From the root directory of this project, run `npm install` to install the project's dependencies.
+
+### Installing PhantomJS
+
+In order for the qunit task to work properly, [PhantomJS](http://www.phantomjs.org/) must be installed and in the system PATH (if you can run "phantomjs" at the command line, this task should work).
+
+Unfortunately, PhantomJS cannot be installed automatically via npm or grunt, so you need to install it yourself. There are a number of ways to install PhantomJS.
+
+* [PhantomJS and Mac OS X](http://ariya.ofilabs.com/2012/02/phantomjs-and-mac-os-x.html)
+* [PhantomJS Installation](http://code.google.com/p/phantomjs/wiki/Installation) (PhantomJS wiki)
+
+Note that the `phantomjs` executable needs to be in the system `PATH` for grunt to see it.
+
+* [How to set the path and environment variables in Windows](http://www.computerhope.com/issues/ch000549.htm)
+* [Where does $PATH get set in OS X 10.6 Snow Leopard?](http://superuser.com/questions/69130/where-does-path-get-set-in-os-x-10-6-snow-leopard)
+* [How do I change the PATH variable in Linux](https://www.google.com/search?q=How+do+I+change+the+PATH+variable+in+Linux)
BIN dist/.DS_Store
Binary file not shown.
179 dist/jquery.scrollz.css
@@ -0,0 +1,179 @@
+/*! jQuery Scrollz - v1.0.0 - 2012-08-15
+* https://github.com/zippy1978/jquery.scrollz
+* Copyright (c) 2012 Gilles Grousset; Licensed MIT, GPL */
+
+/*!
+ * jQuery Scrollz - Easy scrolling plugin
+ * Copyright(c) 2012 Gilles Grousset <gi.grousset@gmail.com>
+ *
+ * http://github.com/zippy1978/jquery.scrollz
+ */
+
+
+.scrollz-thumb {
+ width : 5px;
+ height : 30px;
+ opacity: 0.6;
+ background-color: #333;
+ border: solid 1px rgba(200, 200, 200, .5);
+ margin: 2px 1px;
+ -webkit-border-radius: 5px;
+ -moz-border-radius: 5px;
+ border-radius: 5px;
+ z-index: 1000;
+}
+.scrollz-pull-header {
+ font-family: Helvetica,Arial,sans-serif;
+ color: white;
+ text-shadow: 0 -1px -1px #333;
+ font-size: 16px;
+ font-weight: normal;
+ text-align: center;
+ vertical-align: middle;
+ padding: 100px 0px 15px 0px;
+ background-color: #555;
+}
+
+.scrollz-pull-header .label {
+ margin: 2px 0px 0px 20px;
+}
+
+.scrollz-pull-header .icon {
+ float: left;
+ width: 24px;
+ height: 24px;
+ background-repeat : no-repeat;
+ background-position: left center;
+ background-size: 24px 24px;
+ position : relative;
+ left: 50%;
+ margin-left: -85px;
+}
+
+.scrollz-pull-header.initial .icon {
+ animation: rotate-arrow-up 0.25s linear 0s forwards;
+ -webkit-animation: rotate-arrow-up 0.25s linear 0s forwards;
+ -moz-animation: rotate-arrow-up 0.25s linear 0s forwards;
+ -ms-animation: rotate-arrow-up 0.25s linear 0s forwards;
+ -o-animation: rotate-arrow-up 0.25s linear 0s forwards;
+
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYVJREFUeNq0lb9KA0EQxu88vYtoISpYCVoollaij5DGN7CxEmzyBr6CD2AjYqPYp9JCsLER9Ak0aKGCsTAxuez6DX6Bddnbu5jLwI9sbm7m25n9c6HWOvDYLNgCIdDGb8BxB1yDVlaC8cBvE2DBEgiMcRuM+RLkCUhw7PErQ/RfAhGoGP/tFoVlVDDpaVEwbAV9AZfZoqUK9BOrMgQqngrUqCtIR1WBpq90gdDY/+LrDivQ8QhE9LcGEVgBqwyUZIsgsQ6WeYKnQRU0+UyulgfwlCXwBXbAslF+ynaE1hooxtf4jkzkFtz82WqO23QdHPIO0mxFlCGgmFz8r2AfNOxFtO0OHIEZ9j8miYOY27jCSTVcu8Rlp6AO5nKSJ3znBFw5T6PngyOB52DNce9LUA9MgUuwC74HFRDbABecbWqI9PjsGWyDx8wMIpDDHmiDN4N38AGqefFFBIRj/WtN8MnxQZHYogLz4B4oJq+DuEwBYRN0wQtYKhqXt8i21biwZ0UDfgQYANH+g+OdUGLlAAAAAElFTkSuQmCC);
+}
+
+.scrollz-pull-header.release .icon {
+ animation: rotate-arrow-down 0.25s linear 0s forwards;
+ -webkit-animation: rotate-arrow-down 0.25s linear 0s forwards;
+ -moz-animation: rotate-arrow-down 0.25s linear 0s forwards;
+ -ms-animation: rotate-arrow-down 0.25s linear 0s forwards;
+ -o-animation: rotate-arrow-down 0.5s linear 0s forwards;
+
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAYVJREFUeNq0lb9KA0EQxu88vYtoISpYCVoollaij5DGN7CxEmzyBr6CD2AjYqPYp9JCsLER9Ak0aKGCsTAxuez6DX6Bddnbu5jLwI9sbm7m25n9c6HWOvDYLNgCIdDGb8BxB1yDVlaC8cBvE2DBEgiMcRuM+RLkCUhw7PErQ/RfAhGoGP/tFoVlVDDpaVEwbAV9AZfZoqUK9BOrMgQqngrUqCtIR1WBpq90gdDY/+LrDivQ8QhE9LcGEVgBqwyUZIsgsQ6WeYKnQRU0+UyulgfwlCXwBXbAslF+ynaE1hooxtf4jkzkFtz82WqO23QdHPIO0mxFlCGgmFz8r2AfNOxFtO0OHIEZ9j8miYOY27jCSTVcu8Rlp6AO5nKSJ3znBFw5T6PngyOB52DNce9LUA9MgUuwC74HFRDbABecbWqI9PjsGWyDx8wMIpDDHmiDN4N38AGqefFFBIRj/WtN8MnxQZHYogLz4B4oJq+DuEwBYRN0wQtYKhqXt8i21biwZ0UDfgQYANH+g+OdUGLlAAAAAElFTkSuQmCC);
+}
+
+.scrollz-pull-header.waiting .icon {
+ background-image: url(data:image/gif;base64,R0lGODlhGAAYAPcAAFVVVVtbW1xcXGdnZ2lpaW9vb3BwcHd3d3l5eX19fYODg4WFhYqKio2NjZKSkpSUlJmZmZ6enqGhoaSkpKqqqq2trbKysrS0tLu7u7y8vMPDw8TExMnJyczMzNPT09TU1Nvb293d3eLi4ufn5+jo6O/v7/Hx8fb29vr6+llZWW5ubnV1dXt7e35+foKCgouLi5GRkZWVlZ2dnaOjo6ampqmpqbm5ub+/v8HBwcbGxsrKytHR0dXV1d7e3uPj4+rq6vDw8FpaWmRkZGhoaG1tbXJycnh4eIGBgYSEhIiIiI6Ojri4uL6+vsvLy9LS0tfX19nZ2eXl5ezs7PPz82pqanFxcYaGhpaWlpiYmKKioqenp66urrOzs7a2ttjY2Pn5+WZmZoeHh4mJiY+Pj5CQkJubm6Wlpa+vr7e3t8LCwsfHx83Nzdra2vT09GxsbHp6en9/f6ioqKysrLq6usXFxc7OznZ2dpeXl729vdDQ0Nzc3O3t7Zqamqurq9bW1ubm5vf39wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/i1NYWRlIGJ5IEtyYXNpbWlyYSBOZWpjaGV2YSAod3d3LmxvYWRpbmZvLm5ldCkAIfkEAAoA/wAsAAAAABgAGAAABv9AgHBILBqPwwAicikUKhCEAFlUbEYlEgIxIokyigA1MCGZzqTEoUvyRpDix+hsSnPbXoe4GKA8AnIlIRgEBBcgXg0BDRF7QgtnEIATBUkFE3oOICEJSRwoKCYPVAANICJfewgloCgVpBKoIiEHQhEnoCOVVAQesqMAFifDGo5HARchyq8ABQnPu6QEBwcIuwNOBQOkQtPVBEIOGuMS3AEVHx4fE0IKGhsbGNu8HCAgHw7dGfAb+VQSH+x5qAUgQAR4YAoiYeDB3gVHBjJoUABgwQNwQgIUiMAgAMMOnYgsCLNgA4cMEgYMqLDBA4eODSAgKdmhJgcDBjqkc8lA4RElBxtqduBArYOHoxwacDtAweQGnEY3TCBozgCjQhEWFDDGrSuRIAAh+QQACgD/ACwAAAAAGAAYAAAG/0CAcEgsGo/DACJyKRQqEIQAWVRsRiUSAjEiiTKKADUwIZnOpMShS/JGkOLH6GxKc9teh7gYoDwCciUhGAQEFyBeDQENEXtCC2cQgBMFSQUTeg4gIQlJHCgoJg9UAA0gIl97CCWgKBWkEqgiIQdCESegI5VUBB6yowAWJ8MajkcBFyHKrwAFCc+7pAQHBwi7A04FA6RC09UEQg4a4xLcARUfHh8TQgoaGxsY27wcICAfDt0Z8Bv5VBIf7HmoBSBABHhgCiJh4MHeBUcGMmhQAGDBA3BCAhSIwCAAww6diCwIs2ADhwwSBgyosMEDh44NICAp2aEmBwMGOqRzyUDhESUHG2p24ECtg4ejHBpwO0DB5AacRjdMIGjOAKNCERYUMMatK5EgACH5BAAKAP8ALAAAAAAYABgAAAf/gACCg4SFhoeDKSwzNgYqFzIsKYiEKQs6JShALSw/JSM4C5OIKTVAKKhALp1ArSM0o4YpEKepnD+tnzEpsYIpDS6zmaAqBDY9QCO7MDO9Kzc3wTE1KokqNcs+Pi6JMzk50ZQAMD4/PzejKjffOTDiNOY/PiyCC+w2A+IqIPEQgjA4As4QByDFhRHlaggaoKJhPoIEWLBYUQ0AAQMYH4qLuIKiIAg6QsISZ7BHjxEKAVwKiYPAxg7afPiziKNDyJmIZiD0AWKFLxo6OuRYUBBRAxDabMRSgWMogAYQXPpSMaPBLxAgWhRqQPSFjh05ahAgcEEHiB0NxkHoVTCF1x1wKzt05IH1rFW2g2B8jWtgxY66O2KQZHGhA9yOf3XU8ElQkAFmY6uqwNu4ciAAIfkEAAoA/wAsAAAAABgAGAAAB/+AAIKDhIWGh4MpLDM2BioXMiwpiIQpCzolKEAtLD8lIzgLk4gpNUAoqEAunUCtIzSjhikQp6mcP62fMSmxgikNLrOZoCoENj1AI7swM70rNzfBMTUqiSo1yz4+LokzOTnRlAAwPj8/N6MqN985MOI05j8+LIIL7DYD4iog8RCCMDgCzhAHIMWFEeVqCBqgomE+ggRYsFhRDQABAxgfiou4gqIgCDpCwhJnsEePEQoBXAqJg8DGDtp8+LOIo0PImYhmIPQBYoUvGjo65FhQEFEDENpsxFKBYyiABhBc+lIxo8EvECBaFGpA9IWOHTlqECBwQQeIHQ3GQehVMIXXHXArO3TkgfWsVbaDYHyNa2DFjro7YpBkcaED3I5/ddTwSVCQAWZjq6rA27hyIAAh+QQACgD/ACwAAAAAGAAYAAAH/4AAgoOEhYaHg0FESRBDRUsSRkGIhEErNEw5OEQJQChATUiTlEqZOZqcniifNYiTSKZNmwlTq58QQaOEMKJHmRYRQkQ4JLdBMTS6AEanvUpCgwJEFjHHUVFIiTVNHTnZlDEkQEA5AoLDHR1NMZQANeNAJEaCSekdTEPtRCHwEoIQ3ZrQaAcgCBMpJKRYEOSoSBFoBIckSGCkCMMVK4rki2ikIxFBEZx0cFJBmaEgS66RqEDPicscGxERgZKQBIRzOVw6idCOhhSEUeYVrOGyQxJXAGCIIEGCiTlBRZo0OapEwhBdRGgoSXrtSCEYSYIocQKlQwUhQ5ZAEQFFSRAYETlMFkxCFgoUJyuMhBAhIkqIreDqQnmCcS9fKOwoBTFiwckTvHrZWlgh15CAFciIDKEBg0hlgqABBAIAIfkEAAoA/wAsAAAAABgAGAAAB/+AAIKDhIWGh4NBREkQQ0VLEkZBiIRBKzRMOThECUAoQE1Ik5RKmTmanJ4onzWIk0imTZsJU6ufEEGjhDCiR5kWEUJEOCS3QTE0ugBGp71KQoMCRBYxx1FRSIk1TR052ZQxJEBAOQKCwx0dTTGUADXjQCRGgknpHUxD7UQh8BKCEN2a0GgHIAgTKSSkWBDkqEgRaASHJEhgpAjDFSuK5ItopCMRQRGcdHBSQZmhIEuukahAz4nLHBsREYGSkASEczlcOonQjoYUhFHmFazhskMSVwBgiCBBgok5QUWaNDmqRMIQXURoKEl67UghGEmCKHECpUMFIUOWQBEBRUkQGBE5TBZMQhYKFCcrjIQQISJKiK3g6kJ5gnEvXyjsKAUxYsHJE7x62VpYIdeQgBXIiAyhAYNIZYKgAQQCACH5BAAKAP8ALAAAAAAYABgAAAf/gACCg4SFhoeDQVUNEVRUVwwqiIYHFBtNG1UqGhpMWgeTAEEOGx2mTZoaG6tMDUGIQValTbSpqxtMCkGvhEGQAFYaTUxZQkIRNrgKAFgUvIIspgxBCg5Cz0IOulgmUlaJW05OHQyh3F9fTQKCVU3iTlihW+hfJi2CDU4fThtUoSpR6GURFMGUE2ehAGg4wdCGICoHqhwQkhCAihYYqzw8wNFfQiosQkoCEGGfly0JgzD58UNKF3xevIToMBKRihBSukVg1yFEzIGTKJgY+oOFoCBbPnhx0iDUlShDNTw70OGDKwdZqPBSRMHBKJbLCF1x1cCLCCdcHNnw8kOE1ytAOQ058BJFhIgQB1iIkMLSrahDQbCEsHs3bxSWP0LEC9Vi7d2Qh0N0MVoxyIErFFRQ0eKgyrOKoAcFAgAh+QQACgD/ACwAAAAAGAAYAAAH/4AAgoOEhYaHg0FVDRFUVFcMKoiGBxQbTRtVKhoaTFoHkwBBDhsdpk2aGhurTA1BiEFWpU20qasbTApBr4RBkABWGk1MWUJCETa4CgBYFLyCLKYMQQoOQs9CDrpYJlJWiVtOTh0ModxfX00CglVN4k5YoVvoXyYtgg1OH04bVKEqUehlERTBlBNnoQBoOMHQhiAqB6ocEJIQgIoWGKs8PMDRX0IqLEJKAhBhn5ctCYMw+fFDShd8XryE6DASkYoQUrpFYNchRMyBkyiYGPqDhaAgWz54cdIg1JUoQzU8O9DhgysHWajwUkTBwSiWywhdcdXAiwgnXBzZ8PJDhNcrQDkNOfASRYSIEAdYiJDC0q2oQ0GwhLB7N28Ulj9CxAvVYu3dkIdDdDFaMciBKxRUUNHioMqziqAHBQIAIfkEAAoA/wAsAAAAABgAGAAAB/+AAIKDhIWGh4NBRWQSBAQyY0VBiIRBLGdrTmsrRWpNaRQrk5RXmU6anJ5NTWpklEFiTaeaRZ1ra59hQaOJZWMAsU6gYGASTKxhAEe/hAmnv2FlYLxgV8kJTEwriVxsbE7MiEdMampmo0VO3jwylABk5WpMKoJj3mxNBO4EaOVpYoJk8HDCgwsvRDLSMElTRhCBFRD1uQMARoVFMA5ZRJwIQEWCj0UEURARQgSag4aCpGnDEkM9ETB50KOk4seXmxIEFeEBU4QZd2dufgHCQlAQNDDZhDtUpsTNJrxWeBsThJGKUYrOkAlSBgiQZISuUCUj4gcbNI7SiCjx40qQKz8zD5H9QTcKCxY/SgApEcUtSqMyytZdgdcr33busJW1i7eECAxFOQpYUeaMRQpXJHHcbCgQACH5BAAKAP8ALAAAAAAYABgAAAf/gACCg4SFhoeDQUVkEgQEMmNFQYiEQSxna05rK0VqTWkUK5OUV5lOmpyeTU1qZJRBYk2nmkWda2ufYUGjiWVjALFOoGBgEkysYQBHv4QJp79hZWC8YFfJCUxMK4lcbGxOzIhHTGpqZqNFTt48MpQAZOVqTCqCY95sTQTuBGjlaWKCZPBwwoMLL0Qy0jBJU0YQgRUQ9bkDAEaFRTAOWUScCEBFgo9FBFEQEUIEmoOGgqRpwxJDPREwedCjpOLHl5sSBBXhAVOEGXdnbn4BwkJQEDQw2YQ7VKbEzSa8VngbE4SRilGKzpAJUgYIkGSErlAlI+IHGzSO0ogo8eNKkCs/Mw+R/UE3CgsWP0oAKRHFLUqjMsrWXYHXK9927rCVtYu3hAgMRTkKWFHmjEUKVyRx3GwoEAAh+QQACgD/ACwAAAAAGAAYAAAH/4AAgoOEhYaHgwErMDRubhIwRQGIhAFwFzsfHysrdTtNckeTlFg7IKebRZ47O3UPlAEvpqicq58vhwEyMAAvdSB0cgRgcXS3AFYwo4Jwp2OxWASJBFhWAVZ0dG+JcyIiIGOUyHRNdXGjRT0iIyIS4g91dU10boIw3iI70pQEGfJN4QDQ6EHwwjJEEuY1kSGIwJsVb/aJA1Ok4j43bzJKpETRTREwguKMGDnn4CEZdDTQeQUAxo+XIIpMvDCPDi4A6V7+oCEORjY6GeoBCHDhpQhlJgcdyZBNwrI36pTBiCN0aJELWAIcSbmiEBZlD34A6TGHgBs6P1AAyXrkpqEHIzJKAAHy4w0cICjyroUlYcRcunbb5G1TQkZSQkc0xK17F8UPDXDEJXoj44IjrJIka0YUCAAh+QQACgD/ACwAAAAAGAAYAAAH/4AAgoOEhYaHgwErMDRubhIwRQGIhAFwFzsfHysrdTtNckeTlFg7IKebRZ47O3UPlAEvpqicq58vhwEyMAAvdSB0cgRgcXS3AFYwo4Jwp2OxWASJBFhWAVZ0dG+JcyIiIGOUyHRNdXGjRT0iIyIS4g91dU10boIw3iI70pQEGfJN4QDQ6EHwwjJEEuY1kSGIwJsVb/aJA1Ok4j43bzJKpETRTREwguKMGDnn4CEZdDTQeQUAxo+XIIpMvDCPDi4A6V7+oCEORjY6GeoBCHDhpQhlJgcdyZBNwrI36pTBiCN0aJELWAIcSbmiEBZlD34A6TGHgBs6P1AAyXrkpqEHIzJKAAHy4w0cICjyroUlYcRcunbb5G1TQkZSQkc0xK17F8UPDXDEJXoj44IjrJIka0YUCAAh+QQACgD/ACwAAAAAGAAYAAAH/4AAgoOEhYaHgwF2d3JERGYOdgGIhAFwNiA+ekZ2HyB5NkaUghGZPpp2dnkgrB9Yk4cBDaaonayfDbESDgEOmXlcBANyOZ9JAUl3sIJHp7wNEQTLBBHHSR0dcIl4Pz8+uYjIHR95crB2Pt0/EqNY5B8dRYIO6iBEowQ5eeQOgmY/I0bYWIaIArY87AAQMcKQwChBBFLZcagQDsN7DwcU2UhRzp4fe/AQPGSGg8kIgu4AWelDHqUBeDpw6JBEUBEfK4HIGXUHW4ccLgPg2QNkRD9KVnLMpLDMSDplWLhgBBBgwJ0jAZLmsFMogjKVKH7gECbBRg48WK0cNYQFCIq3QC/gEMGToy7aUWb2vEURlwiOujjw1KQU4AgHvVOO+D0bweVDAUbKFrF6RNrDy4gCAQAh+QQACgD/ACwAAAAAGAAYAAAH/4AAgoOEhYaHgwF2d3JERGYOdgGIhAFwNiA+ekZ2HyB5NkaUghGZPpp2dnkgrB9Yk4cBDaaonayfDbESDgEOmXlcBANyOZ9JAUl3sIJHp7wNEQTLBBHHSR0dcIl4Pz8+uYjIHR95crB2Pt0/EqNY5B8dRYIO6iBEowQ5eeQOgmY/I0bYWIaIArY87AAQMcKQwChBBFLZcagQDsN7DwcU2UhRzp4fe/AQPGSGg8kIgu4AWelDHqUBeDpw6JBEUBEfK4HIGXUHW4ccLgPg2QNkRD9KVnLMpLDMSDplWLhgBBBgwJ0jAZLmsFMogjKVKH7gECbBRg48WK0cNYQFCIq3QC/gEMGToy7aUWb2vEURlwiOujjw1KQU4AgHvVOO+D0bweVDAUbKFrF6RNrDy4gCAQAh+QQACgD/ACwAAAAAGAAYAAAI/wABCBxIsKDBgwMDGOFzpkCBPg9WBEBYUIEGEXv+GDGiR4SeGy0mIgwQQQSJkxo5iljpJQLFADBM7iGRsuMfjzAOBjDzAKZJL1yoUOHiRIQXJQGU8BEpUAHNnjAkUGFKRQJSJU6ctEioAQiQPw8oAsCqB6jIFX+87ukjNoKXt05WCOTjFYieAmKpNPFSNieAMzP33BAr8Iyfw2YEFmixES9hKisiO17cooVjsUKqrKhCRSAXQEAAqWGKsI+TJk4kzP3C+o9cilTUZHXidwUJ1l+4iOUzu0MVgQE0APoCBEYA0gRfoHbChWmLPUD4AFgAQwhBKnwWTG/SwUjBCNIV3EnQwCWCECpmNDRRo/2FX4ITxauZr6FAFTXc1y84jvDFjflq3FDFfU0UqMELhK0gwX832IefBhL8RhgAARSwAB9CPbDAVBN2aFBAACH5BAAKAP8ALAAAAAAYABgAAAj/AAEIHEiwoMGDAwMY4XOmQIE+D1YEQFhQgQYRe/4YMaJHhJ4bLSYiDBBBBImTGjmKWOklAsUAMEzuIZGy4x+PMA4GMPMApkkvXKhQ4eJEhBclAZTwESlQAc2eMCRQYUpFAlIlTpy0SKgBCJA/DygCwKoHqMgVf7zu6SM2gpe3TlYI5OMViJ4CYqk08VI2J4AzM/fcECvwjJ/DZgQWaLERL2EqKyI7XtyihWOxQqqsqEJFIBdAQACpYYqwj5MmTiTM/cL6j1yKVNRkdeJ3BQnWX7iI5TO7QxWBATQA+gIERgDSBF+gdsKFaYs9QPgAWABDCEEqfBZMb9LBSMEI0hXcSdDAJYIQKmY0NFGj/YVfghPFq5mvoUAVNdzXLziO8MWN+WrcUMV9TRSowQuErSDBfzfYh58GEvxGGAABFLAAH0I9sMBUE3ZoUEAAOw==);
+}
+
+/* Retina support */
+@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
+ only screen and (min--moz-device-pixel-ratio: 1.5),
+ only screen and (min-resolution: 240dpi) {
+
+ .scrollz-pull-header.initial .icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA09JREFUeNrUmTuLFEEUhbu6e9b1iYHgqoEIRv4E8YEaC4KZmZGCgS4YimAmoiYiiiYusj4CE41EVBQTH6AoqKCBGAiCbqLIusx0eYq9hUVZVdP1aLfmwsdMzzDddfueU3WnmnHOi4hYBzaBxvAdo1fTBTh93wfPwa/QAdRFXCwBa2kgrGUC6nE/dgyxCciBMkcCzFKFwvCb/55AqZyDDRkYd0hqQRPoGe64LbjmgegqpEygzWC4xcwLlgCjc3CHjFwm5rl4gAd4gOVQASkhHuiBkZCQSzYjLaGCVvCRl1CRSwI+EpIJNLl4oGmxmGUtocZDQmq1mlxMPAj0wCCnCvhISE2gyMEDg4gE2KhJSDXxIIdZqBeYQBYSEhWo6DzMshZwRwXqXExsqgBzrAHZeECauA5MoM5BQjEJVF1WoDRodiEqUCptx78DcGxsbQB7wCrav6m0LRTxwwmw3tKNMscawJVp9L2yscVpoJJx8BS8Ap98ExCxDRxXBlgS8n2jGJF5LmSSWrnDKuLzL+Ao+DqsPLZ4DKbBGFVBMqDXQpGQpGf4zPZ5RYPVzys+mwVnXYNva+Krxfz+52Y6aalVgmnvXRXgmlRK7VV2tUI6F8DLFLNQn+7ERrCajnUpMcsWo20h0//Uqx5aBu6Ba63mcY/d6S3gtCY/VwLMMQNxg2EbktlncAB8LzymqDbxBEyB5aRdF7XhfT3kN2OUxMm2g/dNQMQlMvYKpQ+qPBOyIaRzEbzwagUCHnCsIX2KhxtzBikNM7EqH/m6FNwGk969TOATmu3gsjIwl5FdCQgWgY9gH/gW0suExCNwnqRkkkPZ4rgk3YsqHgsZfEwCIs6Bu2Clhx9K7T+E0P0Z8Cy4HY58yCf8cIf6od+GBU1vI1Tdi9nsJjgY1c9HJiBiB7ih7bQxQzOnan8xeAd2+0yZqSUk4yE4ZfBDadH/OLUkk7GDT1UBOW1eB3vBD8uNkf28mDKPUK9T5JKA9MN96plmLRISVboC9qe6aMoEROwkU1falgkn3b8Gu8BMqguWRdp4AE7Q4qROm+L4J804M0mvKCrQAbf4fMwRIg51ca2uEpgAH/jfmOroOsk9oPtBmPot2JpcOh2ZWI/D4A15o5P4I8AAEM5Kye0FusoAAAAASUVORK5CYII=);
+ }
+
+ .scrollz-pull-header.release .icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA09JREFUeNrUmTuLFEEUhbu6e9b1iYHgqoEIRv4E8YEaC4KZmZGCgS4YimAmoiYiiiYusj4CE41EVBQTH6AoqKCBGAiCbqLIusx0eYq9hUVZVdP1aLfmwsdMzzDddfueU3WnmnHOi4hYBzaBxvAdo1fTBTh93wfPwa/QAdRFXCwBa2kgrGUC6nE/dgyxCciBMkcCzFKFwvCb/55AqZyDDRkYd0hqQRPoGe64LbjmgegqpEygzWC4xcwLlgCjc3CHjFwm5rl4gAd4gOVQASkhHuiBkZCQSzYjLaGCVvCRl1CRSwI+EpIJNLl4oGmxmGUtocZDQmq1mlxMPAj0wCCnCvhISE2gyMEDg4gE2KhJSDXxIIdZqBeYQBYSEhWo6DzMshZwRwXqXExsqgBzrAHZeECauA5MoM5BQjEJVF1WoDRodiEqUCptx78DcGxsbQB7wCrav6m0LRTxwwmw3tKNMscawJVp9L2yscVpoJJx8BS8Ap98ExCxDRxXBlgS8n2jGJF5LmSSWrnDKuLzL+Ao+DqsPLZ4DKbBGFVBMqDXQpGQpGf4zPZ5RYPVzys+mwVnXYNva+Krxfz+52Y6aalVgmnvXRXgmlRK7VV2tUI6F8DLFLNQn+7ERrCajnUpMcsWo20h0//Uqx5aBu6Ba63mcY/d6S3gtCY/VwLMMQNxg2EbktlncAB8LzymqDbxBEyB5aRdF7XhfT3kN2OUxMm2g/dNQMQlMvYKpQ+qPBOyIaRzEbzwagUCHnCsIX2KhxtzBikNM7EqH/m6FNwGk969TOATmu3gsjIwl5FdCQgWgY9gH/gW0suExCNwnqRkkkPZ4rgk3YsqHgsZfEwCIs6Bu2Clhx9K7T+E0P0Z8Cy4HY58yCf8cIf6od+GBU1vI1Tdi9nsJjgY1c9HJiBiB7ih7bQxQzOnan8xeAd2+0yZqSUk4yE4ZfBDadH/OLUkk7GDT1UBOW1eB3vBD8uNkf28mDKPUK9T5JKA9MN96plmLRISVboC9qe6aMoEROwkU1falgkn3b8Gu8BMqguWRdp4AE7Q4qROm+L4J804M0mvKCrQAbf4fMwRIg51ca2uEpgAH/jfmOroOsk9oPtBmPot2JpcOh2ZWI/D4A15o5P4I8AAEM5Kye0FusoAAAAASUVORK5CYII=);
+ }
+
+ .scrollz-pull-header.waiting .icon {
+ background-image: url(data:image/gif;base64,R0lGODlhMAAwAPcAAFVVVVtbW19fX2JiYmVlZWtra2xsbHJycnR0dHh4eH19fYKCgoeHh4mJiYyMjJCQkJSUlJiYmJ2dnaKioqSkpKqqqq+vr7Ozs7e3t7u7u7+/v8PDw8TExMvLy83NzdLS0tfX19nZ2d/f3+Dg4OTk5Ovr6+3t7fPz8/f39/n5+VlZWV1dXWZmZm9vb3d3d3t7e3x8fISEhIqKipKSkpaWlpmZmZycnKOjo62trbCwsLm5uby8vMHBwcbGxsrKys7OztDQ0NXV1dra2tvb2+bm5urq6vDw8PX19fr6+mBgYGhoaG1tbXFxcXp6en5+fo2NjZGRkZWVlZubm6GhoaampqmpqaysrLW1tbq6usDAwMjIyMzMzNbW1tzc3OLi4ufn5+jo6O7u7lxcXGFhYWRkZHNzc4CAgIiIiJeXl6urq7Gxsba2tri4uMfHx8/Pz9jY2N7e3uXl5enp6fb29lpaWm5ubnl5eX9/f4GBgYuLi46Ojp+fn6enp6ioqK6uru/v72dnZ2pqanV1dYaGhpOTk5qamqCgoL29vcLCwuHh4WlpaXZ2dt3d3YODg8XFxePj4wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/i1NYWRlIGJ5IEtyYXNpbWlyYSBOZWpjaGV2YSAod3d3LmxvYWRpbmZvLm5ldCkAIfkEAAoA/wAsAAAAADAAMAAABv9AgHBILBqPyKRyyWw6kwHDAoIQHh6KQuDJFQoUlM7IZIoIISVSaDNJbLtKgYNDOp1MdzMAQir5SyIaDQJwRwocJXZkinpof38jGgmFQwIUdXdkmmVnj31/IhOEcAEXeJuKeGYBjp9/nxZvXQ1jeHepnHuPfq4hDF2jAA0imagnja+7vkOySgMZDkMMI7eLZBBCD7ufy0IMFsFJFSklDdLEeCUeFA0EQgQMYSN/3QAMICMSSwsmKeTRQmiZ2LAgHBEBDDao+eUtxAg1CpII6ODPXwmA9iAYPDIgAkMACxySeJhhoxAHJyr6M4Gx0L2HIx6K+EgkAEWVFs0VCjkSZsz/kkYQ9MOZAoW+QhBixuxJQkSVIhOI+vNgsokADQ9H9sxXZKLUFC3hLFi6dOSGZgZISA0xgJKQAXSyLgVhgAgCFFIvuB0yYatWERGl4SX6YK83uT5bFoAgoXEExwUMAxjwAEJlyxAgHJDMuTNneAwYLAgduq3hAQ5SO3iguu6QBBs4yJ7NIfDeBSBC5A7BO4ROIQZi05aNzbAE3b15g5hU6cJw2ReqOhGwAbn1DpGJSHgu2/bOD713h9BgcAF3DhVMwxGQIXzyo0QIYOjA/XeXB+5B5P7wtAiE5x1k0F8XC3TgHm8YNDOEARoMlwFzlBRonW4LIMEKbQ8OocAgTAwAzUGFQkiIHAbSySdbhkLA1oEFCVQ1QAMZfFDbECJ6MKAhG6AIAGwe9MgBBhAkYNoADEyggQcfJNmBbRIWtkQADkDIYwdU9kglQw0kqaWWHYBojyhdqEjlmGNiueWZHHipIBMKaEDmmx3o5MCZZy5ZCCsdeABnmSfRuaUHxZHSwAZ7Uimnn0luwMCaXBxAAQd66jmmTlnS2cEE2RkWgAIVcPDmoVtyIMFmngEQwAEOXEBonAF90IEG7LhTahEBEIAAA64VoIAB0s3q669cBAEAIfkEAAoA/wAsAAAAADAAMAAABv9AgHBILBqPyKRyyWw6kwHDAoIQHh6KQuDJFQoUlM7IZIoIISVSaDNJbLtKgYNDOp1MdzMAQir5SyIaDQJwRwocJXZkinpof38jGgmFQwIUdXdkmmVnj31/IhOEcAEXeJuKeGYBjp9/nxZvXQ1jeHepnHuPfq4hDF2jAA0imagnja+7vkOySgMZDkMMI7eLZBBCD7ufy0IMFsFJFSklDdLEeCUeFA0EQgQMYSN/3QAMICMSSwsmKeTRQmiZ2LAgHBEBDDao+eUtxAg1CpII6ODPXwmA9iAYPDIgAkMACxySeJhhoxAHJyr6M4Gx0L2HIx6K+EgkAEWVFs0VCjkSZsz/kkYQ9MOZAoW+QhBixuxJQkSVIhOI+vNgsokADQ9H9sxXZKLUFC3hLFi6dOSGZgZISA0xgJKQAXSyLgVhgAgCFFIvuB0yYatWERGl4SX6YK83uT5bFoAgoXEExwUMAxjwAEJlyxAgHJDMuTNneAwYLAgduq3hAQ5SO3iguu6QBBs4yJ7NIfDeBSBC5A7BO4ROIQZi05aNzbAE3b15g5hU6cJw2ReqOhGwAbn1DpGJSHgu2/bOD713h9BgcAF3DhVMwxGQIXzyo0QIYOjA/XeXB+5B5P7wtAiE5x1k0F8XC3TgHm8YNDOEARoMlwFzlBRonW4LIMEKbQ8OocAgTAwAzUGFQkiIHAbSySdbhkLA1oEFCVQ1QAMZfFDbECJ6MKAhG6AIAGwe9MgBBhAkYNoADEyggQcfJNmBbRIWtkQADkDIYwdU9kglQw0kqaWWHYBojyhdqEjlmGNiueWZHHipIBMKaEDmmx3o5MCZZy5ZCCsdeABnmSfRuaUHxZHSwAZ7Uimnn0luwMCaXBxAAQd66jmmTlnS2cEE2RkWgAIVcPDmoVtyIMFmngEQwAEOXEBonAF90IEG7LhTahEBEIAAA64VoIAB0s3q669cBAEAIfkEAAoA/wAsAAAAADAAMAAAB/+AAIKDhIWGh4iJiouMjY6JKy0LNC+CLzMLBSuPnIIrMBVARUcpNoI1JkVDPjcvKp2LKw4/Rim2tqYAqEa8qT0ym7CGCz+kt7e5Nb28vEU9MMKDAxUmx9bJJr3ZzEQUA8IqOtbjyctGJtnbOa+wDkXjx+Xb58sjMp3sADLv8KWCNNsC8rI3KJ+iFZQGySDSr8Y/dMuyjYihEMe3RQ566KgkaF8KJEiMAKkgg4UgFjJCFWFGsKOQIhQWHdjRQyNHfaN+xAhmaICMHiZa6ntZZAQ0RCoo1KyJ4aaMGjwTDahxT1AMokWK8IhK6AWPHl9rbozWcUjWs0SqFlJxY2nYHhj/XESLYfbs2a2GCugAu3QpRWE17NodcVNhX749KHB9NICH4LM3DLXlG5ZHYVgxiDx2ZnDA3rc1cSzmNMDHZiEHCLWg6RbsDLKDKGwmclSQi7dva5ONsbnI60EsYggfLvwibBY0kitPnvqQQdjQo8MqEEOG9erCTUJn4WAGjRnev7fo6sPHj/LmfSyIHmOE+/fuHajucd58fYfQb7gnMoI/f92eofdDfRgYJ8wAPeynYBDjEXJDfQKqBxtd/PVnIQ/PxVBefQSO1sgKPPhnoXuRFVLADhDa94N8gI34HhFCyGVIDRCe9wMPunGyQBD7VTjCDqMdQJ+AOE4YhI/8/XVI5A0q7lAbDDN4SMhU61nF43s6SKlXeUUKsgB9OsDgoU88CPFDlQDEcCQRQeRoiIZdAgBDD0AAEcQPOtSwwEXBKRWEEIBKaOUI+CmiwgxP+mDnokHUWZUDQvwZKaBmohmDYp3AoKidjdZZJ4uQUhrEn38KCsBzjCywaaec2vmoqIBKKoSpnKhQw6Kt4gpqpJLKGmkNqDZy6KaedhrErrDG+kuwjxyAA6N1jrrrqKL+QEGD0H2Sww92cgvEq6L6QIELzEajwgE07MXtrjfiIEMB0iWiQgEvyNBgCzC0MEC58fbrb3SBAAAh+QQACgD/ACwAAAAAMAAwAAAH/4AAgoOEhYaHiImKi4yNjokrLQs0L4IvMwsFK4+cgiswFUBFRyk2gjUmRUM+Ny8qnYsrDj9GKba2pgCoRrypPTKbsIYLP6S3t7k1vby8RT0wwoMDFSbH1skmvdnMRBQDwio61uPJy0Ym2ds5r7AORePH5dvnyyMynewAMu/wpYI02wLysjcon6IVlAbJINKvxj90y7KNiKEQx7dFDnroqCRoXwokSIwAqSCDhSAWMkIVYUawo5AiFBYd2NFDI0d9o37ECGZogIweJlrqe1lkBDREKijUrInhpowaPBMNqHFPUAyiRYrwiEroBY8eX2tujNZxSNazRKoWUnFjadgeGP9cRIth9uzZrYYK6AC7dClFYTXs2h1xU2Ffvj0ocH00gIfgszcMteUblkdhWDGIPHZmcMDetzVxLOY0wMdmIQcItaDpFuwMsoMobCZyVJCLt29rk42xucjrQSxiCB8u/CJsFjSSK0+e+pBB2NCjwyoQQ4b16sJNQmfhYAaNGd6/t+jqw8eP8uZ9LIgeY4T79+4dqO5x3nx9h9BvuCcygj9/3Z6h90N9GBgnzAA97KdgEOMRckN9AqoHG1389WchD8/FUF59BI7WyAo8+Gehe5EVUsAOENr3g3yAjfgeEULIZUgNEJ73Aw+6cbJAEPtVOMIOox1An4A4ThiEj/z9dUjkDSruUBsMM3hIyFTrWcXjezpIqVd5RQqyAH06wOChTzwI8UOVAMRwJBFB5GiIhl0CAEMPQAARxA861LDARcEpFYQQgEpo5Qj4KaLCDE/6YOeiQdRZlQNC/BkpoGaiGYNincCgqJ2N1lkni5BSGsSffwoKwHOMLLBpp5za+aiogEoqhKmcqFDDoq3iCmqkksoaaQ2oNnLopp52GsSusMb6S7CPHIADo3WOuuuoov5AQYPQfZLDD3ZyC8SrovpAgQvMRqPCATTsxe2uN+IgQwHSJaJCAS/I0GALMLQwQLnx9utvdIEAACH5BAAKAP8ALAAAAAAwADAAAAf/gACCg4SFhoeIiYqLjI2OiSosCAtKgghRC0sCj5yCKktPVVhZWQyCEkgnX0BVTZudiipOVDsctlocpgBSKEi+SGFaT6+whUxUWRy4tra6Ur+9vidaCyrFggJPtczcuYLPKNHRwFVJxSpR3eq6qL3j41fWsE1Y6tzOvu75vmAynfIA6NlrdgqaPiRf/GFrJECCk0EC7S34Ng5JtH6DnlwxtwiKliwPBTWphStLFRlNOLKQUYXLiV8YBcnwEobKIgQctnwMGXDUFAQACyWRsQVJTAAzT5zwwtOQiipbom4BOWgSsURJJChESjNMmBMcrhJyokVqVKrXuJ7wyvbJoac6/6WWRQtrJtu1XzkEFbQkZ1mzWnTBkvKVrVdVTQU9MRu1rBWxj5JowauUrU1CcOU2TtxJBhi8d7VcZbFD51+dVyBzSrIFNF4vSwj1bWxWStpBVQp/XQtmIsSyf4Fzrmv4bhRCSmTIYMBguXIWtwWxiCKlOvUoURBE3869uyAlT8LLEC8DenQWULCrx85kLBAgXN7DByI4LQMw+POHAQOFEBMt8sUX3xTbUYHffvuB8YVvgiSRBRfxBbgDR9cIoEV+B4IxRGyEVBFghPTd5hkYCOYXViEyfCgfFhR2kgQHJMboFRgEFqIEByDCF19/xUiAYYJgMHXIFCAKqMVwjTAwxPCBQGahGgIAgkjNbUrqF+RWhkyhIxBTCrIAFKoJJYFgVR64Q5gALJHFe10CsIAWQ2CxQIuEsPCEFl5wwSADXeDXBZKDEHXkIAzAOcQQQOzgkEosbeHFo3mSuaQEjKDD4JuHcnHopm4BAAWkoOa5ZxVoYjYIphAOAeGqnX4aKqRcCLYXI4Vmqqqmqg7R6qugxnqNBLhqKqywnUbB66NfdFEjLAJEYWiquXKx67FAPDErJwhcgeummk4b6hBVcBidLFdsoWkXqnr7KCtMXJuWCkxIUZqmChk7xBZXyFCJd04p0YQMHC6xABNJuMvvwQgXEwgAIfkEAAoA/wAsAAAAADAAMAAAB/+AAIKDhIWGh4iJiouMjY6JKiwIC0qCCFELSwKPnIIqS09VWFlZDIISSCdfQFVNm52KKk5UOxy2WhymAFIoSL5IYVpPr7CFTFRZHLi2trpSv72+J1oLKsWCAk+1zNy5gs8o0dHAVUnFKlHd6rqovePjV9awTVjq3M6+7vm+YDKd8gDo2Wt2Cpo+JF/8YWskQIKTQQLtLfg2Dkm0foOeXDG3CIqWLA8FNamFK0sVGU04spBRhcuJXxgFyfAShsoiBBy2fAwZcNQUBAALJZGxBUlMADNPnPDC05CKKluibgE5aBKxREkkKERKM0yYExyuEnKiRWpUqte4nvDK9smhpzr/pZZFC2sm27VfOQQVtCRnWbNadMGS8pWtV1VNBT0xG7WsFbGPkmjBq5StTUJw5TZO3EkGGLx3tVxlsUPnX51XIHNKsgU0Xi9LCPVtbFZK2kFVCn9dC2YixLJ/gXOua/huFEJKZMhgwGC5cha3BbGIIqU69ShREETfzr27ICVPwssQLwN6dBZQsKvHzmQsECBc3sMHIjgtAzD484cBA4UQEy3yxRffFNtRgd9++4HxhW+CJJEFF/EFuANH1wigRX4HgjFEbIRUEWCE9N3mGRgI5hdWITJ8KB8WFHaSBAckxugVGAQWogQHIMIXX3/FSIBhgmAwdcgUIAqoxXCNMDDE8IFAZqEaAgCCSM1tSuoX5FaGTKEjEFMKsgAUqgklgWBVHrhDmAAskcV7XQKwgBZDYLFAi4Sw8IQWXnDBIANd4NcFkoMQdeQgDMA5xBBA7OCQSixt4cWjeZK5pASMoMPgm4dyceimbgEABaSg5rlnFWhiNgimEA4B4aqdfhoqpFwIthcjhWaqqqaqDtHqq6DGeo0EuGoqrLCdRsHro190USMsAkRhaKq5crHrsUA8MSsnCFyB66aaThvqEFVwGJ0sV2yhaReqevsoK0xcm5YKTEhRmqYKGTvEFlfIUIl3TinRhAwcLrEAE0m4y+/BCBcTCAAh+QQACgD/ACwAAAAAMAAwAAAH/4AAgoOEhYaHiImKi4yNjokqSi5mBoJKZmVkKo+cgiplUBY8Pj5nggxtPGxpZwabnYoqTmltPm6kpac8bbw8GhMvr7CFZWm4x26mAGa8qc08E2XDg2JQPG63x7YMur2pu21sT2LDKjba2dnKzODgzm1owpxOo9rHygztzc5sL53CLzRgI5XNFr5vvcD1o9ZIzAQzgwKms0WKG4B8z/j5E/SEzRhGET74gCgoIK42aqC8+AhAzIsnaTQ0WyjojBwkFha58PHhQxsnETX4oBAMkYoXFFRtBGATCRITJA+JUdOzpw+gJRnIM8rAxaAnN5HMQeKGpSEzbqpajToMrFinSP+MPDmkwoLaqlenNYU7liw5Yjzv9pw7zEbft05NYCUERXBPNX9hjfnAF3GaQnUdf2AL64kRuIg/RAYwhodjj9MEjXkDGq6cSoMMpBUcIfUgC5WdGrFYMi2Iu7xTn+l7eA4aQkqePDmzXPkZMrYFkYlgo7p1G16ja9/OHUByKMqfgH8CPToZNBHSq4+QXZATEHDeyJcPIrheE0aM4M9v4vigMm7MJ+AE2qWB334HsjVGGwLKB0cbZg0zGX8m7OcFbIOoEV+Dbyijlxz5hZifG6MxxaF8PETIyRhu6LcffxQYokSAJ/oHiw0vumiEHEsRQkGDILzhBmePnOGFfiHi10buiYK44MaG8g1pm5EH5icHYYao8OOGUgpiBhpMGkLGBLwZiaQRPIRpCYNCRsUACF5owICaY0DhhhxvlHmkEV4shsgZIHR50QdeFPrgQ+UlZ1cccjQKh54mELiIChG4SWihmBYKhSBoNOqpCY56eIYFamI2iBlwZprppgB02iion+bpyVaMvKmqqqy66umueHrIiZa3qupfp7DC6mkcMQ4jBhqpBpvrq582CgIUtHLiAhtwFBrHqpzy2qgXFmBomwoMsPEGrt0aC4IFLlSbmgou2MBDqs968cEaUCjRHV0GOPGENAAYgImK+xZscHSBAAAh+QQACgD/ACwAAAAAMAAwAAAH/4AAgoOEhYaHiImKi4yNjokqSi5mBoJKZmVkKo+cgiplUBY8Pj5nggxtPGxpZwabnYoqTmltPm6kpac8bbw8GhMvr7CFZWm4x26mAGa8qc08E2XDg2JQPG63x7YMur2pu21sT2LDKjba2dnKzODgzm1owpxOo9rHygztzc5sL53CLzRgI5XNFr5vvcD1o9ZIzAQzgwKms0WKG4B8z/j5E/SEzRhGET74gCgoIK42aqC8+AhAzIsnaTQ0WyjojBwkFha58PHhQxsnETX4oBAMkYoXFFRtBGATCRITJA+JUdOzpw+gJRnIM8rAxaAnN5HMQeKGpSEzbqpajToMrFinSP+MPDmkwoLaqlenNYU7liw5Yjzv9pw7zEbft05NYCUERXBPNX9hjfnAF3GaQnUdf2AL64kRuIg/RAYwhodjj9MEjXkDGq6cSoMMpBUcIfUgC5WdGrFYMi2Iu7xTn+l7eA4aQkqePDmzXPkZMrYFkYlgo7p1G16ja9/OHUByKMqfgH8CPToZNBHSq4+QXZATEHDeyJcPIrheE0aM4M9v4vigMm7MJ+AE2qWB334HsjVGGwLKB0cbZg0zGX8m7OcFbIOoEV+Dbyijlxz5hZifG6MxxaF8PETIyRhu6LcffxQYokSAJ/oHiw0vumiEHEsRQkGDILzhBmePnOGFfiHi10buiYK44MaG8g1pm5EH5icHYYao8OOGUgpiBhpMGkLGBLwZiaQRPIRpCYNCRsUACF5owICaY0DhhhxvlHmkEV4shsgZIHR50QdeFPrgQ+UlZ1cccjQKh54mELiIChG4SWihmBYKhSBoNOqpCY56eIYFamI2iBlwZprppgB02iion+bpyVaMvKmqqqy66umueHrIiZa3qupfp7DC6mkcMQ4jBhqpBpvrq582CgIUtHLiAhtwFBrHqpzy2qgXFmBomwoMsPEGrt0aC4IFLlSbmgou2MBDqs968cEaUCjRHV0GOPGENAAYgImK+xZscHSBAAAh+QQACgD/ACwAAAAAMAAwAAAH/4AAgoOEhYaHiImKi4yNjol0SnZ5dYJKDC4EdI+cgnQHaBhtQEB5ggwdbRp+D3WbnYp0eBc/QEG2pacdHT+7bX13r7CFLhekx7imAHm7zby/B8ODYmgdpEG3uEB6us7OGw9iw3Q32rfYuNzL3s69UsKceKPm17kAqOzNG3adwnfz2dDZw5dPAz9PjZL0wTPoXz1kygi22zfIDhpxi6SE+MFQ0B1rtn5gQIMniaAkFi+06dWBoiA7GjaoS2TnR4iNHQE49GMHoyExdvy0PAjAjo42bXREQyQGw82bHBvmgceUAVGjbTZsaMPHZyE8QW6GDdEh5zCsWbe6LETHaYixN//LSnNxVGtarlQBHADytG+IB9IYIFW7VVWlQmjEvr3ZRYfJcXzSqs0605PbxWLNwrIz+S4feEk69B27wSssMX0G292ggwChOjZHh7ghjZCeyXY1LPUYtrdYBrUrFs5K3KySB3qSK9fjOjgAMQwY4JEenYES59izaxdU58EDNN7DNw9OQMoeKejP73FBiIGX9yTev1cWPA+K+0hQIMmPhtCBEPIFyAd2fuyH335GmJXEDwHK18F4wyQRhIH57VfEbgDQcUGD8tE3jB5GVKiffkCYpgeH77XxGCxJALGfiPv1YUgdQKDohRTS3PDiiPv9ccchfaAYBHDD5FHEjhX+YJrsIC4AGOCQ9R2pH4KVFeLHk0TeI8WKiRDAB315kEBhB0sOQuN7UJ4SQhEbMFAmAAQ8AMQfXoB5JBJF/KiIHl0EkRMDaxZRhBcdfNnccRcE8YcRRtAJJgkoDLgIHXtkCaigmGIKGABo/LFoo546OogeF7zJ1iCXZprppp2G6qoRddaWRxeqqtofAFK4qqsRXVTpCB191GqrIK2GCiqjRUh6mkbCCnprrq8y+kcXFwXnggYkCHtrsZ8aQcIFhzkHnQZeDIsro9J2cQFR2tFhxw1thJDttn+QEIQOrWx3SCR4PLBUHQwcAKG+BBeMXSAAIfkEAAoA/wAsAAAAADAAMAAAB/+AAIKDhIWGh4iJiouMjY6JdEp2eXWCSgwuBHSPnIJ0B2gYbUBAeYIMHW0afg91m52KdHgXP0BBtqWnHR0/u219d6+whS4XpMe4pgB5u828vwfDg2JoHaRBt7hAerrOzhsPYsN0N9q32Ljcy97OvVLCnHij5te5AKjszRt2ncJ389nQ2cOXTwM/T42S9MEz6F89ZMoItts3yA4acYukhPjBUNAda7Z+YECDJ4mgJBYvtOnVgaIgOxo2qEtk50eIjR0BOPRjB6MhMXb8tDwIwI6ONm10REMkBsPNmxwb5oHHlAFRo202bGjDx2chPEFuhg3RIecwrFm3uixEx2mIsTf/y0pzcVRrWq5UARwA8rRviAfSGCBVu1VVpUJoxL692UWHyXF80qrNOtOT28VizcKyM/kuH3hJOvQdu8ErLDF9BtvdoIMAoTo2R4e4IY2Qnsl2NSz1GLa3WAa1KxbOStyskgd6kivX4zo4ADEMGOCRHp2BEufYs2sXVOfBAzTewzcPTkDKHinoz+9xQYiBl/ck3r9XFjwPivtIUCDJj4bQgRDyBcgHdn7sh99+RpiVxA8BytfBeMMkEYSB+e1XxG4A0HFBg/LRN4weRlSon35AmKYHh++18RgsSQCxn4j79WFIHUCg6IUU0tzw4oj7/XHHIX2gGARww+RRxI4V/mCa7CAuABjgkPUdqR+ClRXix5NE3iPFiokQwAd9eZBAYQdLDkLje1CeEkIRGzBQJgAEPADEH16AeSQSRfyoiB5dBJETA2sWUYQXHXzZ3HEXBPGHEUbQCSYJKAy4CB17ZAmooJhiChgAaPyxaKOeOjqIHhe8ydYgl2aa6aadhuqqEXXWlkcXqqraHwBSuKqrEV1U6QgdfdRqqyCthgoqo0VIeppGwgp6a66vMvpHFxcF54IGJAh7a7GfGkHCBYc5B50GXgyLK6PSdnEBUdrRYccNbYSQ7bZ/kBCEDq1sd0gkeDywVB0MHAChvgQXjF0gACH5BAAKAP8ALAAAAAAwADAAAAj/AAEIHEiwoMGDCBMqXMiwocOEKwI1kVFGYJ1BTQDRechRIB1BhTJ04DLEgUAHQIC0uUCozMaOClcswABkiM0hXEwCkMElZcotF+68hFmwCQaSNrkoLXnSZ0+lQCsSFbii0JakSkmS1InyacqeQLZEWUGUTh+tWpPi5AoEbNuvKQ0N5bigA86sWJkCQNnW7ds2CzrSeVkXZ94hcHTy9Nk3ZYc7HZNYGDSwMN67MgTKYMy4DWSBd/aQXbgnERfKAgvbBHKo0B1AApMsCAnX88A7hzoQWthkSCLTgVOPxNBktEGZF1R+BnAH0ZYOiARBzPD79+nKMuYiXOFgeRPnHcL3/9E+cBCcRHHioB+Cemrz8B22PF9OkA716tW5BIf5HX54+eMZVIZvv6mnXiJRTCWDf/Bt0YZUBBVSIH6JHGJcRytYwCB8u9WHwYEH/tYeUU208Zx/QM0FCBAhopdIG0lMRdUFJzZ4CGwDDVhgiHzIOBAhGz4nXWXn4adeZj4yt2F4+wEQCCFQRgkljj4mMcggMmQpA5Z1JOnll2BaFMWYZI5JpYyyXbnAlVcGQtAgRcQpZ5w6+VgiIm20gSciiDQpSCJzymmBlw7kyaeeekIIQBJABBrnFjHKuEIfhlaawZl0YOBonB1O9R2ieuIZIEGEbFpEB5HCRAcfeYKaZ50DBf8yhKmGTDUInogeeoiiA12w6RBIwiRIBrm2KtdBTcARKHt2Erunnk0kZMGczAo0yB6pJgRIH3UOG2obxyJUBxdxVgvAIIn8sYUM2RIESBRDoFBEsE0Q20YGvBpESCLAlpeIEQAXsQUfMuD4JAZwoIDEwkXUWS8iwSZEhyHByvDvHxgDjHGCABSi8MJIfDzvQE0QQt5BQ6FrBMYss1yIQIWEjMLHMyPR8EAnK2Txyi23zPEeIIcctM2wctQHwEj3/MfLHQv9cdAo9DHVCqVp/AfPGDO9x9NPI4Fgzg59V0TSLgu0tdALy4tBl17SIUMbcVytMccxg4xCHMSBXVYTfHQfAOjSZqPwBxyHRMF2mAXRUccCUQx5URlnIi755F4GBAAh+QQACgD/ACwAAAAAMAAwAAAI/wABCBxIsKDBgwgTKlzIsKHDhCsCNZFRRmCdQU0A0XnIUSAdQYUydOAyxIFAB0CAtLlAqMzGjgpXLMAAZIjNIVxMApDBJWXKLRfuvIRZsAkGkja5KC150mdPpUArEhW4otCWpEpJktSJ8mnKnkC2RFlBlE4frVqT4uQKBGzbrykNDeW4oAPOrFiZAkDZ1u3bNgs60nlZF2feIXB08vTZN2WHOx2TWBg0sDDeuzIEymDMuA1kgXf2kF24JxEXygIL2wRyqNAdQAKTLAgJ1/PAO4c6EFrYZEgi04FTj8TQZLRBmRdUfgZwB9GWDogEQczw+/fpyjLmIlzhYHkT5x3C9//RPnAQnERx4qAfgnpq8/AdtjxfTpAO9erVuQSH+R1+ePnjGVSGb7+pp14iUUwlg3/wbdGGVAQVUiB+iRxiXEcrWMAgfLvVh8GBB/7WHlFNtPGcf0DNBQgQIaKXSBtJTEXVBSc2eAhsAw1YYIh8yDgQIRs+J11l5+GnXmY+MrdhePsBEAghUEYJJY4+JjHIIDJkKQOWdSTp5ZdgWhTFmGSOSaWMsl25wJVXBkLQIEXEKWecOvlYIiJttIEnIog0KUgic8ppgZcO5MmnnnpCCEASQAQa5xYxyrhCH4ZWmsGZdGDgaJwdTvUdonriGSBBhGxaRAeRwkQHH3mCmmedAwX/MoSphkw1CJ6IHnqIogNdsOkQSMIkSAa5tirXQU3AESh7dhK7p55NJGTBnMwKNMgeqSYESB91DhtqG8ciVAcXcVYLwCCJ/LGFDNkSBEgUQ6BQRLBNENtGBrwaREgiwJaXiBEAF7EFHzLg+CQGcKCAxMJF1FkvIsEmRIchwcrw7x8YA4xxggAUovDCSHw870BNEELeQUOhawTGLLNciECFhIzCxzMj0fBAJytk8cott8zxHiCHHLTNsHLUB8BI9/zHyx0L/XHQKPQx1QqlafwHzxgzvcfTTyOBYM4OfVdE0i4LtLXQC8uLQZde0iFDG3FcrTHHMYOMQhzEgV1WE3x0HwDo0maj8Acch0TBdpgF0VHHAlEMeVEZZyIu+eReBgQAIfkEAAoA/wAsAAAAADAAMAAACP8AAQgcSLCgwYMIEypcyLChw4R0DDjRw0SggTN2FNF5yFHgikV7snBhNIKQQD1DuGzJUIPJxo4K6QzSQDLOCJsmAejhMqQnFyBXFryEWdAOzZsjkMbJidJnz5RX7BAduOLGkKQ2bd5cepKL15Rgh2ypsYIonTRKkaplCpan26c3hnIcxEWr3aQlT7bdO8THgo5lBdJVmhVn17BeefodKDfhmAtnBi6oixVvXgBnED9dLHDBjcAKb8hhNGjgYJuMstwYNEbgGCd7NADZ/FegEx9AaCx0MkKOnCGlBXMZkcFJY6pOMnDhDGABbpVSEa7QIMeE7yGROxM6bnAFodrNcQP/GX8BdMEzcXxbN8Eo+1QnPcb/VAmeIB3qvvOPDg7z9k8g/wFRnkGL9KZffroRpcd4DHoFxBYVFbSHetVVmEVrRK1wAYAB/lRDQfdZV2F1cbhH1ALyzTfeFXKRwYV+Im6B4VRjZMBggz4oQlCBFa4nRxpTEVRDg/JtEZ1g6R0ohx5BSgbgk0+aaAANVFZJAyFkNOnaGXp0ySWXBmgp5phkWlTDmWjWQEOWWo4xyBlwxnlGmAOdYYIRJtxpBJ45NXmbDz5sIaig/AGwSBx74omnCRaISQiggwLqwyIEjTHEnnreOQSbU61wxRaBQrpFFpwCQEcGmKZqQp9EwSdooKBu/2FBYzRkqqcRW5TKkYav9roFqxaNkGiqRtww1RmhvhpoDxEWdMGd0O45gokcOZFFrLH6kAZ3diA67AhMBmlHFqGG6kRCV6QaR7iY3TCjYxgNZC222ybExKVGTDuQHnIgsYUe7xK0wgIW9JDBkeMKmkWzCNEgx7QvndEvEkgYwYUFZ7D5Gg0XZNHDxwcPNK4P7CZUFbsSU4wECioj8WFzPXj8scwZUGobDeY1lDLFLK9M8cuDfBzzzCAfCVPKPfPs8x6CCe200CHDRIcFSSvtM9BDCy1zzCXvKlrLSqPANABBa+30FcY1aUcPRoDNMtZZf6zBmmOuoIcPJoANt9BX0CZgAHdBrmBHGkDI8XbTGlywhxMBl+mRAQvQYDMZiyiSs+OYZ65lQAAh+QQACgD/ACwAAAAAMAAwAAAI/wABCBxIsKDBgwgTKlzIsKHDhHQMONHDRKCBM3YU0XnIUeCKRXuycGE0gpBAPUO4bMlQg8nGjgrpDNJAMs4ImyYB6OEypCcXIFcWvIRZ0A7NmyOQxsmJ0mfPlFfsEB244saQpDZt3lx6kovXlGCHbKmxgiidNEqRqmUKlqfbpzeGchzERavdpCVPtt07xMeCjmUF0lWaFWfXsF55+h0oN+GYC2cGLqiLFW9eAGcQP10scMGNwApvyGE0aOBgm4yy3Bg0RuAYJ3s0ANn8V6ATH0BoLHQyQo6cIaUFcxmRwUljqk4ycOEMYAFulVIRrtAgx4TvIZE7EzpucAWh2s1xA/8ZfwF0wTNxfFs3wSj7VCc9xv9UCZ4gHeq+848ODvP2TyD/AVGeQYv0pl9+uhGlx3gMegXEFhUVtId61VWYRWtErXABgAH+VENB91lXYXVxuEfUAvLNN94VcpHBhX4iboHhVGNkwGCDPihCUIEVridHGlMRVEOD8m0RnWDpHSiHHkFKBuCTT5poAA1UVkkDIWQ06doZenTJJZcGaCnmmGRaVMOZaNZAQ5ZajjHIGXDGeUaYA51hghEm3GkEnjk1eZsPPmwhqKD8AbBIHHviiacJFohJCKCDAurDIgSNMcSeet45BJtTrXDFFoFCukUWnAJARwaYpmpCn0TBJ2igoG7/YUFjNGSqpxFblMqRhq/2ugWrFo2QaKpG3DDVGaG+GmgPERZ0wZ3Q7jmCiRw5kUWssfqQBnd2IDrsCEwGaUcWoYbqREJXpBpHuJjdMKNjGA1kLbbbJsTEpUZMO5AeciCxhR7vErTCAhb0kMGR4wqaRbMI0SDHtC+d0S8SSBjBhQVnsPkaDRdk0cPHBw80rg/sJlQVuxJTjAQKKiPxYXM9ePyxzBlQahsN5jWUMsUsr0zxy4N8HPPMIB8JU8o98+zzHoIJ7bTQIcNEhwVJK+0z0EMLLXPMJe8qWstKo8A0AEFr7fQVxjVpRw9GgM0y1ll/rMGaY66ghw8mgA230FfQJmAAd0GuYEcaQMjxdtMaXLCHEwGX6ZEBC9BgMxmLKJKz45hnrmVAACH5BAAKAP8ALAAAAAAwADAAAAj/AAEIHEiwoMGDCBMqXMiwocOEKwLdgSJIIBMHdwLRechR4IoXExwNIfEHgkAoj+AEOWRD0MaOClecQfSoiE2bJgGgfMTz0RAdjVbAPPgCEYmbSHOihMOTaUodL4YOXDEBDtKrSntq5TnEhlCYdNRcHZvVKVOnj0j0ednxzJCxSQVC2JqyZ5BBHb8CGPQWbkm5TQPz/NBoIFuFY644GMgXbhEoJ+nyvDuwUZ8xDKn8eXSGcd8ijxxNGERA4JhBNhDBcRqksMBGHYZIWXiHhBEjnD2TOBQU4YpBh1S6BtBoy5AhW6L6dmTkzx/cnQUOgqIXIoThsIcECTLkSnWCDoo0/3cOXerr2NzT4zVIB9F4589zD72DXvvx7ocFCrIN//nt2UNBwd1x6Q3xQUUFTfBcf885gtlQK+hAoH3a2VAQHcwxaEQR0UnVyAcEDvjTYQQM8R55H5Rm3gqHULjdEB0EQtB+DDpnhXkD2VDgi0EoJ12Nzy2GI3EDijhEhwAsIQUEUjTJpBQq4jiGAw5AQaWVDjAx5JZcdmmRDWCGCaaMQ45xBpVoUrkEQWfMkcKbcKYA2ZDFfWDnnR8gKcgfccJ5xZZS4HlncgSNQUKfb8IRJYQ6CGqnI2QK1B6ib+Yk1R1bOPqBdwVBQWkKQSzK0QpXBKGppQMtwSelfUgFhaYfbPqBYEFXUPqHkDDd4YimauQn0AurwskhjroKusVwF9YqLK53DPLdQVP6WOydVjw7EBOPvFkEri/o4AgVL1gLwGlXdIDIHQPRZ2cHsyIEwRzbDtStI/Qi0ocD4ZrWiBQ6dOCvudLuOqdCdNw7kCDe0quwI4i4dsa/EAOcrlcNsTXvwogovN4ZW0T877mGdXRxxgvTu3HHHvuLiI8dQUFyyQo7nHIHW2zhyMAd0XFGwjAjsnHENXfMm68cLTHBywv/DHHHiEDJJR0vUIG0IycH3cEhUjBBtHl0LOFAHzog4rNADyNyxQSNiOqlpAQwkZFAgbywhLhr1223VAEBACH5BAAKAP8ALAAAAAAwADAAAAj/AAEIHEiwoMGDCBMqXMiwocOEKwLdgSJIIBMHdwLRechR4IoXExwNIfEHgkAoj+AEOWRD0MaOClecQfSoiE2bJgGgfMTz0RAdjVbAPPgCEYmbSHOihMOTaUodL4YOXDEBDtKrSntq5TnEhlCYdNRcHZvVKVOnj0j0ednxzJCxSQVC2JqyZ5BBHb8CGPQWbkm5TQPz/NBoIFuFY644GMgXbhEoJ+nyvDuwUZ8xDKn8eXSGcd8ijxxNGERA4JhBNhDBcRqksMBGHYZIWXiHhBEjnD2TOBQU4YpBh1S6BtBoy5AhW6L6dmTkzx/cnQUOgqIXIoThsIcECTLkSnWCDoo0/3cOXerr2NzT4zVIB9F4589zD72DXvvx7ocFCrIN//nt2UNBwd1x6Q3xQUUFTfBcf885gtlQK+hAoH3a2VAQHcwxaEQR0UnVyAcEDvjTYQQM8R55H5Rm3gqHULjdEB0EQtB+DDpnhXkD2VDgi0EoJ12Nzy2GI3EDijhEhwAsIQUEUjTJpBQq4jiGAw5AQaWVDjAx5JZcdmmRDWCGCaaMQ45xBpVoUrkEQWfMkcKbcKYA2ZDFfWDnnR8gKcgfccJ5xZZS4HlncgSNQUKfb8IRJYQ6CGqnI2QK1B6ib+Yk1R1bOPqBdwVBQWkKQSzK0QpXBKGppQMtwSelfUgFhaYfbPqBYEFXUPqHkDDd4YimauQn0AurwskhjroKusVwF9YqLK53DPLdQVP6WOydVjw7EBOPvFkEri/o4AgVL1gLwGlXdIDIHQPRZ2cHsyIEwRzbDtStI/Qi0ocD4ZrWiBQ6dOCvudLuOqdCdNw7kCDe0quwI4i4dsa/EAOcrlcNsTXvwogovN4ZW0T877mGdXRxxgvTu3HHHvuLiI8dQUFyyQo7nHIHW2zhyMAd0XFGwjAjsnHENXfMm68cLTHBywv/DHHHiEDJJR0vUIG0IycH3cEhUjBBtHl0LOFAHzog4rNADyNyxQSNiOqlpAQwkZFAgbywhLhr1223VAEBADs=);
+ }
+
+}
+
+/* Arrow down animation */
+@keyframes rotate-arrow-down {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(-180deg);
+ }
+}
+@-webkit-keyframes rotate-arrow-down {
+ from {
+ -webkit-transform: rotate(0deg);
+ }
+ to {
+ -webkit-transform: rotate(-180deg);
+ }
+}
+@-moz-keyframes rotate-arrow-down {
+ from {
+ -moz-transform: rotate(0deg);
+ }
+ to {
+ -moz-transform: rotate(-180deg);
+ }
+}
+@-ms-keyframes rotate-arrow-down {
+ from {
+ -ms-transform: rotate(0deg);
+ }
+ to {
+ -ms-transform: rotate(-180deg);
+ }
+}
+@-o-keyframes rotate-arrow-down {
+ from {
+ -o-transform: rotate(0deg);
+ }
+ to {
+ -o-transform: rotate(-180deg);
+ }
+}
+
+/* Arrow up animation */
+@keyframes rotate-arrow-up {
+ from {
+ transform: rotate(-180deg);
+ }
+ to {
+ transform: rotate(0deg);
+ }
+}
+@-webkit-keyframes rotate-arrow-up {
+ from {
+ -webkit-transform: rotate(-180deg);
+ }
+ to {
+ -webkit-transform: rotate(0deg);
+ }
+}
+@-moz-keyframes rotate-arrow-up {
+ from {
+ -moz-transform: rotate(-180deg);
+ }
+ to {
+ -moz-transform: rotate(0deg);
+ }
+}
+@-ms-keyframes rotate-arrow-up {
+ from {
+ -ms-transform: rotate(-180deg);
+ }
+ to {
+ -ms-transform: rotate(0deg);
+ }
+}
+@-o-keyframes rotate-arrow-up {
+ from {
+ -o-transform: rotate(-180deg);
+ }
+ to {
+ -o-transform: rotate(0deg);
+ }
+}
+
704 dist/jquery.scrollz.js
@@ -0,0 +1,704 @@
+/*! jQuery Scrollz - v1.0.0 - 2012-08-15
+* https://github.com/zippy1978/jquery.scrollz
+* Copyright (c) 2012 Gilles Grousset; Licensed MIT, GPL */
+
+(function($) {
+
+ // Methods definition
+ var methods = {
+
+ /* Initialization */
+ init : function(options) {
+
+ // Default options
+ var settings = $.extend( {
+ 'pull' : false, // Pull support
+ 'pullHeaderHTML' : {
+ 'initial' : '<div><div class="icon"/><div class="label">Pull to refresh</div></div>', // Pull header on initial state
+ 'release' : '<div><div class="icon"/><div class="label">Release to refresh</div></div>', // Pull header on release state
+ 'waiting' : '<div><div class="icon"/><div class="label">Refreshing...</div></div>' // Pull header waiting state
+ },
+ 'inertia' : true, // Inertia support
+ 'emulateTouchEvents' : false // Emulate touch events when device is not a touch device
+ }, options);
+
+ // Define easeOutCubic easing function (if not defined yet)
+ if ($.easing.easeOutCubic === undefined) {
+ $.easing.easeOutCubic = function (x, t, b, c, d) {
+ return c*((t=t/d-1)*t*t + 1) + b;
+ };
+ }
+
+ // Define scrollstart and scrollstop special events (if not defined yet)
+ // As explained here: http://james.padolsey.com/javascript/special-scroll-events-for-jquery
+ if (!$.event.special.scrollstart && !$.event.special.scrollend) {
+
+ var special = $.event.special,
+ uid1 = 'D' + (+new Date()),
+ uid2 = 'D' + (+new Date() + 1);
+
+ special.scrollstart = {
+ setup: function() {
+
+ var timer,
+ handler = function(evt) {
+
+ var _self = this,
+ _args = arguments;
+
+ if (timer) {
+ clearTimeout(timer);
+ } else {
+ evt.type = 'scrollstart';
+ $.event.handle.apply(_self, _args);
+ }
+
+ timer = setTimeout(function(){
+ timer = null;
+ }, special.scrollstop.latency);
+
+ };
+
+ $(this).bind('scroll', handler).data(uid1, handler);
+
+ },
+ teardown: function(){
+ $(this).unbind('scroll', $(this).data(uid1));
+ }
+ };
+
+ special.scrollstop = {
+ latency: 300,
+ setup: function() {
+
+ var timer,
+ handler = function(evt) {
+
+ var _self = this,
+ _args = arguments;
+
+ if (timer) {
+ clearTimeout(timer);
+ }
+
+ timer = setTimeout(function(){
+
+ timer = null;
+ evt.type = 'scrollstop';
+ $.event.handle.apply(_self, _args);
+
+ }, special.scrollstop.latency);
+
+ };
+
+ $(this).bind('scroll', handler).data(uid2, handler);
+
+ },
+ teardown: function() {
+ $(this).unbind('scroll', $(this).data(uid2));
+ }
+ };
+ }
+
+ return this.each(function() {
+
+ var $this = $(this);
+
+ // If the plugin hasn't been initialized yet
+ if (!$this.data('scrollz')) {
+
+ // Store options
+ $this.data('options', settings);
+
+ // Create markup
+ _createMarkup($this);
+
+ var container = _getMarkupCache($this, 'container');
+
+ // Store container initial position
+ _putTrackingData($this, 'initialScrollPosition', container.scrollTop());
+
+ // Add touch start listener
+ container.bind(_getTouchEventName($this, 'touchstart'), function(event) {
+ // Handle
+ _handleTouchStartEvent(event, $this);
+ });
+
+ // Add touch move listener
+ container.bind(_getTouchEventName($this, 'touchmove'), function(event) {
+ // Prevent default behaviour
+ event.preventDefault();
+ // Handle
+ _handleTouchMoveEvent(event, $this);
+ });
+
+ // Add touch end listener
+ container.bind(_getTouchEventName($this, 'touchend'), function(event) {
+ // Prevent default behaviour
+ event.preventDefault();
+ // Handle
+ _handleTouchEndEvent(event, $this);
+ });
+
+ // Add touch end listener when outside container (in case the last touch is outside the container)
+ $('*').not(container).bind(_getTouchEventName($this, 'touchend'), function(event) {
+ // Handle
+ _handleTouchEndEvent(event, $this);
+ });
+
+ // Add mousewheel listener
+ container.bind('mousewheel DOMMouseScroll', function(event) {
+ // Prevent default behaviour
+ event.preventDefault();
+ // Handle
+ _handleMouseWheelEvent(event, $this);
+ });
+
+ // Add scroll listener
+ container.scroll(function(event) {
+ // Handle
+ _handleScrollEvent(event, $this);
+ });
+
+ // Add scroll start listener
+ container.bind('scrollstart', function(event) {
+ // Handle
+ _handleScrollStartEvent(event, $this);
+ });
+
+ // Add scroll stop listener
+ container.bind('scrollstop', function(event) {
+ // Handle
+ _handleScrollStopEvent(event, $this);
+ });
+
+ // Mark plugin as initialized
+ $this.data('scrollz', true);
+
+ }
+
+ });
+
+ },
+
+ /* Sets container height */
+ height : function(height) {
+
+ return this.each(function() {
+
+ var $this = $(this);
+
+ // If plugin initialized
+ if ($this.data('scrollz')) {
+
+ var settings = $this.data('options');
+ var container = _getMarkupCache($this, 'container');
+
+ container.height(height);
+ $this.css('min-height', container.css('height'));
+ }
+
+ });
+ },
+
+ /* Hides pull header */
+ hidePullHeader: function() {
+
+ return this.each(function() {
+
+ var $this = $(this);
+
+ // If plugin initialized
+ if ($this.data('scrollz')) {
+
+ var settings = $this.data('options');
+ var container = _getMarkupCache($this, 'container');
+
+ if (settings.pull) {
+ container.animate({scrollTop: _getPullHeaderHeight($this)}, 'fast', function() {
+ _changePullHeaderState($this, 'initial');
+ });
+ }
+ }
+
+ });
+ }
+ };
+
+ // Private functions
+
+ /* Tests if current device is a touch device. */
+ function _isTouchDevice() {
+ return ('ontouchstart' in document.documentElement);
+ }
+
+ /* Get container height. */
+ function _getContainerHeight(instance) {
+ return instance.height();
+ }
+
+ /* Get pull header height. */
+ function _getPullHeaderHeight(instance) {
+
+ var contentWrapper = _getMarkupCache(instance, 'contentWrapper');
+ var pullHeader = _getMarkupCache(instance, 'pullHeader');
+ if (pullHeader) {
+ return pullHeader.outerHeight(true);
+ } else {
+ return 0;
+ }
+
+ }
+
+ /* Converts a touch event name into a supported event name (in case the device is not touch compliant). */
+ function _getTouchEventName(instance, eventName) {
+
+ var settings = instance.data('options');
+
+ if (!_isTouchDevice() && settings.emulateTouchEvents) {
+ switch (eventName) {
+ case 'touchstart' : return 'mousedown';
+ case 'touchend' : return 'mouseup';
+ case 'touchmove' : return 'mousemove';
+ }
+ }
+
+ return eventName;
+
+ }
+
+ /* Puts (set or replace) an item into the markup cache of the instance .*/
+ function _putMarkupCache(instance, key, value) {
+
+ var markup = instance.data('markup');
+
+ // Create cache if not found
+ if (!markup) {
+ markup ={};
+ instance.data('markup', markup);
+ }
+
+ // Store
+ markup[key] = value;
+ }
+
+ /* Retieves an item from the markup cache. */
+ function _getMarkupCache(instance, key) {
+
+ var markup = instance.data('markup');
+ if (markup) {
+ return markup[key];
+ } else {
+ return null;
+ }
+ }
+
+ /* Puts (set or replace) an item into tracking data. */
+ function _putTrackingData(instance, key, value) {
+
+ var tracking = instance.data('tracking');
+
+ // Create array if not found
+ if (!tracking) {
+ tracking = {};
+ instance.data('tracking', tracking);
+ }
+
+ // Store
+ tracking[key] = value;
+ }
+
+ /* Retieves an item from tracking data. */
+ function _getTrackingData(instance, key) {
+
+ var tracking = instance.data('tracking');
+ if (tracking) {
+ return tracking[key];
+ } else {
+ return null;
+ }
+ }
+
+ /* Resets tracking data. */
+ function _resetTouchTrackingData(instance) {
+
+ _putTrackingData(instance, 'startTouchTime', null);
+ _putTrackingData(instance, 'startTouchY', null);
+ _putTrackingData(instance, 'lastTouchY', null);
+
+ }
+
+ /* Makes element unselectable. */
+ function _makeUnselectable(element) {
+
+ element.attr('unselectable', 'on')
+ .css({
+ '-moz-user-select':'none',
+ '-webkit-user-select':'none',
+ 'user-select':'none',
+ '-ms-user-select':'none'
+ })
+ .each(function() {
+ this.onselectstart = function() { return false; };
+ });
+
+ }
+
+ /* Fixes scrollTop value for container. */
+ function _fixContainerScrollTopBounds(instance, scrollTopValue) {
+
+ var settings = instance.data('options');
+
+ var pullHeaderHeight = _getPullHeaderHeight(instance);
+
+ if (settings.pull && (scrollTopValue < pullHeaderHeight)) {
+ return pullHeaderHeight;
+ } else {
+ return scrollTopValue;
+ }
+ }
+
+ /* Creates plugin markup */
+ function _createMarkup(instance) {
+
+ var settings = instance.data('options');
+
+ // Create content wrapper
+ var contentWrapper = $('<div class="scrollz-content-wrapper"/>');
+
+ // Create container
+ var container = $('<div class="scrollz-container"/>');
+ container.css('height', _getContainerHeight(instance));
+ container.css('overflow-x', 'hidden');
+ container.css('overflow-y', 'hidden');
+ if (settings.styleClass) {
+ container.addClass(settings.styleClass);
+ }
+
+ // Wrap container arround content wrapper
+ instance.wrap(container).wrap(contentWrapper);
+ instance.css('overflow-y', 'visible');
+
+ // Update references
+ contentWrapper = instance.parent();
+ container = contentWrapper.parent();
+
+ // Create scroll thumb (and hide it)
+ var scrollThumb = $('<div class="scrollz-thumb"></div>');
+ scrollThumb.css('position', 'absolute');
+ container.prepend(scrollThumb);
+ scrollThumb = container.find('.scrollz-thumb');
+ scrollThumb.hide();
+
+ // Remove height from content
+ instance.css('height', 'auto');
+ instance.css('min-height', _getContainerHeight(instance));
+
+ // Store generated markup refrerences into object data
+ _putMarkupCache(instance, 'contentWrapper', contentWrapper);
+ _putMarkupCache(instance, 'container', container);
+ _putMarkupCache(instance, 'scrollThumb', scrollThumb);
+
+ // Pull support setup
+ if (settings.pull) {
+
+ // Create pull header
+ var pullHeader = $(settings.pullHeaderHTML.initial);
+ pullHeader.addClass('scrollz-pull-header').addClass('initial');
+
+ // Add pull header
+ contentWrapper.prepend(pullHeader);
+
+ // Move container to hide header
+ container.scrollTop(_getPullHeaderHeight(instance));
+
+ // Make container unselectable
+ _makeUnselectable(container);
+
+ // Store pull header in markup cache
+ _putMarkupCache(instance, 'pullHeader', contentWrapper.children('.scrollz-pull-header'));
+
+ }
+ }
+
+ /* Change pull header state. */
+ function _changePullHeaderState(instance, state) {
+
+ var settings = instance.data('options');
+ var contentWrapper = _getMarkupCache(instance, 'contentWrapper');
+ var pullHeader = contentWrapper.children('.scrollz-pull-header');
+
+ if (!pullHeader.hasClass(state)) {
+ pullHeader.replaceWith($(settings.pullHeaderHTML[state]).addClass('scrollz-pull-header').addClass(state));
+ }
+
+ // Update pull header in stored markup
+ _putMarkupCache(instance, 'pullHeader', contentWrapper.children('.scrollz-pull-header'));
+
+ // Store current state
+ instance.data('pullHeaderState', state);
+
+ }
+
+ /* Returns pull header state */
+ function _getPullHeaderState(instance) {
+
+ var state = instance.data('pullHeaderState');
+ if (!state) {
+ // If unknown : take 'initial' as default
+ state = 'initial';
+ }
+
+ return state;
+ }
+
+ /* Handles pull header */
+ function _handlePullHeader(instance) {
+
+ var settings = instance.data('options');
+ var container = _getMarkupCache(instance, 'container');
+ var pullHeaderHeight = _getPullHeaderHeight(instance);
+
+ if (settings.pull && (container.scrollTop() < pullHeaderHeight) && (_getPullHeaderState(instance) !== 'waiting')) {
+
+ // Handle pull to refresh (half of the header height)
+ if (container.scrollTop() < (pullHeaderHeight / 2)) {
+
+ // Trigger event
+ _changePullHeaderState(instance, 'waiting');
+ instance.trigger('pulled');
+
+ } else {
+ // Animate scroll : move back to initial position
+ container.animate({scrollTop: pullHeaderHeight}, 'fast');
+ }
+
+ }
+ }
+
+ /* Handles inertia. */
+ function _handleInertia(instance) {
+
+ var settings = instance.data('options');
+ var container = _getMarkupCache(instance, 'container');
+
+ // Compute speed and distance
+ var startTouchY = _getTrackingData(instance, 'startTouchY');
+ var lastTouchY = _getTrackingData(instance, 'lastTouchY');
+ var startTouchTime = _getTrackingData(instance, 'startTouchTime');
+ var duration = new Date() - startTouchTime;
+ var distance = startTouchY - lastTouchY;
+ var speed = Math.abs(distance / duration);
+
+ // Inertia move
+ if (settings.inertia) {
+ var offset = Math.pow(speed, 2) * Math.abs(distance);
+ if (distance < 0) {
+ offset *= -1;
+ }
+
+ container.stop(true, true);
+ container.animate({scrollTop: _fixContainerScrollTopBounds(instance, container.scrollTop() + offset)}, {duration: speed * 500, easing : 'easeOutCubic'});
+ }
+ }
+
+ /* Handles touchstart event. */
+ function _handleTouchStartEvent(event, instance) {
+
+ if (_getPullHeaderState(instance) !== 'waiting') {
+
+ var settings = instance.data('options');
+ var container = _getMarkupCache(instance, 'container');
+
+ // Stop animation (if any)
+ container.stop();
+
+ // Capture initial contact point
+ if (_isTouchDevice()) {
+ _putTrackingData(instance, 'startTouchY', event.originalEvent.targetTouches[0].screenY);
+ } else {
+ _putTrackingData(instance, 'startTouchY', event.screenY);
+ }
+
+ _putTrackingData(instance, 'startTouchTime', new Date());
+ _putTrackingData(instance, 'lastTouchY', _getTrackingData(instance, 'startTouchTime'));
+ _putTrackingData(instance, 'initialScrollPosition', container.scrollTop());
+
+ }
+ }
+
+ /* Handles touchmove event. */
+ function _handleTouchMoveEvent(event, instance) {
+
+ var container = _getMarkupCache(instance, 'container');
+
+ var startTouchY = _getTrackingData(instance, 'startTouchY');
+ var initialScrollPosition = _getTrackingData(instance, 'initialScrollPosition');
+
+ if (startTouchY) {
+
+ // Compute move and store last touch
+ var moveTo = 0;
+ if (_isTouchDevice()) {
+ moveTo = (startTouchY - event.originalEvent.changedTouches[0].screenY) + initialScrollPosition;
+ _putTrackingData(instance, 'lastTouchY',event.originalEvent.targetTouches[0].screenY);
+ } else {
+ moveTo = (startTouchY - event.screenY) + initialScrollPosition;
+ _putTrackingData(instance, 'lastTouchY',event.screenY);
+ }
+
+ // Move
+ container.scrollTop(moveTo);
+ }
+ }
+
+ /* Handles touchend event. */
+ function _handleTouchEndEvent(event, instance) {
+
+ var container = _getMarkupCache(instance, 'container');
+
+ var startTouchY = _getTrackingData(instance, 'startTouchY');
+ var lastTouchY = _getTrackingData(instance, 'lastTouchY');
+ var initialScrollPosition = _getTrackingData(instance, 'initialScrollPosition');
+
+ if (!startTouchY) {
+ // Nothing to do : touch was already processed
+ return;
+ }
+
+ var pullHeaderHeight = _getPullHeaderHeight(instance);
+
+ if ((startTouchY < lastTouchY) && (container.scrollTop() < pullHeaderHeight)) {
+
+ _handlePullHeader(instance);
+
+ } else {
+
+ _handleInertia(instance);
+ }
+
+ // Reset data
+ _resetTouchTrackingData(instance);
+
+ }
+
+ /* Handles mousewheel event. */
+ function _handleMouseWheelEvent(event, instance) {
+
+ if (_getPullHeaderState(instance) !== 'waiting') {
+
+ var container = _getMarkupCache(instance, 'container');
+
+ var initialScrollPosition = _getTrackingData(instance, 'initialScrollPosition');
+
+ // Move
+ var offset = 0;
+ if (event.type === 'mousewheel') {
+ offset = event.originalEvent.screenY - (event.originalEvent.screenY + event.originalEvent.wheelDeltaY);
+ } else {
+ offset = event.originalEvent.screenY - (event.originalEvent.screenY + (event.originalEvent.detail * -1 * 3));
+ }
+
+ // Slowdown scroll if reaching the pull header
+ if ((container.scrollTop() + offset) < _getPullHeaderHeight(instance)) {
+ offset *= 0.03;
+ }
+
+ container.scrollTop(container.scrollTop() + offset);
+
+ }
+ }
+
+ /* Handles scroll event. */
+ function _handleScrollEvent(event, instance) {
+
+ var settings = instance.data('options');
+
+ var container = _getMarkupCache(instance, 'container');
+ var contentWrapper = _getMarkupCache(instance, 'contentWrapper');
+ var scrollThumb = _getMarkupCache(instance, 'scrollThumb');
+
+ // Bottom reached
+ if ((container.scrollTop() + container.height()) === container.get(0).scrollHeight) {
+ // Trigger event
+ instance.trigger('bottomreached');
+ }
+
+ // Refresh threshold reached (half of the header height)
+ if (settings.pull) {
+ if (container.scrollTop() < (_getPullHeaderHeight(instance) / 2)) {
+ _changePullHeaderState(instance, 'release');
+ } else {
+ _changePullHeaderState(instance, 'initial');
+ }
+ }
+
+ var pullHeaderHeight = _getPullHeaderHeight(instance);
+ if (container.scrollTop() >= pullHeaderHeight) {
+
+ // Fix the collapsing maring problem
+ var firstContentChild = instance.children().first();
+ var lastContentChild = instance.children().last();
+ if (firstContentChild && parseInt(firstContentChild.css('marginTop'), 10) >= 0) {
+ instance.css('padding-top', '1px');
+ }
+ if (lastContentChild && parseInt(lastContentChild.css('marginBottom'), 10) >= 0) {
+ instance.css('padding-bottom', '1px');
+ }
+
+ // Resize and move scroll thumb
+ scrollThumb.height((container.innerHeight() / contentWrapper.outerHeight(true) * (container.innerHeight() + pullHeaderHeight)) -(scrollThumb.outerHeight(true) - scrollThumb.outerHeight()));
+ scrollThumb.css('top', container.position().top + ((container.scrollTop() - pullHeaderHeight) / contentWrapper.outerHeight(true) * container.innerHeight()));
+ scrollThumb.css('left', container.position().left + container.width() - scrollThumb.outerWidth(true));
+
+ } else {
+ // Hide scroll thumb when on pull header
+ scrollThumb.hide();
+ }
+ }
+
+ /* Handles scrollstart event. */
+ function _handleScrollStartEvent(event, instance) {
+
+ var container = _getMarkupCache(instance, 'container');
+ var scrollThumb = _getMarkupCache(instance, 'scrollThumb');
+
+ // Show scroll thumb only if not on pull header
+ if (container.scrollTop() > _getPullHeaderHeight(instance)) {
+ scrollThumb.stop(true, true);
+ scrollThumb.fadeIn(500);
+ }
+ }
+
+ /* Handles scrollstop event. */
+ function _handleScrollStopEvent(event, instance) {
+
+ var scrollThumb = _getMarkupCache(instance, 'scrollThumb');
+
+ // Hide scroll thumb
+ scrollThumb.stop(true, true);
+ scrollThumb.delay(300).fadeOut(1000);
+
+ // Handle pull header for none touch devices (case of scroll with mouse wheel)
+ var startTouchY = _getTrackingData(instance, 'startTouchY');
+ if (!_isTouchDevice() && !startTouchY) {
+ _handlePullHeader(instance);
+ }
+
+ }
+
+ // Public declaration
+ $.fn.scrollz = function(method) {
+
+ // Method calling logic
+ if (methods[method]) {
+ return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if (typeof method === 'object' || ! method) {
+ return methods.init.apply(this, arguments);
+ } else {
+ $.error('Method ' + method + ' does not exist');
+ return null;
+ }
+
+ };
+}(jQuery));
4 dist/jquery.scrollz.min.js
@@ -0,0 +1,4 @@
+/*! jQuery Scrollz - v1.0.0 - 2012-08-15
+* https://github.com/zippy1978/jquery.scrollz
+* Copyright (c) 2012 Gilles Grousset; Licensed MIT, GPL */
+(function(a){function c(){return"ontouchstart"in document.documentElement}function d(a){return a.height()}function e(a){var b=h(a,"contentWrapper"),c=h(a,"pullHeader");return c?c.outerHeight(!0):0}function f(a,b){var d=a.data("options");if(!c()&&d.emulateTouchEvents)switch(b){case"touchstart":return"mousedown";case"touchend":return"mouseup";case"touchmove":return"mousemove"}return b}function g(a,b,c){var d=a.data("markup");d||(d={},a.data("markup",d)),d[b]=c}function h(a,b){var c=a.data("markup");return c?c[b]:null}function i(a,b,c){var d=a.data("tracking");d||(d={},a.data("tracking",d)),d[b]=c}function j(a,b){var c=a.data("tracking");return c?c[b]:null}function k(a){i(a,"startTouchTime",null),i(a,"startTouchY",null),i(a,"lastTouchY",null)}function l(a){a.attr("unselectable","on").css({"-moz-user-select":"none","-webkit-user-select":"none","user-select":"none","-ms-user-select":"none"}).each(function(){this.onselectstart=function(){return!1}})}function m(a,b){var c=a.data("options"),d=e(a);return c.pull&&b<d?d:b}function n(b){var c=b.data("options"),f=a('<div class="scrollz-content-wrapper"/>'),h=a('<div class="scrollz-container"/>');h.css("height",d(b)),h.css("overflow-x","hidden"),h.css("overflow-y","hidden"),c.styleClass&&h.addClass(c.styleClass),b.wrap(h).wrap(f),b.css("overflow-y","visible"),f=b.parent(),h=f.parent();var i=a('<div class="scrollz-thumb"></div>');i.css("position","absolute"),h.prepend(i),i=h.find(".scrollz-thumb"),i.hide(),b.css("height","auto"),b.css("min-height",d(b)),g(b,"contentWrapper",f),g(b,"container",h),g(b,"scrollThumb",i);if(c.pull){var j=a(c.pullHeaderHTML.initial);j.addClass("scrollz-pull-header").addClass("initial"),f.prepend(j),h.scrollTop(e(b)),l(h),g(b,"pullHeader",f.children(".scrollz-pull-header"))}}function o(b,c){var d=b.data("options"),e=h(b,"contentWrapper"),f=e.children(".scrollz-pull-header");f.hasClass(c)||f.replaceWith(a(d.pullHeaderHTML[c]).addClass("scrollz-pull-header").addClass(c)),g(b,"pullHeader",e.children(".scrollz-pull-header")),b.data("pullHeaderState",c)}function p(a){var b=a.data("pullHeaderState");return b||(b="initial"),b}function q(a){var b=a.data("options"),c=h(a,"container"),d=e(a);b.pull&&c.scrollTop()<d&&p(a)!=="waiting"&&(c.scrollTop()<d/2?(o(a,"waiting"),a.trigger("pulled")):c.animate({scrollTop:d},"fast"))}function r(a){var b=a.data("options"),c=h(a,"container"),d=j(a,"startTouchY"),e=j(a,"lastTouchY"),f=j(a,"startTouchTime"),g=new Date-f,i=d-e,k=Math.abs(i/g);if(b.inertia){var l=Math.pow(k,2)*Math.abs(i);i<0&&(l*=-1),c.stop(!0,!0),c.animate({scrollTop:m(a,c.scrollTop()+l)},{duration:k*500,easing:"easeOutCubic"})}}function s(a,b){if(p(b)!=="waiting"){var d=b.data("options"),e=h(b,"container");e.stop(),c()?i(b,"startTouchY",a.originalEvent.targetTouches[0].screenY):i(b,"startTouchY",a.screenY),i(b,"startTouchTime",new Date),i(b,"lastTouchY",j(b,"startTouchTime")),i(b,"initialScrollPosition",e.scrollTop())}}function t(a,b){var d=h(b,"container"),e=j(b,"startTouchY"),f=j(b,"initialScrollPosition");if(e){var g=0;c()?(g=e-a.originalEvent.changedTouches[0].screenY+f,i(b,"lastTouchY",a.originalEvent.targetTouches[0].screenY)):(g=e-a.screenY+f,i(b,"lastTouchY",a.screenY)),d.scrollTop(g)}}function u(a,b){var c=h(b,"container"),d=j(b,"startTouchY"),f=j(b,"lastTouchY"),g=j(b,"initialScrollPosition");if(!d)return;var i=e(b);d<f&&c.scrollTop()<i?q(b):r(b),k(b)}function v(a,b){if(p(b)!=="waiting"){var c=h(b,"container"),d=j(b,"initialScrollPosition"),f=0;a.type==="mousewheel"?f=a.originalEvent.screenY-(a.originalEvent.screenY+a.originalEvent.wheelDeltaY):f=a.originalEvent.screenY-(a.originalEvent.screenY+a.originalEvent.detail*-1*3),c.scrollTop()+f<e(b)&&(f*=.03),c.scrollTop(c.scrollTop()+f)}}function w(a,b){var c=b.data("options"),d=h(b,"container"),f=h(b,"contentWrapper"),g=h(b,"scrollThumb");d.scrollTop()+d.height()===d.get(0).scrollHeight&&b.trigger("bottomreached"),c.pull&&(d.scrollTop()<e(b)/2?o(b,"release"):o(b,"initial"));var i=e(b);if(d.scrollTop()>=i){var j=b.children().first(),k=b.children().last();j&&parseInt(j.css("marginTop"),10)>=0&&b.css("padding-top","1px"),k&&parseInt(k.css("marginBottom"),10)>=0&&b.css("padding-bottom","1px"),g.height(d.innerHeight()/f.outerHeight(!0)*(d.innerHeight()+i)-(g.outerHeight(!0)-g.outerHeight())),g.css("top",d.position().top+(d.scrollTop()-i)/f.outerHeight(!0)*d.innerHeight()),g.css("left",d.position().left+d.width()-g.outerWidth(!0))}else g.hide()}function x(a,b){var c=h(b,"container"),d=h(b,"scrollThumb");c.scrollTop()>e(b)&&(d.stop(!0,!0),d.fadeIn(500))}function y(a,b){var d=h(b,"scrollThumb");d.stop(!0,!0),d.delay(300).fadeOut(1e3);var e=j(b,"startTouchY");!c()&&!e&&q(b)}var b={init:function(b){var c=a.extend({pull:!1,pullHeaderHTML:{initial:'<div><div class="icon"/><div class="label">Pull to refresh</div></div>',release:'<div><div class="icon"/><div class="label">Release to refresh</div></div>',waiting:'<div><div class="icon"/><div class="label">Refreshing...</div></div>'},inertia:!0,emulateTouchEvents:!1},b);a.easing.easeOutCubic===undefined&&(a.easing.easeOutCubic=function(a,b,c,d,e){return d*((b=b/e-1)*b*b+1)+c});if(!a.event.special.scrollstart&&!a.event.special.scrollend){var d=a.event.special,e="D"+ +(new Date),g="D"+(+(new Date)+1);d.scrollstart={setup:function(){var b,c=function(c){var e=this,f=arguments;b?clearTimeout(b):(c.type="scrollstart",a.event.handle.apply(e,f)),b=setTimeout(function(){b=null},d.scrollstop.latency)};a(this).bind("scroll",c).data(e,c)},teardown:function(){a(this).unbind("scroll",a(this).data(e))}},d.scrollstop={latency:300,setup:function(){var b,c=function(c){var e=this,f=arguments;b&&clearTimeout(b),b=setTimeout(function(){b=null,c.type="scrollstop",a.event.handle.apply(e,f)},d.scrollstop.latency)};a(this).bind("scroll",c).data(g,c)},teardown:function(){a(this).unbind("scroll",a(this).data(g))}}}return this.each(function(){var b=a(this);if(!b.data("scrollz")){b.data("options",c),n(b);var d=h(b,"container");i(b,"initialScrollPosition",d.scrollTop()),d.bind(f(b,"touchstart"),function(a){s(a,b)}),d.bind(f(b,"touchmove"),function(a){a.preventDefault(),t(a,b)}),d.bind(f(b,"touchend"),function(a){a.preventDefault(),u(a,b)}),a("*").not(d).bind(f(b,"touchend"),function(a){u(a,b)}),d.bind("mousewheel DOMMouseScroll",function(a){a.preventDefault(),v(a,b)}),d.scroll(function(a){w(a,b)}),d.bind("scrollstart",function(a){x(a,b)}),d.bind("scrollstop",function(a){y(a,b)}),b.data("scrollz",!0)}})},height:function(b){return this.each(function(){var c=a(this);if(c.data("scrollz")){var d=c.data("options"),e=h(c,"container");e.height(b),c.css("min-height",e.css("height"))}})},hidePullHeader:function(){return this.each(function(){var b=a(this);if(b.data("scrollz")){var c=b.data("options"),d=h(b,"container");c.pull&&d.animate({scrollTop:e(b)},"fast",function(){o(b,"initial")})}})}};a.fn.scrollz=function(c){return b[c]?b[c].apply(this,Array.prototype.slice.call(arguments,1)):typeof c=="object"||!c?b.init.apply(this,arguments):(a.error("Method "+c+" does not exist"),null)}})(jQuery);
BIN examples/.DS_Store
Binary file not shown.
295 examples/doc.html
@@ -0,0 +1,295 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>jQuery Scrollz</title>
+
+ <!-- Scrollz CSS -->
+ <link rel="stylesheet" href="../src/jquery.scrollz.css"/>
+
+ <!-- jQuery library -->
+ <script src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
+
+ <!-- Scrollz plugin -->
+ <script src="../src/jquery.scrollz.js"></script>
+
+ <style>
+
+ body {
+ color: #fff;
+ margin: 20px;
+ font: 13px 'trebuchet MS',Arial,Helvetica;
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+ background: #45484d; /* Old browsers */
+ background: -moz-linear-gradient(top, #45484d 0%, #000000 100%); /* FF3.6+ */
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#45484d), color-stop(100%,#000000)); /* Chrome,Safari4+ */
+ background: -webkit-linear-gradient(top, #45484d 0%,#000000 100%); /* Chrome10+,Safari5.1+ */
+ background: -o-linear-gradient(top, #45484d 0%,#000000 100%); /* Opera 11.10+ */
+ background: -ms-linear-gradient(top, #45484d 0%,#000000 100%); /* IE10+ */
+ background: linear-gradient(to bottom, #45484d 0%,#000000 100%); /* W3C */
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#45484d', endColorstr='#000000',GradientType=0 ); /* IE6-9 */
+ }
+
+ a {
+ color: #1BA8E0;
+ }
+
+ .small-container {
+ width: 336px;
+ margin: 0pt auto;
+ }
+
+ .scrollz-container {
+ border: solid #FFF 1px;
+ background-color: #666;
+ box-shadow:inset 0 0 10px #000000;
+ }
+
+ .scrollz-container ul{
+ padding: 0px;
+ }
+ .scrollz-container li{
+ border-bottom: 1px #FFF solid;
+ padding : 5px 0px;
+ }
+
+ </style>
+
+</head>
+<body>
+
+ <h1>jQuery Scrollz v1.0</h1>
+ <p>
+ Scrollz is a jQuery plugin that adds modern scrolling features to HTML content.
+ Primarly designed to work on touch devices, the plugin works as well on desktop browsers.
+ At the moment only vertical scroll is supported.
+ </p>
+
+ <h2>Basic use</h2>
+ <p>
+ Call <b>.scrollz()</b> on any element selector to make it scrollable.
+ The scrollable content supports mouse and touch events.
+ By default scrolling uses inertia (this can be disabled with options).
+ The width of the scrollable area is based on the parent element.
+ The height is determined by the one initially set on the content.
+ </p>
+ <h3>Markup</h3>
+ <p>TODO</p>
+ <p>Before calling <b>.scrollz()</b> remember to always give a height to the content.</p>
+ <h3>Code</h3>
+ <pre>
+ $('#scrollz1').scrollz();
+
+ // Disable image dragging in content
+ $('#scrollz1').find('img').bind('mousedown', function(event) {
+ event.preventDefault();
+ });
+ </pre>
+ <h3>Result</h3>
+ <div class="small-container">
+ <div id='scrollz1' style="height: 400px; padding: 0px 15px;">
+ <p>Scroll to see other pictures</p>
+ <p>
+ <img src="http://distilleryimage7.s3.amazonaws.com/8114892a1fff11e19896123138142014_6.jpg"/>
+ </p>
+ <p>
+ <img src="http://distillery.s3.amazonaws.com/media/2011/10/05/9302b8e1f5ee40ddaff9dec24f9a77f5_6.jpg"/>
+ </p>
+ <p>
+ <img src="http://distillery.s3.amazonaws.com/media/2011/09/23/e21e8552138146ac945876dbcb043ecc_6.jpg"/>
+ </p>
+ <p>
+ <img src="http://distillery.s3.amazonaws.com/media/2011/08/24/4768d409cf944eb3932aae917070932e_6.jpg"/>
+ </p>
+ <p>End of content</p>
+ </div>
+ </div>
+ <script>
+ $(document).ready(function() {
+ $('#scrollz1').scrollz();
+
+ // Disable image dragging in content
+ $('#scrollz1').find('img').bind('mousedown', function(event) {
+ event.preventDefault();
+ });
+ });
+ </script>
+
+ <h2>Options</h2>
+ <p>
+ <ul>
+ <li><b>styleClass (string)</b> : style class to apply on the scrolling area (default: none).</li>
+ <li><b>inertia (boolean)</b> : should scrolling area scroll with inertia effect (default: true).</li>
+ <li><b>pull (boolean)</b> : should scrolling area support 'pull' feature. In this case, a pull header is added on top of the content. When scrolling area is 'pull' at its top, the header appears (default: false).</li>
+ <li><b>pullHeaderHTML (map)</b> : HTML code used to render the pull header for the following states : 'initial', 'release' and 'waiting'. A default HTML rendition is provided for each state.</li>
+ <li><b>emulateTouchEvents (boolean)</b> : should the plugin emulate touch events on devices without touch support (default: false).</li>
+ </ul>
+ </p>
+ <h3>Code</h3>
+ <pre>
+ $('#scrollz1').scrollz({
+ // Options here
+ });
+ </pre>
+
+ <h2>Methods</h2>
+ <p>
+ Scrollz provides the following methods :
+ <ul>
+ <li><b>height(int height)</b> : redefines scrolling area height.</li>
+ <li><b>hidePullHeader</b> : hides the pull header (must be called after processing of the pull action completed).</li>
+ </ul>
+ </p>
+ <h3>Code</h3>
+ <pre>
+ // Resize
+ $('#scrollz1').scrollz('resize', 500);
+
+ // Hide pull header
+ $('#scrollz1').bind('pulled', function() {
+
+ // Process pull action
+
+ $('#scrollz1').scrollz('hidePullHeader');
+ });
+ </pre>
+
+ <h2>Events</h2>
+ <p>
+ Scrollz can trigger 2 types of events :
+ <ul>
+ <li><b>bottomreached</b> : notifies that the bottom of the scrolling area is reached.</li>
+ <li><b>pulled</b> : notifies that the scrolling area (with pull header) was pulled.</li>
+ </ul>
+ The <b>bottomreached</b> event is usefull to implement 'infinte scroll' feature.
+ </p>
+ <h3>Code (infinite scroll and pull to refresh)</h3>
+ <pre>
+ // Scroll with pull support
+ $('#scrollz2').scrollz({
+ pull : true
+ });
+
+ // Bind events
+ $('#scrollz2').bind('bottomreached', function() {
+ // Load more
+ loadMore();
+ });
+
+ $('#scrollz2').bind('pulled', function() {
+ // Reset page index
+ nextPageIndex = 0;
+
+ // Reload
+ loadMore();
+ });
+
+ // Load more function : used to load AJAX content (uses Twitter JSONP)
+ var nextPageIndex = 0;
+ var loading = false;
+ function loadMore() {
+
+ loading = true;
+
+ $.getJSON('https://api.twitter.com/1/statuses/user_timeline.json?page=' + nextPageIndex +
+ '&include_entities=true&include_rts=true&screen_name=zippy1978&count=25&callback=?', function(JSON) {
+
+ // First page
+ if (nextPageIndex == 0) {
+ $('#scrollz2 ul').empty();
+ }
+
+ $.each(JSON, function() {
+ lastPageIndex = this.id;
+ $('#scrollz2 ul').append('&lt;li&gt;' + this.text + '&lt;/li&gt;');
+ });
+
+ // Hide pull header after first page is loaded
+ if (nextPageIndex == 0) {
+ $('#scrollz2').scrollz('hidePullHeader');
+ }
+
+ nextPageIndex++;
+
+ loading = false;
+ });
+
+ }
+ </pre>
+ <h3>Result</h3>
+ <div class="small-container">
+ <div id='scrollz2' style="height: 346px; padding: 0px 10px">
+ <ul></ul>
+ </div>
+ </div>
+ <script>
+ $(document).ready(function() {
+ // Scroll with pull support
+ $('#scrollz2').scrollz({
+ pull : true
+ });
+
+ // Bind events
+ $('#scrollz2').bind('bottomreached', function() {
+ // Load more
+ loadMore();
+ });
+
+ $('#scrollz2').bind('pulled', function() {
+ // Reset page index
+ nextPageIndex = 0;
+
+ // Reload
+ loadMore();
+ });
+
+ // Load more function : used to load AJAX content (uses Twitter JSONP)
+ var nextPageIndex = 0;
+ var loading = false;
+ function loadMore() {
+
+ loading = true;
+
+ $.getJSON('https://api.twitter.com/1/statuses/user_timeline.json?page=' + nextPageIndex + '&include_entities=true&include_rts=true&screen_name=zippy1978&count=25&callback=?', function(JSON) {
+
+ // First page
+ if (nextPageIndex == 0) {
+ $('#scrollz2 ul').empty();
+ }
+
+ $.each(JSON, function() {
+ lastPageIndex = this.id;
+ $('#scrollz2 ul').append('<li>' + this.text + '</li>');
+ });
+
+ // Hide pull header after first page is loaded
+ if (nextPageIndex == 0) {
+ $('#scrollz2').scrollz('hidePullHeader');
+ }
+
+ nextPageIndex++;
+
+ loading = false;
+ });
+
+ }
+
+ // Initial loading
+ loadMore();
+ });
+ </script>
+
+ <h2>Styling</h2>
+ <p>
+ The plugin is provided with a default CSS. This CSS includes pull header and scroll thumb styling.
+ <ul>
+ <li>Styling on the different pull header states (based on the default pullHeaderHTML markup).</li>
+ <li>Pull header icons (as base64) : arrow and animated loader, with retina support.</li>
+ <li>Pull header arrow animations (up and down).</li>
+ <li>Scroll thumb simple styling.</li>
+ </ul>
+
+ </p>
+
+</body>
+</html>
172 examples/mobile.html
@@ -0,0 +1,172 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>jQuery Scrollz</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"/>
+ <meta name="apple-mobile-web-app-capable" content="yes"/>
+ <meta name="apple-mobile-web-app-status-bar-style" content="black" />
+ <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css"/>
+ <script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
+
+ <!-- Scrollz plugin -->
+ <script src="../src/jquery.scrollz.js"></script>
+
+ <!-- Scrollz CSS -->
+ <link rel="stylesheet" href="../src/jquery.scrollz.css"/>
+
+
+ <style>
+ /* Wrapping */
+ .wrapping-truncate {
+ white-space: nowrap;
+ }
+
+ .wrapping-new-line {
+ white-space: normal;
+ }
+
+ /* List icon positioning */
+ .ui-li-has-icon .ui-btn-inner a.ui-link-inherit, .ui-li-static.ui-li-has-icon {
+ min-height: 40px;
+ padding-left: 60px;
+ }
+
+ /* List links */
+ .ui-li a {
+ color: #1BA8E0;
+ text-decoration: none;
+ }
+ </style>
+
+ <script>
+ // Next page index loaded
+ var nextPageIndex = 0;
+
+ // Indicates if new entries loading is in progress
+ var loading = false;
+
+ // Parsing utilities from: http://www.simonwhatley.co.uk/parsing-twitter-usernames-hashtags-and-urls-with-javascript
+ String.prototype.parseURL = function() {
+ return this.replace(/[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&~\?\/.=]+/g, function(url) {
+ return url.link(url);
+ });
+ };
+ String.prototype.parseUsername = function() {
+ return this.replace(/[@]+[A-Za-z0-9-_]+/g, function(u) {
+ var username = u.replace("@","")
+ return u.link("http://twitter.com/"+username);
+ });
+ };
+ String.prototype.parseHashtag = function() {
+ return this.replace(/[#]+[A-Za-z0-9-_]+/g, function(t) {
+ var tag = t.replace("#","%23")
+ return t.link("http://search.twitter.com/search?q="+tag);
+ });
+ };
+
+ // Load more function
+ function loadMore() {
+ loading = true;
+
+ $.getJSON('https://api.twitter.com/1/statuses/user_timeline.json?page=' + nextPageIndex + '&include_entities=true&include_rts=true&screen_name=zippy1978&count=25&callback=?', function(JSON) {
+ var targetList = $('#tweets');
+
+ // First page
+ if (nextPageIndex == 0) {
+ targetList.empty();
+ }
+
+ $.each(JSON, function() {
+ lastPageIndex = this.id;
+ targetList.append('<li><img class="ui-li-icon" src="' + this.user.profile_image_url + '"/><h3 class="wrapping-new-line">' + this.text.parseURL().parseUsername().parseHashtag() + '</h3></li>');
+ });
+
+ targetList.listview("refresh");
+
+ // Force resize
+ $(window).resize();
+
+ // Hide pull header after first page is loaded
+ if (nextPageIndex == 0) {
+ $('#content').scrollz('hidePullHeader');
+ }
+
+ nextPageIndex++;
+
+ loading = false;
+ });
+
+ }
+
+ /* Global init */
+ $(document).bind("mobileinit", function() {
+
+ $(document).bind("pageshow", function() {
+
+ // Enable scrollz
+ $('#content').scrollz({
+ pull: true,
+ emulateTouchEvents : true
+ });
+
+ // Force resize (otherwise initial sizes cannot be calculated)
+ $(window).resize();
+ $('#content').scrollz('hidePullHeader');
+
+ // Bind events
+ $('#content').bind('bottomreached', function() {
+ // Load more
+ loadMore();
+ });
+
+ $('#content').bind('pulled', function() {
+ // Reset page index
+ nextPageIndex = 0;
+
+ // Reload
+ loadMore();
+ });
+
+ // Load initial tweets
+ loadMore();
+ });
+
+ $(window).resize(function() {
+ // Compute content heights (between header and footer, if any) visible and full
+ var visibleContentHeight = (window.innerHeight ? window.innerHeight : $(window).height()) - $(".ui-page-active div.ui-footer").outerHeight() - $(".ui-page-active div.ui-header").outerHeight();
+ var activePageHeight = $('.ui-page-active').height();
+ var fullContentHeight = activePageHeight > visibleContentHeight ? activePageHeight : visibleContentHeight;
+ $('#content').scrollz('height', fullContentHeight);
+ });
+
+ });
+
+ </script>
+
+ <script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
+
+ </head>
+ <body>
+ <div id="index" data-role="page" data-theme="a">
+ <!-- Header -->
+ <div data-role="header" data-position="fixed" data-tap-toggle="false">
+ <div>
+ <h1 class='ui-title' role='heading' aria-level='1'>jQuery Scrollz</h1>
+ </div>
+ </div>
+
+ <!-- Content -->
+ <div id="content" data-role="content" data-theme="e">
+ <ul id="tweets" data-role='listview' data-inset='false' data-theme='c'>
+
+ </ul>
+ </div>
+
+ <!-- Footer -->
+ <div data-role="footer" data-position="fixed" data-tap-toggle="false"></div>
+
+
+ </div>
+ </body>
+</html>
64 grunt.js
@@ -0,0 +1,64 @@
+/*global module:false*/
+module.exports = function(grunt) {
+
+ // Project configuration.
+ grunt.initConfig({
+ pkg: '<json:jquery.scrollz.jquery.json>',
+ meta: {
+ banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
+ '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
+ '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' +
+ '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
+ ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */'
+ },
+ concat: {
+ js: {
+ src: ['<banner:meta.banner>', '<file_strip_banner:src/<%= pkg.name %>.js>'],
+ dest: 'dist/<%= pkg.name %>.js'
+ },
+ css: {
+ src: ['<banner:meta.banner>', '<file_strip_banner:src/<%= pkg.name %>.css>'],
+ dest: 'dist/<%= pkg.name %>.css'
+ }
+ },
+ min: {
+ js: {
+ src: ['<banner:meta.banner>', '<config:concat.js.dest>'],
+ dest: 'dist/<%= pkg.name %>.min.js'
+ }
+ },
+ qunit: {
+ files: ['test/**/*.html']
+ },
+ lint: {
+ files: ['grunt.js', 'src/**/*.js', 'test/**/*.js']
+ },
+ watch: {
+ files: '<config:lint.files>',
+ tasks: 'lint qunit'
+ },
+ jshint: {
+ options: {
+ curly: true,
+ eqeqeq: true,
+ immed: true,
+ latedef: true,
+ newcap: true,
+ noarg: true,
+ sub: true,
+ undef: true,
+ boss: true,
+ eqnull: true,
+ browser: true
+ },
+ globals: {
+ jQuery: true
+ }
+ },
+ uglify: {}
+ });
+
+ // Default task.
+ grunt.registerTask('default', 'lint qunit concat min');
+
+};
33 jquery.scrollz.jquery.json
@@ -0,0 +1,33 @@
+{
+ "name": "jquery.scrollz",
+ "title": "jQuery Scrollz",
+ "description": "Modern scrolling for jQuery and jQuery Mobile.",
+ "version": "1.0.0",
+ "homepage": "https://github.com/zippy1978/jquery.scrollz",
+ "author": {
+ "name": "Gilles Grousset",
+ "email": "gi.grousset@gmail.com",
+ "url": "http://zippy1978.tumblr.com"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/zippy1978/jquery.scrollz"
+ },
+ "bugs": {
+ "url": "https://github.com/zippy1978/jquery.scrollz/issues"
+ },
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "https://github.com/zippy1978/jquery.scrollz/blob/master/LICENSE-MIT"
+ },
+ {
+ "type": "GPL",
+ "url": "https://github.com/zippy1978/jquery.scrollz/blob/master/LICENSE-GPL"
+ }
+ ],
+ "dependencies": {
+ "jquery": ">= 1.6"
+ },
+ "keywords": []
+}
14 libs/jquery-loader.js
@@ -0,0 +1,14 @@
+(function() {
+ // Get any jquery=___ param from the query string.
+ var jqversion = location.search.match(/[?&]jquery=(.*?)(?=&|$)/);
+ var path;
+ if (jqversion) {
+ // A version was specified, load that version from code.jquery.com.
+ path = 'http://code.jquery.com/jquery-' + jqversion[1] + '.js';
+ } else {
+ // No version was specified, load the local version.
+ path = '../libs/jquery/jquery.js';
+ }
+ // This is the only time I'll ever use document.write, I promise!
+ document.write('<script src="' + path + '"></script>');
+}());
9,404 libs/jquery/jquery.js
9,404 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
232 libs/qunit/qunit.css
@@ -0,0 +1,232 @@
+/**
+ * QUnit v1.4.0 - A JavaScript Unit Testing Framework
+ *
+ * http://docs.jquery.com/QUnit
+ *
+ * Copyright (c) 2012 John Resig, Jörn Zaefferer
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * or GPL (GPL-LICENSE.txt) licenses.
+ */
+
+/** Font Family and Sizes */
+
+#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
+}
+
+#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
+#qunit-tests { font-size: smaller; }
+
+
+/** Resets */
+
+#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
+ margin: 0;
+ padding: 0;
+}
+
+
+/** Header */
+
+#qunit-header {
+ padding: 0.5em 0 0.5em 1em;
+
+ color: #8699a4;
+ background-color: #0d3349;
+
+ font-size: 1.5em;
+ line-height: 1em;
+ font-weight: normal;
+
+ border-radius: 15px 15px 0 0;
+ -moz-border-radius: 15px 15px 0 0;
+ -webkit-border-top-right-radius: 15px;
+ -webkit-border-top-left-radius: 15px;
+}
+
+#qunit-header a {
+ text-decoration: none;
+ color: #c2ccd1;
+}
+
+#qunit-header a:hover,
+#qunit-header a:focus {
+ color: #fff;
+}
+
+#qunit-header label {
+ display: inline-block;
+}
+
+#qunit-banner {
+ height: 5px;
+}
+
+#qunit-testrunner-toolbar {
+ padding: 0.5em 0 0.5em 2em;
+ color: #5E740B;
+ background-color: #eee;
+}
+
+#qunit-userAgent {
+ padding: 0.5em 0 0.5em 2.5em;
+ background-color: #2b81af;
+ color: #fff;
+ text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
+}
+
+
+/** Tests: Pass/Fail */
+
+#qunit-tests {
+ list-style-position: inside;
+}
+
+#qunit-tests li {
+ padding: 0.4em 0.5em 0.4em 2.5em;
+ border-bottom: 1px solid #fff;
+ list-style-position: inside;
+}
+
+#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
+ display: none;
+}
+
+#qunit-tests li strong {
+ cursor: pointer;
+}
+
+#qunit-tests li a {
+ padding: 0.5em;
+ color: #c2ccd1;
+ text-decoration: none;
+}
+#qunit-tests li a:hover,
+#qunit-tests li a:focus {
+ color: #000;
+}
+
+#qunit-tests ol {
+ margin-top: 0.5em;
+ padding: 0.5em;
+
+ background-color: #fff;
+
+ border-radius: 15px;
+ -moz-border-radius: 15px;
+ -webkit-border-radius: 15px;
+
+ box-shadow: inset 0px 2px 13px #999;
+ -moz-box-shadow: inset 0px 2px 13px #999;
+ -webkit-box-shadow: inset 0px 2px 13px #999;
+}
+
+#qunit-tests table {
+ border-collapse: collapse;
+ margin-top: .2em;
+}
+
+#qunit-tests th {
+ text-align: right;
+ vertical-align: top;
+ padding: 0 .5em 0 0;
+}
+
+#qunit-tests td {
+ vertical-align: top;
+}
+
+#qunit-tests pre {
+ margin: 0;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+#qunit-tests del {
+ background-color: #e0f2be;
+ color: #374e0c;
+ text-decoration: none;
+}
+
+#qunit-tests ins {
+ background-color: #ffcaca;
+ color: #500;
+ text-decoration: none;
+}
+
+/*** Test Counts */
+
+#qunit-tests b.counts { color: black; }
+#qunit-tests b.passed { color: #5E740B; }
+#qunit-tests b.failed { color: #710909; }
+
+#qunit-tests li li {
+ margin: 0.5em;
+ padding: 0.4em 0.5em 0.4em 0.5em;
+ background-color: #fff;
+ border-bottom: none;
+ list-style-position: inside;
+}
+
+/*** Passing Styles */
+
+#qunit-tests li li.pass {
+ color: #5E740B;
+ background-color: #fff;
+ border-left: 26px solid #C6E746;
+}
+
+#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
+#qunit-tests .pass .test-name { color: #366097; }
+
+#qunit-tests .pass .test-actual,
+#qunit-tests .pass .test-expected { color: #999999; }
+
+#qunit-banner.qunit-pass { background-color: #C6E746; }
+
+/*** Failing Styles */
+
+#qunit-tests li li.fail {
+ color: #710909;
+ background-color: #fff;
+ border-left: 26px solid #EE5757;
+ white-space: pre;
+}
+
+#qunit-tests > li:last-child {
+ border-radius: 0 0 15px 15px;
+ -moz-border-radius: 0 0 15px 15px;
+ -webkit-border-bottom-right-radius: 15px;
+ -webkit-border-bottom-left-radius: 15px;
+}
+
+#qunit-tests .fail { color: #000000; background-color: #EE5757; }
+#qunit-tests .fail .test-name,
+#qunit-tests .fail .module-name { color: #000000; }
+
+#qunit-tests .fail .test-actual { color: #EE5757; }
+#qunit-tests .fail .test-expected { color: green; }
+
+#qunit-banner.qunit-fail { background-color: #EE5757; }
+
+
+/** Result */
+
+#qunit-testresult {
+ padding: 0.5em 0.5em 0.5em 2.5em;
+
+ color: #2b81af;
+ background-color: #D2E0E6;
+
+ border-bottom: 1px solid white;
+}
+
+/** Fixture */
+
+#qunit-fixture {
+ position: absolute;
+ top: -10000px;
+ left: -10000px;
+ width: 1000px;
+ height: 1000px;
+}
1,659 libs/qunit/qunit.js
@@ -0,0 +1,1659 @@
+/**
+ * QUnit v1.4.0 - A JavaScript Unit Testing Framework
+ *
+ * http://docs.jquery.com/QUnit
+ *
+ * Copyright (c) 2012 John Resig, Jörn Zaefferer
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * or GPL (GPL-LICENSE.txt) licenses.
+ */
+
+(function(window) {
+
+var defined = {
+ setTimeout: typeof window.setTimeout !== "undefined",
+ sessionStorage: (function() {
+ var x = "qunit-test-string";
+ try {
+ sessionStorage.setItem(x, x);
+ sessionStorage.removeItem(x);
+ return true;
+ } catch(e) {
+ return false;
+ }
+ }())
+};
+
+var testId = 0,
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty;
+
+var Test = function(name, testName, expected, async, callback) {
+ this.name = name;
+ this.testName = testName;
+ this.expected = expected;
+ this.async = async;
+ this.callback = callback;
+ this.assertions = [];
+};
+Test.prototype = {
+ init: function() {
+ var tests = id("qunit-tests");
+ if (tests) {
+ var b = document.createElement("strong");
+ b.innerHTML = "Running " + this.name;
+ var li = document.createElement("li");
+ li.appendChild( b );
+ li.className = "running";
+ li.id = this.id = "test-output" + testId++;
+ tests.appendChild( li );
+ }
+ },
+ setup: function() {
+ if (this.module != config.previousModule) {
+ if ( config.previousModule ) {
+ runLoggingCallbacks('moduleDone', QUnit, {
+ name: config.previousModule,
+ failed: config.moduleStats.bad,
+ passed: config.moduleStats.all - config.moduleStats.bad,
+ total: config.moduleStats.all
+ } );
+ }
+ config.previousModule = this.module;
+ config.moduleStats = { all: 0, bad: 0 };
+ runLoggingCallbacks( 'moduleStart', QUnit, {
+ name: this.module
+ } );
+ } else if (config.autorun) {
+ runLoggingCallbacks( 'moduleStart', QUnit, {
+ name: this.module
+ } );
+ }
+
+ config.current = this;
+ this.testEnvironment = extend({
+ setup: function() {},
+ teardown: function() {}
+ }, this.moduleTestEnvironment);
+
+ runLoggingCallbacks( 'testStart', QUnit, {
+ name: this.testName,
+ module: this.module
+ });
+
+ // allow utility functions to access the current test environment
+ // TODO why??
+ QUnit.current_testEnvironment = this.testEnvironment;
+
+ if ( !config.pollution ) {
+ saveGlobal();
+ }
+ if ( config.notrycatch ) {
+ this.testEnvironment.setup.call(this.testEnvironment);
+ return;
+ }
+ try {
+ this.testEnvironment.setup.call(this.testEnvironment);
+ } catch(e) {
+ QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
+ }
+ },
+ run: function() {
+ config.current = this;
+ if ( this.async ) {
+ QUnit.stop();
+ }
+
+ if ( config.notrycatch ) {
+ this.callback.call(this.testEnvironment);
+ return;
+ }
+ try {
+ this.callback.call(this.testEnvironment);
+ } catch(e) {
+ QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + ": " + e.message, extractStacktrace( e, 1 ) );
+ // else next test will carry the responsibility
+ saveGlobal();
+
+ // Restart the tests if they're blocking
+ if ( config.blocking ) {
+ QUnit.start();
+ }
+ }
+ },
+ teardown: function() {
+ config.current = this;
+ if ( config.notrycatch ) {
+ this.testEnvironment.teardown.call(this.testEnvironment);
+ return;
+ } else {
+ try {
+ this.testEnvironment.teardown.call(this.testEnvironment);
+ } catch(e) {
+ QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
+ }
+ }
+ checkPollution();
+ },
+ finish: function() {
+ config.current = this;
+ if ( this.expected != null && this.expected != this.assertions.length ) {
+ QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
+ } else if ( this.expected == null && !this.assertions.length ) {
+ QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions." );
+ }
+
+ var good = 0, bad = 0,
+ li, i,
+ tests = id("qunit-tests");
+
+ config.stats.all += this.assertions.length;
+ config.moduleStats.all += this.assertions.length;
+
+ if ( tests ) {
+ var ol = document.createElement("ol");
+
+ for ( i = 0; i < this.assertions.length; i++ ) {
+ var assertion = this.assertions[i];
+
+ li = document.createElement("li");
+ li.className = assertion.result ? "pass" : "fail";
+ li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
+ ol.appendChild( li );
+
+ if ( assertion.result ) {
+ good++;
+ } else {
+ bad++;
+ config.stats.bad++;
+ config.moduleStats.bad++;
+ }
+ }
+
+ // store result when possible
+ if ( QUnit.config.reorder && defined.sessionStorage ) {
+ if (bad) {
+ sessionStorage.setItem("qunit-test-" + this.module + "-" + this.testName, bad);
+ } else {
+ sessionStorage.removeItem("qunit-test-" + this.module + "-" + this.testName);
+ }
+ }
+
+ if (bad === 0) {
+ ol.style.display = "none";
+ }
+
+ var b = document.createElement("strong");
+ b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";