├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── MIT-LICENSE ├── README.md ├── examples ├── anim.html ├── iphone │ ├── index.html │ └── iphone.css ├── load_jquery_on_ie.html ├── snow │ └── index.html └── touch_events.html ├── make ├── package.json ├── script ├── guard └── test ├── src ├── ajax.js ├── assets.js ├── data.js ├── detect.js ├── event.js ├── form.js ├── fx.js ├── fx_methods.js ├── gesture.js ├── polyfill.js ├── selector.js ├── stack.js ├── touch.js └── zepto.js ├── test ├── ajax.html ├── assets_functional.html ├── data.html ├── detect.html ├── event.html ├── evidence_runner.js ├── fixtures │ ├── ajax_load_selector.html │ ├── ajax_load_selector_javascript.html │ ├── ajax_load_simple.html │ ├── iframe_document.html │ └── zepto.json ├── form.html ├── fx.html ├── fx_functional.html ├── gesture_functional.html ├── polyfill.html ├── runner.coffee ├── selector.html ├── server.coffee ├── stack.html ├── test.css ├── touch.html ├── touch_functional.html ├── touchcancel_functional.html └── zepto.html └── vendor └── evidence.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | dist/ 3 | dist/zepto.min.js 4 | dist/zepto.js 5 | pkg 6 | *.swp 7 | docs/* 8 | .jhw-cache 9 | .rbenv-version 10 | public/ 11 | node_modules 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.6 4 | script: script/test 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Code style guidelines 2 | 3 | * `function name() { }` for named functions 4 | * `function(){ }` for anonymous functions 5 | * No curly braces for single-line control flow statements such as `if` & friends 6 | * Don't write [semicolons that are optional][optional] 7 | * Put a single semicolon _before_ statements that start with `(` or `[` 8 | (see above article as for why it's needed) 9 | * Use long, descriptive variable and method names 10 | * Use blank lines to separate "paragraphs" of code for readability 11 | * Use comments to describe non-obvious code behavior 12 | 13 | 14 | [optional]: http://mislav.uniqpath.com/2010/05/semicolons/ 15 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2012 Thomas Fuchs 2 | http://zeptojs.com/ 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Zepto.js – a minimalist JavaScript library 2 | 3 | Zepto is a minimalist JavaScript library for modern browsers with a 4 | largely jQuery-compatible API. If you use jQuery, you already know how to use Zepto. 5 | 6 | See [zeptojs.com][] for an extended introduction, downloads 7 | and documentation. 8 | 9 | Zepto.js is licensed under the terms of the MIT License. 10 | 11 | ## Building 12 | 13 | [![Build Status](https://secure.travis-ci.org/madrobby/zepto.png?branch=master)](http://travis-ci.org/madrobby/zepto) 14 | 15 | The official site offers a download of the default distribution of Zepto. This 16 | is good for starting out. However, at some point you might want to add some 17 | optional modules and remove some of the default ones you don't need, to keep the 18 | size at minimum. That's when you need to check out Zepto's source code and use 19 | the build commands. 20 | 21 | You will need Node.js installed on your system. 22 | 23 | ~~~ sh 24 | $ npm install 25 | $ npm run-script dist 26 | ~~~ 27 | 28 | The resulting files are: 29 | 30 | 1. `dist/zepto.js` 31 | 2. `dist/zepto.min.js` 32 | 33 | If you install CoffeeScript globally, you can run `make` directly: 34 | 35 | ~~~ sh 36 | $ coffee make dist 37 | $ MODULES="zepto event data ..." ./make dist 38 | ~~~ 39 | 40 | ## Zepto modules 41 | 42 | Zepto modules are individual files in the "src/" directory. 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 84 | 85 | 86 | 88 | 89 | 90 | 91 | 92 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 104 | 105 | 106 | 108 | 109 | 113 | 114 | 115 | 117 | 118 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 |
module default description
polyfill 53 | Provides String.prototype.trim and Array.prototype.reduce methods 54 | if they are missing (required for iOS 3.x) 55 |
zeptoCore module; contains most methods
eventEvent handling via on() & off()
detectProvides $.os and $.browser information
fxThe animate() method
fx_methods 81 | Animated show, hide, toggle, 82 | and fade*() methods. 83 |
ajaxXMLHttpRequest and JSONP functionality
formSerialize & submit web forms
assets 101 | Experimental support for cleaning up iOS memory after removing 102 | image elements from the DOM. 103 |
data 110 | A full-blown data() method, capable of storing arbitrary 111 | objects in memory. 112 |
selector 119 | Experimental jQuery 120 | CSS extensions support for functionality such as $('div:first') and 121 | el.is(':visible'). 122 |
touchFires tap– and swipe–related events on touch devices
gestureFires pinch gesture events on touch devices
stackProvides andSelf & end() chaining methods
141 | 142 | ## Contributing 143 | 144 | Get in touch: 145 | 146 | * IRC channel: [#zepto on freenode.net](irc://irc.freenode.net/zepto) 147 | * @[zeptojs](http://twitter.com/zeptojs) 148 | 149 | ### Write documentation 150 | 151 | Zepto docs are written in Markdown and live in the ["gh-pages" branch][docs]. 152 | They are published on [zeptojs.com][]. 153 | 154 | You can use GitHub's web interface to make quick changes to documentation for 155 | specific Zepto features 156 | ([example: ajaxSettings](https://github.com/madrobby/zepto/blob/gh-pages/ajax/_posts/1900-01-01-Z-ajaxSettings.md)). 157 | This will submit a pull request to us that we can review. 158 | 159 | ### Report a bug 160 | 161 | 1. Check if the bug is already fixed in the [master branch][master] since the 162 | last release. 163 | 2. Check [existing issues][issues]. Open a new one, including exact browser & 164 | platform information. For better formatting of your report, see 165 | [GitHub-flavored Markdown][mkd]. 166 | 167 | ### Running tests 168 | 169 | You will need to install [PhantomJS][]. On OS X, that's easy: 170 | 171 | ~~~ sh 172 | $ brew install phantomjs 173 | ~~~ 174 | 175 | To run the test suite, these are all equivalent: 176 | 177 | ~~~ sh 178 | $ npm test 179 | $ ./make test 180 | $ script/test 181 | ~~~ 182 | 183 | 184 | [zeptojs.com]: http://zeptojs.com 185 | [master]: https://github.com/madrobby/zepto/commits/master 186 | [issues]: https://github.com/madrobby/zepto/issues 187 | [docs]: https://github.com/madrobby/zepto/tree/gh-pages#readme 188 | [mkd]: http://github.github.com/github-flavored-markdown/ 189 | [evidence.js]: https://github.com/tobie/Evidence 190 | [phantomjs]: http://code.google.com/p/phantomjs/wiki/Installation 191 | -------------------------------------------------------------------------------- /examples/anim.html: -------------------------------------------------------------------------------- 1 |
HELLO WORLD
2 | 3 | 4 | 5 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /examples/iphone/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | iPhone.Zepto 5 | 6 | 7 | 8 | 9 | 10 | 11 | 23 | 32 | 33 | 34 | 35 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /examples/iphone/iphone.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0px; 3 | padding: 0px; 4 | } 5 | .toolbar { 6 | -webkit-box-sizing: border-box; 7 | background: #6D84A2 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAArCAIAAAA2QHWOAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEpJREFUeNpNjCEOgEAQA5v9/9eQaAQCd57L0WXTDSmimXZEse1HnNcIIINZYTPVv7Ac4/EWe7OTsC/ec+nDgcj/dpcH7EXt8up4AfRWcOjLIqWFAAAAAElFTkSuQmCC) repeat-x; 8 | border-bottom: 1px solid #2D3642; 9 | height: 45px; 10 | padding: 10px; 11 | position: relative; 12 | } 13 | body { 14 | -webkit-perspective: 800; 15 | -webkit-text-size-adjust: none; 16 | -webkit-transform-style: preserve-3d; 17 | -webkit-user-select: none; 18 | font-family: Helvetica; 19 | overflow-x: hidden; 20 | height: 100%; 21 | width: 100%; 22 | } 23 | 24 | body > section { 25 | background: #C5CCD3 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAABCAIAAACdaSOZAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABdJREFUeNpiPHrmCgMC/GNjYwNSAAEGADdNA3dnzPlQAAAAAElFTkSuQmCC); 26 | display: block; 27 | left: 0px; 28 | position: absolute; 29 | width: 100%; 30 | -webkit-transform: translate3d(100%,0%,0%); 31 | opacity: 0; 32 | -webkit-transition: all 0.25s ease-in-out; 33 | -webkit-transform-style: preserve-3d; 34 | z-index: -10; 35 | } 36 | 37 | .reverse { 38 | -webkit-transform: translate3d(-100%, 0%, 0%); 39 | opacity: 0; 40 | } 41 | 42 | .current { 43 | -webkit-transform: translate3d(0%,0%, 0%); 44 | opacity: 1; 45 | z-index: 1; 46 | } 47 | h1, h2 { 48 | color: #4C566C; 49 | font: normal normal bold 18px/normal Helvetica; 50 | margin: 10px 20px 6px; 51 | text-shadow: rgba(255, 255, 255, 0.19) 52 | } 53 | body.landscape .toolbar > h1 { 54 | margin-left: -125px; 55 | width: 250px; 56 | } 57 | 58 | .toolbar > h1 { 59 | color: white; 60 | font-size: 20px; 61 | font-weight: bold; 62 | height: 40px; 63 | left: 50%; 64 | line-height: 1em; 65 | margin: 1px 0px 0px -75px; 66 | overflow: hidden; 67 | position: absolute; 68 | text-align: center; 69 | text-overflow: ellipsis; 70 | text-shadow: rgba(0, 0, 0, 0.398438) 0px -1px 0px; 71 | top: 10px; 72 | white-space: nowrap; 73 | width: 150px; 74 | } 75 | 76 | ul { 77 | background: white; 78 | border: 1px solid #B4B4B4; 79 | border-bottom-left-radius: 8px 8px; 80 | border-bottom-right-radius: 8px 8px; 81 | border-top-left-radius: 8px 8px; 82 | border-top-right-radius: 8px 8px; 83 | color: black; 84 | font: normal normal bold 17px/normal Helvetica; 85 | margin: 15px 10px 17px; 86 | padding: 0px; 87 | } 88 | 89 | ul li { 90 | color: black; 91 | font-size: 14px; 92 | border-top: 1px solid #B4B4B4; 93 | color: #666; 94 | list-style-type: none; 95 | padding: 10px; 96 | } 97 | 98 | li:first-child, li:first-child a { 99 | border-top: 0px; 100 | border-top-left-radius: 8px 8px; 101 | border-top-right-radius: 8px 8px; 102 | } 103 | 104 | ul li.arrow { 105 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAUCAYAAAB4d5a9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAKVJREFUeNpi/P//PwOtARMDHcDwsYQFRJSXl8P4dVC6CZvizs5O8i1BsqARid9Ei+BiQ2KDLKumhSU1QNyKxG+hlkXoEQ+yqAPNogpapK5KNIvaKbUIVxKeAsTvkPg5QCxETUukgfgAkqFPgdgBzVKKLIFZoIJmwR1qBRdNLEC2BJQpV9LCAmRL/gBxAtRwqlqAXqzcgRrOQE0LQIBxtNIiBQAEGAA7xCa2yF9zEgAAAABJRU5ErkJggg==); 106 | background-position: 100% 50%; 107 | background-repeat: no-repeat; 108 | } 109 | 110 | body > .current { 111 | display: block !important; 112 | } 113 | 114 | body.landscape > * { 115 | min-height: 320px; 116 | } 117 | 118 | 119 | ul li a, li.img a + a { 120 | color: black; 121 | display: block; 122 | margin: -10px; 123 | overflow: hidden; 124 | padding: 12px 10px; 125 | text-decoration: none; 126 | text-overflow: ellipsis; 127 | white-space: nowrap; 128 | } 129 | 130 | 131 | .button, .back, .cancel, .add { 132 | -webkit-border-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAeCAIAAACqmwlGAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAatJREFUeNqVlMtKw0AUhqfJSRpTk5Za1FAvxS6sgrVeFrpQdOcT+IpuBVHcuVBQpCDYjeAdkSpavDTGNJkz43FpFZz8DISZ+b75J5tJbWwfMsZuby7rh/vN5v1H+539TMZxPa84v7g0WirTFISQR/t7jdOTcqVaGp+ze50uIfDbL62H3a3NqerMwtIqXF9dnF+dL6+t9/S6hmFqut4luHns80aKpcrxwY43PKK7+f6J2ZVcYTCl6ZIxKuwatEhbpmU72UKjfgCakXFz/TwW7L8QlgIbTNtBwVAgU0g644IGZoycqYVgQGRRLBQFggEpHJUFBI4iUhYIBhSSPooCwUB0zEWiBsERlRsECJSIUlEgOHmDH3TM909FgWBgqe8oCgRrLGGADlcvIDB5A2nfQ7kD6D5J/jn5lZILBoAUQgWVUgDoWi5r8zhUEXgU5nOOVp0sB+0W+f8WEDY9MabpYNUqQ29Pd1Hoi79eA1qkLQIIY5CGIOwUvYHBQr5xdvn4/BSGnS7BstIDhexUbVo3jNarD1Ky5xfftszF+WqPZcKvp5IjfoYRoYHv0/QLHvXjAb8xkEMAAAAASUVORK5CYII=) 0 5 0 5 stretch stretch; 133 | background: none; 134 | border-width: 0px 5px; 135 | color: white; 136 | font-family: inherit; 137 | font-size: 12px; 138 | font-weight: bold; 139 | height: 30px; 140 | line-height: 30px; 141 | margin: 0px; 142 | overflow: ; 143 | padding: 0px 3px; 144 | position: absolute; 145 | right: 6px; 146 | text-decoration: none; 147 | text-overflow: ellipsis; 148 | text-shadow: rgba(0, 0, 0, 0.496094) 0px -1px 0px; 149 | top: 8px; 150 | white-space: nowrap; 151 | width: auto; 152 | } 153 | 154 | .back { 155 | -webkit-border-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACsAAAAeCAIAAAA6iHCJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAArFJREFUeNrF0mtPE2EQBeB5t9vSRqEUKiAFkasggmC5x3CRECHx8sH/qMHEoKkiMUQTNJIIaAjeiFGUcEsotN22u9vtOzMKogYL39rlyfkBk3NG3HsyC8dLJhOzM8+/fvkcjexAtghRXVN/dXCkvOIcAKhEDMdY+7ESejgeqKpv7x3J9/ogS5hoc33l0YPxju6+YFefSkRwlI311cnQRM/gbd+ZUpfb43CokD2nCv2lFbVvZib9JaUqMUEGTYtNPZ7oHLpZXBJQFIUYSCJkj3A4830lrd3Di2/nVcxYwTSMift3m4LXCovKCYGQIDe8vrJveNQfTE+Fys9f8pdVSSTILeFyF/z/B8uflnSLG+qakRlRQo45XO5DHejJxOtXLzsG7yA7ME2Qe0hw6A+mn4ZqLvY4XR6UCLZAxH8rfFx6Z6BaXVJlSQS7SKSDC+JabGFurrnvBvJ+NXZBYpX4oP9AQ5eietKSAMDuDt4vLkhHfr6/XCKCvZBIjezuLMzPN/XcIhIADPYiZPXFs8lAQ7dwOA8KsL8Dr7/CoDzSDDgJCT2ljI0OOzBGKMVJACGUXU2/3h/Uo5vMDCdBQSRLcn9nox7bEgLsDoDCDKYli4p89ZXFZjJ6Ah3wvlhcb2mqPa1a0jIBhJ1RiPl3wpH4QG+bTGwDo51DKPwHMccS5mBvSzyyCcB2rgB/k5aoqK72xgo9FrbxgsMSulkZOBvwu1O6BjnGhO48p8IZfj1EsPWCR+jSMiCX0pbh9xXsrZCZrW1toOcy6mFMpyA3mNnUwi2NtQoxZwaZwzF9bKhTpHaN+C7KNGQPobTMhBZe7Wyts1iox729RNyOJkeHOj4sf19Z3YgmsraI251X6ve2BYMEIqIlVebjd0rj2la0rrrySkuDU1UhSySiYVrbkbiZ2qv2J5+53J2yzo3SAAAAAElFTkSuQmCC) 0 8 0 14 stretch stretch; 156 | border-width: 0px 8px 0px 14px; 157 | left: 6px; 158 | max-width: 55px; 159 | padding: 0px; 160 | right: auto; 161 | } 162 | -------------------------------------------------------------------------------- /examples/load_jquery_on_ie.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Load jQuery if Zepto is not supported 5 | 6 | 7 |

Load jQuery if Zepto is not supported

8 | 9 | 10 | 18 | 19 | -------------------------------------------------------------------------------- /examples/snow/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Let it snow with Zepto.js 5 | 6 | 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Tweet 19 | 20 | 21 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /examples/touch_events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 20 | 21 | 31 | -------------------------------------------------------------------------------- /make: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | require 'shelljs/make' 3 | fs = require 'fs' 4 | 5 | version = '1.0' 6 | zepto_js = 'dist/zepto.js' 7 | zepto_min = 'dist/zepto.min.js' 8 | zepto_gz = 'dist/zepto.min.gz' 9 | 10 | port = 3999 11 | root = __dirname + '/' 12 | 13 | target.all = -> 14 | target[zepto_js]() 15 | target.test() 16 | 17 | ## TASKS ## 18 | 19 | target.test = -> 20 | test_app = require './test/server' 21 | server = test_app.listen port 22 | exec "phantomjs test/runner.coffee 'http://localhost:#{port}/'", (code) -> 23 | server.close -> exit(code) 24 | 25 | target[zepto_js] = -> 26 | target.build() unless test('-e', zepto_js) 27 | 28 | target[zepto_min] = -> 29 | target.minify() if stale(zepto_min, zepto_js) 30 | 31 | target[zepto_gz] = -> 32 | target.compress() if stale(zepto_gz, zepto_min) 33 | 34 | target.dist = -> 35 | target.build() 36 | target.minify() 37 | target.compress() 38 | 39 | target.build = -> 40 | cd __dirname 41 | mkdir '-p', 'dist' 42 | modules = (env['MODULES'] || 'polyfill zepto detect event ajax form fx').split(' ') 43 | module_files = ( "src/#{module}.js" for module in modules ) 44 | intro = "/* Zepto #{describe_version()} - #{modules.join(' ')} - zeptojs.com/license */\n" 45 | dist = intro + cat(module_files).replace(/^\/[\/*].*$/mg, '').replace(/\n{3,}/g, "\n\n") 46 | dist.to(zepto_js) 47 | report_size(zepto_js) 48 | 49 | target.minify = -> 50 | target.build() unless test('-e', zepto_js) 51 | zepto_code = cat(zepto_js) 52 | intro = zepto_code.slice(0, zepto_code.indexOf("\n") + 1) 53 | (intro + minify(zepto_code)).to(zepto_min) 54 | report_size(zepto_min) 55 | 56 | target.compress = -> 57 | gzip = require('zlib').createGzip() 58 | inp = fs.createReadStream(zepto_min) 59 | out = fs.createWriteStream(zepto_gz) 60 | inp.pipe(gzip).pipe(out) 61 | out.on 'close', -> 62 | report_size(zepto_gz) 63 | factor = fsize(zepto_js) / fsize(zepto_gz) 64 | echo "compression factor: #{format_number(factor)}" 65 | 66 | ## HELPERS ## 67 | 68 | stale = (file, source) -> 69 | target[source]() 70 | !test('-e', file) || mtime(file) < mtime(source) 71 | 72 | mtime = (file) -> 73 | fs.statSync(file).mtime.getTime() 74 | 75 | fsize = (file) -> 76 | fs.statSync(file).size 77 | 78 | format_number = (size, precision = 1) -> 79 | factor = Math.pow(10, precision) 80 | decimal = Math.round(size * factor) % factor 81 | parseInt(size) + "." + decimal 82 | 83 | report_size = (file) -> 84 | echo "#{file}: #{format_number(fsize(file) / 1024)} KiB" 85 | 86 | describe_version = -> 87 | desc = exec "git --git-dir='#{root + '.git'}' describe --tags HEAD", silent: true 88 | if desc.code is 0 then desc.output.replace(/\s+$/, '') else version 89 | 90 | minify = (source_code) -> 91 | uglify = require('uglify-js') 92 | ast = uglify.parser.parse(source_code) 93 | ast = uglify.uglify.ast_mangle(ast) 94 | ast = uglify.uglify.ast_squeeze(ast) 95 | uglify.uglify.gen_code(ast) 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zepto" 3 | , "version": "1.0.0" 4 | , "homepage": "http://zeptojs.com" 5 | , "scripts": { 6 | "test": "coffee make test" 7 | , "dist": "coffee make dist" 8 | , "start": "coffee test/server.coffee" 9 | } 10 | , "repository": { 11 | "type": "git" 12 | , "url": "https://github.com/madrobby/zepto.git" 13 | } 14 | , "licenses": [ 15 | { 16 | "type": "MIT" 17 | , "url": "http://zeptojs.com/license/" 18 | } 19 | ] 20 | , "devDependencies": { 21 | "uglify-js": "1.2.6" 22 | , "express": "3.1.x" 23 | , "coffee-script": "1.5.x" 24 | , "shelljs": "0.1.x" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /script/guard: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -z "$BUNDLE_GEMFILE" ]; then 3 | export BUNDLE_GEMFILE=shutup 4 | fi 5 | 6 | exec guard --no-notify "$@" 7 | -------------------------------------------------------------------------------- /script/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | port=3999 3 | pidfile=test/server.pid 4 | 5 | ./node_modules/.bin/coffee test/server.coffee $port & 6 | pid=$! 7 | 8 | terminate_test_server() { 9 | kill $pid 10 | } 11 | trap terminate_test_server EXIT 12 | 13 | phantomjs test/runner.coffee "http://localhost:${port}/" "$@" 14 | exit $? 15 | -------------------------------------------------------------------------------- /src/ajax.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | var jsonpID = 0, 7 | document = window.document, 8 | key, 9 | name, 10 | rscript = /)<[^<]*)*<\/script>/gi, 11 | scriptTypeRE = /^(?:text|application)\/javascript/i, 12 | xmlTypeRE = /^(?:text|application)\/xml/i, 13 | jsonType = 'application/json', 14 | htmlType = 'text/html', 15 | blankRE = /^\s*$/ 16 | 17 | // trigger a custom event and return false if it was cancelled 18 | function triggerAndReturn(context, eventName, data) { 19 | var event = $.Event(eventName) 20 | $(context).trigger(event, data) 21 | return !event.defaultPrevented 22 | } 23 | 24 | // trigger an Ajax "global" event 25 | function triggerGlobal(settings, context, eventName, data) { 26 | if (settings.global) return triggerAndReturn(context || document, eventName, data) 27 | } 28 | 29 | // Number of active Ajax requests 30 | $.active = 0 31 | 32 | function ajaxStart(settings) { 33 | if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart') 34 | } 35 | function ajaxStop(settings) { 36 | if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop') 37 | } 38 | 39 | // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable 40 | function ajaxBeforeSend(xhr, settings) { 41 | var context = settings.context 42 | if (settings.beforeSend.call(context, xhr, settings) === false || 43 | triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false) 44 | return false 45 | 46 | triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]) 47 | } 48 | function ajaxSuccess(data, xhr, settings) { 49 | var context = settings.context, status = 'success' 50 | settings.success.call(context, data, status, xhr) 51 | triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]) 52 | ajaxComplete(status, xhr, settings) 53 | } 54 | // type: "timeout", "error", "abort", "parsererror" 55 | function ajaxError(error, type, xhr, settings) { 56 | var context = settings.context 57 | settings.error.call(context, xhr, type, error) 58 | triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error]) 59 | ajaxComplete(type, xhr, settings) 60 | } 61 | // status: "success", "notmodified", "error", "timeout", "abort", "parsererror" 62 | function ajaxComplete(status, xhr, settings) { 63 | var context = settings.context 64 | settings.complete.call(context, xhr, status) 65 | triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]) 66 | ajaxStop(settings) 67 | } 68 | 69 | // Empty function, used as default callback 70 | function empty() {} 71 | 72 | $.ajaxJSONP = function(options){ 73 | if (!('type' in options)) return $.ajax(options) 74 | 75 | var callbackName = 'jsonp' + (++jsonpID), 76 | script = document.createElement('script'), 77 | cleanup = function() { 78 | clearTimeout(abortTimeout) 79 | $(script).remove() 80 | delete window[callbackName] 81 | }, 82 | abort = function(type){ 83 | cleanup() 84 | // In case of manual abort or timeout, keep an empty function as callback 85 | // so that the SCRIPT tag that eventually loads won't result in an error. 86 | if (!type || type == 'timeout') window[callbackName] = empty 87 | ajaxError(null, type || 'abort', xhr, options) 88 | }, 89 | xhr = { abort: abort }, abortTimeout 90 | 91 | if (ajaxBeforeSend(xhr, options) === false) { 92 | abort('abort') 93 | return false 94 | } 95 | 96 | window[callbackName] = function(data){ 97 | cleanup() 98 | ajaxSuccess(data, xhr, options) 99 | } 100 | 101 | script.onerror = function() { abort('error') } 102 | 103 | script.src = options.url.replace(/=\?/, '=' + callbackName) 104 | $('head').append(script) 105 | 106 | if (options.timeout > 0) abortTimeout = setTimeout(function(){ 107 | abort('timeout') 108 | }, options.timeout) 109 | 110 | return xhr 111 | } 112 | 113 | $.ajaxSettings = { 114 | // Default type of request 115 | type: 'GET', 116 | // Callback that is executed before request 117 | beforeSend: empty, 118 | // Callback that is executed if the request succeeds 119 | success: empty, 120 | // Callback that is executed the the server drops error 121 | error: empty, 122 | // Callback that is executed on request complete (both: error and success) 123 | complete: empty, 124 | // The context for the callbacks 125 | context: null, 126 | // Whether to trigger "global" Ajax events 127 | global: true, 128 | // Transport 129 | xhr: function () { 130 | return new window.XMLHttpRequest() 131 | }, 132 | // MIME types mapping 133 | accepts: { 134 | script: 'text/javascript, application/javascript', 135 | json: jsonType, 136 | xml: 'application/xml, text/xml', 137 | html: htmlType, 138 | text: 'text/plain' 139 | }, 140 | // Whether the request is to another domain 141 | crossDomain: false, 142 | // Default timeout 143 | timeout: 0, 144 | // Whether data should be serialized to string 145 | processData: true, 146 | // Whether the browser should be allowed to cache GET responses 147 | cache: true 148 | } 149 | 150 | function mimeToDataType(mime) { 151 | if (mime) mime = mime.split(';', 2)[0] 152 | return mime && ( mime == htmlType ? 'html' : 153 | mime == jsonType ? 'json' : 154 | scriptTypeRE.test(mime) ? 'script' : 155 | xmlTypeRE.test(mime) && 'xml' ) || 'text' 156 | } 157 | 158 | function appendQuery(url, query) { 159 | return (url + '&' + query).replace(/[&?]{1,2}/, '?') 160 | } 161 | 162 | // serialize payload and append it to the URL for GET requests 163 | function serializeData(options) { 164 | if (options.processData && options.data && $.type(options.data) != "string") 165 | options.data = $.param(options.data, options.traditional) 166 | if (options.data && (!options.type || options.type.toUpperCase() == 'GET')) 167 | options.url = appendQuery(options.url, options.data) 168 | } 169 | 170 | $.ajax = function(options){ 171 | var settings = $.extend({}, options || {}) 172 | for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key] 173 | 174 | ajaxStart(settings) 175 | 176 | if (!settings.crossDomain) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) && 177 | RegExp.$2 != window.location.host 178 | 179 | if (!settings.url) settings.url = window.location.toString() 180 | serializeData(settings) 181 | if (settings.cache === false) settings.url = appendQuery(settings.url, '_=' + Date.now()) 182 | 183 | var dataType = settings.dataType, hasPlaceholder = /=\?/.test(settings.url) 184 | if (dataType == 'jsonp' || hasPlaceholder) { 185 | if (!hasPlaceholder) settings.url = appendQuery(settings.url, 'callback=?') 186 | return $.ajaxJSONP(settings) 187 | } 188 | 189 | var mime = settings.accepts[dataType], 190 | baseHeaders = { }, 191 | protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol, 192 | xhr = settings.xhr(), abortTimeout 193 | 194 | if (!settings.crossDomain) baseHeaders['X-Requested-With'] = 'XMLHttpRequest' 195 | if (mime) { 196 | baseHeaders['Accept'] = mime 197 | if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0] 198 | xhr.overrideMimeType && xhr.overrideMimeType(mime) 199 | } 200 | if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET')) 201 | baseHeaders['Content-Type'] = (settings.contentType || 'application/x-www-form-urlencoded') 202 | settings.headers = $.extend(baseHeaders, settings.headers || {}) 203 | 204 | xhr.onreadystatechange = function(){ 205 | if (xhr.readyState == 4) { 206 | xhr.onreadystatechange = empty; 207 | clearTimeout(abortTimeout) 208 | var result, error = false 209 | if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) { 210 | dataType = dataType || mimeToDataType(xhr.getResponseHeader('content-type')) 211 | result = xhr.responseText 212 | 213 | try { 214 | // http://perfectionkills.com/global-eval-what-are-the-options/ 215 | if (dataType == 'script') (1,eval)(result) 216 | else if (dataType == 'xml') result = xhr.responseXML 217 | else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result) 218 | } catch (e) { error = e } 219 | 220 | if (error) ajaxError(error, 'parsererror', xhr, settings) 221 | else ajaxSuccess(result, xhr, settings) 222 | } else { 223 | ajaxError(null, xhr.status ? 'error' : 'abort', xhr, settings) 224 | } 225 | } 226 | } 227 | 228 | var async = 'async' in settings ? settings.async : true 229 | xhr.open(settings.type, settings.url, async) 230 | 231 | for (name in settings.headers) xhr.setRequestHeader(name, settings.headers[name]) 232 | 233 | if (ajaxBeforeSend(xhr, settings) === false) { 234 | xhr.abort() 235 | return false 236 | } 237 | 238 | if (settings.timeout > 0) abortTimeout = setTimeout(function(){ 239 | xhr.onreadystatechange = empty 240 | xhr.abort() 241 | ajaxError(null, 'timeout', xhr, settings) 242 | }, settings.timeout) 243 | 244 | // avoid sending empty string (#319) 245 | xhr.send(settings.data ? settings.data : null) 246 | return xhr 247 | } 248 | 249 | // handle optional data/success arguments 250 | function parseArguments(url, data, success, dataType) { 251 | var hasData = !$.isFunction(data) 252 | return { 253 | url: url, 254 | data: hasData ? data : undefined, 255 | success: !hasData ? data : $.isFunction(success) ? success : undefined, 256 | dataType: hasData ? dataType || success : success 257 | } 258 | } 259 | 260 | $.get = function(url, data, success, dataType){ 261 | return $.ajax(parseArguments.apply(null, arguments)) 262 | } 263 | 264 | $.post = function(url, data, success, dataType){ 265 | var options = parseArguments.apply(null, arguments) 266 | options.type = 'POST' 267 | return $.ajax(options) 268 | } 269 | 270 | $.getJSON = function(url, data, success){ 271 | var options = parseArguments.apply(null, arguments) 272 | options.dataType = 'json' 273 | return $.ajax(options) 274 | } 275 | 276 | $.fn.load = function(url, data, success){ 277 | if (!this.length) return this 278 | var self = this, parts = url.split(/\s/), selector, 279 | options = parseArguments(url, data, success), 280 | callback = options.success 281 | if (parts.length > 1) options.url = parts[0], selector = parts[1] 282 | options.success = function(response){ 283 | self.html(selector ? 284 | $('
').html(response.replace(rscript, "")).find(selector) 285 | : response) 286 | callback && callback.apply(self, arguments) 287 | } 288 | $.ajax(options) 289 | return this 290 | } 291 | 292 | var escape = encodeURIComponent 293 | 294 | function serialize(params, obj, traditional, scope){ 295 | var type, array = $.isArray(obj) 296 | $.each(obj, function(key, value) { 297 | type = $.type(value) 298 | if (scope) key = traditional ? scope : scope + '[' + (array ? '' : key) + ']' 299 | // handle data in serializeArray() format 300 | if (!scope && array) params.add(value.name, value.value) 301 | // recurse into nested objects 302 | else if (type == "array" || (!traditional && type == "object")) 303 | serialize(params, value, traditional, key) 304 | else params.add(key, value) 305 | }) 306 | } 307 | 308 | $.param = function(obj, traditional){ 309 | var params = [] 310 | params.add = function(k, v){ this.push(escape(k) + '=' + escape(v)) } 311 | serialize(params, obj, traditional) 312 | return params.join('&').replace(/%20/g, '+') 313 | } 314 | })(Zepto) 315 | -------------------------------------------------------------------------------- /src/assets.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | var cache = [], timeout 7 | 8 | $.fn.remove = function(){ 9 | return this.each(function(){ 10 | if(this.parentNode){ 11 | if(this.tagName === 'IMG'){ 12 | cache.push(this) 13 | this.src = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' 14 | if (timeout) clearTimeout(timeout) 15 | timeout = setTimeout(function(){ cache = [] }, 60000) 16 | } 17 | this.parentNode.removeChild(this) 18 | } 19 | }) 20 | } 21 | })(Zepto) 22 | -------------------------------------------------------------------------------- /src/data.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | // The following code is heavily inspired by jQuery's $.fn.data() 6 | 7 | ;(function($) { 8 | var data = {}, dataAttr = $.fn.data, camelize = $.camelCase, 9 | exp = $.expando = 'Zepto' + (+new Date()) 10 | 11 | // Get value from node: 12 | // 1. first try key as given, 13 | // 2. then try camelized key, 14 | // 3. fall back to reading "data-*" attribute. 15 | function getData(node, name) { 16 | var id = node[exp], store = id && data[id] 17 | if (name === undefined) return store || setData(node) 18 | else { 19 | if (store) { 20 | if (name in store) return store[name] 21 | var camelName = camelize(name) 22 | if (camelName in store) return store[camelName] 23 | } 24 | return dataAttr.call($(node), name) 25 | } 26 | } 27 | 28 | // Store value under camelized key on node 29 | function setData(node, name, value) { 30 | var id = node[exp] || (node[exp] = ++$.uuid), 31 | store = data[id] || (data[id] = attributeData(node)) 32 | if (name !== undefined) store[camelize(name)] = value 33 | return store 34 | } 35 | 36 | // Read all "data-*" attributes from a node 37 | function attributeData(node) { 38 | var store = {} 39 | $.each(node.attributes, function(i, attr){ 40 | if (attr.name.indexOf('data-') == 0) 41 | store[camelize(attr.name.replace('data-', ''))] = 42 | $.zepto.deserializeValue(attr.value) 43 | }) 44 | return store 45 | } 46 | 47 | $.fn.data = function(name, value) { 48 | return value === undefined ? 49 | // set multiple values via object 50 | $.isPlainObject(name) ? 51 | this.each(function(i, node){ 52 | $.each(name, function(key, value){ setData(node, key, value) }) 53 | }) : 54 | // get value from first element 55 | this.length == 0 ? undefined : getData(this[0], name) : 56 | // set value on all elements 57 | this.each(function(){ setData(this, name, value) }) 58 | } 59 | 60 | $.fn.removeData = function(names) { 61 | if (typeof names == 'string') names = names.split(/\s+/) 62 | return this.each(function(){ 63 | var id = this[exp], store = id && data[id] 64 | if (store) $.each(names, function(){ delete store[camelize(this)] }) 65 | }) 66 | } 67 | })(Zepto) 68 | -------------------------------------------------------------------------------- /src/detect.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | function detect(ua){ 7 | var os = this.os = {}, browser = this.browser = {}, 8 | webkit = ua.match(/WebKit\/([\d.]+)/), 9 | android = ua.match(/(Android)\s+([\d.]+)/), 10 | ipad = ua.match(/(iPad).*OS\s([\d_]+)/), 11 | iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/), 12 | webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/), 13 | touchpad = webos && ua.match(/TouchPad/), 14 | kindle = ua.match(/Kindle\/([\d.]+)/), 15 | silk = ua.match(/Silk\/([\d._]+)/), 16 | blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/), 17 | bb10 = ua.match(/(BB10).*Version\/([\d.]+)/), 18 | rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/), 19 | playbook = ua.match(/PlayBook/), 20 | chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/), 21 | firefox = ua.match(/Firefox\/([\d.]+)/) 22 | 23 | // Todo: clean this up with a better OS/browser seperation: 24 | // - discern (more) between multiple browsers on android 25 | // - decide if kindle fire in silk mode is android or not 26 | // - Firefox on Android doesn't specify the Android version 27 | // - possibly devide in os, device and browser hashes 28 | 29 | if (browser.webkit = !!webkit) browser.version = webkit[1] 30 | 31 | if (android) os.android = true, os.version = android[2] 32 | if (iphone) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.') 33 | if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.') 34 | if (webos) os.webos = true, os.version = webos[2] 35 | if (touchpad) os.touchpad = true 36 | if (blackberry) os.blackberry = true, os.version = blackberry[2] 37 | if (bb10) os.bb10 = true, os.version = bb10[2] 38 | if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2] 39 | if (playbook) browser.playbook = true 40 | if (kindle) os.kindle = true, os.version = kindle[1] 41 | if (silk) browser.silk = true, browser.version = silk[1] 42 | if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true 43 | if (chrome) browser.chrome = true, browser.version = chrome[1] 44 | if (firefox) browser.firefox = true, browser.version = firefox[1] 45 | 46 | os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) || (firefox && ua.match(/Tablet/))) 47 | os.phone = !!(!os.tablet && (android || iphone || webos || blackberry || bb10 || 48 | (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) || (firefox && ua.match(/Mobile/)))) 49 | } 50 | 51 | detect.call($, navigator.userAgent) 52 | // make available to unit tests 53 | $.__detect = detect 54 | 55 | })(Zepto) 56 | -------------------------------------------------------------------------------- /src/event.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | var $$ = $.zepto.qsa, handlers = {}, _zid = 1, specialEvents={}, 7 | hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' } 8 | 9 | specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' 10 | 11 | function zid(element) { 12 | return element._zid || (element._zid = _zid++) 13 | } 14 | function findHandlers(element, event, fn, selector) { 15 | event = parse(event) 16 | if (event.ns) var matcher = matcherFor(event.ns) 17 | return (handlers[zid(element)] || []).filter(function(handler) { 18 | return handler 19 | && (!event.e || handler.e == event.e) 20 | && (!event.ns || matcher.test(handler.ns)) 21 | && (!fn || zid(handler.fn) === zid(fn)) 22 | && (!selector || handler.sel == selector) 23 | }) 24 | } 25 | function parse(event) { 26 | var parts = ('' + event).split('.') 27 | return {e: parts[0], ns: parts.slice(1).sort().join(' ')} 28 | } 29 | function matcherFor(ns) { 30 | return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') 31 | } 32 | 33 | function eachEvent(events, fn, iterator){ 34 | if ($.type(events) != "string") $.each(events, iterator) 35 | else events.split(/\s/).forEach(function(type){ iterator(type, fn) }) 36 | } 37 | 38 | function eventCapture(handler, captureSetting) { 39 | return handler.del && 40 | (handler.e == 'focus' || handler.e == 'blur') || 41 | !!captureSetting 42 | } 43 | 44 | function realEvent(type) { 45 | return hover[type] || type 46 | } 47 | 48 | function add(element, events, fn, selector, getDelegate, capture){ 49 | var id = zid(element), set = (handlers[id] || (handlers[id] = [])) 50 | eachEvent(events, fn, function(event, fn){ 51 | var handler = parse(event) 52 | handler.fn = fn 53 | handler.sel = selector 54 | // emulate mouseenter, mouseleave 55 | if (handler.e in hover) fn = function(e){ 56 | var related = e.relatedTarget 57 | if (!related || (related !== this && !$.contains(this, related))) 58 | return handler.fn.apply(this, arguments) 59 | } 60 | handler.del = getDelegate && getDelegate(fn, event) 61 | var callback = handler.del || fn 62 | handler.proxy = function (e) { 63 | var result = callback.apply(element, [e].concat(e.data)) 64 | if (result === false) e.preventDefault(), e.stopPropagation() 65 | return result 66 | } 67 | handler.i = set.length 68 | set.push(handler) 69 | element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 70 | }) 71 | } 72 | function remove(element, events, fn, selector, capture){ 73 | var id = zid(element) 74 | eachEvent(events || '', fn, function(event, fn){ 75 | findHandlers(element, event, fn, selector).forEach(function(handler){ 76 | delete handlers[id][handler.i] 77 | element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 78 | }) 79 | }) 80 | } 81 | 82 | $.event = { add: add, remove: remove } 83 | 84 | $.proxy = function(fn, context) { 85 | if ($.isFunction(fn)) { 86 | var proxyFn = function(){ return fn.apply(context, arguments) } 87 | proxyFn._zid = zid(fn) 88 | return proxyFn 89 | } else if (typeof context == 'string') { 90 | return $.proxy(fn[context], fn) 91 | } else { 92 | throw new TypeError("expected function") 93 | } 94 | } 95 | 96 | $.fn.bind = function(event, callback){ 97 | return this.each(function(){ 98 | add(this, event, callback) 99 | }) 100 | } 101 | $.fn.unbind = function(event, callback){ 102 | return this.each(function(){ 103 | remove(this, event, callback) 104 | }) 105 | } 106 | $.fn.one = function(event, callback){ 107 | return this.each(function(i, element){ 108 | add(this, event, callback, null, function(fn, type){ 109 | return function(){ 110 | var result = fn.apply(element, arguments) 111 | remove(element, type, fn) 112 | return result 113 | } 114 | }) 115 | }) 116 | } 117 | 118 | var returnTrue = function(){return true}, 119 | returnFalse = function(){return false}, 120 | ignoreProperties = /^([A-Z]|layer[XY]$)/, 121 | eventMethods = { 122 | preventDefault: 'isDefaultPrevented', 123 | stopImmediatePropagation: 'isImmediatePropagationStopped', 124 | stopPropagation: 'isPropagationStopped' 125 | } 126 | function createProxy(event) { 127 | var key, proxy = { originalEvent: event } 128 | for (key in event) 129 | if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] 130 | 131 | $.each(eventMethods, function(name, predicate) { 132 | proxy[name] = function(){ 133 | this[predicate] = returnTrue 134 | return event[name].apply(event, arguments) 135 | } 136 | proxy[predicate] = returnFalse 137 | }) 138 | return proxy 139 | } 140 | 141 | // emulates the 'defaultPrevented' property for browsers that have none 142 | function fix(event) { 143 | if (!('defaultPrevented' in event)) { 144 | event.defaultPrevented = false 145 | var prevent = event.preventDefault 146 | event.preventDefault = function() { 147 | this.defaultPrevented = true 148 | prevent.call(this) 149 | } 150 | } 151 | } 152 | 153 | $.fn.delegate = function(selector, event, callback){ 154 | return this.each(function(i, element){ 155 | add(element, event, callback, selector, function(fn){ 156 | return function(e){ 157 | var evt, match = $(e.target).closest(selector, element).get(0) 158 | if (match) { 159 | evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) 160 | return fn.apply(match, [evt].concat([].slice.call(arguments, 1))) 161 | } 162 | } 163 | }) 164 | }) 165 | } 166 | $.fn.undelegate = function(selector, event, callback){ 167 | return this.each(function(){ 168 | remove(this, event, callback, selector) 169 | }) 170 | } 171 | 172 | $.fn.live = function(event, callback){ 173 | $(document.body).delegate(this.selector, event, callback) 174 | return this 175 | } 176 | $.fn.die = function(event, callback){ 177 | $(document.body).undelegate(this.selector, event, callback) 178 | return this 179 | } 180 | 181 | $.fn.on = function(event, selector, callback){ 182 | return !selector || $.isFunction(selector) ? 183 | this.bind(event, selector || callback) : this.delegate(selector, event, callback) 184 | } 185 | $.fn.off = function(event, selector, callback){ 186 | return !selector || $.isFunction(selector) ? 187 | this.unbind(event, selector || callback) : this.undelegate(selector, event, callback) 188 | } 189 | 190 | $.fn.trigger = function(event, data){ 191 | if (typeof event == 'string' || $.isPlainObject(event)) event = $.Event(event) 192 | fix(event) 193 | event.data = data 194 | return this.each(function(){ 195 | // items in the collection might not be DOM elements 196 | // (todo: possibly support events on plain old objects) 197 | if('dispatchEvent' in this) this.dispatchEvent(event) 198 | }) 199 | } 200 | 201 | // triggers event handlers on current element just as if an event occurred, 202 | // doesn't trigger an actual event, doesn't bubble 203 | $.fn.triggerHandler = function(event, data){ 204 | var e, result 205 | this.each(function(i, element){ 206 | e = createProxy(typeof event == 'string' ? $.Event(event) : event) 207 | e.data = data 208 | e.target = element 209 | $.each(findHandlers(element, event.type || event), function(i, handler){ 210 | result = handler.proxy(e) 211 | if (e.isImmediatePropagationStopped()) return false 212 | }) 213 | }) 214 | return result 215 | } 216 | 217 | // shortcut methods for `.bind(event, fn)` for each event type 218 | ;('focusin focusout load resize scroll unload click dblclick '+ 219 | 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ 220 | 'change select keydown keypress keyup error').split(' ').forEach(function(event) { 221 | $.fn[event] = function(callback) { 222 | return callback ? 223 | this.bind(event, callback) : 224 | this.trigger(event) 225 | } 226 | }) 227 | 228 | ;['focus', 'blur'].forEach(function(name) { 229 | $.fn[name] = function(callback) { 230 | if (callback) this.bind(name, callback) 231 | else this.each(function(){ 232 | try { this[name]() } 233 | catch(e) {} 234 | }) 235 | return this 236 | } 237 | }) 238 | 239 | $.Event = function(type, props) { 240 | if (typeof type != 'string') props = type, type = props.type 241 | var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true 242 | if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) 243 | event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null) 244 | event.isDefaultPrevented = function(){ return this.defaultPrevented } 245 | return event 246 | } 247 | 248 | })(Zepto) 249 | -------------------------------------------------------------------------------- /src/form.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function ($) { 6 | $.fn.serializeArray = function () { 7 | var result = [], el 8 | $( Array.prototype.slice.call(this.get(0).elements) ).each(function () { 9 | el = $(this) 10 | var type = el.attr('type') 11 | if (this.nodeName.toLowerCase() != 'fieldset' && 12 | !this.disabled && type != 'submit' && type != 'reset' && type != 'button' && 13 | ((type != 'radio' && type != 'checkbox') || this.checked)) 14 | result.push({ 15 | name: el.attr('name'), 16 | value: el.val() 17 | }) 18 | }) 19 | return result 20 | } 21 | 22 | $.fn.serialize = function () { 23 | var result = [] 24 | this.serializeArray().forEach(function (elm) { 25 | result.push( encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value) ) 26 | }) 27 | return result.join('&') 28 | } 29 | 30 | $.fn.submit = function (callback) { 31 | if (callback) this.bind('submit', callback) 32 | else if (this.length) { 33 | var event = $.Event('submit') 34 | this.eq(0).trigger(event) 35 | if (!event.defaultPrevented) this.get(0).submit() 36 | } 37 | return this 38 | } 39 | 40 | })(Zepto) 41 | -------------------------------------------------------------------------------- /src/fx.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($, undefined){ 6 | var prefix = '', eventPrefix, endEventName, endAnimationName, 7 | vendors = { Webkit: 'webkit', Moz: '', O: 'o', ms: 'MS' }, 8 | document = window.document, testEl = document.createElement('div'), 9 | supportedTransforms = /^((translate|rotate|scale)(X|Y|Z|3d)?|matrix(3d)?|perspective|skew(X|Y)?)$/i, 10 | transform, 11 | transitionProperty, transitionDuration, transitionTiming, 12 | animationName, animationDuration, animationTiming, 13 | cssReset = {} 14 | 15 | function dasherize(str) { return downcase(str.replace(/([a-z])([A-Z])/, '$1-$2')) } 16 | function downcase(str) { return str.toLowerCase() } 17 | function normalizeEvent(name) { return eventPrefix ? eventPrefix + name : downcase(name) } 18 | 19 | $.each(vendors, function(vendor, event){ 20 | if (testEl.style[vendor + 'TransitionProperty'] !== undefined) { 21 | prefix = '-' + downcase(vendor) + '-' 22 | eventPrefix = event 23 | return false 24 | } 25 | }) 26 | 27 | transform = prefix + 'transform' 28 | cssReset[transitionProperty = prefix + 'transition-property'] = 29 | cssReset[transitionDuration = prefix + 'transition-duration'] = 30 | cssReset[transitionTiming = prefix + 'transition-timing-function'] = 31 | cssReset[animationName = prefix + 'animation-name'] = 32 | cssReset[animationDuration = prefix + 'animation-duration'] = 33 | cssReset[animationTiming = prefix + 'animation-timing-function'] = '' 34 | 35 | $.fx = { 36 | off: (eventPrefix === undefined && testEl.style.transitionProperty === undefined), 37 | speeds: { _default: 400, fast: 200, slow: 600 }, 38 | cssPrefix: prefix, 39 | transitionEnd: normalizeEvent('TransitionEnd'), 40 | animationEnd: normalizeEvent('AnimationEnd') 41 | } 42 | 43 | $.fn.animate = function(properties, duration, ease, callback){ 44 | if ($.isPlainObject(duration)) 45 | ease = duration.easing, callback = duration.complete, duration = duration.duration 46 | if (duration) duration = (typeof duration == 'number' ? duration : 47 | ($.fx.speeds[duration] || $.fx.speeds._default)) / 1000 48 | return this.anim(properties, duration, ease, callback) 49 | } 50 | 51 | $.fn.anim = function(properties, duration, ease, callback){ 52 | var key, cssValues = {}, cssProperties, transforms = '', 53 | that = this, wrappedCallback, endEvent = $.fx.transitionEnd 54 | 55 | if (duration === undefined) duration = 0.4 56 | if ($.fx.off) duration = 0 57 | 58 | if (typeof properties == 'string') { 59 | // keyframe animation 60 | cssValues[animationName] = properties 61 | cssValues[animationDuration] = duration + 's' 62 | cssValues[animationTiming] = (ease || 'linear') 63 | endEvent = $.fx.animationEnd 64 | } else { 65 | cssProperties = [] 66 | // CSS transitions 67 | for (key in properties) 68 | if (supportedTransforms.test(key)) transforms += key + '(' + properties[key] + ') ' 69 | else cssValues[key] = properties[key], cssProperties.push(dasherize(key)) 70 | 71 | if (transforms) cssValues[transform] = transforms, cssProperties.push(transform) 72 | if (duration > 0 && typeof properties === 'object') { 73 | cssValues[transitionProperty] = cssProperties.join(', ') 74 | cssValues[transitionDuration] = duration + 's' 75 | cssValues[transitionTiming] = (ease || 'linear') 76 | } 77 | } 78 | 79 | wrappedCallback = function(event){ 80 | if (typeof event !== 'undefined') { 81 | if (event.target !== event.currentTarget) return // makes sure the event didn't bubble from "below" 82 | $(event.target).unbind(endEvent, wrappedCallback) 83 | } 84 | $(this).css(cssReset) 85 | callback && callback.call(this) 86 | } 87 | if (duration > 0) this.bind(endEvent, wrappedCallback) 88 | 89 | // trigger page reflow so new elements can animate 90 | this.size() && this.get(0).clientLeft 91 | 92 | this.css(cssValues) 93 | 94 | if (duration <= 0) setTimeout(function() { 95 | that.each(function(){ wrappedCallback.call(this) }) 96 | }, 0) 97 | 98 | return this 99 | } 100 | 101 | testEl = null 102 | })(Zepto) 103 | -------------------------------------------------------------------------------- /src/fx_methods.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($, undefined){ 6 | var document = window.document, docElem = document.documentElement, 7 | origShow = $.fn.show, origHide = $.fn.hide, origToggle = $.fn.toggle 8 | 9 | function anim(el, speed, opacity, scale, callback) { 10 | if (typeof speed == 'function' && !callback) callback = speed, speed = undefined 11 | var props = { opacity: opacity } 12 | if (scale) { 13 | props.scale = scale 14 | el.css($.fx.cssPrefix + 'transform-origin', '0 0') 15 | } 16 | return el.animate(props, speed, null, callback) 17 | } 18 | 19 | function hide(el, speed, scale, callback) { 20 | return anim(el, speed, 0, scale, function(){ 21 | origHide.call($(this)) 22 | callback && callback.call(this) 23 | }) 24 | } 25 | 26 | $.fn.show = function(speed, callback) { 27 | origShow.call(this) 28 | if (speed === undefined) speed = 0 29 | else this.css('opacity', 0) 30 | return anim(this, speed, 1, '1,1', callback) 31 | } 32 | 33 | $.fn.hide = function(speed, callback) { 34 | if (speed === undefined) return origHide.call(this) 35 | else return hide(this, speed, '0,0', callback) 36 | } 37 | 38 | $.fn.toggle = function(speed, callback) { 39 | if (speed === undefined || typeof speed == 'boolean') 40 | return origToggle.call(this, speed) 41 | else return this.each(function(){ 42 | var el = $(this) 43 | el[el.css('display') == 'none' ? 'show' : 'hide'](speed, callback) 44 | }) 45 | } 46 | 47 | $.fn.fadeTo = function(speed, opacity, callback) { 48 | return anim(this, speed, opacity, null, callback) 49 | } 50 | 51 | $.fn.fadeIn = function(speed, callback) { 52 | var target = this.css('opacity') 53 | if (target > 0) this.css('opacity', 0) 54 | else target = 1 55 | return origShow.call(this).fadeTo(speed, target, callback) 56 | } 57 | 58 | $.fn.fadeOut = function(speed, callback) { 59 | return hide(this, speed, null, callback) 60 | } 61 | 62 | $.fn.fadeToggle = function(speed, callback) { 63 | return this.each(function(){ 64 | var el = $(this) 65 | el[ 66 | (el.css('opacity') == 0 || el.css('display') == 'none') ? 'fadeIn' : 'fadeOut' 67 | ](speed, callback) 68 | }) 69 | } 70 | 71 | })(Zepto) 72 | -------------------------------------------------------------------------------- /src/gesture.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | if ($.os.ios) { 7 | var gesture = {}, gestureTimeout 8 | 9 | function parentIfText(node){ 10 | return 'tagName' in node ? node : node.parentNode 11 | } 12 | 13 | $(document).bind('gesturestart', function(e){ 14 | var now = Date.now(), delta = now - (gesture.last || now) 15 | gesture.target = parentIfText(e.target) 16 | gestureTimeout && clearTimeout(gestureTimeout) 17 | gesture.e1 = e.scale 18 | gesture.last = now 19 | }).bind('gesturechange', function(e){ 20 | gesture.e2 = e.scale 21 | }).bind('gestureend', function(e){ 22 | if (gesture.e2 > 0) { 23 | Math.abs(gesture.e1 - gesture.e2) != 0 && $(gesture.target).trigger('pinch') && 24 | $(gesture.target).trigger('pinch' + (gesture.e1 - gesture.e2 > 0 ? 'In' : 'Out')) 25 | gesture.e1 = gesture.e2 = gesture.last = 0 26 | } else if ('last' in gesture) { 27 | gesture = {} 28 | } 29 | }) 30 | 31 | ;['pinch', 'pinchIn', 'pinchOut'].forEach(function(m){ 32 | $.fn[m] = function(callback){ return this.bind(m, callback) } 33 | }) 34 | } 35 | })(Zepto) 36 | -------------------------------------------------------------------------------- /src/polyfill.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function(undefined){ 6 | if (String.prototype.trim === undefined) // fix for iOS 3.2 7 | String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/g, '') } 8 | 9 | // For iOS 3.x 10 | // from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reduce 11 | if (Array.prototype.reduce === undefined) 12 | Array.prototype.reduce = function(fun){ 13 | if(this === void 0 || this === null) throw new TypeError() 14 | var t = Object(this), len = t.length >>> 0, k = 0, accumulator 15 | if(typeof fun != 'function') throw new TypeError() 16 | if(len == 0 && arguments.length == 1) throw new TypeError() 17 | 18 | if(arguments.length >= 2) 19 | accumulator = arguments[1] 20 | else 21 | do{ 22 | if(k in t){ 23 | accumulator = t[k++] 24 | break 25 | } 26 | if(++k >= len) throw new TypeError() 27 | } while (true) 28 | 29 | while (k < len){ 30 | if(k in t) accumulator = fun.call(undefined, accumulator, t[k], k, t) 31 | k++ 32 | } 33 | return accumulator 34 | } 35 | 36 | })() 37 | -------------------------------------------------------------------------------- /src/selector.js: -------------------------------------------------------------------------------- 1 | ;(function($){ 2 | var zepto = $.zepto, oldQsa = zepto.qsa, oldMatches = zepto.matches 3 | 4 | function visible(elem){ 5 | elem = $(elem) 6 | return !!(elem.width() || elem.height()) && elem.css("display") !== "none" 7 | } 8 | 9 | // Implements a subset from: 10 | // http://api.jquery.com/category/selectors/jquery-selector-extensions/ 11 | // 12 | // Each filter function receives the current index, all nodes in the 13 | // considered set, and a value if there were parentheses. The value 14 | // of `this` is the node currently being considered. The function returns the 15 | // resulting node(s), null, or undefined. 16 | // 17 | // Complex selectors are not supported: 18 | // li:has(label:contains("foo")) + li:has(label:contains("bar")) 19 | // ul.inner:first > li 20 | var filters = $.expr[':'] = { 21 | visible: function(){ if (visible(this)) return this }, 22 | hidden: function(){ if (!visible(this)) return this }, 23 | selected: function(){ if (this.selected) return this }, 24 | checked: function(){ if (this.checked) return this }, 25 | parent: function(){ return this.parentNode }, 26 | first: function(idx){ if (idx === 0) return this }, 27 | last: function(idx, nodes){ if (idx === nodes.length - 1) return this }, 28 | eq: function(idx, _, value){ if (idx === value) return this }, 29 | contains: function(idx, _, text){ if ($(this).text().indexOf(text) > -1) return this }, 30 | has: function(idx, _, sel){ if (zepto.qsa(this, sel).length) return this } 31 | } 32 | 33 | var filterRe = new RegExp('(.*):(\\w+)(?:\\(([^)]+)\\))?$\\s*'), 34 | childRe = /^\s*>/, 35 | classTag = 'Zepto' + (+new Date()) 36 | 37 | function process(sel, fn) { 38 | // quote the hash in `a[href^=#]` expression 39 | sel = sel.replace(/=#\]/g, '="#"]') 40 | var filter, arg, match = filterRe.exec(sel) 41 | if (match && match[2] in filters) { 42 | filter = filters[match[2]], arg = match[3] 43 | sel = match[1] 44 | if (arg) { 45 | var num = Number(arg) 46 | if (isNaN(num)) arg = arg.replace(/^["']|["']$/g, '') 47 | else arg = num 48 | } 49 | } 50 | return fn(sel, filter, arg) 51 | } 52 | 53 | zepto.qsa = function(node, selector) { 54 | return process(selector, function(sel, filter, arg){ 55 | try { 56 | var taggedParent 57 | if (!sel && filter) sel = '*' 58 | else if (childRe.test(sel)) 59 | // support "> *" child queries by tagging the parent node with a 60 | // unique class and prepending that classname onto the selector 61 | taggedParent = $(node).addClass(classTag), sel = '.'+classTag+' '+sel 62 | 63 | var nodes = oldQsa(node, sel) 64 | } catch(e) { 65 | console.error('error performing selector: %o', selector) 66 | throw e 67 | } finally { 68 | if (taggedParent) taggedParent.removeClass(classTag) 69 | } 70 | return !filter ? nodes : 71 | zepto.uniq($.map(nodes, function(n, i){ return filter.call(n, i, nodes, arg) })) 72 | }) 73 | } 74 | 75 | zepto.matches = function(node, selector){ 76 | return process(selector, function(sel, filter, arg){ 77 | return (!sel || oldMatches(node, sel)) && 78 | (!filter || filter.call(node, null, arg) === node) 79 | }) 80 | } 81 | })(Zepto) 82 | -------------------------------------------------------------------------------- /src/stack.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | $.fn.end = function(){ 7 | return this.prevObject || $() 8 | } 9 | 10 | $.fn.andSelf = function(){ 11 | return this.add(this.prevObject || $()) 12 | } 13 | 14 | 'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){ 15 | var fn = $.fn[property] 16 | $.fn[property] = function(){ 17 | var ret = fn.apply(this, arguments) 18 | ret.prevObject = this 19 | return ret 20 | } 21 | }) 22 | })(Zepto) 23 | -------------------------------------------------------------------------------- /src/touch.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | ;(function($){ 6 | var touch = {}, 7 | touchTimeout, tapTimeout, swipeTimeout, 8 | longTapDelay = 750, longTapTimeout 9 | 10 | function parentIfText(node) { 11 | return 'tagName' in node ? node : node.parentNode 12 | } 13 | 14 | function swipeDirection(x1, x2, y1, y2) { 15 | var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2) 16 | return xDelta >= yDelta ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down') 17 | } 18 | 19 | function longTap() { 20 | longTapTimeout = null 21 | if (touch.last) { 22 | touch.el.trigger('longTap') 23 | touch = {} 24 | } 25 | } 26 | 27 | function cancelLongTap() { 28 | if (longTapTimeout) clearTimeout(longTapTimeout) 29 | longTapTimeout = null 30 | } 31 | 32 | function cancelAll() { 33 | if (touchTimeout) clearTimeout(touchTimeout) 34 | if (tapTimeout) clearTimeout(tapTimeout) 35 | if (swipeTimeout) clearTimeout(swipeTimeout) 36 | if (longTapTimeout) clearTimeout(longTapTimeout) 37 | touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null 38 | touch = {} 39 | } 40 | 41 | $(document).ready(function(){ 42 | var now, delta 43 | 44 | $(document.body) 45 | .bind('touchstart', function(e){ 46 | now = Date.now() 47 | delta = now - (touch.last || now) 48 | touch.el = $(parentIfText(e.touches[0].target)) 49 | touchTimeout && clearTimeout(touchTimeout) 50 | touch.x1 = e.touches[0].pageX 51 | touch.y1 = e.touches[0].pageY 52 | if (delta > 0 && delta <= 250) touch.isDoubleTap = true 53 | touch.last = now 54 | longTapTimeout = setTimeout(longTap, longTapDelay) 55 | }) 56 | .bind('touchmove', function(e){ 57 | cancelLongTap() 58 | touch.x2 = e.touches[0].pageX 59 | touch.y2 = e.touches[0].pageY 60 | if (Math.abs(touch.x1 - touch.x2) > 10) 61 | e.preventDefault() 62 | }) 63 | .bind('touchend', function(e){ 64 | cancelLongTap() 65 | 66 | // swipe 67 | if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) || 68 | (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30)) 69 | 70 | swipeTimeout = setTimeout(function() { 71 | touch.el.trigger('swipe') 72 | touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2))) 73 | touch = {} 74 | }, 0) 75 | 76 | // normal tap 77 | else if ('last' in touch) 78 | 79 | // delay by one tick so we can cancel the 'tap' event if 'scroll' fires 80 | // ('tap' fires before 'scroll') 81 | tapTimeout = setTimeout(function() { 82 | 83 | // trigger universal 'tap' with the option to cancelTouch() 84 | // (cancelTouch cancels processing of single vs double taps for faster 'tap' response) 85 | var event = $.Event('tap') 86 | event.cancelTouch = cancelAll 87 | touch.el.trigger(event) 88 | 89 | // trigger double tap immediately 90 | if (touch.isDoubleTap) { 91 | touch.el.trigger('doubleTap') 92 | touch = {} 93 | } 94 | 95 | // trigger single tap after 250ms of inactivity 96 | else { 97 | touchTimeout = setTimeout(function(){ 98 | touchTimeout = null 99 | touch.el.trigger('singleTap') 100 | touch = {} 101 | }, 250) 102 | } 103 | 104 | }, 0) 105 | 106 | }) 107 | .bind('touchcancel', cancelAll) 108 | 109 | $(window).bind('scroll', cancelAll) 110 | }) 111 | 112 | ;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(m){ 113 | $.fn[m] = function(callback){ return this.bind(m, callback) } 114 | }) 115 | })(Zepto) 116 | -------------------------------------------------------------------------------- /src/zepto.js: -------------------------------------------------------------------------------- 1 | // Zepto.js 2 | // (c) 2010-2012 Thomas Fuchs 3 | // Zepto.js may be freely distributed under the MIT license. 4 | 5 | var Zepto = (function() { 6 | var undefined, key, $, classList, emptyArray = [], slice = emptyArray.slice, filter = emptyArray.filter, 7 | document = window.document, 8 | elementDisplay = {}, classCache = {}, 9 | getComputedStyle = document.defaultView.getComputedStyle, 10 | cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 }, 11 | fragmentRE = /^\s*<(\w+|!)[^>]*>/, 12 | tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, 13 | rootNodeRE = /^(?:body|html)$/i, 14 | 15 | // special attributes that should be get/set via method calls 16 | methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'], 17 | 18 | adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ], 19 | table = document.createElement('table'), 20 | tableRow = document.createElement('tr'), 21 | containers = { 22 | 'tr': document.createElement('tbody'), 23 | 'tbody': table, 'thead': table, 'tfoot': table, 24 | 'td': tableRow, 'th': tableRow, 25 | '*': document.createElement('div') 26 | }, 27 | readyRE = /complete|loaded|interactive/, 28 | classSelectorRE = /^\.([\w-]+)$/, 29 | idSelectorRE = /^#([\w-]*)$/, 30 | tagSelectorRE = /^[\w-]+$/, 31 | class2type = {}, 32 | toString = class2type.toString, 33 | zepto = {}, 34 | camelize, uniq, 35 | tempParent = document.createElement('div') 36 | 37 | zepto.matches = function(element, selector) { 38 | if (!element || element.nodeType !== 1) return false 39 | var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector || 40 | element.oMatchesSelector || element.matchesSelector 41 | if (matchesSelector) return matchesSelector.call(element, selector) 42 | // fall back to performing a selector: 43 | var match, parent = element.parentNode, temp = !parent 44 | if (temp) (parent = tempParent).appendChild(element) 45 | match = ~zepto.qsa(parent, selector).indexOf(element) 46 | temp && tempParent.removeChild(element) 47 | return match 48 | } 49 | 50 | function type(obj) { 51 | return obj == null ? String(obj) : 52 | class2type[toString.call(obj)] || "object" 53 | } 54 | 55 | function isFunction(value) { return type(value) == "function" } 56 | function isWindow(obj) { return obj != null && obj == obj.window } 57 | function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE } 58 | function isObject(obj) { return type(obj) == "object" } 59 | function isPlainObject(obj) { 60 | return isObject(obj) && !isWindow(obj) && obj.__proto__ == Object.prototype 61 | } 62 | function isArray(value) { return value instanceof Array } 63 | function likeArray(obj) { return typeof obj.length == 'number' } 64 | 65 | function compact(array) { return filter.call(array, function(item){ return item != null }) } 66 | function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array } 67 | camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) } 68 | function dasherize(str) { 69 | return str.replace(/::/g, '/') 70 | .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') 71 | .replace(/([a-z\d])([A-Z])/g, '$1_$2') 72 | .replace(/_/g, '-') 73 | .toLowerCase() 74 | } 75 | uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) } 76 | 77 | function classRE(name) { 78 | return name in classCache ? 79 | classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')) 80 | } 81 | 82 | function maybeAddPx(name, value) { 83 | return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value 84 | } 85 | 86 | function defaultDisplay(nodeName) { 87 | var element, display 88 | if (!elementDisplay[nodeName]) { 89 | element = document.createElement(nodeName) 90 | document.body.appendChild(element) 91 | display = getComputedStyle(element, '').getPropertyValue("display") 92 | element.parentNode.removeChild(element) 93 | display == "none" && (display = "block") 94 | elementDisplay[nodeName] = display 95 | } 96 | return elementDisplay[nodeName] 97 | } 98 | 99 | function children(element) { 100 | return 'children' in element ? 101 | slice.call(element.children) : 102 | $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node }) 103 | } 104 | 105 | // `$.zepto.fragment` takes a html string and an optional tag name 106 | // to generate DOM nodes nodes from the given html string. 107 | // The generated DOM nodes are returned as an array. 108 | // This function can be overriden in plugins for example to make 109 | // it compatible with browsers that don't support the DOM fully. 110 | zepto.fragment = function(html, name, properties) { 111 | if (html.replace) html = html.replace(tagExpanderRE, "<$1>") 112 | if (name === undefined) name = fragmentRE.test(html) && RegExp.$1 113 | if (!(name in containers)) name = '*' 114 | 115 | var nodes, dom, container = containers[name] 116 | container.innerHTML = '' + html 117 | dom = $.each(slice.call(container.childNodes), function(){ 118 | container.removeChild(this) 119 | }) 120 | if (isPlainObject(properties)) { 121 | nodes = $(dom) 122 | $.each(properties, function(key, value) { 123 | if (methodAttributes.indexOf(key) > -1) nodes[key](value) 124 | else nodes.attr(key, value) 125 | }) 126 | } 127 | return dom 128 | } 129 | 130 | // `$.zepto.Z` swaps out the prototype of the given `dom` array 131 | // of nodes with `$.fn` and thus supplying all the Zepto functions 132 | // to the array. Note that `__proto__` is not supported on Internet 133 | // Explorer. This method can be overriden in plugins. 134 | zepto.Z = function(dom, selector) { 135 | dom = dom || [] 136 | dom.__proto__ = $.fn 137 | dom.selector = selector || '' 138 | return dom 139 | } 140 | 141 | // `$.zepto.isZ` should return `true` if the given object is a Zepto 142 | // collection. This method can be overriden in plugins. 143 | zepto.isZ = function(object) { 144 | return object instanceof zepto.Z 145 | } 146 | 147 | // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and 148 | // takes a CSS selector and an optional context (and handles various 149 | // special cases). 150 | // This method can be overriden in plugins. 151 | zepto.init = function(selector, context) { 152 | // If nothing given, return an empty Zepto collection 153 | if (!selector) return zepto.Z() 154 | // If a function is given, call it when the DOM is ready 155 | else if (isFunction(selector)) return $(document).ready(selector) 156 | // If a Zepto collection is given, juts return it 157 | else if (zepto.isZ(selector)) return selector 158 | else { 159 | var dom 160 | // normalize array if an array of nodes is given 161 | if (isArray(selector)) dom = compact(selector) 162 | // Wrap DOM nodes. If a plain object is given, duplicate it. 163 | else if (isObject(selector)) 164 | dom = [isPlainObject(selector) ? $.extend({}, selector) : selector], selector = null 165 | // If it's a html fragment, create nodes from it 166 | else if (fragmentRE.test(selector)) 167 | dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null 168 | // If there's a context, create a collection on that context first, and select 169 | // nodes from there 170 | else if (context !== undefined) return $(context).find(selector) 171 | // And last but no least, if it's a CSS selector, use it to select nodes. 172 | else dom = zepto.qsa(document, selector) 173 | // create a new Zepto collection from the nodes found 174 | return zepto.Z(dom, selector) 175 | } 176 | } 177 | 178 | // `$` will be the base `Zepto` object. When calling this 179 | // function just call `$.zepto.init, which makes the implementation 180 | // details of selecting nodes and creating Zepto collections 181 | // patchable in plugins. 182 | $ = function(selector, context){ 183 | return zepto.init(selector, context) 184 | } 185 | 186 | function extend(target, source, deep) { 187 | for (key in source) 188 | if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { 189 | if (isPlainObject(source[key]) && !isPlainObject(target[key])) 190 | target[key] = {} 191 | if (isArray(source[key]) && !isArray(target[key])) 192 | target[key] = [] 193 | extend(target[key], source[key], deep) 194 | } 195 | else if (source[key] !== undefined) target[key] = source[key] 196 | } 197 | 198 | // Copy all but undefined properties from one or more 199 | // objects to the `target` object. 200 | $.extend = function(target){ 201 | var deep, args = slice.call(arguments, 1) 202 | if (typeof target == 'boolean') { 203 | deep = target 204 | target = args.shift() 205 | } 206 | args.forEach(function(arg){ extend(target, arg, deep) }) 207 | return target 208 | } 209 | 210 | // `$.zepto.qsa` is Zepto's CSS selector implementation which 211 | // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`. 212 | // This method can be overriden in plugins. 213 | zepto.qsa = function(element, selector){ 214 | var found 215 | return (isDocument(element) && idSelectorRE.test(selector)) ? 216 | ( (found = element.getElementById(RegExp.$1)) ? [found] : [] ) : 217 | (element.nodeType !== 1 && element.nodeType !== 9) ? [] : 218 | slice.call( 219 | classSelectorRE.test(selector) ? element.getElementsByClassName(RegExp.$1) : 220 | tagSelectorRE.test(selector) ? element.getElementsByTagName(selector) : 221 | element.querySelectorAll(selector) 222 | ) 223 | } 224 | 225 | function filtered(nodes, selector) { 226 | return selector == null ? $(nodes) : $(nodes).filter(selector) 227 | } 228 | 229 | $.contains = function(parent, node) { 230 | return parent !== node && parent.contains(node) 231 | } 232 | 233 | function funcArg(context, arg, idx, payload) { 234 | return isFunction(arg) ? arg.call(context, idx, payload) : arg 235 | } 236 | 237 | function setAttribute(node, name, value) { 238 | value == null ? node.removeAttribute(name) : node.setAttribute(name, value) 239 | } 240 | 241 | // access className property while respecting SVGAnimatedString 242 | function className(node, value){ 243 | var klass = node.className, 244 | svg = klass && klass.baseVal !== undefined 245 | 246 | if (value === undefined) return svg ? klass.baseVal : klass 247 | svg ? (klass.baseVal = value) : (node.className = value) 248 | } 249 | 250 | // "true" => true 251 | // "false" => false 252 | // "null" => null 253 | // "42" => 42 254 | // "42.5" => 42.5 255 | // JSON => parse if valid 256 | // String => self 257 | function deserializeValue(value) { 258 | var num 259 | try { 260 | return value ? 261 | value == "true" || 262 | ( value == "false" ? false : 263 | value == "null" ? null : 264 | !isNaN(num = Number(value)) ? num : 265 | /^[\[\{]/.test(value) ? $.parseJSON(value) : 266 | value ) 267 | : value 268 | } catch(e) { 269 | return value 270 | } 271 | } 272 | 273 | $.type = type 274 | $.isFunction = isFunction 275 | $.isWindow = isWindow 276 | $.isArray = isArray 277 | $.isPlainObject = isPlainObject 278 | 279 | $.isEmptyObject = function(obj) { 280 | var name 281 | for (name in obj) return false 282 | return true 283 | } 284 | 285 | $.inArray = function(elem, array, i){ 286 | return emptyArray.indexOf.call(array, elem, i) 287 | } 288 | 289 | $.camelCase = camelize 290 | $.trim = function(str) { 291 | return str == null ? "" : String.prototype.trim.call(str) 292 | } 293 | 294 | // plugin compatibility 295 | $.uuid = 0 296 | $.support = { } 297 | $.expr = { } 298 | 299 | $.map = function(elements, callback){ 300 | var value, values = [], i, key 301 | if (likeArray(elements)) 302 | for (i = 0; i < elements.length; i++) { 303 | value = callback(elements[i], i) 304 | if (value != null) values.push(value) 305 | } 306 | else 307 | for (key in elements) { 308 | value = callback(elements[key], key) 309 | if (value != null) values.push(value) 310 | } 311 | return flatten(values) 312 | } 313 | 314 | $.each = function(elements, callback){ 315 | var i, key 316 | if (likeArray(elements)) { 317 | for (i = 0; i < elements.length; i++) 318 | if (callback.call(elements[i], i, elements[i]) === false) return elements 319 | } else { 320 | for (key in elements) 321 | if (callback.call(elements[key], key, elements[key]) === false) return elements 322 | } 323 | 324 | return elements 325 | } 326 | 327 | $.grep = function(elements, callback){ 328 | return filter.call(elements, callback) 329 | } 330 | 331 | if (window.JSON) $.parseJSON = JSON.parse 332 | 333 | // Populate the class2type map 334 | $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { 335 | class2type[ "[object " + name + "]" ] = name.toLowerCase() 336 | }) 337 | 338 | // Define methods that will be available on all 339 | // Zepto collections 340 | $.fn = { 341 | // Because a collection acts like an array 342 | // copy over these useful array functions. 343 | forEach: emptyArray.forEach, 344 | reduce: emptyArray.reduce, 345 | push: emptyArray.push, 346 | sort: emptyArray.sort, 347 | indexOf: emptyArray.indexOf, 348 | concat: emptyArray.concat, 349 | 350 | // `map` and `slice` in the jQuery API work differently 351 | // from their array counterparts 352 | map: function(fn){ 353 | return $($.map(this, function(el, i){ return fn.call(el, i, el) })) 354 | }, 355 | slice: function(){ 356 | return $(slice.apply(this, arguments)) 357 | }, 358 | 359 | ready: function(callback){ 360 | if (readyRE.test(document.readyState)) callback($) 361 | else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false) 362 | return this 363 | }, 364 | get: function(idx){ 365 | return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length] 366 | }, 367 | toArray: function(){ return this.get() }, 368 | size: function(){ 369 | return this.length 370 | }, 371 | remove: function(){ 372 | return this.each(function(){ 373 | if (this.parentNode != null) 374 | this.parentNode.removeChild(this) 375 | }) 376 | }, 377 | each: function(callback){ 378 | emptyArray.every.call(this, function(el, idx){ 379 | return callback.call(el, idx, el) !== false 380 | }) 381 | return this 382 | }, 383 | filter: function(selector){ 384 | if (isFunction(selector)) return this.not(this.not(selector)) 385 | return $(filter.call(this, function(element){ 386 | return zepto.matches(element, selector) 387 | })) 388 | }, 389 | add: function(selector,context){ 390 | return $(uniq(this.concat($(selector,context)))) 391 | }, 392 | is: function(selector){ 393 | return this.length > 0 && zepto.matches(this[0], selector) 394 | }, 395 | not: function(selector){ 396 | var nodes=[] 397 | if (isFunction(selector) && selector.call !== undefined) 398 | this.each(function(idx){ 399 | if (!selector.call(this,idx)) nodes.push(this) 400 | }) 401 | else { 402 | var excludes = typeof selector == 'string' ? this.filter(selector) : 403 | (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector) 404 | this.forEach(function(el){ 405 | if (excludes.indexOf(el) < 0) nodes.push(el) 406 | }) 407 | } 408 | return $(nodes) 409 | }, 410 | has: function(selector){ 411 | return this.filter(function(){ 412 | return isObject(selector) ? 413 | $.contains(this, selector) : 414 | $(this).find(selector).size() 415 | }) 416 | }, 417 | eq: function(idx){ 418 | return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1) 419 | }, 420 | first: function(){ 421 | var el = this[0] 422 | return el && !isObject(el) ? el : $(el) 423 | }, 424 | last: function(){ 425 | var el = this[this.length - 1] 426 | return el && !isObject(el) ? el : $(el) 427 | }, 428 | find: function(selector){ 429 | var result, $this = this 430 | if (typeof selector == 'object') 431 | result = $(selector).filter(function(){ 432 | var node = this 433 | return emptyArray.some.call($this, function(parent){ 434 | return $.contains(parent, node) 435 | }) 436 | }) 437 | else if (this.length == 1) result = $(zepto.qsa(this[0], selector)) 438 | else result = this.map(function(){ return zepto.qsa(this, selector) }) 439 | return result 440 | }, 441 | closest: function(selector, context){ 442 | var node = this[0], collection = false 443 | if (typeof selector == 'object') collection = $(selector) 444 | while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector))) 445 | node = node !== context && !isDocument(node) && node.parentNode 446 | return $(node) 447 | }, 448 | parents: function(selector){ 449 | var ancestors = [], nodes = this 450 | while (nodes.length > 0) 451 | nodes = $.map(nodes, function(node){ 452 | if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) { 453 | ancestors.push(node) 454 | return node 455 | } 456 | }) 457 | return filtered(ancestors, selector) 458 | }, 459 | parent: function(selector){ 460 | return filtered(uniq(this.pluck('parentNode')), selector) 461 | }, 462 | children: function(selector){ 463 | return filtered(this.map(function(){ return children(this) }), selector) 464 | }, 465 | contents: function() { 466 | return this.map(function() { return slice.call(this.childNodes) }) 467 | }, 468 | siblings: function(selector){ 469 | return filtered(this.map(function(i, el){ 470 | return filter.call(children(el.parentNode), function(child){ return child!==el }) 471 | }), selector) 472 | }, 473 | empty: function(){ 474 | return this.each(function(){ this.innerHTML = '' }) 475 | }, 476 | // `pluck` is borrowed from Prototype.js 477 | pluck: function(property){ 478 | return $.map(this, function(el){ return el[property] }) 479 | }, 480 | show: function(){ 481 | return this.each(function(){ 482 | this.style.display == "none" && (this.style.display = null) 483 | if (getComputedStyle(this, '').getPropertyValue("display") == "none") 484 | this.style.display = defaultDisplay(this.nodeName) 485 | }) 486 | }, 487 | replaceWith: function(newContent){ 488 | return this.before(newContent).remove() 489 | }, 490 | wrap: function(structure){ 491 | var func = isFunction(structure) 492 | if (this[0] && !func) 493 | var dom = $(structure).get(0), 494 | clone = dom.parentNode || this.length > 1 495 | 496 | return this.each(function(index){ 497 | $(this).wrapAll( 498 | func ? structure.call(this, index) : 499 | clone ? dom.cloneNode(true) : dom 500 | ) 501 | }) 502 | }, 503 | wrapAll: function(structure){ 504 | if (this[0]) { 505 | $(this[0]).before(structure = $(structure)) 506 | var children 507 | // drill down to the inmost element 508 | while ((children = structure.children()).length) structure = children.first() 509 | $(structure).append(this) 510 | } 511 | return this 512 | }, 513 | wrapInner: function(structure){ 514 | var func = isFunction(structure) 515 | return this.each(function(index){ 516 | var self = $(this), contents = self.contents(), 517 | dom = func ? structure.call(this, index) : structure 518 | contents.length ? contents.wrapAll(dom) : self.append(dom) 519 | }) 520 | }, 521 | unwrap: function(){ 522 | this.parent().each(function(){ 523 | $(this).replaceWith($(this).children()) 524 | }) 525 | return this 526 | }, 527 | clone: function(){ 528 | return this.map(function(){ return this.cloneNode(true) }) 529 | }, 530 | hide: function(){ 531 | return this.css("display", "none") 532 | }, 533 | toggle: function(setting){ 534 | return this.each(function(){ 535 | var el = $(this) 536 | ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide() 537 | }) 538 | }, 539 | prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') }, 540 | next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') }, 541 | html: function(html){ 542 | return arguments.length === 0 ? 543 | (this.length > 0 ? this[0].innerHTML : null) : 544 | this.each(function(idx){ 545 | var originHtml = this.innerHTML 546 | $(this).empty().append( funcArg(this, html, idx, originHtml) ) 547 | }) 548 | }, 549 | text: function(text){ 550 | return arguments.length === 0 ? 551 | (this.length > 0 ? this[0].textContent : null) : 552 | this.each(function(){ this.textContent = text }) 553 | }, 554 | attr: function(name, value){ 555 | var result 556 | return (typeof name == 'string' && value === undefined) ? 557 | (this.length == 0 || this[0].nodeType !== 1 ? undefined : 558 | (name == 'value' && this[0].nodeName == 'INPUT') ? this.val() : 559 | (!(result = this[0].getAttribute(name)) && name in this[0]) ? this[0][name] : result 560 | ) : 561 | this.each(function(idx){ 562 | if (this.nodeType !== 1) return 563 | if (isObject(name)) for (key in name) setAttribute(this, key, name[key]) 564 | else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))) 565 | }) 566 | }, 567 | removeAttr: function(name){ 568 | return this.each(function(){ this.nodeType === 1 && setAttribute(this, name) }) 569 | }, 570 | prop: function(name, value){ 571 | return (value === undefined) ? 572 | (this[0] && this[0][name]) : 573 | this.each(function(idx){ 574 | this[name] = funcArg(this, value, idx, this[name]) 575 | }) 576 | }, 577 | data: function(name, value){ 578 | var data = this.attr('data-' + dasherize(name), value) 579 | return data !== null ? deserializeValue(data) : undefined 580 | }, 581 | val: function(value){ 582 | return arguments.length === 0 ? 583 | (this[0] && (this[0].multiple ? 584 | $(this[0]).find('option').filter(function(o){ return this.selected }).pluck('value') : 585 | this[0].value) 586 | ) : 587 | this.each(function(idx){ 588 | this.value = funcArg(this, value, idx, this.value) 589 | }) 590 | }, 591 | offset: function(coordinates){ 592 | if (coordinates) return this.each(function(index){ 593 | var $this = $(this), 594 | coords = funcArg(this, coordinates, index, $this.offset()), 595 | parentOffset = $this.offsetParent().offset(), 596 | props = { 597 | top: coords.top - parentOffset.top, 598 | left: coords.left - parentOffset.left 599 | } 600 | 601 | if ($this.css('position') == 'static') props['position'] = 'relative' 602 | $this.css(props) 603 | }) 604 | if (this.length==0) return null 605 | var obj = this[0].getBoundingClientRect() 606 | return { 607 | left: obj.left + window.pageXOffset, 608 | top: obj.top + window.pageYOffset, 609 | width: Math.round(obj.width), 610 | height: Math.round(obj.height) 611 | } 612 | }, 613 | css: function(property, value){ 614 | if (arguments.length < 2 && typeof property == 'string') 615 | return this[0] && (this[0].style[camelize(property)] || getComputedStyle(this[0], '').getPropertyValue(property)) 616 | 617 | var css = '' 618 | if (type(property) == 'string') { 619 | if (!value && value !== 0) 620 | this.each(function(){ this.style.removeProperty(dasherize(property)) }) 621 | else 622 | css = dasherize(property) + ":" + maybeAddPx(property, value) 623 | } else { 624 | for (key in property) 625 | if (!property[key] && property[key] !== 0) 626 | this.each(function(){ this.style.removeProperty(dasherize(key)) }) 627 | else 628 | css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';' 629 | } 630 | 631 | return this.each(function(){ this.style.cssText += ';' + css }) 632 | }, 633 | index: function(element){ 634 | return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]) 635 | }, 636 | hasClass: function(name){ 637 | return emptyArray.some.call(this, function(el){ 638 | return this.test(className(el)) 639 | }, classRE(name)) 640 | }, 641 | addClass: function(name){ 642 | return this.each(function(idx){ 643 | classList = [] 644 | var cls = className(this), newName = funcArg(this, name, idx, cls) 645 | newName.split(/\s+/g).forEach(function(klass){ 646 | if (!$(this).hasClass(klass)) classList.push(klass) 647 | }, this) 648 | classList.length && className(this, cls + (cls ? " " : "") + classList.join(" ")) 649 | }) 650 | }, 651 | removeClass: function(name){ 652 | return this.each(function(idx){ 653 | if (name === undefined) return className(this, '') 654 | classList = className(this) 655 | funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){ 656 | classList = classList.replace(classRE(klass), " ") 657 | }) 658 | className(this, classList.trim()) 659 | }) 660 | }, 661 | toggleClass: function(name, when){ 662 | return this.each(function(idx){ 663 | var $this = $(this), names = funcArg(this, name, idx, className(this)) 664 | names.split(/\s+/g).forEach(function(klass){ 665 | (when === undefined ? !$this.hasClass(klass) : when) ? 666 | $this.addClass(klass) : $this.removeClass(klass) 667 | }) 668 | }) 669 | }, 670 | scrollTop: function(){ 671 | if (!this.length) return 672 | return ('scrollTop' in this[0]) ? this[0].scrollTop : this[0].scrollY 673 | }, 674 | position: function() { 675 | if (!this.length) return 676 | 677 | var elem = this[0], 678 | // Get *real* offsetParent 679 | offsetParent = this.offsetParent(), 680 | // Get correct offsets 681 | offset = this.offset(), 682 | parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset() 683 | 684 | // Subtract element margins 685 | // note: when an element has margin: auto the offsetLeft and marginLeft 686 | // are the same in Safari causing offset.left to incorrectly be 0 687 | offset.top -= parseFloat( $(elem).css('margin-top') ) || 0 688 | offset.left -= parseFloat( $(elem).css('margin-left') ) || 0 689 | 690 | // Add offsetParent borders 691 | parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0 692 | parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0 693 | 694 | // Subtract the two offsets 695 | return { 696 | top: offset.top - parentOffset.top, 697 | left: offset.left - parentOffset.left 698 | } 699 | }, 700 | offsetParent: function() { 701 | return this.map(function(){ 702 | var parent = this.offsetParent || document.body 703 | while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static") 704 | parent = parent.offsetParent 705 | return parent 706 | }) 707 | } 708 | } 709 | 710 | // for now 711 | $.fn.detach = $.fn.remove 712 | 713 | // Generate the `width` and `height` functions 714 | ;['width', 'height'].forEach(function(dimension){ 715 | $.fn[dimension] = function(value){ 716 | var offset, el = this[0], 717 | Dimension = dimension.replace(/./, function(m){ return m[0].toUpperCase() }) 718 | if (value === undefined) return isWindow(el) ? el['inner' + Dimension] : 719 | isDocument(el) ? el.documentElement['offset' + Dimension] : 720 | (offset = this.offset()) && offset[dimension] 721 | else return this.each(function(idx){ 722 | el = $(this) 723 | el.css(dimension, funcArg(this, value, idx, el[dimension]())) 724 | }) 725 | } 726 | }) 727 | 728 | function traverseNode(node, fun) { 729 | fun(node) 730 | for (var key in node.childNodes) traverseNode(node.childNodes[key], fun) 731 | } 732 | 733 | // Generate the `after`, `prepend`, `before`, `append`, 734 | // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods. 735 | adjacencyOperators.forEach(function(operator, operatorIndex) { 736 | var inside = operatorIndex % 2 //=> prepend, append 737 | 738 | $.fn[operator] = function(){ 739 | // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings 740 | var argType, nodes = $.map(arguments, function(arg) { 741 | argType = type(arg) 742 | return argType == "object" || argType == "array" || arg == null ? 743 | arg : zepto.fragment(arg) 744 | }), 745 | parent, copyByClone = this.length > 1 746 | if (nodes.length < 1) return this 747 | 748 | return this.each(function(_, target){ 749 | parent = inside ? target : target.parentNode 750 | 751 | // convert all methods to a "before" operation 752 | target = operatorIndex == 0 ? target.nextSibling : 753 | operatorIndex == 1 ? target.firstChild : 754 | operatorIndex == 2 ? target : 755 | null 756 | 757 | nodes.forEach(function(node){ 758 | if (copyByClone) node = node.cloneNode(true) 759 | else if (!parent) return $(node).remove() 760 | 761 | traverseNode(parent.insertBefore(node, target), function(el){ 762 | if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' && 763 | (!el.type || el.type === 'text/javascript') && !el.src) 764 | window['eval'].call(window, el.innerHTML) 765 | }) 766 | }) 767 | }) 768 | } 769 | 770 | // after => insertAfter 771 | // prepend => prependTo 772 | // before => insertBefore 773 | // append => appendTo 774 | $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){ 775 | $(html)[operator](this) 776 | return this 777 | } 778 | }) 779 | 780 | zepto.Z.prototype = $.fn 781 | 782 | // Export internal API functions in the `$.zepto` namespace 783 | zepto.uniq = uniq 784 | zepto.deserializeValue = deserializeValue 785 | $.zepto = zepto 786 | 787 | return $ 788 | })() 789 | 790 | // If `$` is not yet defined, point it to `Zepto` 791 | window.Zepto = Zepto 792 | window.$ === undefined && (window.$ = Zepto) 793 | -------------------------------------------------------------------------------- /test/assets_functional.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Zepto assets functional test 6 | 7 | 8 | 9 | 22 | 23 | 24 |

Zepto assets functional test

25 | 26 |
27 | 28 | 33 | 34 |

When you run this test without the assets plugin, Mobile Safari will stop loading after on average 8 images (that's on the iPad with 1 MB images). It might also crash.

35 |

PLEASE NOTE: You must restart Safari between runs (click the home button to return to the home screen, double-click the home button, tap-and-hold the Safari icon, then tap the minus badge).

36 | 37 |
38 | 39 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /test/data.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zepto extended data() unit tests 8 | 9 | 10 | 11 | 12 | 13 | 14 |

Zepto data() tests

15 |

16 | Running… see browser console for results 17 |

18 |
19 |
20 |
21 |
22 |
    23 |
  1. 24 |
  2. 25 |
26 |
    27 |
  1. 28 |
  2. 29 |
30 |
31 | 32 | 214 | 215 | 216 | -------------------------------------------------------------------------------- /test/detect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zepto environment detection unit tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

Browser detection

16 |

17 | Running… see browser console for results 18 |

19 | 20 | 312 | 313 | 314 | -------------------------------------------------------------------------------- /test/event.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zepto event tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

Zepto event tests

16 |

17 | Running… see browser console for results 18 |

19 |
20 |
21 | 22 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /test/evidence_runner.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var ConsoleTestRunner = Evidence.AutoRunner.RUNNERS.console, 3 | ConsoleTestResult = Evidence.UI.Console.TestResult, 4 | AutoRunner = Evidence.AutoRunner, 5 | printf = Evidence.UI.printf 6 | 7 | function inherit(superclass, extra) { 8 | var klass = function(){ 9 | this.initialize.apply(this, arguments) 10 | } 11 | var tmp = function(){} 12 | tmp.prototype = superclass.prototype 13 | klass.prototype = new tmp() 14 | klass.prototype.constructor = klass 15 | klass.prototype.initialize = function(){ 16 | superclass.apply(this, arguments) 17 | } 18 | 19 | if (extra) { 20 | var methods = extra.call(klass, superclass.prototype) 21 | for (var method in methods) klass.prototype[method] = methods[method] 22 | } 23 | return klass 24 | } 25 | 26 | var TestRunner = inherit(ConsoleTestRunner, function(_super) { 27 | AutoRunner.RUNNERS.zepto = this 28 | return { 29 | _makeResult: function() { return new TestResult(this.logger) } 30 | } 31 | }) 32 | 33 | var TestResult = inherit(ConsoleTestResult, function(_super) { 34 | return { 35 | start: function(t0) { 36 | Evidence.TestResult.prototype.start.call(this, t0) 37 | this.logger.debug('Started tests.') 38 | }, 39 | stop: function(t1) { 40 | _super.stop.call(this, t1) 41 | displayResults(this, (t1-this.t0)/1000) 42 | checkLeakedGlobals() 43 | }, 44 | pauseTest: function(testcase) { 45 | this.logger.debug('Paused testcase ' + testcase + '.') 46 | }, 47 | restartTest: function(testcase) { 48 | this.logger.debug('Restarted testcase ' + testcase + '.') 49 | }, 50 | startSuite: function(suite) { 51 | this.logger.debug('Started suite ' + suite + '.') 52 | } 53 | } 54 | }) 55 | 56 | // HACK: force our test runner as default 57 | ;(function(){ 58 | var _super = AutoRunner.prototype.retrieveOptions 59 | AutoRunner.prototype.retrieveOptions = function() { 60 | var options = _super.call(this) 61 | if (!options.runner) options.runner = 'zepto' 62 | return options 63 | } 64 | })() 65 | 66 | function $(id) { return document.getElementById(id) } 67 | 68 | function displayResults(results, seconds) { 69 | var container = $('results') 70 | if (container) { 71 | if (results.failureCount || results.errorCount) { 72 | container.className = 'failed' 73 | container.innerHTML = printf("Finished in %d s. – %d failures, %d errors (%d assertions)", 74 | [seconds, results.failureCount, results.errorCount, results.assertionCount]) 75 | } else { 76 | container.className = 'passed' 77 | container.innerHTML = printf("Finished in %d s. – %d tests passed (%d assertions)", 78 | [seconds, results.testCount, results.assertionCount]) 79 | } 80 | container.className += ' finished' 81 | } 82 | } 83 | 84 | var globals = [], expected = ['Zepto', '$', 'Evidence'] 85 | for (var key in window) globals.push(key) 86 | 87 | function checkLeakedGlobals() { 88 | var opera = /^Opera\b/.test(navigator.userAgent) 89 | for (var key in window) 90 | if ( globals.indexOf(key) < 0 && expected.indexOf(key) < 0 && 91 | (!opera || typeof window[key] != 'object' || window[key].id != key) && 92 | window.console && console.warn 93 | ) 94 | console.warn("unexpected global: " + key) 95 | } 96 | })() 97 | -------------------------------------------------------------------------------- /test/fixtures/ajax_load_selector.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | testAjaxLoad 6 | 7 | 8 |
ajax load with selector
9 |
10 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /test/fixtures/ajax_load_selector_javascript.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | testAjaxLoad 6 | 7 | 8 |
ajax load with selector
9 |
10 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. 11 |
12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /test/fixtures/ajax_load_simple.html: -------------------------------------------------------------------------------- 1 | simple ajax load 2 | -------------------------------------------------------------------------------- /test/fixtures/iframe_document.html: -------------------------------------------------------------------------------- 1 | Hello from iframe! 2 | -------------------------------------------------------------------------------- /test/fixtures/zepto.json: -------------------------------------------------------------------------------- 1 | { 2 | "zepto": "awesomeness" 3 | } 4 | -------------------------------------------------------------------------------- /test/form.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zepto form unit tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Zepto form tests

17 |

18 | Running… see browser console for results 19 |

20 | 21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | 33 | 34 | 35 |
36 | 37 | 42 | 43 |
44 | 45 | 46 | 47 | 48 |
49 |
50 | 51 | 52 |
53 | 54 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /test/fx.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zepto FX unit tests 8 | 9 | 10 | 11 | 12 | 13 | 49 | 50 | 51 |

Zepto effects tests

52 |

53 | Running… see browser console for results 54 |

55 | 56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | 68 |
69 | 70 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /test/fx_functional.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zepto fx functional test 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 |

Zepto fx functional

20 | 21 |
22 |
I ♥ fx
23 |
24 | 25 |
26 |
I ♥ fx
27 |
28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | 39 | 40 | 41 |
42 | 43 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /test/gesture_functional.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Zepto gesture functional test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

Zepto gestures functional test

14 | 15 |
16 | gesture events test 17 |
18 | 19 |
20 | 21 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /test/polyfill.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zepto compatibility unit tests 8 | 9 | 10 | 11 | 12 | 13 | 14 |

Compatibility tests

15 |

16 | Running… see browser console for results 17 |

18 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /test/runner.coffee: -------------------------------------------------------------------------------- 1 | # Test runner for PhantomJS 2 | # Usage: 3 | # $ phantomjs test/runner.coffee [, , ...] 4 | # 5 | # When no test pages specified, runs all automated tests. 6 | 7 | system = require('system') 8 | fs = require('fs') 9 | 10 | args = system.args.slice(1) 11 | prefix = args.shift() || "file://#{fs.workingDirectory}/" 12 | 13 | if args.length > 0 14 | # list of test pages to run 15 | suites = args 16 | else 17 | # by default, run all test/*.html pages 18 | modules = 'zepto ajax data detect event form fx selector stack'.split /\s+/ 19 | suites = modules.map (name)-> "test/#{name}.html" 20 | 21 | page = require('webpage').create() 22 | 23 | page.onConsoleMessage = (msg) -> 24 | console.log msg 25 | 26 | page.onError = (msg, trace) -> 27 | console.log 'ERROR: ' + msg 28 | 29 | # used for waiting until the tests finish running 30 | waitFor = (testFn, onReady, timeout=3000) -> 31 | start = new Date() 32 | interval = setInterval -> 33 | if testFn() 34 | clearInterval interval 35 | onReady() 36 | else if new Date() - start > timeout 37 | console.log "timed out." 38 | phantom.exit(1) 39 | , 100 40 | 41 | loadNextSuite = -> 42 | if not suites.length 43 | phantom.exit() 44 | else 45 | url = suites.shift() + "?verbosity=WARN" 46 | # PhantomJS chokes on the query string on relative paths 47 | url = prefix + url if not /:\/\//.test url 48 | 49 | page.open url, (status) -> 50 | if status isnt "success" 51 | console.log "failed opening #{url}" 52 | phantom.exit(1) 53 | 54 | waitFor -> 55 | page.evaluate -> 56 | # the "#results" element needs to have the "finished" class 57 | res = document.getElementById 'results' 58 | /finished/.test res.className if res 59 | , -> 60 | passed = page.evaluate -> 61 | res = document.getElementById 'results' 62 | paths = location.pathname.split('/') 63 | # echo test results to the console 64 | console.log "#{paths[paths.length - 1]} - " + res.textContent 65 | /passed/.test res.className 66 | 67 | if passed 68 | loadNextSuite() 69 | else 70 | phantom.exit(1) 71 | 72 | loadNextSuite() 73 | -------------------------------------------------------------------------------- /test/selector.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zepto selector tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

Zepto selector tests

16 |

17 | Running… see browser console for results 18 |

19 |
20 |
  • one
  • two
21 |
look at me!
22 | 23 |
    24 |
  1. child1
  2. 25 |
  3. child2 26 |
      27 |
    • child3
    • 28 |
    • child4
    • 29 |
    30 |
  4. 31 |
32 |
33 | 34 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /test/server.coffee: -------------------------------------------------------------------------------- 1 | express = require 'express' 2 | app = express() 3 | 4 | module.exports = app 5 | 6 | project_root = __dirname.replace(/\/[^\/]+$/, '/') 7 | app.use express.static(project_root) 8 | app.use express.static(project_root + 'node_modules/mocha') 9 | app.use express.static(project_root + 'node_modules/chai') 10 | 11 | app.use express.bodyParser() 12 | 13 | mime = (req) -> 14 | type = req.headers['content-type'] or '' 15 | type.split(';')[0] 16 | 17 | dump = (obj) -> 18 | obj = '' unless obj 19 | obj = JSON.stringify(obj) if obj and typeof obj isnt "string" 20 | obj 21 | 22 | app.all '/test/echo', (req, res) -> 23 | res.send """ 24 | #{req.method} ?#{dump(req.query)} 25 | content-type: #{mime(req)} 26 | accept: #{req.headers['accept']} 27 | #{dump(req.body)} 28 | """ 29 | 30 | app.get '/test/jsonp', (req, res) -> 31 | res.jsonp 32 | query: req.query 33 | hello: 'world' 34 | 35 | app.get '/test/json', (req, res) -> 36 | if /json/.test req.headers['accept'] 37 | res.json 38 | query: req.query 39 | hello: 'world' 40 | else 41 | res.send 400, 'FAIL' 42 | 43 | app.all '/test/slow', (req, res) -> 44 | setTimeout -> 45 | res.send 'DONE' 46 | , 200 47 | 48 | app.all '/test/error', (req, res) -> 49 | res.send 500, 'BOOM' 50 | 51 | if process.argv[1] is __filename 52 | port = process.argv[2] 53 | unless port 54 | port = 3000 55 | console.log "Listening on port #{port}" 56 | app.listen port 57 | -------------------------------------------------------------------------------- /test/stack.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zepto Stack unit tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

Zepto DOM unit tests

16 |

17 | Running… see browser console for results 18 |

19 |
20 | 21 |
22 |
23 |
12
24 |
25 | 26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 |
34 | 35 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /test/test.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 1em; 3 | font: medium "American Typewriter", Helvetica, sans-serif; 4 | } 5 | a:link, a:visited { color: darkblue; } 6 | #results.failed em { font-style: normal; color: crimson; } 7 | #results.passed em { font-style: normal; color: darkgreen; } 8 | #fixtures { 9 | position: absolute; 10 | top: -10000px; 11 | left: -10000px; 12 | } 13 | #nav { 14 | list-style: none; 15 | padding: 0; 16 | margin: 2em 0 0 0; 17 | font-size: 12px; 18 | max-width: 30em; 19 | } 20 | #nav a { 21 | padding: .8em 1.5em; 22 | background: #eee; 23 | border-top: 2px solid white; 24 | text-decoration: none; 25 | text-transform: uppercase; 26 | letter-spacing: .1em; 27 | display: block; 28 | color: #555; 29 | } 30 | #nav .current a { background: #ccc; color: black; } 31 | #nav a:active { background: #555; color: white; } 32 | 33 | @media only screen and (max-device-width:480px) { 34 | body { margin: 7px; font-size: small; } 35 | h1 { margin: 0; } 36 | h1, #results { text-align: center; } 37 | #results { min-height: 3em; } 38 | #nav { font-size: 14px; max-width: auto; } 39 | } 40 | -------------------------------------------------------------------------------- /test/touch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Zepto touch unit tests 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |

Touch tests

17 |

18 | Running… see browser console for results 19 |

20 | 21 | 30 | 31 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /test/touch_functional.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Zepto touch functional test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

Zepto touch functional test

14 | 15 |
16 | touch events test 17 |
18 | 19 |
20 | touch events test (scrollable cancel) 21 |
22 | 23 |
24 | touch events test (scrollable cancel, single tap only) 25 |
26 | 27 |
28 | 29 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /test/touchcancel_functional.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Zepto touch functional test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |

Zepto touch functional test

14 | 15 |

16 | Double-tap and hold until the JavaScript alert occurs. After closing the 17 | alert, tap again. Without touchcancel you will see a double tap instead of a tap. 18 |

19 | 20 |
21 | touch events test 22 |
23 | 24 |
25 | 26 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /vendor/evidence.js: -------------------------------------------------------------------------------- 1 | /* evidence.js, version 0.6 2 | * 3 | * Copyright (c) 2009 Tobie Langel (http://tobielangel.com) 4 | * 5 | * evidence.js is freely distributable under the terms of an MIT-style license. 6 | *--------------------------------------------------------------------------*/ 7 | 8 | (function(global) { 9 | var originalEvidence = global.Evidence, 10 | originalOnload = global.onload; 11 | 12 | function Evidence() { 13 | TestCase.extend.apply(TestCase, arguments); 14 | } 15 | 16 | function noConflict() { 17 | global.Evidence = originalEvidence; 18 | return Evidence; 19 | } 20 | 21 | Evidence.noConflict = noConflict; 22 | Evidence.VERSION = '0.6'; 23 | 24 | var FILE_REGEXP = /.*?\/(\w+\.html)(.*)/; 25 | 26 | function getNameFromFile() { 27 | return (global.location || '').toString().replace(FILE_REGEXP, '$1'); 28 | } 29 | 30 | function chain(subclass, superclass) { 31 | function Subclass() {} 32 | Subclass.prototype = superclass.prototype; 33 | subclass.prototype = new Subclass(); 34 | subclass.prototype.constructor = subclass; 35 | return subclass; 36 | } 37 | 38 | function defer(block, context) { 39 | if ('setTimeout' in global) { 40 | window.setTimeout(function() { 41 | block.call(context); 42 | }, 10); 43 | } else { 44 | block.call(context); 45 | } 46 | } 47 | function AssertionSkippedError(message) { 48 | this.message = message; 49 | } 50 | 51 | AssertionSkippedError.displayName = 'AssertionSkippedError'; 52 | 53 | (function(p) { 54 | p.name = 'AssertionSkippedError'; 55 | })(AssertionSkippedError.prototype); 56 | Evidence.AssertionSkippedError = AssertionSkippedError; 57 | function AssertionFailedError(message, template, args) { 58 | this.message = message; 59 | this.template = template || ''; 60 | this.args = args; 61 | } 62 | 63 | AssertionFailedError.displayName = 'AssertionFailedError'; 64 | 65 | (function(p) { 66 | p.name = 'AssertionFailedError'; 67 | })(AssertionFailedError.prototype); 68 | Evidence.AssertionFailedError = AssertionFailedError; 69 | function AssertionMessage(message, template, args) { 70 | this.message = message.replace(/%/g, '%%'); 71 | this.template = template || ''; 72 | this.args = args; 73 | } 74 | 75 | AssertionMessage.displayName = 'AssertionMessage'; 76 | 77 | (function(p) { 78 | function toString() { 79 | return UI.printf(this.message + this.template, this.args); 80 | } 81 | p.toString = toString; 82 | })(AssertionMessage.prototype); 83 | Evidence.AssertionMessage = AssertionMessage; 84 | 85 | var Assertions = (function() { 86 | function _assertExpression(expression, message, template) { 87 | /*for (var i=0; i < 100000; i++) { 88 | (function(){})() 89 | }*/ 90 | if (expression) { 91 | this.addAssertion(); 92 | } else { 93 | var args = Array.prototype.slice.call(arguments, 3); 94 | throw new AssertionFailedError(message, template, args); 95 | } 96 | } 97 | 98 | function skip(message) { 99 | throw new AssertionSkippedError(message || 'Skipped!'); 100 | } 101 | 102 | function fail(message) { 103 | this._assertExpression(false, message || 'Flunked!'); 104 | } 105 | 106 | function assert(test, message) { 107 | this._assertExpression( 108 | !!test, 109 | message || 'Failed assertion.', 110 | 'Expected %o to evaluate to true.', test 111 | ); 112 | } 113 | 114 | function refute(test, message) { 115 | this._assertExpression( 116 | !test, 117 | message || 'Failed refutation.', 118 | 'Expected %o to evaluate to false.', test 119 | ); 120 | } 121 | 122 | function assertTrue(test, message) { 123 | this._assertExpression( 124 | (test === true), 125 | message || 'Failed assertion.', 126 | 'Expected %o to be true.', test 127 | ); 128 | } 129 | 130 | function refuteTrue(test, message) { 131 | this._assertExpression( 132 | (test !== true), 133 | message || 'Failed refutation.', 134 | 'Expected %o to not be true.', test 135 | ); 136 | } 137 | 138 | function assertNull(test, message) { 139 | this._assertExpression( 140 | (test === null), 141 | message || 'Failed assertion.', 142 | 'Expected %o to be null.', test 143 | ); 144 | } 145 | 146 | function refuteNull(test, message) { 147 | this._assertExpression( 148 | (test !== null), 149 | message || 'Failed refutation.', 150 | 'Expected %o to not be null.', test 151 | ); 152 | } 153 | 154 | function assertUndefined(test, message) { 155 | this._assertExpression( 156 | (typeof test === 'undefined'), 157 | message || 'Failed assertion.', 158 | 'Expected %o to be undefined.', test 159 | ); 160 | } 161 | 162 | function refuteUndefined(test, message) { 163 | this._assertExpression( 164 | (typeof test !== 'undefined'), 165 | message || 'Failed refutation.', 166 | 'Expected %o to not be undefined.', test 167 | ); 168 | } 169 | 170 | function assertFalse(test, message) { 171 | this._assertExpression( 172 | (test === false), 173 | message || 'Failed assertion.', 174 | 'Expected %o to be false.', test 175 | ); 176 | } 177 | 178 | function refuteFalse(test, message) { 179 | this._assertExpression( 180 | (test !== false), 181 | message || 'Failed refutation.', 182 | 'Expected %o to not be false.', test 183 | ); 184 | } 185 | 186 | function assertEqual(expected, actual, message) { 187 | this._assertExpression( 188 | (expected == actual), 189 | message || 'Failed assertion.', 190 | 'Expected %o to be == to %o.', actual, expected 191 | ); 192 | } 193 | 194 | function refuteEqual(expected, actual, message) { 195 | this._assertExpression( 196 | (expected != actual), 197 | message || 'Failed refutation.', 198 | 'Expected %o to be != to %o.', actual, expected 199 | ); 200 | } 201 | 202 | function assertIdentical(expected, actual, message) { 203 | this._assertExpression( 204 | (expected === actual), 205 | message || 'Failed assertion.', 206 | 'Expected %o to be === to %o.', actual, expected 207 | ); 208 | } 209 | 210 | function refuteIdentical(expected, actual, message) { 211 | this._assertExpression( 212 | (expected !== actual), 213 | message || 'Failed refutation.', 214 | 'Expected %o to be !== to %o.', actual, expected 215 | ); 216 | } 217 | 218 | function assertIn(property, object, message) { 219 | this._assertExpression( 220 | (property in object), 221 | message || 'Failed assertion.', 222 | 'Expected "%s" to be a property of %o.', property, object 223 | ); 224 | } 225 | 226 | function refuteIn(property, object, message) { 227 | this._assertExpression( 228 | !(property in object), 229 | message || 'Failed refutation.', 230 | 'Expected "%s" to not be a property of %o.', property, object 231 | ); 232 | } 233 | 234 | return { 235 | _assertExpression: _assertExpression, 236 | skip: skip, 237 | assert: assert, 238 | refute: refute, 239 | assertNot: refute, 240 | assertTrue: assertTrue, 241 | assertNull: assertNull, 242 | assertUndefined: assertUndefined, 243 | assertFalse: assertFalse, 244 | assertIdentical: assertIdentical, 245 | refuteIdentical: refuteIdentical, 246 | assertEqual: assertEqual, 247 | refuteEqual: refuteEqual, 248 | assertIn: assertIn, 249 | refuteIn: refuteIn, 250 | fail: fail, 251 | flunk: fail 252 | }; 253 | })(); 254 | Evidence.Assertions = Assertions; 255 | function TestCase(methodName) { 256 | this._methodName = methodName; 257 | this.name = methodName; 258 | } 259 | 260 | (function() { 261 | function extend(name, methods) { 262 | function TestCaseSubclass(methodName) { 263 | TestCase.call(this, methodName); 264 | } 265 | 266 | if (!methods) { 267 | methods = name; 268 | name = getNameFromFile(); 269 | } 270 | 271 | chain(TestCaseSubclass, this); 272 | TestCaseSubclass.displayName = name; 273 | TestCaseSubclass.extend = extend; 274 | 275 | for(var prop in methods) { 276 | TestCaseSubclass.prototype[prop] = methods[prop]; 277 | } 278 | TestCase.subclasses.push(TestCaseSubclass); 279 | return TestCaseSubclass; 280 | } 281 | 282 | function AssertionsMixin() {} 283 | AssertionsMixin.prototype = Assertions; 284 | TestCase.prototype = new AssertionsMixin(); 285 | TestCase.constructor = TestCase; 286 | 287 | TestCase.displayName = 'TestCase'; 288 | TestCase.extend = extend; 289 | TestCase.subclasses = []; 290 | TestCase.defaultTimeout = 10000; 291 | })(); 292 | 293 | (function(p) { 294 | function run(result) { 295 | if (result) { this._result = result; } 296 | try { 297 | if (this._nextAssertions) { 298 | this._result.restartTest(this); 299 | this._nextAssertions(this); 300 | } else { 301 | /*this._globalProperties = objectKeys(global);*/ 302 | this._result.startTest(this); 303 | this.setUp(this); 304 | this[this._methodName](this); 305 | } 306 | } catch(e) { 307 | this._filterException(e); 308 | } finally { 309 | if (this._paused) { 310 | this._result.pauseTest(this); 311 | } else { 312 | try { 313 | this.tearDown(this); 314 | } catch(e) { 315 | this._filterException(e); 316 | } finally { 317 | this._nextAssertions = null; 318 | this._result.stopTest(this); 319 | defer(function() { 320 | this.parent.next(); 321 | }, this); 322 | } 323 | } 324 | } 325 | } 326 | 327 | function _filterException(e) { 328 | var name = e.name; 329 | switch(name) { 330 | case 'AssertionFailedError': 331 | this._result.addFailure(this, e); 332 | break; 333 | case 'AssertionSkippedError': 334 | this._result.addSkip(this, e); 335 | break; 336 | default: 337 | this._result.addError(this, e); 338 | } 339 | } 340 | 341 | function pause(assertions) { 342 | this._paused = true; 343 | var self = this; 344 | if (assertions) { this._nextAssertions = assertions; } 345 | self._timeoutId = global.setTimeout(function() { 346 | self.resume(function() { 347 | self.fail('Test timed out. Testing was not resumed after being paused.'); 348 | }); 349 | }, TestCase.defaultTimeout); 350 | } 351 | 352 | function resume(assertions) { 353 | if (this._paused) { // avoid race conditions 354 | this._paused = false; 355 | global.clearTimeout(this._timeoutId); 356 | if (assertions) { this._nextAssertions = assertions; } 357 | this.run(); 358 | } 359 | } 360 | 361 | function size() { 362 | return 1; 363 | } 364 | 365 | function toString() { 366 | return this.constructor.displayName + '#' + this.name; 367 | } 368 | 369 | function addAssertion() { 370 | this._result.addAssertion(); 371 | } 372 | 373 | p.run = run; 374 | p.addAssertion = addAssertion; 375 | p._filterException = _filterException; 376 | p.pause = pause; 377 | p.resume = resume; 378 | p.size = size; 379 | p.toString = toString; 380 | p.setUp = function() {}; 381 | p.tearDown = function() {}; 382 | })(TestCase.prototype); 383 | Evidence.TestCase = TestCase; 384 | function TestSuite(name, tests) { 385 | this.name = name; 386 | this._tests = []; 387 | if (tests) { 388 | this.push.apply(this, tests); 389 | } 390 | } 391 | 392 | TestSuite.displayName = 'TestSuite'; 393 | 394 | (function(p) { 395 | function run(result) { 396 | this._index = 0; 397 | this._result = result; 398 | result.startSuite(this); 399 | this.next(); 400 | return result; 401 | } 402 | 403 | function next() { 404 | var next = this._tests[this._index]; 405 | if (next) { 406 | this._index++; 407 | next.run(this._result); 408 | } else { 409 | this._result.stopSuite(this); 410 | if (this.parent) { 411 | this.parent.next(); 412 | } else { 413 | this._result.stop(new Date()); 414 | } 415 | } 416 | } 417 | 418 | function push() { 419 | for (var i = 0, length = arguments.length; i < length; i++) { 420 | var test = arguments[i]; 421 | test.parent = this; 422 | this._tests.push(test); 423 | } 424 | } 425 | 426 | function addTest(test) { 427 | test.parent = this; 428 | this._tests.push(test); 429 | } 430 | 431 | function addTests(tests) { 432 | for (var i = 0, length = tests.length; i < length; i++) { 433 | this.addTest(tests[i]); 434 | } 435 | } 436 | 437 | function size() { 438 | var tests = this._tests, 439 | length = tests.length, 440 | sum = 0; 441 | 442 | for (var i = 0; i < length; i++) { 443 | sum += tests[i].size(); 444 | } 445 | return sum; 446 | } 447 | 448 | function isEmpty() { 449 | return this.size() === 0; 450 | } 451 | 452 | function toString() { 453 | return this.name; 454 | } 455 | p.run = run; 456 | p.next = next; 457 | p.push = push; 458 | p.size = size; 459 | p.isEmpty = isEmpty; 460 | p.toString = toString; 461 | })(TestSuite.prototype); 462 | Evidence.TestSuite = TestSuite; 463 | function TestRunner() { 464 | } 465 | 466 | TestRunner.displayName = 'TestRunner'; 467 | 468 | (function(p) { 469 | function run(suite) { 470 | suite.parent = null; 471 | var result = this._makeResult(); 472 | result.start(new Date()); 473 | suite.run(result); 474 | return result; 475 | } 476 | 477 | function _makeResult() { 478 | return new TestResult(); 479 | } 480 | 481 | p.run = run; 482 | p._makeResult = _makeResult; 483 | })(TestRunner.prototype); 484 | Evidence.TestRunner = TestRunner; 485 | function TestLoader() { 486 | } 487 | 488 | TestLoader.displayName = 'TestLoader'; 489 | 490 | (function(p) { 491 | function loadTestsFromTestCase(testcaseClass) { 492 | var suite = new TestSuite(testcaseClass.displayName), 493 | props = this.getTestCaseNames(testcaseClass); 494 | for (var i=0; i < props.length; i++) { 495 | suite.push(new testcaseClass(props[i])); 496 | } 497 | return suite; 498 | } 499 | 500 | function loadTestsFromTestCases(testcases) { 501 | var suite = new TestSuite(getNameFromFile()); 502 | for (var i = 0; i < testcases.length; i++) { 503 | var testcase = testcases[i]; 504 | var subSuite = defaultLoader.loadTestsFromTestCase(testcase); 505 | if (!subSuite.isEmpty()) { suite.push(subSuite); } 506 | } 507 | return suite; 508 | } 509 | 510 | function getTestCaseNames(testcaseClass) { 511 | var results = [], 512 | proto = testcaseClass.prototype, 513 | prefix = this.testMethodPrefix; 514 | 515 | for (var property in proto) { 516 | if (property.indexOf(prefix) === 0) { 517 | results.push(property); 518 | } 519 | } 520 | return results.sort(); 521 | } 522 | 523 | function loadRegisteredTestCases() { 524 | return loadTestsFromTestCases(TestCase.subclasses); 525 | } 526 | 527 | p.loadTestsFromTestCase = loadTestsFromTestCase; 528 | p.loadRegisteredTestCases = loadRegisteredTestCases; 529 | p.loadTestsFromTestCases = loadTestsFromTestCases; 530 | p.testMethodPrefix = 'test'; 531 | p.getTestCaseNames = getTestCaseNames; 532 | 533 | })(TestLoader.prototype); 534 | Evidence.TestLoader = TestLoader; 535 | function AutoRunner() { 536 | if (global.console && global.console.log) { 537 | this.logger = Logger; 538 | } else if (Object.prototype.toString.call(global.environment) === '[object Environment]' && global.print) { 539 | this.logger = CommandLineLogger; 540 | } else { 541 | this.logger = PopupLogger; 542 | } 543 | this.autoRun = true; 544 | this.verbosity = Logger.INFO; 545 | this.runner = ConsoleTestRunner; 546 | } 547 | 548 | (function() { 549 | function run(options) { 550 | var autoRunner = new this(); 551 | options = options || autoRunner.retrieveOptions(); 552 | autoRunner.processOptions(options); 553 | if (autoRunner.autoRun) { autoRunner.run() }; 554 | } 555 | 556 | AutoRunner.run = run; 557 | AutoRunner.displayName = 'AutoRunner'; 558 | AutoRunner.LOGGERS = { 559 | console: Logger, 560 | popup: PopupLogger, 561 | command_line: CommandLineLogger 562 | }; 563 | 564 | AutoRunner.RUNNERS = { 565 | console: ConsoleTestRunner 566 | }; 567 | })(); 568 | 569 | (function(p) { 570 | function run() { 571 | var logger = new this.logger(this.verbosity), 572 | runner = new this.runner(logger), 573 | suite = defaultLoader.loadRegisteredTestCases(); 574 | if (suite._tests.length <= 1) { 575 | suite = suite._tests[0]; 576 | } 577 | return runner.run(suite); 578 | } 579 | 580 | function processQueryString(str) { 581 | var results = {}; 582 | str = (str + '').match(/^(?:[^?#]*\?)([^#]+?)(?:#.*)?$/); 583 | str = str && str[1]; 584 | 585 | if (!str) { return results; } 586 | 587 | var pairs = str.split('&'), 588 | length = pairs.length; 589 | if (!length) { return results; } 590 | 591 | for (var i = 0; i < length; i++) { 592 | var pair = pairs[i].split('='), 593 | key = decodeURIComponent(pair[0]), 594 | value = pair[1]; 595 | value = value ? decodeURIComponent(value) : true; 596 | results[key] = value; 597 | } 598 | return results; 599 | } 600 | 601 | function processArguments(args) { // RHINO 602 | var results = {}; 603 | 604 | for (var i = 0; i < args.length; i++) { 605 | var arg = args[i]; 606 | if (arg.indexOf('-') === 0) { 607 | var value = args[i + 1]; 608 | if (value && value.indexOf('-') !== 0) { 609 | i++; 610 | } else { 611 | value = true; 612 | } 613 | results[arg.substr(1)] = value; 614 | } 615 | } 616 | return results; 617 | } 618 | 619 | function retrieveOptions() { 620 | if (global.location) { 621 | return this.processQueryString(global.location); 622 | } 623 | if (global.arguments) { 624 | return this.processArguments(global.arguments); 625 | } 626 | return {}; 627 | } 628 | 629 | function processOptions(options) { 630 | for(var key in options) { 631 | var value = options[key]; 632 | switch(key) { 633 | case 'timeout': 634 | TestCase.defaultTimeout = global.parseFloat(value) * 1000; 635 | break; 636 | case 'run': 637 | this.autoRun = value === 'false' ? false : true; 638 | break; 639 | case 'logger': 640 | this.logger = AutoRunner.LOGGERS[value]; 641 | break; 642 | case 'verbosity': 643 | var i = global.parseInt(value); 644 | this.verbosity = global.isNaN(i) ? Logger[value] : i; 645 | break; 646 | case 'runner': 647 | this.runner = AutoRunner.RUNNERS[value]; 648 | break; 649 | } 650 | } 651 | } 652 | 653 | p.run = run; 654 | p.processQueryString = processQueryString; 655 | p.processArguments = processArguments; 656 | p.retrieveOptions = retrieveOptions; 657 | p.processOptions = processOptions; 658 | })(AutoRunner.prototype); 659 | Evidence.AutoRunner = AutoRunner; 660 | function TestResult() { 661 | this.testCount = 0; 662 | this.assertionCount = 0; 663 | this.skipCount = 0; 664 | this.skips = []; 665 | this.failureCount = 0; 666 | this.failures = []; 667 | this.errors = []; 668 | this.errorCount = 0; 669 | this.testCount = 0; 670 | } 671 | 672 | TestResult.displayName = 'TestResult'; 673 | 674 | (function(p) { 675 | function addAssertion() { 676 | this.assertionCount++; 677 | } 678 | 679 | function addSkip(testcase, reason) { 680 | this.skipCount++; 681 | this.skips.push(reason); 682 | } 683 | 684 | function addFailure(testcase, reason) { 685 | this.failureCount++; 686 | this.failures.push(reason); 687 | } 688 | 689 | function addError(testcase, error) { 690 | this.errorCount++; 691 | this.errors.push(error); 692 | } 693 | 694 | function startTest(testcase) { 695 | this.testCount++; 696 | } 697 | 698 | function stopTest(testcase) {} 699 | 700 | function pauseTest(testcase) {} 701 | 702 | function restartTest(testcase) {} 703 | 704 | function startSuite(suite) {} 705 | 706 | function stopSuite(suite) {} 707 | 708 | function start(t0) { 709 | this.t0 = t0; 710 | } 711 | 712 | function stop(t1) { 713 | this.t1 = t1; 714 | } 715 | 716 | function toString() { 717 | return this.testCount + ' tests, ' + 718 | this.assertionCount + ' assertions, ' + 719 | this.failureCount + ' failures, ' + 720 | this.errorCount + ' errors, ' + 721 | this.skipCount + ' skips'; 722 | } 723 | 724 | p.addAssertion = addAssertion; 725 | p.addSkip = addSkip; 726 | p.addFailure = addFailure; 727 | p.addError = addError; 728 | p.startTest = startTest; 729 | p.stopTest = stopTest; 730 | p.pauseTest = pauseTest; 731 | p.restartTest = restartTest; 732 | p.startSuite = startSuite; 733 | p.stopSuite = stopSuite; 734 | p.start = start; 735 | p.stop = stop; 736 | p.toString = toString; 737 | })(TestResult.prototype); 738 | Evidence.TestResult = TestResult; 739 | var Console = {}; 740 | 741 | function Logger(level) { 742 | if (typeof level !== 'undefined') { 743 | this.level = level; 744 | } 745 | } 746 | 747 | Logger.displayName = 'Logger'; 748 | Logger.LEVELS = ['NOTSET', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL']; 749 | Logger.CRITICAL = 5; 750 | Logger.ERROR = 4; 751 | Logger.WARN = 3; 752 | Logger.INFO = 2; 753 | Logger.DEBUG = 1; 754 | Logger.NOTSET = 0; 755 | 756 | (function(p) { 757 | function critical(template, params) { 758 | this.log(Logger.CRITICAL, template, params); 759 | } 760 | 761 | function error(template, params) { 762 | this.log(Logger.ERROR, template, params); 763 | } 764 | 765 | function warn(template, params) { 766 | this.log(Logger.WARN, template, params); 767 | } 768 | 769 | function info(template, params) { 770 | this.log(Logger.INFO, template, params); 771 | } 772 | 773 | function debug(template, params) { 774 | this.log(Logger.DEBUG, template, params); 775 | } 776 | 777 | function log(level, template, params) { 778 | level = level || Logger.NOTSET; 779 | var c = global.console; 780 | 781 | var method = Logger.LEVELS[level].toLowerCase(); 782 | if (method === 'critical') { method = 'error'; } 783 | method = (method in c) ? method : 'log'; 784 | 785 | if (level >= this.level) { 786 | if (params) { 787 | params = params.slice(0); 788 | params.unshift(template); 789 | c[method].apply(c, params); 790 | } else { 791 | c[method](template); 792 | } 793 | } 794 | } 795 | 796 | p.log = log; 797 | p.critical = critical; 798 | p.error = error; 799 | p.warn = warn; 800 | p.info = info; 801 | p.debug = debug; 802 | p.level = 0; 803 | })(Logger.prototype); 804 | Console.Logger = Logger; 805 | function PopupLogger(level) { 806 | Logger.call(this, level); 807 | } 808 | 809 | chain(PopupLogger, Logger); 810 | PopupLogger.displayName = 'PopupLogger'; 811 | 812 | (function(p) { 813 | var BASIC_STYLES = 'color: #333; background-color: #fff; font-family: monospace; border-bottom: 1px solid #ccc;'; 814 | var STYLES = { 815 | WARN: 'color: #000; background-color: #fc6;', 816 | ERROR: 'color: #f00; background-color: #fcc;', 817 | CRITICAL: 'color: #fff; background-color: #000;' 818 | }; 819 | 820 | function _cleanup(html) { 821 | return html.replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&').replace(/[\n\r]+/, '
'); 822 | } 823 | 824 | function _makePopup() { 825 | var popup = global.open('','popup','height=400,width=400'); 826 | var doc = popup.document; 827 | doc.write('\ 828 | \ 829 | \ 830 | \ 831 | Console\ 832 | \ 833 |
\ 834 | '); 835 | doc.close(); 836 | popup.focus(); 837 | return popup; 838 | } 839 | 840 | function _appendLine(level, msg) { 841 | this.popup = this.popup || this._makePopup(); 842 | var levelName = Logger.LEVELS[level]; 843 | 844 | var html = '
'; 848 | if (level > Logger.INFO) { 849 | html += ''; 850 | html += levelName; 851 | html += ': '; 852 | } 853 | html += _cleanup(msg); 854 | html += '
'; 855 | var doc = this.popup.document, 856 | div = doc.createElement('div'); 857 | div.innerHTML = html; 858 | html = div.firstChild; 859 | div = null; 860 | doc.getElementById('evidence_console').appendChild(html); 861 | } 862 | 863 | function log(level, msg, params) { 864 | level = level || Logger.NOTSET; 865 | if (level >= this.level) { 866 | if (params) { 867 | msg = UI.printf(msg, params); 868 | } 869 | this._appendLine(level, msg); 870 | } 871 | } 872 | 873 | p.log = log; 874 | p._makePopup = _makePopup; 875 | p._appendLine = _appendLine; 876 | })(PopupLogger.prototype); 877 | Console.PopupLogger = PopupLogger; 878 | function CommandLineLogger(level) { 879 | Logger.call(this, level); 880 | } 881 | 882 | chain(CommandLineLogger, Logger); 883 | CommandLineLogger.displayName = 'CommandLineLogger'; 884 | 885 | (function(p) { 886 | 887 | function log(level, msg, params) { 888 | level = level || Logger.NOTSET; 889 | if (level >= this.level) { 890 | var prefix = ''; 891 | if (level > Logger.INFO) { 892 | prefix = Logger.LEVELS[level]+ ': '; 893 | } 894 | if (params) { 895 | msg = UI.printf(msg, params); 896 | } 897 | global.print(prefix + msg); 898 | } 899 | } 900 | 901 | p.log = log; 902 | })(CommandLineLogger.prototype); 903 | Console.CommandLineLogger = CommandLineLogger; 904 | function ConsoleTestRunner(logger) { 905 | TestRunner.call(this); 906 | this.logger = logger; 907 | } 908 | 909 | chain(ConsoleTestRunner, TestRunner); 910 | ConsoleTestRunner.displayName = 'ConsoleTestRunner'; 911 | 912 | (function(p) { 913 | function _makeResult() { 914 | return new ConsoleTestResult(this.logger); 915 | } 916 | 917 | p._makeResult = _makeResult; 918 | })(ConsoleTestRunner.prototype); 919 | Console.TestRunner = ConsoleTestRunner; 920 | function ConsoleTestResult(logger) { 921 | TestResult.call(this); 922 | this.logger = logger; 923 | } 924 | 925 | chain(ConsoleTestResult, TestResult); 926 | ConsoleTestResult.displayName = 'ConsoleTestResult'; 927 | 928 | (function(p) { 929 | var _super = TestResult.prototype; 930 | 931 | function addAssertion() { 932 | this.assertionCount++; 933 | } 934 | 935 | function addSkip(testcase, msg) { 936 | _super.addSkip.call(this, testcase, msg); 937 | this.logger.warn('Skipping testcase ' + testcase + ': ' + msg.message); 938 | } 939 | 940 | function addFailure(testcase, msg) { 941 | _super.addFailure.call(this, testcase, msg); 942 | this.logger.error(testcase + ': ' + msg.message + ' ' + msg.template, msg.args); 943 | } 944 | 945 | function addError(testcase, error) { 946 | _super.addError.call(this, testcase, error); 947 | this.logger.error(testcase + ' threw an error. ' + error); 948 | } 949 | 950 | function startTest(testcase) { 951 | _super.startTest.call(this, testcase); 952 | this.logger.debug('Started testcase ' + testcase + '.'); 953 | } 954 | 955 | function stopTest(testcase) { 956 | this.logger.debug('Completed testcase ' + testcase + '.'); 957 | } 958 | 959 | function pauseTest(testcase) { 960 | this.logger.info('Paused testcase ' + testcase + '.'); 961 | } 962 | 963 | function restartTest(testcase) { 964 | this.logger.info('Restarted testcase ' + testcase + '.'); 965 | } 966 | 967 | function startSuite(suite) { 968 | this.logger.info('Started suite ' + suite + '.'); 969 | } 970 | 971 | function stopSuite(suite) { 972 | this.logger.info('Completed suite ' + suite + '.'); 973 | } 974 | 975 | function start(t0) { 976 | _super.start.call(this, t0); 977 | this.logger.info('Started tests.'); 978 | } 979 | 980 | function stop(t1) { 981 | _super.stop.call(this, t1); 982 | this.logger.info('Completed tests in ' + ((t1 - this.t0)/1000) + 's.'); 983 | this.logger.info(this.toString() + '.'); 984 | } 985 | 986 | p.addAssertion = addAssertion; 987 | p.addSkip = addSkip; 988 | p.addFailure = addFailure; 989 | p.addError = addError; 990 | p.startTest = startTest; 991 | p.stopTest = stopTest; 992 | p.pauseTest = pauseTest; 993 | p.restartTest = restartTest; 994 | p.startSuite = startSuite; 995 | p.stopSuite = stopSuite; 996 | p.start = start; 997 | p.stop = stop; 998 | })(ConsoleTestResult.prototype); 999 | 1000 | 1001 | Console.TestResult = ConsoleTestResult; 1002 | var UI = (function() { 1003 | function printf(template, args, inspector) { 1004 | var parts = [], m, 1005 | regexp = /(^%|.%)([a-zA-Z])/, 1006 | args = args.splice(0); // clone args 1007 | 1008 | inspector = inspector || String; 1009 | 1010 | if (template.length <= 0) { 1011 | return ''; 1012 | } 1013 | while (m = regexp.exec(template)) { 1014 | var match = m[0], index = m.index, type, arg; 1015 | 1016 | if (match.indexOf('%%') === 0) { 1017 | parts.push(template.substr(0, index)); 1018 | parts.push(match.substr(1)); 1019 | } else { 1020 | parts.push(template.substr(0, match.indexOf('%' === 0) ? index + 1 : index)); 1021 | type = m[2]; 1022 | arg = args.shift(); 1023 | arg = inspector(arg, type); 1024 | parts.push(arg); 1025 | } 1026 | template = template.substr(index + match.length); 1027 | } 1028 | parts.push(template); 1029 | return parts.join(''); 1030 | } 1031 | 1032 | return { 1033 | printf: printf, 1034 | Console: Console 1035 | }; 1036 | })(); 1037 | Evidence.UI = UI; 1038 | 1039 | var defaultLoader = new TestLoader(); 1040 | Evidence.defaultLoader = defaultLoader; 1041 | 1042 | global.Evidence = Evidence; 1043 | 1044 | if (global.location) { 1045 | global.onload = function() { 1046 | if (typeof originalOnload === 'function') { 1047 | originalOnload.call(global); 1048 | } 1049 | AutoRunner.run(); 1050 | }; 1051 | } else if (global.arguments) { 1052 | var runtime = java.lang.Runtime.getRuntime(); 1053 | var thread = new java.lang.Thread(function() { 1054 | AutoRunner.run(); 1055 | }); 1056 | runtime.addShutdownHook(thread); 1057 | } 1058 | 1059 | })(this); 1060 | --------------------------------------------------------------------------------