├── .gitignore ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── index.html ├── jsconfig.json ├── package.json ├── server ├── api.js ├── app.js ├── upload-multer.js ├── upload-multiparty.js └── www ├── src ├── imgs │ ├── lena_std.bmp │ ├── lena_std.gif │ ├── lena_std.ico │ ├── lena_std.jpg │ ├── lena_std.png │ └── lena_std.tif ├── index.html ├── libs │ ├── Zepto-1.2.0.js │ ├── demo.css │ ├── demo.js │ ├── es6-promise.js │ ├── image-compress.js │ ├── init.js │ ├── jQuery-3.1.0.js │ └── whatwg-fetch.js ├── upload-blob-fetch.html ├── upload-blob-jQuery.html ├── upload-blob-xhr.html └── upload-blob-zepto.html └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # upload images 40 | src/imgs/upload/* 41 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "启动", 6 | "type": "node", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/server/www", 9 | "stopOnEntry": false, 10 | "args": [], 11 | "cwd": "${workspaceRoot}", 12 | "preLaunchTask": null, 13 | "runtimeExecutable": null, 14 | "runtimeArgs": [ 15 | "--nolazy" 16 | ], 17 | "env": { 18 | "NODE_ENV": "development" 19 | }, 20 | "console": "internalConsole", 21 | "sourceMaps": false, 22 | "outDir": null 23 | }, 24 | { 25 | "name": "附加", 26 | "type": "node", 27 | "request": "attach", 28 | "port": 5858, 29 | "address": "localhost", 30 | "restart": false, 31 | "sourceMaps": false, 32 | "outDir": null, 33 | "localRoot": "${workspaceRoot}", 34 | "remoteRoot": null 35 | }, 36 | { 37 | "name": "附加到进程", 38 | "type": "node", 39 | "request": "attach", 40 | "processId": "${command.PickProcess}", 41 | "port": 5858, 42 | "sourceMaps": false, 43 | "outDir": null 44 | } 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jake Archibald 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # image-compress 2 | 🗜 Browser side image compress & upload example, Android 4.0 compatible. 3 | 4 | # Quick Look 5 | 6 | [https://blade254353074.github.io/image-compress/](https://blade254353074.github.io/image-compress/) 7 | 8 | This page contain the image compressing and uploading actions step by step. 9 | 10 | # How to start 11 | 12 | 1. Install dependency & start the server 13 | 14 | ```bash 15 | $ npm install 16 | 17 | $ npm run server 18 | ``` 19 | 20 | 2. Open [http://localhost:8080/](http://localhost:8080/) 21 | 22 | # Features 23 | 24 | **1. File upload test server** 25 | 26 | I use two api interface to recive image file: 27 | 28 | * /api/upload/multiparty 29 | 30 | Using [expressjs/node-multiparty](https://github.com/expressjs/node-multiparty), 31 | 32 | file at `/server/upload-multiparty.js`. 33 | 34 | * /api/upload/multer 35 | 36 | Using [expressjs/multer](https://github.com/expressjs/multer), 37 | 38 | file at `/server/upload-multer.js`. 39 | 40 | And, I use [expressjs/cors](https://github.com/expressjs/cors) to make sure all the requests can be sent by browser, whatever crossing domain. 41 | 42 | **2. Browser side** 43 | 44 | * FilReader 45 | * window.URL 46 | * Canvas 47 | * Blob 48 | * BlobBuilder 49 | * atob 50 | * Uint8Array 51 | * [multipart/form-data](https://tools.ietf.org/html/rfc7578) 52 | * FormData 53 | * XMLHttpRequest 54 | * Fetch 55 | 56 | For details to see the article at [https://sebastianblade.com/browser-side-image-compress-and-upload/](https://sebastianblade.com/browser-side-image-compress-and-upload/). 57 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Browser side image compression demo 9 | 10 | 11 | 12 |
13 |

Browser side image compression steps show case:

14 |
15 |

1. Get file object from input[type="file"][accept="image/*"] control.

16 | 17 |
18 | object File: 19 |

20 |
21 | 22 |
Or get lena_std file directly:
23 |
24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 |

2. Use window.URL/FileReader to get image url(DataURL/window.URL), and load by Image DOM object.

33 | Image 34 |
35 | dataURL/BlobURL: 36 |

37 |
38 | 39 | 40 |
41 |
42 |

3. Draw Image to the canvas.

43 |
44 | 45 |
46 |
47 |

4. Use canvas.toDataURL to compress the image, and export the base-64 encode dataURL string.

48 |
49 | mimeType: 50 |

51 | sourceFileSize: 52 |

53 | CompressedFileSize: 54 |

55 | dataURL: 56 |

57 |

58 | CompressedImage: 59 | CompressedImage 60 |

61 | CompressionDuring: 62 | 63 |
64 | 81 | 82 |
83 |
84 |
85 |
86 |

5. Decode the base-64 string to binaryString by using window.atob.

87 |
88 | Base64-ContentType: 89 |

90 | pureBase64Data: 91 |

92 | binaryString: 93 |

 94 |         
 95 |       
96 |
97 |
98 |

6. Concat the binaryString to multipart format string.

99 |
100 | multipart/form-data binaryString: 101 |

102 |         
103 |       
104 |
105 |
106 |

7. Convert the multipart format string to ArrayBuffer by using Uint8Array.

107 |
108 | Uint8Array Buffer: 109 |

110 | 111 |
112 |
113 |
114 |
115 |

116 | Notice: If you want to upload file to the server.
117 | Please try to start with the local server to test.
118 | https://github.com/blade254353074/image-compress 119 |

120 |

0x01. Send the arrayBuffer by XMLHttpRequest.(After step 7)

121 |

122 |       
123 |       
124 |     
125 |
126 |

0x02. Send the blob by XMLHttpRequest and FormData.(After step 4)

127 |

128 |       
129 |       
130 |     
131 | 132 |
133 |

0x03. Use form to upload file.(For comparison)

134 | 135 |
136 | 137 | 138 |
139 |
140 | 141 | 142 |
143 |
144 |
145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // 请访问 https://go.microsoft.com/fwlink/?LinkId=759670 3 | // 参阅有关 jsconfig.json 格式的文档 4 | "exclude": [ 5 | "node_modules", 6 | "bower_components", 7 | "jspm_packages", 8 | "tmp", 9 | "temp" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "image-compress", 3 | "version": "1.0.0", 4 | "description": "Browser side image compress & upload example, Android 4.0 compatible.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "browser-sync start -s --port 8080 -f \"./src,.index.html\" --no-ui", 8 | "server": "cross-env NODE_ENV=development node server/www", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [ 12 | "image", 13 | "compress", 14 | "BrowserSide", 15 | "Android4.0" 16 | ], 17 | "author": "SebastianBlade", 18 | "repository": { 19 | "url": "https://github.com/blade254353074/image-compress.git" 20 | }, 21 | "license": "MIT", 22 | "devDependencies": { 23 | "browser-sync": "^2.15.0", 24 | "cross-env": "^2.0.1" 25 | }, 26 | "dependencies": { 27 | "body-parser": "^1.15.2", 28 | "cookie-parser": "^1.4.3", 29 | "cors": "^2.8.1", 30 | "express": "^4.14.0", 31 | "file-type": "^3.8.0", 32 | "log4js": "^0.6.38", 33 | "multer": "^1.2.0", 34 | "multiparty": "^4.1.2", 35 | "read-chunk": "^2.0.0", 36 | "shortid": "^2.2.6" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/api.js: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | var router = express.Router() 3 | 4 | router.post('/upload/multiparty', require('./upload-multiparty')) 5 | router.post('/upload/multer', require('./upload-multer')) 6 | 7 | module.exports = router 8 | -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | var bodyParser = require('body-parser') 2 | var cookieParser = require('cookie-parser') 3 | var cors = require('cors') 4 | var express = require('express') 5 | var log4js = require('log4js') 6 | var path = require('path') 7 | 8 | var logger = log4js.getLogger('app') 9 | var httpLogger = log4js.connectLogger(log4js.getLogger('http'), { level: 'auto' }) 10 | var app = express() 11 | 12 | app.use(httpLogger) 13 | app.use(bodyParser.json()) 14 | app.use(bodyParser.urlencoded({ extended: true })) 15 | app.use(cookieParser()) 16 | app.use(cors({ 17 | origin: function (origin, callback) { 18 | callback(null, true) 19 | }, 20 | credentials: true 21 | })) 22 | app.use(express.static(path.resolve(__dirname, '../src'))) 23 | 24 | app.use('/api', require('./api')) 25 | 26 | // catch 404 and forward to error handler 27 | app.use((req, res, next) => { 28 | var err = new Error('Not Found') 29 | err.status = 404 30 | next(err) 31 | }) 32 | 33 | // error handlers 34 | 35 | // development error handler 36 | // will print stacktrace 37 | if (app.get('env') === 'development') { 38 | app.use((err, req, res, next) => { 39 | var status = err.status || 500 40 | logger.error(err) 41 | res.status(status) 42 | res.send({ 43 | message: err.message, 44 | status, 45 | stack: err.stack 46 | }) 47 | }) 48 | } 49 | 50 | // production error handler 51 | // no stacktraces leaked to user 52 | app.use((err, req, res, next) => { 53 | var status = err.status || 500 54 | logger.error(err) 55 | res.status(status) 56 | res.send({ 57 | error: { 58 | message: err.message, 59 | status 60 | } 61 | }) 62 | }) 63 | 64 | module.exports = app 65 | -------------------------------------------------------------------------------- /server/upload-multer.js: -------------------------------------------------------------------------------- 1 | var fileType = require('file-type') 2 | var fs = require('fs') 3 | var multer = require('multer') 4 | var path = require('path') 5 | var readChunk = require('read-chunk') 6 | var shortid = require('shortid') 7 | 8 | var uploadDir = path.resolve(process.cwd(), 'src/imgs/upload') 9 | var maxFilesSize = 100 * 1000 * 1000 // 100MB 10 | var storage = multer.diskStorage({ 11 | destination: function (req, file, cb) { 12 | cb(null, uploadDir) 13 | }, 14 | filename: function (req, file, cb) { 15 | var mimetype = file.mimetype 16 | cb(null, shortid.generate() + '.' + mimetype.substr(mimetype.lastIndexOf('/') + 1)) 17 | } 18 | }) 19 | var upload = multer({ 20 | storage: storage, 21 | limits: { 22 | fieldSize: maxFilesSize 23 | } 24 | }) 25 | 26 | function fileQualify (filePath, formats) { 27 | // https://github.com/sindresorhus/file-type 28 | var buffer = readChunk.sync(filePath, 0, 262) 29 | var type = fileType(buffer) 30 | if (formats.indexOf(type && type.ext) === -1) { 31 | unlinkFile(filePath) 32 | throw new Error('Upload error, wrong file type,just accept jpg, jpeg, gif, png.') 33 | } 34 | } 35 | 36 | function unlinkFile (filePath) { 37 | fs.unlink(filePath, function (err) { 38 | if (err) { 39 | throw (err) 40 | } 41 | }) 42 | } 43 | 44 | function uploadMiddleware (req, res) { 45 | return new Promise(function (resolve, reject) { 46 | var uploader = upload.single('file') 47 | uploader(req, res, function (err) { 48 | if (err) { 49 | reject(err) 50 | } 51 | resolve(req, res) 52 | }) 53 | }) 54 | } 55 | 56 | module.exports = function uploadMulter (req, res, next) { 57 | Promise.resolve() 58 | .then(function () { 59 | return uploadMiddleware(req, res) 60 | }) 61 | .then(function (req, res) { 62 | if (req.file) { 63 | fileQualify(req.file.path, ['jpg', 'png', 'gif']) 64 | } else { 65 | throw new Error('Upload error, empty file field.') 66 | } 67 | }) 68 | .then(function () { 69 | res.send({ 70 | path: '/imgs/upload/' + req.file.filename 71 | }) 72 | }) 73 | .catch(function (err) { 74 | err.status = err.status || 400 75 | next(err) 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /server/upload-multiparty.js: -------------------------------------------------------------------------------- 1 | var fileType = require('file-type') 2 | var fs = require('fs') 3 | var multiparty = require('multiparty') 4 | var path = require('path') 5 | var readChunk = require('read-chunk') 6 | 7 | var uploadDir = path.resolve(process.cwd(), 'src/imgs/upload') 8 | var maxFilesSize = 100 * 1000 * 1000 // 100MB 9 | 10 | function uploadMiddleware (req, res) { 11 | return new Promise(function (resolve, reject) { 12 | var form = new multiparty.Form({ 13 | maxFilesSize: maxFilesSize, 14 | uploadDir: uploadDir 15 | }) 16 | 17 | form.on('error', function (err) { 18 | var error = new Error(err.message) 19 | error.status = err.statusCode 20 | reject(error) 21 | }) 22 | 23 | form.parse(req, function (err, fields, files) { 24 | if (err) { 25 | err.message = 'Upload error,file size over limit (100MB).' 26 | return reject(err) 27 | } 28 | resolve(files) 29 | }) 30 | }) 31 | } 32 | 33 | function unlinkFile (filePath) { 34 | fs.unlink(filePath, function (err) { 35 | if (err) { 36 | throw (err) 37 | } 38 | }) 39 | } 40 | 41 | function fileQualify (filePath, formats) { 42 | // https://github.com/sindresorhus/file-type 43 | var buffer = readChunk.sync(filePath, 0, 262) 44 | var type = fileType(buffer) 45 | if (formats.indexOf(type && type.ext) === -1) { 46 | unlinkFile(filePath) 47 | throw new Error('Upload error, wrong file type,just accept jpg, jpeg, gif, png.') 48 | } 49 | } 50 | 51 | module.exports = function uploadMultiparty (req, res, next) { 52 | Promise.resolve() 53 | .then(function () { 54 | return uploadMiddleware(req, res) 55 | }) 56 | .then(function (files) { 57 | if (!files.file) { 58 | throw new Error('Upload error, empty file field.') 59 | } 60 | return files.file[0] 61 | }) 62 | .then(function (file) { 63 | const fileName = file.originalFilename 64 | const filePath = file.path // temporary file path 65 | fileQualify(filePath, ['jpg', 'png', 'gif']) 66 | return path.basename(filePath) 67 | }) 68 | .then(function (filename) { 69 | res.send({ 70 | path: '/imgs/upload/' + filename 71 | }) 72 | }) 73 | .catch(function (err) { 74 | err.status = err.status || 400 75 | next(err) 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /server/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var app = require('./app.js') 4 | var express = require('express') 5 | var http = require('http') 6 | var logger = require('log4js').getLogger('app') 7 | 8 | function normalizePort (val) { 9 | var port = parseInt(val, 10) 10 | 11 | if (isNaN(port)) { 12 | return val 13 | } 14 | 15 | if (port >= 0) { 16 | return port 17 | } 18 | 19 | return false 20 | } 21 | 22 | var port = normalizePort(process.env.PORT || 8080) 23 | var server = http.createServer(app) 24 | 25 | server 26 | .listen(port) 27 | .on('error', function onError (error) { 28 | if (error.syscall !== 'listen') { 29 | throw error 30 | } 31 | 32 | var bind = typeof port === 'string' 33 | ? 'Pipe ' + port 34 | : 'Port ' + port 35 | 36 | switch (error.code) { 37 | case 'EACCES': 38 | logger.error(bind + ' requires elevated privileges') 39 | process.exit(1) 40 | break 41 | case 'EADDRINUSE': 42 | logger.error(bind + ' is already in use') 43 | process.exit(1) 44 | break 45 | default: 46 | throw error 47 | } 48 | }) 49 | .on('listening', function onListening () { 50 | var addr = server.address() 51 | var bind = typeof addr === 'string' 52 | ? 'pipe ' + addr 53 | : 'port ' + addr.port 54 | logger.info('Listening on ' + bind) 55 | }) 56 | -------------------------------------------------------------------------------- /src/imgs/lena_std.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/image-compress/a899541c578b03805e5ff49fa32c84db4af96f0b/src/imgs/lena_std.bmp -------------------------------------------------------------------------------- /src/imgs/lena_std.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/image-compress/a899541c578b03805e5ff49fa32c84db4af96f0b/src/imgs/lena_std.gif -------------------------------------------------------------------------------- /src/imgs/lena_std.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/image-compress/a899541c578b03805e5ff49fa32c84db4af96f0b/src/imgs/lena_std.ico -------------------------------------------------------------------------------- /src/imgs/lena_std.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/image-compress/a899541c578b03805e5ff49fa32c84db4af96f0b/src/imgs/lena_std.jpg -------------------------------------------------------------------------------- /src/imgs/lena_std.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/image-compress/a899541c578b03805e5ff49fa32c84db4af96f0b/src/imgs/lena_std.png -------------------------------------------------------------------------------- /src/imgs/lena_std.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/image-compress/a899541c578b03805e5ff49fa32c84db4af96f0b/src/imgs/lena_std.tif -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Browser side image compression demo 9 | 10 | 11 | 12 |
13 |

Browser side image compression steps show case:

14 |
15 |

1. Get file object from input[type="file"][accept="image/*"] control.

16 | 17 |
18 | object File: 19 |

20 |
21 | 22 |
Or get lena_std file directly:
23 |
24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |
32 |

2. Use window.URL/FileReader to get image url(DataURL/window.URL), and load by Image DOM object.

33 | Image 34 |
35 | dataURL/BlobURL: 36 |

37 |
38 | 39 | 40 |
41 |
42 |

3. Draw Image to the canvas.

43 |
44 | 45 |
46 |
47 |

4. Use canvas.toDataURL to compress the image, and export the base-64 encode dataURL string.

48 |
49 | mimeType: 50 |

51 | sourceFileSize: 52 |

53 | CompressedFileSize: 54 |

55 | dataURL: 56 |

57 |

58 | CompressedImage: 59 | CompressedImage 60 |

61 | CompressionDuring: 62 | 63 |
64 | 81 | 82 |
83 |
84 |
85 |
86 |

5. Decode the base-64 string to binaryString by using window.atob.

87 |
88 | Base64-ContentType: 89 |

90 | pureBase64Data: 91 |

92 | binaryString: 93 |

 94 |         
 95 |       
96 |
97 |
98 |

6. Concat the binaryString to multipart format string.

99 |
100 | multipart/form-data binaryString: 101 |

102 |         
103 |       
104 |
105 |
106 |

7. Convert the multipart format string to ArrayBuffer by using Uint8Array.

107 |
108 | Uint8Array Buffer: 109 |

110 | 111 |
112 |
113 |
114 |
115 |

116 | Notice: If you want to upload file to the server.
117 | Please try to start with the local server to test.
118 | https://github.com/blade254353074/image-compress 119 |

120 |

0x01. Send the arrayBuffer by XMLHttpRequest.(After step 7)

121 |

122 |       
123 |       
124 |     
125 |
126 |

0x02. Send the blob by XMLHttpRequest and FormData.(After step 4)

127 |

128 |       
129 |       
130 |     
131 | 132 |
133 |

0x03. Use form to upload file.(For comparison)

134 | 135 |
136 | 137 | 138 |
139 |
140 | 141 | 142 |
143 |
144 |
145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /src/libs/Zepto-1.2.0.js: -------------------------------------------------------------------------------- 1 | /* Zepto v1.2.0 - zepto event ajax form ie - zeptojs.com/license */ 2 | (function(global, factory) { 3 | if (typeof define === 'function' && define.amd) 4 | define(function() { return factory(global) }) 5 | else 6 | factory(global) 7 | }(this, function(window) { 8 | var Zepto = (function() { 9 | var undefined, key, $, classList, emptyArray = [], concat = emptyArray.concat, filter = emptyArray.filter, slice = emptyArray.slice, 10 | document = window.document, 11 | elementDisplay = {}, classCache = {}, 12 | cssNumber = { 'column-count': 1, 'columns': 1, 'font-weight': 1, 'line-height': 1,'opacity': 1, 'z-index': 1, 'zoom': 1 }, 13 | fragmentRE = /^\s*<(\w+|!)[^>]*>/, 14 | singleTagRE = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, 15 | tagExpanderRE = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, 16 | rootNodeRE = /^(?:body|html)$/i, 17 | capitalRE = /([A-Z])/g, 18 | 19 | // special attributes that should be get/set via method calls 20 | methodAttributes = ['val', 'css', 'html', 'text', 'data', 'width', 'height', 'offset'], 21 | 22 | adjacencyOperators = [ 'after', 'prepend', 'before', 'append' ], 23 | table = document.createElement('table'), 24 | tableRow = document.createElement('tr'), 25 | containers = { 26 | 'tr': document.createElement('tbody'), 27 | 'tbody': table, 'thead': table, 'tfoot': table, 28 | 'td': tableRow, 'th': tableRow, 29 | '*': document.createElement('div') 30 | }, 31 | readyRE = /complete|loaded|interactive/, 32 | simpleSelectorRE = /^[\w-]*$/, 33 | class2type = {}, 34 | toString = class2type.toString, 35 | zepto = {}, 36 | camelize, uniq, 37 | tempParent = document.createElement('div'), 38 | propMap = { 39 | 'tabindex': 'tabIndex', 40 | 'readonly': 'readOnly', 41 | 'for': 'htmlFor', 42 | 'class': 'className', 43 | 'maxlength': 'maxLength', 44 | 'cellspacing': 'cellSpacing', 45 | 'cellpadding': 'cellPadding', 46 | 'rowspan': 'rowSpan', 47 | 'colspan': 'colSpan', 48 | 'usemap': 'useMap', 49 | 'frameborder': 'frameBorder', 50 | 'contenteditable': 'contentEditable' 51 | }, 52 | isArray = Array.isArray || 53 | function(object){ return object instanceof Array } 54 | 55 | zepto.matches = function(element, selector) { 56 | if (!selector || !element || element.nodeType !== 1) return false 57 | var matchesSelector = element.matches || element.webkitMatchesSelector || 58 | element.mozMatchesSelector || element.oMatchesSelector || 59 | element.matchesSelector 60 | if (matchesSelector) return matchesSelector.call(element, selector) 61 | // fall back to performing a selector: 62 | var match, parent = element.parentNode, temp = !parent 63 | if (temp) (parent = tempParent).appendChild(element) 64 | match = ~zepto.qsa(parent, selector).indexOf(element) 65 | temp && tempParent.removeChild(element) 66 | return match 67 | } 68 | 69 | function type(obj) { 70 | return obj == null ? String(obj) : 71 | class2type[toString.call(obj)] || "object" 72 | } 73 | 74 | function isFunction(value) { return type(value) == "function" } 75 | function isWindow(obj) { return obj != null && obj == obj.window } 76 | function isDocument(obj) { return obj != null && obj.nodeType == obj.DOCUMENT_NODE } 77 | function isObject(obj) { return type(obj) == "object" } 78 | function isPlainObject(obj) { 79 | return isObject(obj) && !isWindow(obj) && Object.getPrototypeOf(obj) == Object.prototype 80 | } 81 | 82 | function likeArray(obj) { 83 | var length = !!obj && 'length' in obj && obj.length, 84 | type = $.type(obj) 85 | 86 | return 'function' != type && !isWindow(obj) && ( 87 | 'array' == type || length === 0 || 88 | (typeof length == 'number' && length > 0 && (length - 1) in obj) 89 | ) 90 | } 91 | 92 | function compact(array) { return filter.call(array, function(item){ return item != null }) } 93 | function flatten(array) { return array.length > 0 ? $.fn.concat.apply([], array) : array } 94 | camelize = function(str){ return str.replace(/-+(.)?/g, function(match, chr){ return chr ? chr.toUpperCase() : '' }) } 95 | function dasherize(str) { 96 | return str.replace(/::/g, '/') 97 | .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') 98 | .replace(/([a-z\d])([A-Z])/g, '$1_$2') 99 | .replace(/_/g, '-') 100 | .toLowerCase() 101 | } 102 | uniq = function(array){ return filter.call(array, function(item, idx){ return array.indexOf(item) == idx }) } 103 | 104 | function classRE(name) { 105 | return name in classCache ? 106 | classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')) 107 | } 108 | 109 | function maybeAddPx(name, value) { 110 | return (typeof value == "number" && !cssNumber[dasherize(name)]) ? value + "px" : value 111 | } 112 | 113 | function defaultDisplay(nodeName) { 114 | var element, display 115 | if (!elementDisplay[nodeName]) { 116 | element = document.createElement(nodeName) 117 | document.body.appendChild(element) 118 | display = getComputedStyle(element, '').getPropertyValue("display") 119 | element.parentNode.removeChild(element) 120 | display == "none" && (display = "block") 121 | elementDisplay[nodeName] = display 122 | } 123 | return elementDisplay[nodeName] 124 | } 125 | 126 | function children(element) { 127 | return 'children' in element ? 128 | slice.call(element.children) : 129 | $.map(element.childNodes, function(node){ if (node.nodeType == 1) return node }) 130 | } 131 | 132 | function Z(dom, selector) { 133 | var i, len = dom ? dom.length : 0 134 | for (i = 0; i < len; i++) this[i] = dom[i] 135 | this.length = len 136 | this.selector = selector || '' 137 | } 138 | 139 | // `$.zepto.fragment` takes a html string and an optional tag name 140 | // to generate DOM nodes from the given html string. 141 | // The generated DOM nodes are returned as an array. 142 | // This function can be overridden in plugins for example to make 143 | // it compatible with browsers that don't support the DOM fully. 144 | zepto.fragment = function(html, name, properties) { 145 | var dom, nodes, container 146 | 147 | // A special case optimization for a single tag 148 | if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1)) 149 | 150 | if (!dom) { 151 | if (html.replace) html = html.replace(tagExpanderRE, "<$1>") 152 | if (name === undefined) name = fragmentRE.test(html) && RegExp.$1 153 | if (!(name in containers)) name = '*' 154 | 155 | container = containers[name] 156 | container.innerHTML = '' + html 157 | dom = $.each(slice.call(container.childNodes), function(){ 158 | container.removeChild(this) 159 | }) 160 | } 161 | 162 | if (isPlainObject(properties)) { 163 | nodes = $(dom) 164 | $.each(properties, function(key, value) { 165 | if (methodAttributes.indexOf(key) > -1) nodes[key](value) 166 | else nodes.attr(key, value) 167 | }) 168 | } 169 | 170 | return dom 171 | } 172 | 173 | // `$.zepto.Z` swaps out the prototype of the given `dom` array 174 | // of nodes with `$.fn` and thus supplying all the Zepto functions 175 | // to the array. This method can be overridden in plugins. 176 | zepto.Z = function(dom, selector) { 177 | return new Z(dom, selector) 178 | } 179 | 180 | // `$.zepto.isZ` should return `true` if the given object is a Zepto 181 | // collection. This method can be overridden in plugins. 182 | zepto.isZ = function(object) { 183 | return object instanceof zepto.Z 184 | } 185 | 186 | // `$.zepto.init` is Zepto's counterpart to jQuery's `$.fn.init` and 187 | // takes a CSS selector and an optional context (and handles various 188 | // special cases). 189 | // This method can be overridden in plugins. 190 | zepto.init = function(selector, context) { 191 | var dom 192 | // If nothing given, return an empty Zepto collection 193 | if (!selector) return zepto.Z() 194 | // Optimize for string selectors 195 | else if (typeof selector == 'string') { 196 | selector = selector.trim() 197 | // If it's a html fragment, create nodes from it 198 | // Note: In both Chrome 21 and Firefox 15, DOM error 12 199 | // is thrown if the fragment doesn't begin with < 200 | if (selector[0] == '<' && fragmentRE.test(selector)) 201 | dom = zepto.fragment(selector, RegExp.$1, context), selector = null 202 | // If there's a context, create a collection on that context first, and select 203 | // nodes from there 204 | else if (context !== undefined) return $(context).find(selector) 205 | // If it's a CSS selector, use it to select nodes. 206 | else dom = zepto.qsa(document, selector) 207 | } 208 | // If a function is given, call it when the DOM is ready 209 | else if (isFunction(selector)) return $(document).ready(selector) 210 | // If a Zepto collection is given, just return it 211 | else if (zepto.isZ(selector)) return selector 212 | else { 213 | // normalize array if an array of nodes is given 214 | if (isArray(selector)) dom = compact(selector) 215 | // Wrap DOM nodes. 216 | else if (isObject(selector)) 217 | dom = [selector], selector = null 218 | // If it's a html fragment, create nodes from it 219 | else if (fragmentRE.test(selector)) 220 | dom = zepto.fragment(selector.trim(), RegExp.$1, context), selector = null 221 | // If there's a context, create a collection on that context first, and select 222 | // nodes from there 223 | else if (context !== undefined) return $(context).find(selector) 224 | // And last but no least, if it's a CSS selector, use it to select nodes. 225 | else dom = zepto.qsa(document, selector) 226 | } 227 | // create a new Zepto collection from the nodes found 228 | return zepto.Z(dom, selector) 229 | } 230 | 231 | // `$` will be the base `Zepto` object. When calling this 232 | // function just call `$.zepto.init, which makes the implementation 233 | // details of selecting nodes and creating Zepto collections 234 | // patchable in plugins. 235 | $ = function(selector, context){ 236 | return zepto.init(selector, context) 237 | } 238 | 239 | function extend(target, source, deep) { 240 | for (key in source) 241 | if (deep && (isPlainObject(source[key]) || isArray(source[key]))) { 242 | if (isPlainObject(source[key]) && !isPlainObject(target[key])) 243 | target[key] = {} 244 | if (isArray(source[key]) && !isArray(target[key])) 245 | target[key] = [] 246 | extend(target[key], source[key], deep) 247 | } 248 | else if (source[key] !== undefined) target[key] = source[key] 249 | } 250 | 251 | // Copy all but undefined properties from one or more 252 | // objects to the `target` object. 253 | $.extend = function(target){ 254 | var deep, args = slice.call(arguments, 1) 255 | if (typeof target == 'boolean') { 256 | deep = target 257 | target = args.shift() 258 | } 259 | args.forEach(function(arg){ extend(target, arg, deep) }) 260 | return target 261 | } 262 | 263 | // `$.zepto.qsa` is Zepto's CSS selector implementation which 264 | // uses `document.querySelectorAll` and optimizes for some special cases, like `#id`. 265 | // This method can be overridden in plugins. 266 | zepto.qsa = function(element, selector){ 267 | var found, 268 | maybeID = selector[0] == '#', 269 | maybeClass = !maybeID && selector[0] == '.', 270 | nameOnly = maybeID || maybeClass ? selector.slice(1) : selector, // Ensure that a 1 char tag name still gets checked 271 | isSimple = simpleSelectorRE.test(nameOnly) 272 | return (element.getElementById && isSimple && maybeID) ? // Safari DocumentFragment doesn't have getElementById 273 | ( (found = element.getElementById(nameOnly)) ? [found] : [] ) : 274 | (element.nodeType !== 1 && element.nodeType !== 9 && element.nodeType !== 11) ? [] : 275 | slice.call( 276 | isSimple && !maybeID && element.getElementsByClassName ? // DocumentFragment doesn't have getElementsByClassName/TagName 277 | maybeClass ? element.getElementsByClassName(nameOnly) : // If it's simple, it could be a class 278 | element.getElementsByTagName(selector) : // Or a tag 279 | element.querySelectorAll(selector) // Or it's not simple, and we need to query all 280 | ) 281 | } 282 | 283 | function filtered(nodes, selector) { 284 | return selector == null ? $(nodes) : $(nodes).filter(selector) 285 | } 286 | 287 | $.contains = document.documentElement.contains ? 288 | function(parent, node) { 289 | return parent !== node && parent.contains(node) 290 | } : 291 | function(parent, node) { 292 | while (node && (node = node.parentNode)) 293 | if (node === parent) return true 294 | return false 295 | } 296 | 297 | function funcArg(context, arg, idx, payload) { 298 | return isFunction(arg) ? arg.call(context, idx, payload) : arg 299 | } 300 | 301 | function setAttribute(node, name, value) { 302 | value == null ? node.removeAttribute(name) : node.setAttribute(name, value) 303 | } 304 | 305 | // access className property while respecting SVGAnimatedString 306 | function className(node, value){ 307 | var klass = node.className || '', 308 | svg = klass && klass.baseVal !== undefined 309 | 310 | if (value === undefined) return svg ? klass.baseVal : klass 311 | svg ? (klass.baseVal = value) : (node.className = value) 312 | } 313 | 314 | // "true" => true 315 | // "false" => false 316 | // "null" => null 317 | // "42" => 42 318 | // "42.5" => 42.5 319 | // "08" => "08" 320 | // JSON => parse if valid 321 | // String => self 322 | function deserializeValue(value) { 323 | try { 324 | return value ? 325 | value == "true" || 326 | ( value == "false" ? false : 327 | value == "null" ? null : 328 | +value + "" == value ? +value : 329 | /^[\[\{]/.test(value) ? $.parseJSON(value) : 330 | value ) 331 | : value 332 | } catch(e) { 333 | return value 334 | } 335 | } 336 | 337 | $.type = type 338 | $.isFunction = isFunction 339 | $.isWindow = isWindow 340 | $.isArray = isArray 341 | $.isPlainObject = isPlainObject 342 | 343 | $.isEmptyObject = function(obj) { 344 | var name 345 | for (name in obj) return false 346 | return true 347 | } 348 | 349 | $.isNumeric = function(val) { 350 | var num = Number(val), type = typeof val 351 | return val != null && type != 'boolean' && 352 | (type != 'string' || val.length) && 353 | !isNaN(num) && isFinite(num) || false 354 | } 355 | 356 | $.inArray = function(elem, array, i){ 357 | return emptyArray.indexOf.call(array, elem, i) 358 | } 359 | 360 | $.camelCase = camelize 361 | $.trim = function(str) { 362 | return str == null ? "" : String.prototype.trim.call(str) 363 | } 364 | 365 | // plugin compatibility 366 | $.uuid = 0 367 | $.support = { } 368 | $.expr = { } 369 | $.noop = function() {} 370 | 371 | $.map = function(elements, callback){ 372 | var value, values = [], i, key 373 | if (likeArray(elements)) 374 | for (i = 0; i < elements.length; i++) { 375 | value = callback(elements[i], i) 376 | if (value != null) values.push(value) 377 | } 378 | else 379 | for (key in elements) { 380 | value = callback(elements[key], key) 381 | if (value != null) values.push(value) 382 | } 383 | return flatten(values) 384 | } 385 | 386 | $.each = function(elements, callback){ 387 | var i, key 388 | if (likeArray(elements)) { 389 | for (i = 0; i < elements.length; i++) 390 | if (callback.call(elements[i], i, elements[i]) === false) return elements 391 | } else { 392 | for (key in elements) 393 | if (callback.call(elements[key], key, elements[key]) === false) return elements 394 | } 395 | 396 | return elements 397 | } 398 | 399 | $.grep = function(elements, callback){ 400 | return filter.call(elements, callback) 401 | } 402 | 403 | if (window.JSON) $.parseJSON = JSON.parse 404 | 405 | // Populate the class2type map 406 | $.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { 407 | class2type[ "[object " + name + "]" ] = name.toLowerCase() 408 | }) 409 | 410 | // Define methods that will be available on all 411 | // Zepto collections 412 | $.fn = { 413 | constructor: zepto.Z, 414 | length: 0, 415 | 416 | // Because a collection acts like an array 417 | // copy over these useful array functions. 418 | forEach: emptyArray.forEach, 419 | reduce: emptyArray.reduce, 420 | push: emptyArray.push, 421 | sort: emptyArray.sort, 422 | splice: emptyArray.splice, 423 | indexOf: emptyArray.indexOf, 424 | concat: function(){ 425 | var i, value, args = [] 426 | for (i = 0; i < arguments.length; i++) { 427 | value = arguments[i] 428 | args[i] = zepto.isZ(value) ? value.toArray() : value 429 | } 430 | return concat.apply(zepto.isZ(this) ? this.toArray() : this, args) 431 | }, 432 | 433 | // `map` and `slice` in the jQuery API work differently 434 | // from their array counterparts 435 | map: function(fn){ 436 | return $($.map(this, function(el, i){ return fn.call(el, i, el) })) 437 | }, 438 | slice: function(){ 439 | return $(slice.apply(this, arguments)) 440 | }, 441 | 442 | ready: function(callback){ 443 | // need to check if document.body exists for IE as that browser reports 444 | // document ready when it hasn't yet created the body element 445 | if (readyRE.test(document.readyState) && document.body) callback($) 446 | else document.addEventListener('DOMContentLoaded', function(){ callback($) }, false) 447 | return this 448 | }, 449 | get: function(idx){ 450 | return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length] 451 | }, 452 | toArray: function(){ return this.get() }, 453 | size: function(){ 454 | return this.length 455 | }, 456 | remove: function(){ 457 | return this.each(function(){ 458 | if (this.parentNode != null) 459 | this.parentNode.removeChild(this) 460 | }) 461 | }, 462 | each: function(callback){ 463 | emptyArray.every.call(this, function(el, idx){ 464 | return callback.call(el, idx, el) !== false 465 | }) 466 | return this 467 | }, 468 | filter: function(selector){ 469 | if (isFunction(selector)) return this.not(this.not(selector)) 470 | return $(filter.call(this, function(element){ 471 | return zepto.matches(element, selector) 472 | })) 473 | }, 474 | add: function(selector,context){ 475 | return $(uniq(this.concat($(selector,context)))) 476 | }, 477 | is: function(selector){ 478 | return this.length > 0 && zepto.matches(this[0], selector) 479 | }, 480 | not: function(selector){ 481 | var nodes=[] 482 | if (isFunction(selector) && selector.call !== undefined) 483 | this.each(function(idx){ 484 | if (!selector.call(this,idx)) nodes.push(this) 485 | }) 486 | else { 487 | var excludes = typeof selector == 'string' ? this.filter(selector) : 488 | (likeArray(selector) && isFunction(selector.item)) ? slice.call(selector) : $(selector) 489 | this.forEach(function(el){ 490 | if (excludes.indexOf(el) < 0) nodes.push(el) 491 | }) 492 | } 493 | return $(nodes) 494 | }, 495 | has: function(selector){ 496 | return this.filter(function(){ 497 | return isObject(selector) ? 498 | $.contains(this, selector) : 499 | $(this).find(selector).size() 500 | }) 501 | }, 502 | eq: function(idx){ 503 | return idx === -1 ? this.slice(idx) : this.slice(idx, + idx + 1) 504 | }, 505 | first: function(){ 506 | var el = this[0] 507 | return el && !isObject(el) ? el : $(el) 508 | }, 509 | last: function(){ 510 | var el = this[this.length - 1] 511 | return el && !isObject(el) ? el : $(el) 512 | }, 513 | find: function(selector){ 514 | var result, $this = this 515 | if (!selector) result = $() 516 | else if (typeof selector == 'object') 517 | result = $(selector).filter(function(){ 518 | var node = this 519 | return emptyArray.some.call($this, function(parent){ 520 | return $.contains(parent, node) 521 | }) 522 | }) 523 | else if (this.length == 1) result = $(zepto.qsa(this[0], selector)) 524 | else result = this.map(function(){ return zepto.qsa(this, selector) }) 525 | return result 526 | }, 527 | closest: function(selector, context){ 528 | var nodes = [], collection = typeof selector == 'object' && $(selector) 529 | this.each(function(_, node){ 530 | while (node && !(collection ? collection.indexOf(node) >= 0 : zepto.matches(node, selector))) 531 | node = node !== context && !isDocument(node) && node.parentNode 532 | if (node && nodes.indexOf(node) < 0) nodes.push(node) 533 | }) 534 | return $(nodes) 535 | }, 536 | parents: function(selector){ 537 | var ancestors = [], nodes = this 538 | while (nodes.length > 0) 539 | nodes = $.map(nodes, function(node){ 540 | if ((node = node.parentNode) && !isDocument(node) && ancestors.indexOf(node) < 0) { 541 | ancestors.push(node) 542 | return node 543 | } 544 | }) 545 | return filtered(ancestors, selector) 546 | }, 547 | parent: function(selector){ 548 | return filtered(uniq(this.pluck('parentNode')), selector) 549 | }, 550 | children: function(selector){ 551 | return filtered(this.map(function(){ return children(this) }), selector) 552 | }, 553 | contents: function() { 554 | return this.map(function() { return this.contentDocument || slice.call(this.childNodes) }) 555 | }, 556 | siblings: function(selector){ 557 | return filtered(this.map(function(i, el){ 558 | return filter.call(children(el.parentNode), function(child){ return child!==el }) 559 | }), selector) 560 | }, 561 | empty: function(){ 562 | return this.each(function(){ this.innerHTML = '' }) 563 | }, 564 | // `pluck` is borrowed from Prototype.js 565 | pluck: function(property){ 566 | return $.map(this, function(el){ return el[property] }) 567 | }, 568 | show: function(){ 569 | return this.each(function(){ 570 | this.style.display == "none" && (this.style.display = '') 571 | if (getComputedStyle(this, '').getPropertyValue("display") == "none") 572 | this.style.display = defaultDisplay(this.nodeName) 573 | }) 574 | }, 575 | replaceWith: function(newContent){ 576 | return this.before(newContent).remove() 577 | }, 578 | wrap: function(structure){ 579 | var func = isFunction(structure) 580 | if (this[0] && !func) 581 | var dom = $(structure).get(0), 582 | clone = dom.parentNode || this.length > 1 583 | 584 | return this.each(function(index){ 585 | $(this).wrapAll( 586 | func ? structure.call(this, index) : 587 | clone ? dom.cloneNode(true) : dom 588 | ) 589 | }) 590 | }, 591 | wrapAll: function(structure){ 592 | if (this[0]) { 593 | $(this[0]).before(structure = $(structure)) 594 | var children 595 | // drill down to the inmost element 596 | while ((children = structure.children()).length) structure = children.first() 597 | $(structure).append(this) 598 | } 599 | return this 600 | }, 601 | wrapInner: function(structure){ 602 | var func = isFunction(structure) 603 | return this.each(function(index){ 604 | var self = $(this), contents = self.contents(), 605 | dom = func ? structure.call(this, index) : structure 606 | contents.length ? contents.wrapAll(dom) : self.append(dom) 607 | }) 608 | }, 609 | unwrap: function(){ 610 | this.parent().each(function(){ 611 | $(this).replaceWith($(this).children()) 612 | }) 613 | return this 614 | }, 615 | clone: function(){ 616 | return this.map(function(){ return this.cloneNode(true) }) 617 | }, 618 | hide: function(){ 619 | return this.css("display", "none") 620 | }, 621 | toggle: function(setting){ 622 | return this.each(function(){ 623 | var el = $(this) 624 | ;(setting === undefined ? el.css("display") == "none" : setting) ? el.show() : el.hide() 625 | }) 626 | }, 627 | prev: function(selector){ return $(this.pluck('previousElementSibling')).filter(selector || '*') }, 628 | next: function(selector){ return $(this.pluck('nextElementSibling')).filter(selector || '*') }, 629 | html: function(html){ 630 | return 0 in arguments ? 631 | this.each(function(idx){ 632 | var originHtml = this.innerHTML 633 | $(this).empty().append( funcArg(this, html, idx, originHtml) ) 634 | }) : 635 | (0 in this ? this[0].innerHTML : null) 636 | }, 637 | text: function(text){ 638 | return 0 in arguments ? 639 | this.each(function(idx){ 640 | var newText = funcArg(this, text, idx, this.textContent) 641 | this.textContent = newText == null ? '' : ''+newText 642 | }) : 643 | (0 in this ? this.pluck('textContent').join("") : null) 644 | }, 645 | attr: function(name, value){ 646 | var result 647 | return (typeof name == 'string' && !(1 in arguments)) ? 648 | (0 in this && this[0].nodeType == 1 && (result = this[0].getAttribute(name)) != null ? result : undefined) : 649 | this.each(function(idx){ 650 | if (this.nodeType !== 1) return 651 | if (isObject(name)) for (key in name) setAttribute(this, key, name[key]) 652 | else setAttribute(this, name, funcArg(this, value, idx, this.getAttribute(name))) 653 | }) 654 | }, 655 | removeAttr: function(name){ 656 | return this.each(function(){ this.nodeType === 1 && name.split(' ').forEach(function(attribute){ 657 | setAttribute(this, attribute) 658 | }, this)}) 659 | }, 660 | prop: function(name, value){ 661 | name = propMap[name] || name 662 | return (1 in arguments) ? 663 | this.each(function(idx){ 664 | this[name] = funcArg(this, value, idx, this[name]) 665 | }) : 666 | (this[0] && this[0][name]) 667 | }, 668 | removeProp: function(name){ 669 | name = propMap[name] || name 670 | return this.each(function(){ delete this[name] }) 671 | }, 672 | data: function(name, value){ 673 | var attrName = 'data-' + name.replace(capitalRE, '-$1').toLowerCase() 674 | 675 | var data = (1 in arguments) ? 676 | this.attr(attrName, value) : 677 | this.attr(attrName) 678 | 679 | return data !== null ? deserializeValue(data) : undefined 680 | }, 681 | val: function(value){ 682 | if (0 in arguments) { 683 | if (value == null) value = "" 684 | return this.each(function(idx){ 685 | this.value = funcArg(this, value, idx, this.value) 686 | }) 687 | } else { 688 | return this[0] && (this[0].multiple ? 689 | $(this[0]).find('option').filter(function(){ return this.selected }).pluck('value') : 690 | this[0].value) 691 | } 692 | }, 693 | offset: function(coordinates){ 694 | if (coordinates) return this.each(function(index){ 695 | var $this = $(this), 696 | coords = funcArg(this, coordinates, index, $this.offset()), 697 | parentOffset = $this.offsetParent().offset(), 698 | props = { 699 | top: coords.top - parentOffset.top, 700 | left: coords.left - parentOffset.left 701 | } 702 | 703 | if ($this.css('position') == 'static') props['position'] = 'relative' 704 | $this.css(props) 705 | }) 706 | if (!this.length) return null 707 | if (document.documentElement !== this[0] && !$.contains(document.documentElement, this[0])) 708 | return {top: 0, left: 0} 709 | var obj = this[0].getBoundingClientRect() 710 | return { 711 | left: obj.left + window.pageXOffset, 712 | top: obj.top + window.pageYOffset, 713 | width: Math.round(obj.width), 714 | height: Math.round(obj.height) 715 | } 716 | }, 717 | css: function(property, value){ 718 | if (arguments.length < 2) { 719 | var element = this[0] 720 | if (typeof property == 'string') { 721 | if (!element) return 722 | return element.style[camelize(property)] || getComputedStyle(element, '').getPropertyValue(property) 723 | } else if (isArray(property)) { 724 | if (!element) return 725 | var props = {} 726 | var computedStyle = getComputedStyle(element, '') 727 | $.each(property, function(_, prop){ 728 | props[prop] = (element.style[camelize(prop)] || computedStyle.getPropertyValue(prop)) 729 | }) 730 | return props 731 | } 732 | } 733 | 734 | var css = '' 735 | if (type(property) == 'string') { 736 | if (!value && value !== 0) 737 | this.each(function(){ this.style.removeProperty(dasherize(property)) }) 738 | else 739 | css = dasherize(property) + ":" + maybeAddPx(property, value) 740 | } else { 741 | for (key in property) 742 | if (!property[key] && property[key] !== 0) 743 | this.each(function(){ this.style.removeProperty(dasherize(key)) }) 744 | else 745 | css += dasherize(key) + ':' + maybeAddPx(key, property[key]) + ';' 746 | } 747 | 748 | return this.each(function(){ this.style.cssText += ';' + css }) 749 | }, 750 | index: function(element){ 751 | return element ? this.indexOf($(element)[0]) : this.parent().children().indexOf(this[0]) 752 | }, 753 | hasClass: function(name){ 754 | if (!name) return false 755 | return emptyArray.some.call(this, function(el){ 756 | return this.test(className(el)) 757 | }, classRE(name)) 758 | }, 759 | addClass: function(name){ 760 | if (!name) return this 761 | return this.each(function(idx){ 762 | if (!('className' in this)) return 763 | classList = [] 764 | var cls = className(this), newName = funcArg(this, name, idx, cls) 765 | newName.split(/\s+/g).forEach(function(klass){ 766 | if (!$(this).hasClass(klass)) classList.push(klass) 767 | }, this) 768 | classList.length && className(this, cls + (cls ? " " : "") + classList.join(" ")) 769 | }) 770 | }, 771 | removeClass: function(name){ 772 | return this.each(function(idx){ 773 | if (!('className' in this)) return 774 | if (name === undefined) return className(this, '') 775 | classList = className(this) 776 | funcArg(this, name, idx, classList).split(/\s+/g).forEach(function(klass){ 777 | classList = classList.replace(classRE(klass), " ") 778 | }) 779 | className(this, classList.trim()) 780 | }) 781 | }, 782 | toggleClass: function(name, when){ 783 | if (!name) return this 784 | return this.each(function(idx){ 785 | var $this = $(this), names = funcArg(this, name, idx, className(this)) 786 | names.split(/\s+/g).forEach(function(klass){ 787 | (when === undefined ? !$this.hasClass(klass) : when) ? 788 | $this.addClass(klass) : $this.removeClass(klass) 789 | }) 790 | }) 791 | }, 792 | scrollTop: function(value){ 793 | if (!this.length) return 794 | var hasScrollTop = 'scrollTop' in this[0] 795 | if (value === undefined) return hasScrollTop ? this[0].scrollTop : this[0].pageYOffset 796 | return this.each(hasScrollTop ? 797 | function(){ this.scrollTop = value } : 798 | function(){ this.scrollTo(this.scrollX, value) }) 799 | }, 800 | scrollLeft: function(value){ 801 | if (!this.length) return 802 | var hasScrollLeft = 'scrollLeft' in this[0] 803 | if (value === undefined) return hasScrollLeft ? this[0].scrollLeft : this[0].pageXOffset 804 | return this.each(hasScrollLeft ? 805 | function(){ this.scrollLeft = value } : 806 | function(){ this.scrollTo(value, this.scrollY) }) 807 | }, 808 | position: function() { 809 | if (!this.length) return 810 | 811 | var elem = this[0], 812 | // Get *real* offsetParent 813 | offsetParent = this.offsetParent(), 814 | // Get correct offsets 815 | offset = this.offset(), 816 | parentOffset = rootNodeRE.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset() 817 | 818 | // Subtract element margins 819 | // note: when an element has margin: auto the offsetLeft and marginLeft 820 | // are the same in Safari causing offset.left to incorrectly be 0 821 | offset.top -= parseFloat( $(elem).css('margin-top') ) || 0 822 | offset.left -= parseFloat( $(elem).css('margin-left') ) || 0 823 | 824 | // Add offsetParent borders 825 | parentOffset.top += parseFloat( $(offsetParent[0]).css('border-top-width') ) || 0 826 | parentOffset.left += parseFloat( $(offsetParent[0]).css('border-left-width') ) || 0 827 | 828 | // Subtract the two offsets 829 | return { 830 | top: offset.top - parentOffset.top, 831 | left: offset.left - parentOffset.left 832 | } 833 | }, 834 | offsetParent: function() { 835 | return this.map(function(){ 836 | var parent = this.offsetParent || document.body 837 | while (parent && !rootNodeRE.test(parent.nodeName) && $(parent).css("position") == "static") 838 | parent = parent.offsetParent 839 | return parent 840 | }) 841 | } 842 | } 843 | 844 | // for now 845 | $.fn.detach = $.fn.remove 846 | 847 | // Generate the `width` and `height` functions 848 | ;['width', 'height'].forEach(function(dimension){ 849 | var dimensionProperty = 850 | dimension.replace(/./, function(m){ return m[0].toUpperCase() }) 851 | 852 | $.fn[dimension] = function(value){ 853 | var offset, el = this[0] 854 | if (value === undefined) return isWindow(el) ? el['inner' + dimensionProperty] : 855 | isDocument(el) ? el.documentElement['scroll' + dimensionProperty] : 856 | (offset = this.offset()) && offset[dimension] 857 | else return this.each(function(idx){ 858 | el = $(this) 859 | el.css(dimension, funcArg(this, value, idx, el[dimension]())) 860 | }) 861 | } 862 | }) 863 | 864 | function traverseNode(node, fun) { 865 | fun(node) 866 | for (var i = 0, len = node.childNodes.length; i < len; i++) 867 | traverseNode(node.childNodes[i], fun) 868 | } 869 | 870 | // Generate the `after`, `prepend`, `before`, `append`, 871 | // `insertAfter`, `insertBefore`, `appendTo`, and `prependTo` methods. 872 | adjacencyOperators.forEach(function(operator, operatorIndex) { 873 | var inside = operatorIndex % 2 //=> prepend, append 874 | 875 | $.fn[operator] = function(){ 876 | // arguments can be nodes, arrays of nodes, Zepto objects and HTML strings 877 | var argType, nodes = $.map(arguments, function(arg) { 878 | var arr = [] 879 | argType = type(arg) 880 | if (argType == "array") { 881 | arg.forEach(function(el) { 882 | if (el.nodeType !== undefined) return arr.push(el) 883 | else if ($.zepto.isZ(el)) return arr = arr.concat(el.get()) 884 | arr = arr.concat(zepto.fragment(el)) 885 | }) 886 | return arr 887 | } 888 | return argType == "object" || arg == null ? 889 | arg : zepto.fragment(arg) 890 | }), 891 | parent, copyByClone = this.length > 1 892 | if (nodes.length < 1) return this 893 | 894 | return this.each(function(_, target){ 895 | parent = inside ? target : target.parentNode 896 | 897 | // convert all methods to a "before" operation 898 | target = operatorIndex == 0 ? target.nextSibling : 899 | operatorIndex == 1 ? target.firstChild : 900 | operatorIndex == 2 ? target : 901 | null 902 | 903 | var parentInDocument = $.contains(document.documentElement, parent) 904 | 905 | nodes.forEach(function(node){ 906 | if (copyByClone) node = node.cloneNode(true) 907 | else if (!parent) return $(node).remove() 908 | 909 | parent.insertBefore(node, target) 910 | if (parentInDocument) traverseNode(node, function(el){ 911 | if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' && 912 | (!el.type || el.type === 'text/javascript') && !el.src){ 913 | var target = el.ownerDocument ? el.ownerDocument.defaultView : window 914 | target['eval'].call(target, el.innerHTML) 915 | } 916 | }) 917 | }) 918 | }) 919 | } 920 | 921 | // after => insertAfter 922 | // prepend => prependTo 923 | // before => insertBefore 924 | // append => appendTo 925 | $.fn[inside ? operator+'To' : 'insert'+(operatorIndex ? 'Before' : 'After')] = function(html){ 926 | $(html)[operator](this) 927 | return this 928 | } 929 | }) 930 | 931 | zepto.Z.prototype = Z.prototype = $.fn 932 | 933 | // Export internal API functions in the `$.zepto` namespace 934 | zepto.uniq = uniq 935 | zepto.deserializeValue = deserializeValue 936 | $.zepto = zepto 937 | 938 | return $ 939 | })() 940 | 941 | window.Zepto = Zepto 942 | window.$ === undefined && (window.$ = Zepto) 943 | 944 | ;(function($){ 945 | var _zid = 1, undefined, 946 | slice = Array.prototype.slice, 947 | isFunction = $.isFunction, 948 | isString = function(obj){ return typeof obj == 'string' }, 949 | handlers = {}, 950 | specialEvents={}, 951 | focusinSupported = 'onfocusin' in window, 952 | focus = { focus: 'focusin', blur: 'focusout' }, 953 | hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' } 954 | 955 | specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents' 956 | 957 | function zid(element) { 958 | return element._zid || (element._zid = _zid++) 959 | } 960 | function findHandlers(element, event, fn, selector) { 961 | event = parse(event) 962 | if (event.ns) var matcher = matcherFor(event.ns) 963 | return (handlers[zid(element)] || []).filter(function(handler) { 964 | return handler 965 | && (!event.e || handler.e == event.e) 966 | && (!event.ns || matcher.test(handler.ns)) 967 | && (!fn || zid(handler.fn) === zid(fn)) 968 | && (!selector || handler.sel == selector) 969 | }) 970 | } 971 | function parse(event) { 972 | var parts = ('' + event).split('.') 973 | return {e: parts[0], ns: parts.slice(1).sort().join(' ')} 974 | } 975 | function matcherFor(ns) { 976 | return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)') 977 | } 978 | 979 | function eventCapture(handler, captureSetting) { 980 | return handler.del && 981 | (!focusinSupported && (handler.e in focus)) || 982 | !!captureSetting 983 | } 984 | 985 | function realEvent(type) { 986 | return hover[type] || (focusinSupported && focus[type]) || type 987 | } 988 | 989 | function add(element, events, fn, data, selector, delegator, capture){ 990 | var id = zid(element), set = (handlers[id] || (handlers[id] = [])) 991 | events.split(/\s/).forEach(function(event){ 992 | if (event == 'ready') return $(document).ready(fn) 993 | var handler = parse(event) 994 | handler.fn = fn 995 | handler.sel = selector 996 | // emulate mouseenter, mouseleave 997 | if (handler.e in hover) fn = function(e){ 998 | var related = e.relatedTarget 999 | if (!related || (related !== this && !$.contains(this, related))) 1000 | return handler.fn.apply(this, arguments) 1001 | } 1002 | handler.del = delegator 1003 | var callback = delegator || fn 1004 | handler.proxy = function(e){ 1005 | e = compatible(e) 1006 | if (e.isImmediatePropagationStopped()) return 1007 | e.data = data 1008 | var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)) 1009 | if (result === false) e.preventDefault(), e.stopPropagation() 1010 | return result 1011 | } 1012 | handler.i = set.length 1013 | set.push(handler) 1014 | if ('addEventListener' in element) 1015 | element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 1016 | }) 1017 | } 1018 | function remove(element, events, fn, selector, capture){ 1019 | var id = zid(element) 1020 | ;(events || '').split(/\s/).forEach(function(event){ 1021 | findHandlers(element, event, fn, selector).forEach(function(handler){ 1022 | delete handlers[id][handler.i] 1023 | if ('removeEventListener' in element) 1024 | element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture)) 1025 | }) 1026 | }) 1027 | } 1028 | 1029 | $.event = { add: add, remove: remove } 1030 | 1031 | $.proxy = function(fn, context) { 1032 | var args = (2 in arguments) && slice.call(arguments, 2) 1033 | if (isFunction(fn)) { 1034 | var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) } 1035 | proxyFn._zid = zid(fn) 1036 | return proxyFn 1037 | } else if (isString(context)) { 1038 | if (args) { 1039 | args.unshift(fn[context], fn) 1040 | return $.proxy.apply(null, args) 1041 | } else { 1042 | return $.proxy(fn[context], fn) 1043 | } 1044 | } else { 1045 | throw new TypeError("expected function") 1046 | } 1047 | } 1048 | 1049 | $.fn.bind = function(event, data, callback){ 1050 | return this.on(event, data, callback) 1051 | } 1052 | $.fn.unbind = function(event, callback){ 1053 | return this.off(event, callback) 1054 | } 1055 | $.fn.one = function(event, selector, data, callback){ 1056 | return this.on(event, selector, data, callback, 1) 1057 | } 1058 | 1059 | var returnTrue = function(){return true}, 1060 | returnFalse = function(){return false}, 1061 | ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/, 1062 | eventMethods = { 1063 | preventDefault: 'isDefaultPrevented', 1064 | stopImmediatePropagation: 'isImmediatePropagationStopped', 1065 | stopPropagation: 'isPropagationStopped' 1066 | } 1067 | 1068 | function compatible(event, source) { 1069 | if (source || !event.isDefaultPrevented) { 1070 | source || (source = event) 1071 | 1072 | $.each(eventMethods, function(name, predicate) { 1073 | var sourceMethod = source[name] 1074 | event[name] = function(){ 1075 | this[predicate] = returnTrue 1076 | return sourceMethod && sourceMethod.apply(source, arguments) 1077 | } 1078 | event[predicate] = returnFalse 1079 | }) 1080 | 1081 | event.timeStamp || (event.timeStamp = Date.now()) 1082 | 1083 | if (source.defaultPrevented !== undefined ? source.defaultPrevented : 1084 | 'returnValue' in source ? source.returnValue === false : 1085 | source.getPreventDefault && source.getPreventDefault()) 1086 | event.isDefaultPrevented = returnTrue 1087 | } 1088 | return event 1089 | } 1090 | 1091 | function createProxy(event) { 1092 | var key, proxy = { originalEvent: event } 1093 | for (key in event) 1094 | if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key] 1095 | 1096 | return compatible(proxy, event) 1097 | } 1098 | 1099 | $.fn.delegate = function(selector, event, callback){ 1100 | return this.on(event, selector, callback) 1101 | } 1102 | $.fn.undelegate = function(selector, event, callback){ 1103 | return this.off(event, selector, callback) 1104 | } 1105 | 1106 | $.fn.live = function(event, callback){ 1107 | $(document.body).delegate(this.selector, event, callback) 1108 | return this 1109 | } 1110 | $.fn.die = function(event, callback){ 1111 | $(document.body).undelegate(this.selector, event, callback) 1112 | return this 1113 | } 1114 | 1115 | $.fn.on = function(event, selector, data, callback, one){ 1116 | var autoRemove, delegator, $this = this 1117 | if (event && !isString(event)) { 1118 | $.each(event, function(type, fn){ 1119 | $this.on(type, selector, data, fn, one) 1120 | }) 1121 | return $this 1122 | } 1123 | 1124 | if (!isString(selector) && !isFunction(callback) && callback !== false) 1125 | callback = data, data = selector, selector = undefined 1126 | if (callback === undefined || data === false) 1127 | callback = data, data = undefined 1128 | 1129 | if (callback === false) callback = returnFalse 1130 | 1131 | return $this.each(function(_, element){ 1132 | if (one) autoRemove = function(e){ 1133 | remove(element, e.type, callback) 1134 | return callback.apply(this, arguments) 1135 | } 1136 | 1137 | if (selector) delegator = function(e){ 1138 | var evt, match = $(e.target).closest(selector, element).get(0) 1139 | if (match && match !== element) { 1140 | evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) 1141 | return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) 1142 | } 1143 | } 1144 | 1145 | add(element, event, callback, data, selector, delegator || autoRemove) 1146 | }) 1147 | } 1148 | $.fn.off = function(event, selector, callback){ 1149 | var $this = this 1150 | if (event && !isString(event)) { 1151 | $.each(event, function(type, fn){ 1152 | $this.off(type, selector, fn) 1153 | }) 1154 | return $this 1155 | } 1156 | 1157 | if (!isString(selector) && !isFunction(callback) && callback !== false) 1158 | callback = selector, selector = undefined 1159 | 1160 | if (callback === false) callback = returnFalse 1161 | 1162 | return $this.each(function(){ 1163 | remove(this, event, callback, selector) 1164 | }) 1165 | } 1166 | 1167 | $.fn.trigger = function(event, args){ 1168 | event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) 1169 | event._args = args 1170 | return this.each(function(){ 1171 | // handle focus(), blur() by calling them directly 1172 | if (event.type in focus && typeof this[event.type] == "function") this[event.type]() 1173 | // items in the collection might not be DOM elements 1174 | else if ('dispatchEvent' in this) this.dispatchEvent(event) 1175 | else $(this).triggerHandler(event, args) 1176 | }) 1177 | } 1178 | 1179 | // triggers event handlers on current element just as if an event occurred, 1180 | // doesn't trigger an actual event, doesn't bubble 1181 | $.fn.triggerHandler = function(event, args){ 1182 | var e, result 1183 | this.each(function(i, element){ 1184 | e = createProxy(isString(event) ? $.Event(event) : event) 1185 | e._args = args 1186 | e.target = element 1187 | $.each(findHandlers(element, event.type || event), function(i, handler){ 1188 | result = handler.proxy(e) 1189 | if (e.isImmediatePropagationStopped()) return false 1190 | }) 1191 | }) 1192 | return result 1193 | } 1194 | 1195 | // shortcut methods for `.bind(event, fn)` for each event type 1196 | ;('focusin focusout focus blur load resize scroll unload click dblclick '+ 1197 | 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ 1198 | 'change select keydown keypress keyup error').split(' ').forEach(function(event) { 1199 | $.fn[event] = function(callback) { 1200 | return (0 in arguments) ? 1201 | this.bind(event, callback) : 1202 | this.trigger(event) 1203 | } 1204 | }) 1205 | 1206 | $.Event = function(type, props) { 1207 | if (!isString(type)) props = type, type = props.type 1208 | var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true 1209 | if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name]) 1210 | event.initEvent(type, bubbles, true) 1211 | return compatible(event) 1212 | } 1213 | 1214 | })(Zepto) 1215 | 1216 | ;(function($){ 1217 | var jsonpID = +new Date(), 1218 | document = window.document, 1219 | key, 1220 | name, 1221 | rscript = /)<[^<]*)*<\/script>/gi, 1222 | scriptTypeRE = /^(?:text|application)\/javascript/i, 1223 | xmlTypeRE = /^(?:text|application)\/xml/i, 1224 | jsonType = 'application/json', 1225 | htmlType = 'text/html', 1226 | blankRE = /^\s*$/, 1227 | originAnchor = document.createElement('a') 1228 | 1229 | originAnchor.href = window.location.href 1230 | 1231 | // trigger a custom event and return false if it was cancelled 1232 | function triggerAndReturn(context, eventName, data) { 1233 | var event = $.Event(eventName) 1234 | $(context).trigger(event, data) 1235 | return !event.isDefaultPrevented() 1236 | } 1237 | 1238 | // trigger an Ajax "global" event 1239 | function triggerGlobal(settings, context, eventName, data) { 1240 | if (settings.global) return triggerAndReturn(context || document, eventName, data) 1241 | } 1242 | 1243 | // Number of active Ajax requests 1244 | $.active = 0 1245 | 1246 | function ajaxStart(settings) { 1247 | if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart') 1248 | } 1249 | function ajaxStop(settings) { 1250 | if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop') 1251 | } 1252 | 1253 | // triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable 1254 | function ajaxBeforeSend(xhr, settings) { 1255 | var context = settings.context 1256 | if (settings.beforeSend.call(context, xhr, settings) === false || 1257 | triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false) 1258 | return false 1259 | 1260 | triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]) 1261 | } 1262 | function ajaxSuccess(data, xhr, settings, deferred) { 1263 | var context = settings.context, status = 'success' 1264 | settings.success.call(context, data, status, xhr) 1265 | if (deferred) deferred.resolveWith(context, [data, status, xhr]) 1266 | triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]) 1267 | ajaxComplete(status, xhr, settings) 1268 | } 1269 | // type: "timeout", "error", "abort", "parsererror" 1270 | function ajaxError(error, type, xhr, settings, deferred) { 1271 | var context = settings.context 1272 | settings.error.call(context, xhr, type, error) 1273 | if (deferred) deferred.rejectWith(context, [xhr, type, error]) 1274 | triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type]) 1275 | ajaxComplete(type, xhr, settings) 1276 | } 1277 | // status: "success", "notmodified", "error", "timeout", "abort", "parsererror" 1278 | function ajaxComplete(status, xhr, settings) { 1279 | var context = settings.context 1280 | settings.complete.call(context, xhr, status) 1281 | triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]) 1282 | ajaxStop(settings) 1283 | } 1284 | 1285 | function ajaxDataFilter(data, type, settings) { 1286 | if (settings.dataFilter == empty) return data 1287 | var context = settings.context 1288 | return settings.dataFilter.call(context, data, type) 1289 | } 1290 | 1291 | // Empty function, used as default callback 1292 | function empty() {} 1293 | 1294 | $.ajaxJSONP = function(options, deferred){ 1295 | if (!('type' in options)) return $.ajax(options) 1296 | 1297 | var _callbackName = options.jsonpCallback, 1298 | callbackName = ($.isFunction(_callbackName) ? 1299 | _callbackName() : _callbackName) || ('Zepto' + (jsonpID++)), 1300 | script = document.createElement('script'), 1301 | originalCallback = window[callbackName], 1302 | responseData, 1303 | abort = function(errorType) { 1304 | $(script).triggerHandler('error', errorType || 'abort') 1305 | }, 1306 | xhr = { abort: abort }, abortTimeout 1307 | 1308 | if (deferred) deferred.promise(xhr) 1309 | 1310 | $(script).on('load error', function(e, errorType){ 1311 | clearTimeout(abortTimeout) 1312 | $(script).off().remove() 1313 | 1314 | if (e.type == 'error' || !responseData) { 1315 | ajaxError(null, errorType || 'error', xhr, options, deferred) 1316 | } else { 1317 | ajaxSuccess(responseData[0], xhr, options, deferred) 1318 | } 1319 | 1320 | window[callbackName] = originalCallback 1321 | if (responseData && $.isFunction(originalCallback)) 1322 | originalCallback(responseData[0]) 1323 | 1324 | originalCallback = responseData = undefined 1325 | }) 1326 | 1327 | if (ajaxBeforeSend(xhr, options) === false) { 1328 | abort('abort') 1329 | return xhr 1330 | } 1331 | 1332 | window[callbackName] = function(){ 1333 | responseData = arguments 1334 | } 1335 | 1336 | script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName) 1337 | document.head.appendChild(script) 1338 | 1339 | if (options.timeout > 0) abortTimeout = setTimeout(function(){ 1340 | abort('timeout') 1341 | }, options.timeout) 1342 | 1343 | return xhr 1344 | } 1345 | 1346 | $.ajaxSettings = { 1347 | // Default type of request 1348 | type: 'GET', 1349 | // Callback that is executed before request 1350 | beforeSend: empty, 1351 | // Callback that is executed if the request succeeds 1352 | success: empty, 1353 | // Callback that is executed the the server drops error 1354 | error: empty, 1355 | // Callback that is executed on request complete (both: error and success) 1356 | complete: empty, 1357 | // The context for the callbacks 1358 | context: null, 1359 | // Whether to trigger "global" Ajax events 1360 | global: true, 1361 | // Transport 1362 | xhr: function () { 1363 | return new window.XMLHttpRequest() 1364 | }, 1365 | // MIME types mapping 1366 | // IIS returns Javascript as "application/x-javascript" 1367 | accepts: { 1368 | script: 'text/javascript, application/javascript, application/x-javascript', 1369 | json: jsonType, 1370 | xml: 'application/xml, text/xml', 1371 | html: htmlType, 1372 | text: 'text/plain' 1373 | }, 1374 | // Whether the request is to another domain 1375 | crossDomain: false, 1376 | // Default timeout 1377 | timeout: 0, 1378 | // Whether data should be serialized to string 1379 | processData: true, 1380 | // Whether the browser should be allowed to cache GET responses 1381 | cache: true, 1382 | //Used to handle the raw response data of XMLHttpRequest. 1383 | //This is a pre-filtering function to sanitize the response. 1384 | //The sanitized response should be returned 1385 | dataFilter: empty 1386 | } 1387 | 1388 | function mimeToDataType(mime) { 1389 | if (mime) mime = mime.split(';', 2)[0] 1390 | return mime && ( mime == htmlType ? 'html' : 1391 | mime == jsonType ? 'json' : 1392 | scriptTypeRE.test(mime) ? 'script' : 1393 | xmlTypeRE.test(mime) && 'xml' ) || 'text' 1394 | } 1395 | 1396 | function appendQuery(url, query) { 1397 | if (query == '') return url 1398 | return (url + '&' + query).replace(/[&?]{1,2}/, '?') 1399 | } 1400 | 1401 | // serialize payload and append it to the URL for GET requests 1402 | function serializeData(options) { 1403 | if (options.processData && options.data && $.type(options.data) != "string") 1404 | options.data = $.param(options.data, options.traditional) 1405 | if (options.data && (!options.type || options.type.toUpperCase() == 'GET' || 'jsonp' == options.dataType)) 1406 | options.url = appendQuery(options.url, options.data), options.data = undefined 1407 | } 1408 | 1409 | $.ajax = function(options){ 1410 | var settings = $.extend({}, options || {}), 1411 | deferred = $.Deferred && $.Deferred(), 1412 | urlAnchor, hashIndex 1413 | for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key] 1414 | 1415 | ajaxStart(settings) 1416 | 1417 | if (!settings.crossDomain) { 1418 | urlAnchor = document.createElement('a') 1419 | urlAnchor.href = settings.url 1420 | // cleans up URL for .href (IE only), see https://github.com/madrobby/zepto/pull/1049 1421 | urlAnchor.href = urlAnchor.href 1422 | settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host) 1423 | } 1424 | 1425 | if (!settings.url) settings.url = window.location.toString() 1426 | if ((hashIndex = settings.url.indexOf('#')) > -1) settings.url = settings.url.slice(0, hashIndex) 1427 | serializeData(settings) 1428 | 1429 | var dataType = settings.dataType, hasPlaceholder = /\?.+=\?/.test(settings.url) 1430 | if (hasPlaceholder) dataType = 'jsonp' 1431 | 1432 | if (settings.cache === false || ( 1433 | (!options || options.cache !== true) && 1434 | ('script' == dataType || 'jsonp' == dataType) 1435 | )) 1436 | settings.url = appendQuery(settings.url, '_=' + Date.now()) 1437 | 1438 | if ('jsonp' == dataType) { 1439 | if (!hasPlaceholder) 1440 | settings.url = appendQuery(settings.url, 1441 | settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?') 1442 | return $.ajaxJSONP(settings, deferred) 1443 | } 1444 | 1445 | var mime = settings.accepts[dataType], 1446 | headers = { }, 1447 | setHeader = function(name, value) { headers[name.toLowerCase()] = [name, value] }, 1448 | protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol, 1449 | xhr = settings.xhr(), 1450 | nativeSetHeader = xhr.setRequestHeader, 1451 | abortTimeout 1452 | 1453 | if (deferred) deferred.promise(xhr) 1454 | 1455 | if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest') 1456 | setHeader('Accept', mime || '*/*') 1457 | if (mime = settings.mimeType || mime) { 1458 | if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0] 1459 | xhr.overrideMimeType && xhr.overrideMimeType(mime) 1460 | } 1461 | if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET')) 1462 | setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded') 1463 | 1464 | if (settings.headers) for (name in settings.headers) setHeader(name, settings.headers[name]) 1465 | xhr.setRequestHeader = setHeader 1466 | 1467 | xhr.onreadystatechange = function(){ 1468 | if (xhr.readyState == 4) { 1469 | xhr.onreadystatechange = empty 1470 | clearTimeout(abortTimeout) 1471 | var result, error = false 1472 | if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) { 1473 | dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type')) 1474 | 1475 | if (xhr.responseType == 'arraybuffer' || xhr.responseType == 'blob') 1476 | result = xhr.response 1477 | else { 1478 | result = xhr.responseText 1479 | 1480 | try { 1481 | // http://perfectionkills.com/global-eval-what-are-the-options/ 1482 | // sanitize response accordingly if data filter callback provided 1483 | result = ajaxDataFilter(result, dataType, settings) 1484 | if (dataType == 'script') (1,eval)(result) 1485 | else if (dataType == 'xml') result = xhr.responseXML 1486 | else if (dataType == 'json') result = blankRE.test(result) ? null : $.parseJSON(result) 1487 | } catch (e) { error = e } 1488 | 1489 | if (error) return ajaxError(error, 'parsererror', xhr, settings, deferred) 1490 | } 1491 | 1492 | ajaxSuccess(result, xhr, settings, deferred) 1493 | } else { 1494 | ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred) 1495 | } 1496 | } 1497 | } 1498 | 1499 | if (ajaxBeforeSend(xhr, settings) === false) { 1500 | xhr.abort() 1501 | ajaxError(null, 'abort', xhr, settings, deferred) 1502 | return xhr 1503 | } 1504 | 1505 | var async = 'async' in settings ? settings.async : true 1506 | xhr.open(settings.type, settings.url, async, settings.username, settings.password) 1507 | 1508 | if (settings.xhrFields) for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name] 1509 | 1510 | for (name in headers) nativeSetHeader.apply(xhr, headers[name]) 1511 | 1512 | if (settings.timeout > 0) abortTimeout = setTimeout(function(){ 1513 | xhr.onreadystatechange = empty 1514 | xhr.abort() 1515 | ajaxError(null, 'timeout', xhr, settings, deferred) 1516 | }, settings.timeout) 1517 | 1518 | // avoid sending empty string (#319) 1519 | xhr.send(settings.data ? settings.data : null) 1520 | return xhr 1521 | } 1522 | 1523 | // handle optional data/success arguments 1524 | function parseArguments(url, data, success, dataType) { 1525 | if ($.isFunction(data)) dataType = success, success = data, data = undefined 1526 | if (!$.isFunction(success)) dataType = success, success = undefined 1527 | return { 1528 | url: url 1529 | , data: data 1530 | , success: success 1531 | , dataType: dataType 1532 | } 1533 | } 1534 | 1535 | $.get = function(/* url, data, success, dataType */){ 1536 | return $.ajax(parseArguments.apply(null, arguments)) 1537 | } 1538 | 1539 | $.post = function(/* url, data, success, dataType */){ 1540 | var options = parseArguments.apply(null, arguments) 1541 | options.type = 'POST' 1542 | return $.ajax(options) 1543 | } 1544 | 1545 | $.getJSON = function(/* url, data, success */){ 1546 | var options = parseArguments.apply(null, arguments) 1547 | options.dataType = 'json' 1548 | return $.ajax(options) 1549 | } 1550 | 1551 | $.fn.load = function(url, data, success){ 1552 | if (!this.length) return this 1553 | var self = this, parts = url.split(/\s/), selector, 1554 | options = parseArguments(url, data, success), 1555 | callback = options.success 1556 | if (parts.length > 1) options.url = parts[0], selector = parts[1] 1557 | options.success = function(response){ 1558 | self.html(selector ? 1559 | $('
').html(response.replace(rscript, "")).find(selector) 1560 | : response) 1561 | callback && callback.apply(self, arguments) 1562 | } 1563 | $.ajax(options) 1564 | return this 1565 | } 1566 | 1567 | var escape = encodeURIComponent 1568 | 1569 | function serialize(params, obj, traditional, scope){ 1570 | var type, array = $.isArray(obj), hash = $.isPlainObject(obj) 1571 | $.each(obj, function(key, value) { 1572 | type = $.type(value) 1573 | if (scope) key = traditional ? scope : 1574 | scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']' 1575 | // handle data in serializeArray() format 1576 | if (!scope && array) params.add(value.name, value.value) 1577 | // recurse into nested objects 1578 | else if (type == "array" || (!traditional && type == "object")) 1579 | serialize(params, value, traditional, key) 1580 | else params.add(key, value) 1581 | }) 1582 | } 1583 | 1584 | $.param = function(obj, traditional){ 1585 | var params = [] 1586 | params.add = function(key, value) { 1587 | if ($.isFunction(value)) value = value() 1588 | if (value == null) value = "" 1589 | this.push(escape(key) + '=' + escape(value)) 1590 | } 1591 | serialize(params, obj, traditional) 1592 | return params.join('&').replace(/%20/g, '+') 1593 | } 1594 | })(Zepto) 1595 | 1596 | ;(function($){ 1597 | $.fn.serializeArray = function() { 1598 | var name, type, result = [], 1599 | add = function(value) { 1600 | if (value.forEach) return value.forEach(add) 1601 | result.push({ name: name, value: value }) 1602 | } 1603 | if (this[0]) $.each(this[0].elements, function(_, field){ 1604 | type = field.type, name = field.name 1605 | if (name && field.nodeName.toLowerCase() != 'fieldset' && 1606 | !field.disabled && type != 'submit' && type != 'reset' && type != 'button' && type != 'file' && 1607 | ((type != 'radio' && type != 'checkbox') || field.checked)) 1608 | add($(field).val()) 1609 | }) 1610 | return result 1611 | } 1612 | 1613 | $.fn.serialize = function(){ 1614 | var result = [] 1615 | this.serializeArray().forEach(function(elm){ 1616 | result.push(encodeURIComponent(elm.name) + '=' + encodeURIComponent(elm.value)) 1617 | }) 1618 | return result.join('&') 1619 | } 1620 | 1621 | $.fn.submit = function(callback) { 1622 | if (0 in arguments) this.bind('submit', callback) 1623 | else if (this.length) { 1624 | var event = $.Event('submit') 1625 | this.eq(0).trigger(event) 1626 | if (!event.isDefaultPrevented()) this.get(0).submit() 1627 | } 1628 | return this 1629 | } 1630 | 1631 | })(Zepto) 1632 | 1633 | ;(function(){ 1634 | // getComputedStyle shouldn't freak out when called 1635 | // without a valid element as argument 1636 | try { 1637 | getComputedStyle(undefined) 1638 | } catch(e) { 1639 | var nativeGetComputedStyle = getComputedStyle 1640 | window.getComputedStyle = function(element, pseudoElement){ 1641 | try { 1642 | return nativeGetComputedStyle(element, pseudoElement) 1643 | } catch(e) { 1644 | return null 1645 | } 1646 | } 1647 | } 1648 | })() 1649 | return Zepto 1650 | })) 1651 | -------------------------------------------------------------------------------- /src/libs/demo.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: sans-serif; 3 | -ms-text-size-adjust: 100%; 4 | -webkit-text-size-adjust: 100%; 5 | text-rendering: optimizeLegibility; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | body { 11 | margin: 0; 12 | } 13 | 14 | h1, 15 | h2, 16 | h3, 17 | h4, 18 | h5 { 19 | word-wrap: break-word; 20 | } 21 | 22 | img, 23 | canvas { 24 | max-width: 100%; 25 | border: 1px solid #d3d3d3; 26 | } 27 | 28 | .container { 29 | margin: 0 auto; 30 | padding: 0 15px 40px; 31 | max-width: 720px; 32 | border-right: 1px solid #ddd; 33 | border-left: 1px solid #ddd; 34 | overflow: hidden; 35 | } 36 | 37 | .single-line { 38 | font-size: .8em; 39 | color: #666; 40 | white-space: nowrap; 41 | overflow: auto; 42 | -webkit-overflow-scrolling: touch; 43 | } 44 | 45 | pre { 46 | margin-right: 80px; 47 | max-height: 180px; 48 | font-size: .8em; 49 | color: #666; 50 | overflow: auto; 51 | -webkit-overflow-scrolling: touch; 52 | } 53 | -------------------------------------------------------------------------------- /src/libs/demo.js: -------------------------------------------------------------------------------- 1 | ;(function () { 2 | var file 3 | var fileType 4 | var url 5 | var compressedImageDataURL 6 | var compressedImageBlob 7 | var compressSuccess = false 8 | var contentType // 从 canvas.toDataURL 的结果中获取的 contentType 9 | var binaryString // atob 转码后的 二进制文本 10 | var boundary = 'customFileboundary' 11 | var boundaryString // 构造为 multipart 的文本 12 | var arrayBuffer // 需要用 ajax 发送的 ArrayBuffer 13 | 14 | function asyncClick (doms, i) { 15 | setTimeout(function () { 16 | if (i < doms.length) { 17 | doms[i++].click() 18 | asyncClick(doms, i) 19 | } 20 | }, 50) 21 | } 22 | 23 | function autoClick () { 24 | asyncClick([ 25 | J_GetImageFile, 26 | J_LoadImageByURL, 27 | J_DrawImage, 28 | J_CompressImage, 29 | J_Atob, 30 | J_ConcatBinaryStirng, 31 | J_String2ArrayBuffer 32 | ], 0) 33 | } 34 | 35 | // file on change 36 | J_File.addEventListener('change', function (e) { 37 | if (e.target.value) { 38 | J_GetImageFile.removeAttribute('disabled') 39 | } else { 40 | J_GetImageFile.setAttribute('disabled', true) 41 | } 42 | // autoClick() // Test 43 | }) 44 | 45 | // get file 46 | J_GetImageFile.addEventListener('click', function () { 47 | var fileName 48 | file = J_File.files[0] 49 | fileName = file.name 50 | fileType = file.type || 'image/' + fileName.substr(fileName.lastIndexOf('.') + 1) 51 | J_ImageObject.innerText = [ 52 | 'file.name: ' + fileName, 53 | 'file.type: ' + fileType, 54 | 'file.size: ' + file.size 55 | ].join('\r\n') 56 | J_LoadImageByURL.removeAttribute('disabled') 57 | J_LoadImageByFileReader.removeAttribute('disabled') 58 | }) 59 | 60 | // get file directly 61 | J_GetLenaFile.addEventListener('click', function (e) { 62 | var button 63 | 64 | if (e.target && e.target.nodeName === 'BUTTON') { 65 | var fileName 66 | button = e.target 67 | url = button.getAttribute('data-url') 68 | fileName = url.substr(url.lastIndexOf('/') + 1) 69 | 70 | getFile(url + '?t=' + (new Date()).valueOf(), function (err, res) { 71 | if (err) { 72 | return alert(err) 73 | } 74 | file = { 75 | name: fileName, 76 | size: res.contentLength, 77 | type: res.contentType 78 | } 79 | fileType = res.contentType 80 | J_ImageObject.innerText = [ 81 | 'file.name: ' + fileName, 82 | 'file.type: ' + fileType, 83 | 'file.size: ' + file.size 84 | ].join('\r\n') 85 | }) 86 | 87 | // reset 88 | J_ImageObject.innerText = '' 89 | J_Image.removeAttribute('src') 90 | J_ImageCanvas.getContext('2d').clearRect(0, 0, J_ImageCanvas.width, J_ImageCanvas.height) 91 | J_ImageCanvas.width = 300 92 | J_ImageCanvas.height = 150 93 | setTimeout(function () { 94 | J_Image.src = url 95 | J_ImageURL.innerText = url 96 | }, 0) 97 | } 98 | }) 99 | 100 | // image load 101 | J_Image.addEventListener('load', function () { 102 | J_DrawImage.removeAttribute('disabled') 103 | }) 104 | 105 | J_Image.addEventListener('error', function () { 106 | alert('image load error') 107 | }) 108 | 109 | // get dataURL/BlobURL 110 | // URL 111 | J_LoadImageByURL.addEventListener('click', function () { 112 | if (url) { 113 | window.URL.revokeObjectURL(url) 114 | } 115 | url = window.URL.createObjectURL(file) 116 | 117 | J_Image.removeAttribute('src') 118 | setTimeout(function () { 119 | J_Image.src = url 120 | J_ImageURL.innerText = url 121 | }, 0) 122 | }) 123 | // FileReader 124 | J_LoadImageByFileReader.addEventListener('click', function () { 125 | var fileReader = new FileReader() 126 | 127 | J_Image.removeAttribute('src') 128 | fileReader.onload = function (e) { 129 | var dataURL = e.target.result 130 | J_Image.src = dataURL 131 | J_ImageURL.innerText = dataURL 132 | } 133 | fileReader.readAsDataURL(file) 134 | }) 135 | 136 | // drawImage to canvas 137 | J_DrawImage.addEventListener('click', function () { 138 | var sourceImage = J_Image 139 | var canvas = J_ImageCanvas 140 | var context = canvas.getContext('2d') 141 | 142 | if (!isCanvasBlank(canvas)) { 143 | context.clearRect(0, 0, canvas.width, canvas.height) 144 | } 145 | canvas.width = sourceImage.naturalWidth 146 | canvas.height = sourceImage.naturalHeight 147 | context.drawImage(sourceImage, 0, 0) 148 | 149 | J_CompressImage.removeAttribute('disabled') 150 | }) 151 | 152 | J_CompressImage.addEventListener('click', function () { 153 | /* 154 | * HTMLCanvasElement.toDataURL() 不支持传入的类型非“image/png”, 155 | * 但是值以“data:image/png”开头的类型。 156 | */ 157 | var canvas = J_ImageCanvas 158 | var mimeType = fileType || 'image/png' 159 | var quality = J_CompressQuality.value 160 | var startTime 161 | var endTime 162 | 163 | 164 | J_MimeType.innerText = '' 165 | J_CompressedImageDataURL.innerText = '' 166 | J_SourceFileSize.innerText = '' 167 | J_CompressedFileSize.innerText = '' 168 | J_CompressedImage.removeAttribute('src') 169 | 170 | startTime = Date.now() 171 | compressedImageDataURL = canvas.toDataURL(mimeType, quality / 100) 172 | endTime = Date.now() 173 | compressedImageBlob = dataURL2Blob(compressedImageDataURL) 174 | 175 | setTimeout(function () { 176 | J_MimeType.innerText = mimeType 177 | J_CompressedImageDataURL.innerText = compressedImageDataURL 178 | J_SourceFileSize.innerText = file.size 179 | J_CompressedFileSize.innerText = compressedImageBlob.size 180 | J_CompressDuring.innerText = endTime - startTime + 'ms' 181 | J_CompressedImage.src = compressedImageDataURL 182 | J_Atob.removeAttribute('disabled') 183 | J_XHRBlobMultiparty.removeAttribute('disabled') 184 | J_XHRBlobMulter.removeAttribute('disabled') 185 | }, 0) 186 | 187 | if (compressedImageBlob.size > file.size) { 188 | // Compressed file size > Original file size 189 | console.log(compressedImageBlob.size + ' > ' + file.size) 190 | return 191 | } 192 | compressSuccess = true 193 | }) 194 | 195 | // atob 196 | J_Atob.addEventListener('click', function () { 197 | // 不包含 /^data:(image\/.+);base64,/ 的 base64 字符串 198 | var pureBase64ImageData = compressedImageDataURL.replace(/^data:(image\/.+);base64,/, function ($0, $1) { 199 | contentType = $1 200 | return '' 201 | }) 202 | 203 | // atob 204 | binaryString = atob(pureBase64ImageData) 205 | 206 | J_PureBase64Data.innerText = pureBase64ImageData 207 | J_ContentType.innerText = contentType 208 | J_BinaryString.innerText = binaryString 209 | J_ConcatBinaryStirng.removeAttribute('disabled') 210 | }) 211 | 212 | J_ConcatBinaryStirng.addEventListener('click', function () { 213 | boundaryString = [ 214 | '--' + boundary, 215 | 'Content-Disposition: form-data; name="file"; filename="' + (file.name || 'blob') + '"', 216 | 'Content-Type: ' + contentType, 217 | '', binaryString, 218 | '--' + boundary + '--', '' 219 | ].join('\r\n') 220 | 221 | J_MultipartBinaryString.innerText = boundaryString 222 | J_String2ArrayBuffer.removeAttribute('disabled') 223 | }) 224 | 225 | J_String2ArrayBuffer.addEventListener('click', function () { 226 | arrayBuffer = string2ArrayBuffer(boundaryString) 227 | 228 | J_ArrayBuffer.innerText = arrayBuffer 229 | J_XHRMultiparty.removeAttribute('disabled') 230 | J_XHRMulter.removeAttribute('disabled') 231 | }) 232 | 233 | function sendArrayBuffer (url) { 234 | return function () { 235 | var button = this 236 | var buttonText = this.innerText 237 | var xhr = new XMLHttpRequest() 238 | 239 | xhr.open('POST', url, true) 240 | xhr.withCredentials = true 241 | xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary) 242 | 243 | xhr.upload.addEventListener('progress', function (e) { 244 | var progress 245 | if (e.lengthComputable) { 246 | progress = (e.loaded / e.total * 100).toFixed(2) 247 | button.innerText = progress + '%' 248 | } 249 | }) 250 | xhr.addEventListener('load', function () { 251 | if ( 252 | xhr.status >= 200 && xhr.status < 300 || 253 | xhr.status == 304 254 | ) { 255 | J_UploadResult_XHR.innerText = '--- SUCCESS ---\n' + JSON.stringify(JSON.parse(xhr.responseText), null, 2) 256 | } else { 257 | J_UploadResult_XHR.innerText = 258 | '--- ERROR: ' + xhr.status + ' ---\n' + JSON.stringify(JSON.parse(xhr.responseText), null, 2) 259 | } 260 | button.innerText = buttonText 261 | button.removeAttribute('disabled') 262 | }) 263 | 264 | button.setAttribute('disabled', 'true') 265 | xhr.send(arrayBuffer) 266 | } 267 | } 268 | 269 | // use XMLHttpRequest send Array Buffer 270 | J_XHRMultiparty.addEventListener('click', sendArrayBuffer('/api/upload/multiparty')) 271 | J_XHRMulter.addEventListener('click', sendArrayBuffer('/api/upload/multer')) 272 | 273 | function sendBlob (url) { 274 | return function () { 275 | var button = this 276 | var buttonText = this.innerText 277 | var formData = new FormData() 278 | var xhr = new XMLHttpRequest() 279 | var blobFile = compressedImageBlob 280 | 281 | formData.append('file', blobFile, file.name) 282 | 283 | xhr.open('POST', url, true) 284 | xhr.withCredentials = true 285 | 286 | xhr.upload.addEventListener('progress', function (e) { 287 | var progress 288 | if (e.lengthComputable) { 289 | progress = (e.loaded / e.total * 100).toFixed(2) 290 | button.innerText = progress + '%' 291 | } 292 | }) 293 | xhr.addEventListener('load', function () { 294 | if ( 295 | xhr.status >= 200 && xhr.status < 300 || 296 | xhr.status == 304 297 | ) { 298 | J_UploadResult_XHRBlob.innerText = '--- SUCCESS ---\n' + JSON.stringify(JSON.parse(xhr.responseText), null, 2) 299 | } else { 300 | J_UploadResult_XHRBlob.innerText = 301 | '--- ERROR: ' + xhr.status + ' ---\n' + JSON.stringify(JSON.parse(xhr.responseText), null, 2) 302 | } 303 | button.innerText = buttonText 304 | button.removeAttribute('disabled') 305 | }) 306 | 307 | button.setAttribute('disabled', 'true') 308 | xhr.send(formData) 309 | } 310 | } 311 | 312 | // use XMLHttpRequest & FormData send blob 313 | J_XHRBlobMultiparty.addEventListener('click', sendBlob('/api/upload/multiparty')) 314 | J_XHRBlobMulter.addEventListener('click', sendBlob('/api/upload/multer')) 315 | }()) 316 | -------------------------------------------------------------------------------- /src/libs/es6-promise.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * @overview es6-promise - a tiny implementation of Promises/A+. 3 | * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) 4 | * @license Licensed under MIT license 5 | * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE 6 | * @version 3.2.2+39aa2571 7 | */ 8 | 9 | (function() { 10 | "use strict"; 11 | function lib$es6$promise$utils$$objectOrFunction(x) { 12 | return typeof x === 'function' || (typeof x === 'object' && x !== null); 13 | } 14 | 15 | function lib$es6$promise$utils$$isFunction(x) { 16 | return typeof x === 'function'; 17 | } 18 | 19 | function lib$es6$promise$utils$$isMaybeThenable(x) { 20 | return typeof x === 'object' && x !== null; 21 | } 22 | 23 | var lib$es6$promise$utils$$_isArray; 24 | if (!Array.isArray) { 25 | lib$es6$promise$utils$$_isArray = function (x) { 26 | return Object.prototype.toString.call(x) === '[object Array]'; 27 | }; 28 | } else { 29 | lib$es6$promise$utils$$_isArray = Array.isArray; 30 | } 31 | 32 | var lib$es6$promise$utils$$isArray = lib$es6$promise$utils$$_isArray; 33 | var lib$es6$promise$asap$$len = 0; 34 | var lib$es6$promise$asap$$vertxNext; 35 | var lib$es6$promise$asap$$customSchedulerFn; 36 | 37 | var lib$es6$promise$asap$$asap = function asap(callback, arg) { 38 | lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len] = callback; 39 | lib$es6$promise$asap$$queue[lib$es6$promise$asap$$len + 1] = arg; 40 | lib$es6$promise$asap$$len += 2; 41 | if (lib$es6$promise$asap$$len === 2) { 42 | // If len is 2, that means that we need to schedule an async flush. 43 | // If additional callbacks are queued before the queue is flushed, they 44 | // will be processed by this flush that we are scheduling. 45 | if (lib$es6$promise$asap$$customSchedulerFn) { 46 | lib$es6$promise$asap$$customSchedulerFn(lib$es6$promise$asap$$flush); 47 | } else { 48 | lib$es6$promise$asap$$scheduleFlush(); 49 | } 50 | } 51 | } 52 | 53 | function lib$es6$promise$asap$$setScheduler(scheduleFn) { 54 | lib$es6$promise$asap$$customSchedulerFn = scheduleFn; 55 | } 56 | 57 | function lib$es6$promise$asap$$setAsap(asapFn) { 58 | lib$es6$promise$asap$$asap = asapFn; 59 | } 60 | 61 | var lib$es6$promise$asap$$browserWindow = (typeof window !== 'undefined') ? window : undefined; 62 | var lib$es6$promise$asap$$browserGlobal = lib$es6$promise$asap$$browserWindow || {}; 63 | var lib$es6$promise$asap$$BrowserMutationObserver = lib$es6$promise$asap$$browserGlobal.MutationObserver || lib$es6$promise$asap$$browserGlobal.WebKitMutationObserver; 64 | var lib$es6$promise$asap$$isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; 65 | 66 | // test for web worker but not in IE10 67 | var lib$es6$promise$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' && 68 | typeof importScripts !== 'undefined' && 69 | typeof MessageChannel !== 'undefined'; 70 | 71 | // node 72 | function lib$es6$promise$asap$$useNextTick() { 73 | // node version 0.10.x displays a deprecation warning when nextTick is used recursively 74 | // see https://github.com/cujojs/when/issues/410 for details 75 | return function() { 76 | process.nextTick(lib$es6$promise$asap$$flush); 77 | }; 78 | } 79 | 80 | // vertx 81 | function lib$es6$promise$asap$$useVertxTimer() { 82 | return function() { 83 | lib$es6$promise$asap$$vertxNext(lib$es6$promise$asap$$flush); 84 | }; 85 | } 86 | 87 | function lib$es6$promise$asap$$useMutationObserver() { 88 | var iterations = 0; 89 | var observer = new lib$es6$promise$asap$$BrowserMutationObserver(lib$es6$promise$asap$$flush); 90 | var node = document.createTextNode(''); 91 | observer.observe(node, { characterData: true }); 92 | 93 | return function() { 94 | node.data = (iterations = ++iterations % 2); 95 | }; 96 | } 97 | 98 | // web worker 99 | function lib$es6$promise$asap$$useMessageChannel() { 100 | var channel = new MessageChannel(); 101 | channel.port1.onmessage = lib$es6$promise$asap$$flush; 102 | return function () { 103 | channel.port2.postMessage(0); 104 | }; 105 | } 106 | 107 | function lib$es6$promise$asap$$useSetTimeout() { 108 | return function() { 109 | setTimeout(lib$es6$promise$asap$$flush, 1); 110 | }; 111 | } 112 | 113 | var lib$es6$promise$asap$$queue = new Array(1000); 114 | function lib$es6$promise$asap$$flush() { 115 | for (var i = 0; i < lib$es6$promise$asap$$len; i+=2) { 116 | var callback = lib$es6$promise$asap$$queue[i]; 117 | var arg = lib$es6$promise$asap$$queue[i+1]; 118 | 119 | callback(arg); 120 | 121 | lib$es6$promise$asap$$queue[i] = undefined; 122 | lib$es6$promise$asap$$queue[i+1] = undefined; 123 | } 124 | 125 | lib$es6$promise$asap$$len = 0; 126 | } 127 | 128 | function lib$es6$promise$asap$$attemptVertx() { 129 | try { 130 | var r = require; 131 | var vertx = r('vertx'); 132 | lib$es6$promise$asap$$vertxNext = vertx.runOnLoop || vertx.runOnContext; 133 | return lib$es6$promise$asap$$useVertxTimer(); 134 | } catch(e) { 135 | return lib$es6$promise$asap$$useSetTimeout(); 136 | } 137 | } 138 | 139 | var lib$es6$promise$asap$$scheduleFlush; 140 | // Decide what async method to use to triggering processing of queued callbacks: 141 | if (lib$es6$promise$asap$$isNode) { 142 | lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useNextTick(); 143 | } else if (lib$es6$promise$asap$$BrowserMutationObserver) { 144 | lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMutationObserver(); 145 | } else if (lib$es6$promise$asap$$isWorker) { 146 | lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useMessageChannel(); 147 | } else if (lib$es6$promise$asap$$browserWindow === undefined && typeof require === 'function') { 148 | lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$attemptVertx(); 149 | } else { 150 | lib$es6$promise$asap$$scheduleFlush = lib$es6$promise$asap$$useSetTimeout(); 151 | } 152 | function lib$es6$promise$then$$then(onFulfillment, onRejection) { 153 | var parent = this; 154 | 155 | var child = new this.constructor(lib$es6$promise$$internal$$noop); 156 | 157 | if (child[lib$es6$promise$$internal$$PROMISE_ID] === undefined) { 158 | lib$es6$promise$$internal$$makePromise(child); 159 | } 160 | 161 | var state = parent._state; 162 | 163 | if (state) { 164 | var callback = arguments[state - 1]; 165 | lib$es6$promise$asap$$asap(function(){ 166 | lib$es6$promise$$internal$$invokeCallback(state, child, callback, parent._result); 167 | }); 168 | } else { 169 | lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection); 170 | } 171 | 172 | return child; 173 | } 174 | var lib$es6$promise$then$$default = lib$es6$promise$then$$then; 175 | function lib$es6$promise$promise$resolve$$resolve(object) { 176 | /*jshint validthis:true */ 177 | var Constructor = this; 178 | 179 | if (object && typeof object === 'object' && object.constructor === Constructor) { 180 | return object; 181 | } 182 | 183 | var promise = new Constructor(lib$es6$promise$$internal$$noop); 184 | lib$es6$promise$$internal$$resolve(promise, object); 185 | return promise; 186 | } 187 | var lib$es6$promise$promise$resolve$$default = lib$es6$promise$promise$resolve$$resolve; 188 | var lib$es6$promise$$internal$$PROMISE_ID = Math.random().toString(36).substring(16); 189 | 190 | function lib$es6$promise$$internal$$noop() {} 191 | 192 | var lib$es6$promise$$internal$$PENDING = void 0; 193 | var lib$es6$promise$$internal$$FULFILLED = 1; 194 | var lib$es6$promise$$internal$$REJECTED = 2; 195 | 196 | var lib$es6$promise$$internal$$GET_THEN_ERROR = new lib$es6$promise$$internal$$ErrorObject(); 197 | 198 | function lib$es6$promise$$internal$$selfFulfillment() { 199 | return new TypeError("You cannot resolve a promise with itself"); 200 | } 201 | 202 | function lib$es6$promise$$internal$$cannotReturnOwn() { 203 | return new TypeError('A promises callback cannot return that same promise.'); 204 | } 205 | 206 | function lib$es6$promise$$internal$$getThen(promise) { 207 | try { 208 | return promise.then; 209 | } catch(error) { 210 | lib$es6$promise$$internal$$GET_THEN_ERROR.error = error; 211 | return lib$es6$promise$$internal$$GET_THEN_ERROR; 212 | } 213 | } 214 | 215 | function lib$es6$promise$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) { 216 | try { 217 | then.call(value, fulfillmentHandler, rejectionHandler); 218 | } catch(e) { 219 | return e; 220 | } 221 | } 222 | 223 | function lib$es6$promise$$internal$$handleForeignThenable(promise, thenable, then) { 224 | lib$es6$promise$asap$$asap(function(promise) { 225 | var sealed = false; 226 | var error = lib$es6$promise$$internal$$tryThen(then, thenable, function(value) { 227 | if (sealed) { return; } 228 | sealed = true; 229 | if (thenable !== value) { 230 | lib$es6$promise$$internal$$resolve(promise, value); 231 | } else { 232 | lib$es6$promise$$internal$$fulfill(promise, value); 233 | } 234 | }, function(reason) { 235 | if (sealed) { return; } 236 | sealed = true; 237 | 238 | lib$es6$promise$$internal$$reject(promise, reason); 239 | }, 'Settle: ' + (promise._label || ' unknown promise')); 240 | 241 | if (!sealed && error) { 242 | sealed = true; 243 | lib$es6$promise$$internal$$reject(promise, error); 244 | } 245 | }, promise); 246 | } 247 | 248 | function lib$es6$promise$$internal$$handleOwnThenable(promise, thenable) { 249 | if (thenable._state === lib$es6$promise$$internal$$FULFILLED) { 250 | lib$es6$promise$$internal$$fulfill(promise, thenable._result); 251 | } else if (thenable._state === lib$es6$promise$$internal$$REJECTED) { 252 | lib$es6$promise$$internal$$reject(promise, thenable._result); 253 | } else { 254 | lib$es6$promise$$internal$$subscribe(thenable, undefined, function(value) { 255 | lib$es6$promise$$internal$$resolve(promise, value); 256 | }, function(reason) { 257 | lib$es6$promise$$internal$$reject(promise, reason); 258 | }); 259 | } 260 | } 261 | 262 | function lib$es6$promise$$internal$$handleMaybeThenable(promise, maybeThenable, then) { 263 | if (maybeThenable.constructor === promise.constructor && 264 | then === lib$es6$promise$then$$default && 265 | constructor.resolve === lib$es6$promise$promise$resolve$$default) { 266 | lib$es6$promise$$internal$$handleOwnThenable(promise, maybeThenable); 267 | } else { 268 | if (then === lib$es6$promise$$internal$$GET_THEN_ERROR) { 269 | lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$GET_THEN_ERROR.error); 270 | } else if (then === undefined) { 271 | lib$es6$promise$$internal$$fulfill(promise, maybeThenable); 272 | } else if (lib$es6$promise$utils$$isFunction(then)) { 273 | lib$es6$promise$$internal$$handleForeignThenable(promise, maybeThenable, then); 274 | } else { 275 | lib$es6$promise$$internal$$fulfill(promise, maybeThenable); 276 | } 277 | } 278 | } 279 | 280 | function lib$es6$promise$$internal$$resolve(promise, value) { 281 | if (promise === value) { 282 | lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$selfFulfillment()); 283 | } else if (lib$es6$promise$utils$$objectOrFunction(value)) { 284 | lib$es6$promise$$internal$$handleMaybeThenable(promise, value, lib$es6$promise$$internal$$getThen(value)); 285 | } else { 286 | lib$es6$promise$$internal$$fulfill(promise, value); 287 | } 288 | } 289 | 290 | function lib$es6$promise$$internal$$publishRejection(promise) { 291 | if (promise._onerror) { 292 | promise._onerror(promise._result); 293 | } 294 | 295 | lib$es6$promise$$internal$$publish(promise); 296 | } 297 | 298 | function lib$es6$promise$$internal$$fulfill(promise, value) { 299 | if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } 300 | 301 | promise._result = value; 302 | promise._state = lib$es6$promise$$internal$$FULFILLED; 303 | 304 | if (promise._subscribers.length !== 0) { 305 | lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, promise); 306 | } 307 | } 308 | 309 | function lib$es6$promise$$internal$$reject(promise, reason) { 310 | if (promise._state !== lib$es6$promise$$internal$$PENDING) { return; } 311 | promise._state = lib$es6$promise$$internal$$REJECTED; 312 | promise._result = reason; 313 | 314 | lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publishRejection, promise); 315 | } 316 | 317 | function lib$es6$promise$$internal$$subscribe(parent, child, onFulfillment, onRejection) { 318 | var subscribers = parent._subscribers; 319 | var length = subscribers.length; 320 | 321 | parent._onerror = null; 322 | 323 | subscribers[length] = child; 324 | subscribers[length + lib$es6$promise$$internal$$FULFILLED] = onFulfillment; 325 | subscribers[length + lib$es6$promise$$internal$$REJECTED] = onRejection; 326 | 327 | if (length === 0 && parent._state) { 328 | lib$es6$promise$asap$$asap(lib$es6$promise$$internal$$publish, parent); 329 | } 330 | } 331 | 332 | function lib$es6$promise$$internal$$publish(promise) { 333 | var subscribers = promise._subscribers; 334 | var settled = promise._state; 335 | 336 | if (subscribers.length === 0) { return; } 337 | 338 | var child, callback, detail = promise._result; 339 | 340 | for (var i = 0; i < subscribers.length; i += 3) { 341 | child = subscribers[i]; 342 | callback = subscribers[i + settled]; 343 | 344 | if (child) { 345 | lib$es6$promise$$internal$$invokeCallback(settled, child, callback, detail); 346 | } else { 347 | callback(detail); 348 | } 349 | } 350 | 351 | promise._subscribers.length = 0; 352 | } 353 | 354 | function lib$es6$promise$$internal$$ErrorObject() { 355 | this.error = null; 356 | } 357 | 358 | var lib$es6$promise$$internal$$TRY_CATCH_ERROR = new lib$es6$promise$$internal$$ErrorObject(); 359 | 360 | function lib$es6$promise$$internal$$tryCatch(callback, detail) { 361 | try { 362 | return callback(detail); 363 | } catch(e) { 364 | lib$es6$promise$$internal$$TRY_CATCH_ERROR.error = e; 365 | return lib$es6$promise$$internal$$TRY_CATCH_ERROR; 366 | } 367 | } 368 | 369 | function lib$es6$promise$$internal$$invokeCallback(settled, promise, callback, detail) { 370 | var hasCallback = lib$es6$promise$utils$$isFunction(callback), 371 | value, error, succeeded, failed; 372 | 373 | if (hasCallback) { 374 | value = lib$es6$promise$$internal$$tryCatch(callback, detail); 375 | 376 | if (value === lib$es6$promise$$internal$$TRY_CATCH_ERROR) { 377 | failed = true; 378 | error = value.error; 379 | value = null; 380 | } else { 381 | succeeded = true; 382 | } 383 | 384 | if (promise === value) { 385 | lib$es6$promise$$internal$$reject(promise, lib$es6$promise$$internal$$cannotReturnOwn()); 386 | return; 387 | } 388 | 389 | } else { 390 | value = detail; 391 | succeeded = true; 392 | } 393 | 394 | if (promise._state !== lib$es6$promise$$internal$$PENDING) { 395 | // noop 396 | } else if (hasCallback && succeeded) { 397 | lib$es6$promise$$internal$$resolve(promise, value); 398 | } else if (failed) { 399 | lib$es6$promise$$internal$$reject(promise, error); 400 | } else if (settled === lib$es6$promise$$internal$$FULFILLED) { 401 | lib$es6$promise$$internal$$fulfill(promise, value); 402 | } else if (settled === lib$es6$promise$$internal$$REJECTED) { 403 | lib$es6$promise$$internal$$reject(promise, value); 404 | } 405 | } 406 | 407 | function lib$es6$promise$$internal$$initializePromise(promise, resolver) { 408 | try { 409 | resolver(function resolvePromise(value){ 410 | lib$es6$promise$$internal$$resolve(promise, value); 411 | }, function rejectPromise(reason) { 412 | lib$es6$promise$$internal$$reject(promise, reason); 413 | }); 414 | } catch(e) { 415 | lib$es6$promise$$internal$$reject(promise, e); 416 | } 417 | } 418 | 419 | var lib$es6$promise$$internal$$id = 0; 420 | function lib$es6$promise$$internal$$nextId() { 421 | return lib$es6$promise$$internal$$id++; 422 | } 423 | 424 | function lib$es6$promise$$internal$$makePromise(promise) { 425 | promise[lib$es6$promise$$internal$$PROMISE_ID] = lib$es6$promise$$internal$$id++; 426 | promise._state = undefined; 427 | promise._result = undefined; 428 | promise._subscribers = []; 429 | } 430 | 431 | function lib$es6$promise$promise$all$$all(entries) { 432 | return new lib$es6$promise$enumerator$$default(this, entries).promise; 433 | } 434 | var lib$es6$promise$promise$all$$default = lib$es6$promise$promise$all$$all; 435 | function lib$es6$promise$promise$race$$race(entries) { 436 | /*jshint validthis:true */ 437 | var Constructor = this; 438 | 439 | if (!lib$es6$promise$utils$$isArray(entries)) { 440 | return new Constructor(function(resolve, reject) { 441 | reject(new TypeError('You must pass an array to race.')); 442 | }); 443 | } else { 444 | return new Constructor(function(resolve, reject) { 445 | var length = entries.length; 446 | for (var i = 0; i < length; i++) { 447 | Constructor.resolve(entries[i]).then(resolve, reject); 448 | } 449 | }); 450 | } 451 | } 452 | var lib$es6$promise$promise$race$$default = lib$es6$promise$promise$race$$race; 453 | function lib$es6$promise$promise$reject$$reject(reason) { 454 | /*jshint validthis:true */ 455 | var Constructor = this; 456 | var promise = new Constructor(lib$es6$promise$$internal$$noop); 457 | lib$es6$promise$$internal$$reject(promise, reason); 458 | return promise; 459 | } 460 | var lib$es6$promise$promise$reject$$default = lib$es6$promise$promise$reject$$reject; 461 | 462 | 463 | function lib$es6$promise$promise$$needsResolver() { 464 | throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); 465 | } 466 | 467 | function lib$es6$promise$promise$$needsNew() { 468 | throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); 469 | } 470 | 471 | var lib$es6$promise$promise$$default = lib$es6$promise$promise$$Promise; 472 | /** 473 | Promise objects represent the eventual result of an asynchronous operation. The 474 | primary way of interacting with a promise is through its `then` method, which 475 | registers callbacks to receive either a promise's eventual value or the reason 476 | why the promise cannot be fulfilled. 477 | 478 | Terminology 479 | ----------- 480 | 481 | - `promise` is an object or function with a `then` method whose behavior conforms to this specification. 482 | - `thenable` is an object or function that defines a `then` method. 483 | - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). 484 | - `exception` is a value that is thrown using the throw statement. 485 | - `reason` is a value that indicates why a promise was rejected. 486 | - `settled` the final resting state of a promise, fulfilled or rejected. 487 | 488 | A promise can be in one of three states: pending, fulfilled, or rejected. 489 | 490 | Promises that are fulfilled have a fulfillment value and are in the fulfilled 491 | state. Promises that are rejected have a rejection reason and are in the 492 | rejected state. A fulfillment value is never a thenable. 493 | 494 | Promises can also be said to *resolve* a value. If this value is also a 495 | promise, then the original promise's settled state will match the value's 496 | settled state. So a promise that *resolves* a promise that rejects will 497 | itself reject, and a promise that *resolves* a promise that fulfills will 498 | itself fulfill. 499 | 500 | 501 | Basic Usage: 502 | ------------ 503 | 504 | ```js 505 | var promise = new Promise(function(resolve, reject) { 506 | // on success 507 | resolve(value); 508 | 509 | // on failure 510 | reject(reason); 511 | }); 512 | 513 | promise.then(function(value) { 514 | // on fulfillment 515 | }, function(reason) { 516 | // on rejection 517 | }); 518 | ``` 519 | 520 | Advanced Usage: 521 | --------------- 522 | 523 | Promises shine when abstracting away asynchronous interactions such as 524 | `XMLHttpRequest`s. 525 | 526 | ```js 527 | function getJSON(url) { 528 | return new Promise(function(resolve, reject){ 529 | var xhr = new XMLHttpRequest(); 530 | 531 | xhr.open('GET', url); 532 | xhr.onreadystatechange = handler; 533 | xhr.responseType = 'json'; 534 | xhr.setRequestHeader('Accept', 'application/json'); 535 | xhr.send(); 536 | 537 | function handler() { 538 | if (this.readyState === this.DONE) { 539 | if (this.status === 200) { 540 | resolve(this.response); 541 | } else { 542 | reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); 543 | } 544 | } 545 | }; 546 | }); 547 | } 548 | 549 | getJSON('/posts.json').then(function(json) { 550 | // on fulfillment 551 | }, function(reason) { 552 | // on rejection 553 | }); 554 | ``` 555 | 556 | Unlike callbacks, promises are great composable primitives. 557 | 558 | ```js 559 | Promise.all([ 560 | getJSON('/posts'), 561 | getJSON('/comments') 562 | ]).then(function(values){ 563 | values[0] // => postsJSON 564 | values[1] // => commentsJSON 565 | 566 | return values; 567 | }); 568 | ``` 569 | 570 | @class Promise 571 | @param {function} resolver 572 | Useful for tooling. 573 | @constructor 574 | */ 575 | function lib$es6$promise$promise$$Promise(resolver) { 576 | this[lib$es6$promise$$internal$$PROMISE_ID] = lib$es6$promise$$internal$$nextId(); 577 | this._result = this._state = undefined; 578 | this._subscribers = []; 579 | 580 | if (lib$es6$promise$$internal$$noop !== resolver) { 581 | typeof resolver !== 'function' && lib$es6$promise$promise$$needsResolver(); 582 | this instanceof lib$es6$promise$promise$$Promise ? lib$es6$promise$$internal$$initializePromise(this, resolver) : lib$es6$promise$promise$$needsNew(); 583 | } 584 | } 585 | 586 | lib$es6$promise$promise$$Promise.all = lib$es6$promise$promise$all$$default; 587 | lib$es6$promise$promise$$Promise.race = lib$es6$promise$promise$race$$default; 588 | lib$es6$promise$promise$$Promise.resolve = lib$es6$promise$promise$resolve$$default; 589 | lib$es6$promise$promise$$Promise.reject = lib$es6$promise$promise$reject$$default; 590 | lib$es6$promise$promise$$Promise._setScheduler = lib$es6$promise$asap$$setScheduler; 591 | lib$es6$promise$promise$$Promise._setAsap = lib$es6$promise$asap$$setAsap; 592 | lib$es6$promise$promise$$Promise._asap = lib$es6$promise$asap$$asap; 593 | 594 | lib$es6$promise$promise$$Promise.prototype = { 595 | constructor: lib$es6$promise$promise$$Promise, 596 | 597 | /** 598 | The primary way of interacting with a promise is through its `then` method, 599 | which registers callbacks to receive either a promise's eventual value or the 600 | reason why the promise cannot be fulfilled. 601 | 602 | ```js 603 | findUser().then(function(user){ 604 | // user is available 605 | }, function(reason){ 606 | // user is unavailable, and you are given the reason why 607 | }); 608 | ``` 609 | 610 | Chaining 611 | -------- 612 | 613 | The return value of `then` is itself a promise. This second, 'downstream' 614 | promise is resolved with the return value of the first promise's fulfillment 615 | or rejection handler, or rejected if the handler throws an exception. 616 | 617 | ```js 618 | findUser().then(function (user) { 619 | return user.name; 620 | }, function (reason) { 621 | return 'default name'; 622 | }).then(function (userName) { 623 | // If `findUser` fulfilled, `userName` will be the user's name, otherwise it 624 | // will be `'default name'` 625 | }); 626 | 627 | findUser().then(function (user) { 628 | throw new Error('Found user, but still unhappy'); 629 | }, function (reason) { 630 | throw new Error('`findUser` rejected and we're unhappy'); 631 | }).then(function (value) { 632 | // never reached 633 | }, function (reason) { 634 | // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. 635 | // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. 636 | }); 637 | ``` 638 | If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. 639 | 640 | ```js 641 | findUser().then(function (user) { 642 | throw new PedagogicalException('Upstream error'); 643 | }).then(function (value) { 644 | // never reached 645 | }).then(function (value) { 646 | // never reached 647 | }, function (reason) { 648 | // The `PedgagocialException` is propagated all the way down to here 649 | }); 650 | ``` 651 | 652 | Assimilation 653 | ------------ 654 | 655 | Sometimes the value you want to propagate to a downstream promise can only be 656 | retrieved asynchronously. This can be achieved by returning a promise in the 657 | fulfillment or rejection handler. The downstream promise will then be pending 658 | until the returned promise is settled. This is called *assimilation*. 659 | 660 | ```js 661 | findUser().then(function (user) { 662 | return findCommentsByAuthor(user); 663 | }).then(function (comments) { 664 | // The user's comments are now available 665 | }); 666 | ``` 667 | 668 | If the assimliated promise rejects, then the downstream promise will also reject. 669 | 670 | ```js 671 | findUser().then(function (user) { 672 | return findCommentsByAuthor(user); 673 | }).then(function (comments) { 674 | // If `findCommentsByAuthor` fulfills, we'll have the value here 675 | }, function (reason) { 676 | // If `findCommentsByAuthor` rejects, we'll have the reason here 677 | }); 678 | ``` 679 | 680 | Simple Example 681 | -------------- 682 | 683 | Synchronous Example 684 | 685 | ```javascript 686 | var result; 687 | 688 | try { 689 | result = findResult(); 690 | // success 691 | } catch(reason) { 692 | // failure 693 | } 694 | ``` 695 | 696 | Errback Example 697 | 698 | ```js 699 | findResult(function(result, err){ 700 | if (err) { 701 | // failure 702 | } else { 703 | // success 704 | } 705 | }); 706 | ``` 707 | 708 | Promise Example; 709 | 710 | ```javascript 711 | findResult().then(function(result){ 712 | // success 713 | }, function(reason){ 714 | // failure 715 | }); 716 | ``` 717 | 718 | Advanced Example 719 | -------------- 720 | 721 | Synchronous Example 722 | 723 | ```javascript 724 | var author, books; 725 | 726 | try { 727 | author = findAuthor(); 728 | books = findBooksByAuthor(author); 729 | // success 730 | } catch(reason) { 731 | // failure 732 | } 733 | ``` 734 | 735 | Errback Example 736 | 737 | ```js 738 | 739 | function foundBooks(books) { 740 | 741 | } 742 | 743 | function failure(reason) { 744 | 745 | } 746 | 747 | findAuthor(function(author, err){ 748 | if (err) { 749 | failure(err); 750 | // failure 751 | } else { 752 | try { 753 | findBoooksByAuthor(author, function(books, err) { 754 | if (err) { 755 | failure(err); 756 | } else { 757 | try { 758 | foundBooks(books); 759 | } catch(reason) { 760 | failure(reason); 761 | } 762 | } 763 | }); 764 | } catch(error) { 765 | failure(err); 766 | } 767 | // success 768 | } 769 | }); 770 | ``` 771 | 772 | Promise Example; 773 | 774 | ```javascript 775 | findAuthor(). 776 | then(findBooksByAuthor). 777 | then(function(books){ 778 | // found books 779 | }).catch(function(reason){ 780 | // something went wrong 781 | }); 782 | ``` 783 | 784 | @method then 785 | @param {Function} onFulfilled 786 | @param {Function} onRejected 787 | Useful for tooling. 788 | @return {Promise} 789 | */ 790 | then: lib$es6$promise$then$$default, 791 | 792 | /** 793 | `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same 794 | as the catch block of a try/catch statement. 795 | 796 | ```js 797 | function findAuthor(){ 798 | throw new Error('couldn't find that author'); 799 | } 800 | 801 | // synchronous 802 | try { 803 | findAuthor(); 804 | } catch(reason) { 805 | // something went wrong 806 | } 807 | 808 | // async with promises 809 | findAuthor().catch(function(reason){ 810 | // something went wrong 811 | }); 812 | ``` 813 | 814 | @method catch 815 | @param {Function} onRejection 816 | Useful for tooling. 817 | @return {Promise} 818 | */ 819 | 'catch': function(onRejection) { 820 | return this.then(null, onRejection); 821 | } 822 | }; 823 | var lib$es6$promise$enumerator$$default = lib$es6$promise$enumerator$$Enumerator; 824 | function lib$es6$promise$enumerator$$Enumerator(Constructor, input) { 825 | this._instanceConstructor = Constructor; 826 | this.promise = new Constructor(lib$es6$promise$$internal$$noop); 827 | 828 | if (!this.promise[lib$es6$promise$$internal$$PROMISE_ID]) { 829 | lib$es6$promise$$internal$$makePromise(this.promise); 830 | } 831 | 832 | if (lib$es6$promise$utils$$isArray(input)) { 833 | this._input = input; 834 | this.length = input.length; 835 | this._remaining = input.length; 836 | 837 | this._result = new Array(this.length); 838 | 839 | if (this.length === 0) { 840 | lib$es6$promise$$internal$$fulfill(this.promise, this._result); 841 | } else { 842 | this.length = this.length || 0; 843 | this._enumerate(); 844 | if (this._remaining === 0) { 845 | lib$es6$promise$$internal$$fulfill(this.promise, this._result); 846 | } 847 | } 848 | } else { 849 | lib$es6$promise$$internal$$reject(this.promise, lib$es6$promise$enumerator$$validationError()); 850 | } 851 | } 852 | 853 | function lib$es6$promise$enumerator$$validationError() { 854 | return new Error('Array Methods must be provided an Array'); 855 | } 856 | 857 | lib$es6$promise$enumerator$$Enumerator.prototype._enumerate = function() { 858 | var length = this.length; 859 | var input = this._input; 860 | 861 | for (var i = 0; this._state === lib$es6$promise$$internal$$PENDING && i < length; i++) { 862 | this._eachEntry(input[i], i); 863 | } 864 | }; 865 | 866 | lib$es6$promise$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) { 867 | var c = this._instanceConstructor; 868 | var resolve = c.resolve; 869 | 870 | if (resolve === lib$es6$promise$promise$resolve$$default) { 871 | var then = lib$es6$promise$$internal$$getThen(entry); 872 | 873 | if (then === lib$es6$promise$then$$default && 874 | entry._state !== lib$es6$promise$$internal$$PENDING) { 875 | this._settledAt(entry._state, i, entry._result); 876 | } else if (typeof then !== 'function') { 877 | this._remaining--; 878 | this._result[i] = entry; 879 | } else if (c === lib$es6$promise$promise$$default) { 880 | var promise = new c(lib$es6$promise$$internal$$noop); 881 | lib$es6$promise$$internal$$handleMaybeThenable(promise, entry, then); 882 | this._willSettleAt(promise, i); 883 | } else { 884 | this._willSettleAt(new c(function(resolve) { resolve(entry); }), i); 885 | } 886 | } else { 887 | this._willSettleAt(resolve(entry), i); 888 | } 889 | }; 890 | 891 | lib$es6$promise$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) { 892 | var promise = this.promise; 893 | 894 | if (promise._state === lib$es6$promise$$internal$$PENDING) { 895 | this._remaining--; 896 | 897 | if (state === lib$es6$promise$$internal$$REJECTED) { 898 | lib$es6$promise$$internal$$reject(promise, value); 899 | } else { 900 | this._result[i] = value; 901 | } 902 | } 903 | 904 | if (this._remaining === 0) { 905 | lib$es6$promise$$internal$$fulfill(promise, this._result); 906 | } 907 | }; 908 | 909 | lib$es6$promise$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) { 910 | var enumerator = this; 911 | 912 | lib$es6$promise$$internal$$subscribe(promise, undefined, function(value) { 913 | enumerator._settledAt(lib$es6$promise$$internal$$FULFILLED, i, value); 914 | }, function(reason) { 915 | enumerator._settledAt(lib$es6$promise$$internal$$REJECTED, i, reason); 916 | }); 917 | }; 918 | function lib$es6$promise$polyfill$$polyfill() { 919 | var local; 920 | 921 | if (typeof global !== 'undefined') { 922 | local = global; 923 | } else if (typeof self !== 'undefined') { 924 | local = self; 925 | } else { 926 | try { 927 | local = Function('return this')(); 928 | } catch (e) { 929 | throw new Error('polyfill failed because global object is unavailable in this environment'); 930 | } 931 | } 932 | 933 | var P = local.Promise; 934 | 935 | if (P && Object.prototype.toString.call(P.resolve()) === '[object Promise]' && !P.cast) { 936 | return; 937 | } 938 | 939 | local.Promise = lib$es6$promise$promise$$default; 940 | } 941 | var lib$es6$promise$polyfill$$default = lib$es6$promise$polyfill$$polyfill; 942 | 943 | lib$es6$promise$promise$$default.Promise = lib$es6$promise$promise$$default; 944 | lib$es6$promise$promise$$default.polyfill = lib$es6$promise$polyfill$$default; 945 | 946 | /* global define:true module:true window: true */ 947 | if (typeof define === 'function' && define['amd']) { 948 | define(function() { return lib$es6$promise$promise$$default; }); 949 | } else if (typeof module !== 'undefined' && module['exports']) { 950 | module['exports'] = lib$es6$promise$promise$$default; 951 | } else if (typeof this !== 'undefined') { 952 | this['Promise'] = lib$es6$promise$promise$$default; 953 | } 954 | 955 | lib$es6$promise$polyfill$$default(); 956 | }).call(this); 957 | -------------------------------------------------------------------------------- /src/libs/image-compress.js: -------------------------------------------------------------------------------- 1 | function imageComporess (file, quality, callback) { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/libs/init.js: -------------------------------------------------------------------------------- 1 | // init 2 | window.URL = window.URL || window.webkitURL 3 | 4 | function isCanvasBlank (canvas) { 5 | var blank = document.createElement('canvas') 6 | blank.width = canvas.width 7 | blank.height = canvas.height 8 | 9 | return canvas.toDataURL() == blank.toDataURL() 10 | } 11 | 12 | function newBlob (data, datatype) { 13 | var out 14 | try { 15 | out = new Blob([data], { type: datatype }) 16 | } catch (e) { 17 | window.BlobBuilder = window.BlobBuilder || 18 | window.WebKitBlobBuilder || 19 | window.MozBlobBuilder || 20 | window.MSBlobBuilder 21 | 22 | if (e.name == 'TypeError' && window.BlobBuilder) { 23 | var bb = new BlobBuilder() 24 | bb.append(data) 25 | out = bb.getBlob(datatype) 26 | } else if (e.name == 'InvalidStateError') { 27 | out = new Blob([data], { type: datatype }) 28 | } else { 29 | throw new Error('Your browser does not support Blob & BlobBuilder!') 30 | } 31 | } 32 | return out 33 | } 34 | 35 | function dataURL2Blob (dataURI) { 36 | var byteStr 37 | var intArray 38 | var ab 39 | var i 40 | var mimetype 41 | var parts 42 | 43 | parts = dataURI.split(',') 44 | parts[1] = parts[1].replace(/\s/g, '') 45 | 46 | if (~parts[0].indexOf('base64')) { 47 | byteStr = atob(parts[1]) 48 | } else { 49 | byteStr = decodeURIComponent(parts[1]) 50 | } 51 | 52 | ab = new ArrayBuffer(byteStr.length) 53 | intArray = new Uint8Array(ab) 54 | 55 | for (i = 0; i < byteStr.length; i++) { 56 | intArray[i] = byteStr.charCodeAt(i) 57 | } 58 | 59 | mimetype = parts[0].split(':')[1].split(';')[0] 60 | 61 | return new newBlob(ab, mimetype) 62 | } 63 | 64 | function string2ArrayBuffer (string) { 65 | var bytes = Array.prototype.map.call(string, function (c) { 66 | return c.charCodeAt(0) & 0xff 67 | }) 68 | return new Uint8Array(bytes).buffer 69 | } 70 | 71 | function getFile (url, callback) { 72 | var obj = new XMLHttpRequest() 73 | obj.open('HEAD', url, true) 74 | obj.onreadystatechange = function () { 75 | if (obj.readyState == 4) { 76 | if (obj.status == 200) { 77 | callback && callback(null, { 78 | contentLength: obj.getResponseHeader('Content-Length'), 79 | contentType: obj.getResponseHeader('Content-Type') 80 | }) 81 | } else { 82 | callback && callback(new Error('Load error')) 83 | } 84 | } 85 | } 86 | obj.send(null) 87 | } 88 | -------------------------------------------------------------------------------- /src/libs/whatwg-fetch.js: -------------------------------------------------------------------------------- 1 | (function(self) { 2 | 'use strict'; 3 | 4 | if (self.fetch) { 5 | return 6 | } 7 | 8 | var support = { 9 | searchParams: 'URLSearchParams' in self, 10 | iterable: 'Symbol' in self && 'iterator' in Symbol, 11 | blob: 'FileReader' in self && 'Blob' in self && (function() { 12 | try { 13 | new Blob() 14 | return true 15 | } catch(e) { 16 | return false 17 | } 18 | })(), 19 | formData: 'FormData' in self, 20 | arrayBuffer: 'ArrayBuffer' in self 21 | } 22 | 23 | function normalizeName(name) { 24 | if (typeof name !== 'string') { 25 | name = String(name) 26 | } 27 | if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) { 28 | throw new TypeError('Invalid character in header field name') 29 | } 30 | return name.toLowerCase() 31 | } 32 | 33 | function normalizeValue(value) { 34 | if (typeof value !== 'string') { 35 | value = String(value) 36 | } 37 | return value 38 | } 39 | 40 | // Build a destructive iterator for the value list 41 | function iteratorFor(items) { 42 | var iterator = { 43 | next: function() { 44 | var value = items.shift() 45 | return {done: value === undefined, value: value} 46 | } 47 | } 48 | 49 | if (support.iterable) { 50 | iterator[Symbol.iterator] = function() { 51 | return iterator 52 | } 53 | } 54 | 55 | return iterator 56 | } 57 | 58 | function Headers(headers) { 59 | this.map = {} 60 | 61 | if (headers instanceof Headers) { 62 | headers.forEach(function(value, name) { 63 | this.append(name, value) 64 | }, this) 65 | 66 | } else if (headers) { 67 | Object.getOwnPropertyNames(headers).forEach(function(name) { 68 | this.append(name, headers[name]) 69 | }, this) 70 | } 71 | } 72 | 73 | Headers.prototype.append = function(name, value) { 74 | name = normalizeName(name) 75 | value = normalizeValue(value) 76 | var list = this.map[name] 77 | if (!list) { 78 | list = [] 79 | this.map[name] = list 80 | } 81 | list.push(value) 82 | } 83 | 84 | Headers.prototype['delete'] = function(name) { 85 | delete this.map[normalizeName(name)] 86 | } 87 | 88 | Headers.prototype.get = function(name) { 89 | var values = this.map[normalizeName(name)] 90 | return values ? values[0] : null 91 | } 92 | 93 | Headers.prototype.getAll = function(name) { 94 | return this.map[normalizeName(name)] || [] 95 | } 96 | 97 | Headers.prototype.has = function(name) { 98 | return this.map.hasOwnProperty(normalizeName(name)) 99 | } 100 | 101 | Headers.prototype.set = function(name, value) { 102 | this.map[normalizeName(name)] = [normalizeValue(value)] 103 | } 104 | 105 | Headers.prototype.forEach = function(callback, thisArg) { 106 | Object.getOwnPropertyNames(this.map).forEach(function(name) { 107 | this.map[name].forEach(function(value) { 108 | callback.call(thisArg, value, name, this) 109 | }, this) 110 | }, this) 111 | } 112 | 113 | Headers.prototype.keys = function() { 114 | var items = [] 115 | this.forEach(function(value, name) { items.push(name) }) 116 | return iteratorFor(items) 117 | } 118 | 119 | Headers.prototype.values = function() { 120 | var items = [] 121 | this.forEach(function(value) { items.push(value) }) 122 | return iteratorFor(items) 123 | } 124 | 125 | Headers.prototype.entries = function() { 126 | var items = [] 127 | this.forEach(function(value, name) { items.push([name, value]) }) 128 | return iteratorFor(items) 129 | } 130 | 131 | if (support.iterable) { 132 | Headers.prototype[Symbol.iterator] = Headers.prototype.entries 133 | } 134 | 135 | function consumed(body) { 136 | if (body.bodyUsed) { 137 | return Promise.reject(new TypeError('Already read')) 138 | } 139 | body.bodyUsed = true 140 | } 141 | 142 | function fileReaderReady(reader) { 143 | return new Promise(function(resolve, reject) { 144 | reader.onload = function() { 145 | resolve(reader.result) 146 | } 147 | reader.onerror = function() { 148 | reject(reader.error) 149 | } 150 | }) 151 | } 152 | 153 | function readBlobAsArrayBuffer(blob) { 154 | var reader = new FileReader() 155 | reader.readAsArrayBuffer(blob) 156 | return fileReaderReady(reader) 157 | } 158 | 159 | function readBlobAsText(blob) { 160 | var reader = new FileReader() 161 | reader.readAsText(blob) 162 | return fileReaderReady(reader) 163 | } 164 | 165 | function Body() { 166 | this.bodyUsed = false 167 | 168 | this._initBody = function(body) { 169 | this._bodyInit = body 170 | if (typeof body === 'string') { 171 | this._bodyText = body 172 | } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { 173 | this._bodyBlob = body 174 | } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { 175 | this._bodyFormData = body 176 | } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { 177 | this._bodyText = body.toString() 178 | } else if (!body) { 179 | this._bodyText = '' 180 | } else if (support.arrayBuffer && ArrayBuffer.prototype.isPrototypeOf(body)) { 181 | // Only support ArrayBuffers for POST method. 182 | // Receiving ArrayBuffers happens via Blobs, instead. 183 | } else { 184 | throw new Error('unsupported BodyInit type') 185 | } 186 | 187 | if (!this.headers.get('content-type')) { 188 | if (typeof body === 'string') { 189 | this.headers.set('content-type', 'text/plain;charset=UTF-8') 190 | } else if (this._bodyBlob && this._bodyBlob.type) { 191 | this.headers.set('content-type', this._bodyBlob.type) 192 | } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { 193 | this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8') 194 | } 195 | } 196 | } 197 | 198 | if (support.blob) { 199 | this.blob = function() { 200 | var rejected = consumed(this) 201 | if (rejected) { 202 | return rejected 203 | } 204 | 205 | if (this._bodyBlob) { 206 | return Promise.resolve(this._bodyBlob) 207 | } else if (this._bodyFormData) { 208 | throw new Error('could not read FormData body as blob') 209 | } else { 210 | return Promise.resolve(new Blob([this._bodyText])) 211 | } 212 | } 213 | 214 | this.arrayBuffer = function() { 215 | return this.blob().then(readBlobAsArrayBuffer) 216 | } 217 | 218 | this.text = function() { 219 | var rejected = consumed(this) 220 | if (rejected) { 221 | return rejected 222 | } 223 | 224 | if (this._bodyBlob) { 225 | return readBlobAsText(this._bodyBlob) 226 | } else if (this._bodyFormData) { 227 | throw new Error('could not read FormData body as text') 228 | } else { 229 | return Promise.resolve(this._bodyText) 230 | } 231 | } 232 | } else { 233 | this.text = function() { 234 | var rejected = consumed(this) 235 | return rejected ? rejected : Promise.resolve(this._bodyText) 236 | } 237 | } 238 | 239 | if (support.formData) { 240 | this.formData = function() { 241 | return this.text().then(decode) 242 | } 243 | } 244 | 245 | this.json = function() { 246 | return this.text().then(JSON.parse) 247 | } 248 | 249 | return this 250 | } 251 | 252 | // HTTP methods whose capitalization should be normalized 253 | var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] 254 | 255 | function normalizeMethod(method) { 256 | var upcased = method.toUpperCase() 257 | return (methods.indexOf(upcased) > -1) ? upcased : method 258 | } 259 | 260 | function Request(input, options) { 261 | options = options || {} 262 | var body = options.body 263 | if (Request.prototype.isPrototypeOf(input)) { 264 | if (input.bodyUsed) { 265 | throw new TypeError('Already read') 266 | } 267 | this.url = input.url 268 | this.credentials = input.credentials 269 | if (!options.headers) { 270 | this.headers = new Headers(input.headers) 271 | } 272 | this.method = input.method 273 | this.mode = input.mode 274 | if (!body) { 275 | body = input._bodyInit 276 | input.bodyUsed = true 277 | } 278 | } else { 279 | this.url = input 280 | } 281 | 282 | this.credentials = options.credentials || this.credentials || 'omit' 283 | if (options.headers || !this.headers) { 284 | this.headers = new Headers(options.headers) 285 | } 286 | this.method = normalizeMethod(options.method || this.method || 'GET') 287 | this.mode = options.mode || this.mode || null 288 | this.referrer = null 289 | 290 | if ((this.method === 'GET' || this.method === 'HEAD') && body) { 291 | throw new TypeError('Body not allowed for GET or HEAD requests') 292 | } 293 | this._initBody(body) 294 | } 295 | 296 | Request.prototype.clone = function() { 297 | return new Request(this) 298 | } 299 | 300 | function decode(body) { 301 | var form = new FormData() 302 | body.trim().split('&').forEach(function(bytes) { 303 | if (bytes) { 304 | var split = bytes.split('=') 305 | var name = split.shift().replace(/\+/g, ' ') 306 | var value = split.join('=').replace(/\+/g, ' ') 307 | form.append(decodeURIComponent(name), decodeURIComponent(value)) 308 | } 309 | }) 310 | return form 311 | } 312 | 313 | function headers(xhr) { 314 | var head = new Headers() 315 | var pairs = (xhr.getAllResponseHeaders() || '').trim().split('\n') 316 | pairs.forEach(function(header) { 317 | var split = header.trim().split(':') 318 | var key = split.shift().trim() 319 | var value = split.join(':').trim() 320 | head.append(key, value) 321 | }) 322 | return head 323 | } 324 | 325 | Body.call(Request.prototype) 326 | 327 | function Response(bodyInit, options) { 328 | if (!options) { 329 | options = {} 330 | } 331 | 332 | this.type = 'default' 333 | this.status = options.status 334 | this.ok = this.status >= 200 && this.status < 300 335 | this.statusText = options.statusText 336 | this.headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers) 337 | this.url = options.url || '' 338 | this._initBody(bodyInit) 339 | } 340 | 341 | Body.call(Response.prototype) 342 | 343 | Response.prototype.clone = function() { 344 | return new Response(this._bodyInit, { 345 | status: this.status, 346 | statusText: this.statusText, 347 | headers: new Headers(this.headers), 348 | url: this.url 349 | }) 350 | } 351 | 352 | Response.error = function() { 353 | var response = new Response(null, {status: 0, statusText: ''}) 354 | response.type = 'error' 355 | return response 356 | } 357 | 358 | var redirectStatuses = [301, 302, 303, 307, 308] 359 | 360 | Response.redirect = function(url, status) { 361 | if (redirectStatuses.indexOf(status) === -1) { 362 | throw new RangeError('Invalid status code') 363 | } 364 | 365 | return new Response(null, {status: status, headers: {location: url}}) 366 | } 367 | 368 | self.Headers = Headers 369 | self.Request = Request 370 | self.Response = Response 371 | 372 | self.fetch = function(input, init) { 373 | return new Promise(function(resolve, reject) { 374 | var request 375 | if (Request.prototype.isPrototypeOf(input) && !init) { 376 | request = input 377 | } else { 378 | request = new Request(input, init) 379 | } 380 | 381 | var xhr = new XMLHttpRequest() 382 | 383 | function responseURL() { 384 | if ('responseURL' in xhr) { 385 | return xhr.responseURL 386 | } 387 | 388 | // Avoid security warnings on getResponseHeader when not allowed by CORS 389 | if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) { 390 | return xhr.getResponseHeader('X-Request-URL') 391 | } 392 | 393 | return 394 | } 395 | 396 | xhr.onload = function() { 397 | var options = { 398 | status: xhr.status, 399 | statusText: xhr.statusText, 400 | headers: headers(xhr), 401 | url: responseURL() 402 | } 403 | var body = 'response' in xhr ? xhr.response : xhr.responseText 404 | resolve(new Response(body, options)) 405 | } 406 | 407 | xhr.onerror = function() { 408 | reject(new TypeError('Network request failed')) 409 | } 410 | 411 | xhr.ontimeout = function() { 412 | reject(new TypeError('Network request failed')) 413 | } 414 | 415 | xhr.open(request.method, request.url, true) 416 | 417 | if (request.credentials === 'include') { 418 | xhr.withCredentials = true 419 | } 420 | 421 | if ('responseType' in xhr && support.blob) { 422 | xhr.responseType = 'blob' 423 | } 424 | 425 | request.headers.forEach(function(value, name) { 426 | xhr.setRequestHeader(name, value) 427 | }) 428 | 429 | xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit) 430 | }) 431 | } 432 | self.fetch.polyfill = true 433 | })(typeof self !== 'undefined' ? self : this); 434 | -------------------------------------------------------------------------------- /src/upload-blob-fetch.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/image-compress/a899541c578b03805e5ff49fa32c84db4af96f0b/src/upload-blob-fetch.html -------------------------------------------------------------------------------- /src/upload-blob-jQuery.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/image-compress/a899541c578b03805e5ff49fa32c84db4af96f0b/src/upload-blob-jQuery.html -------------------------------------------------------------------------------- /src/upload-blob-xhr.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/image-compress/a899541c578b03805e5ff49fa32c84db4af96f0b/src/upload-blob-xhr.html -------------------------------------------------------------------------------- /src/upload-blob-zepto.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blade254353074/image-compress/a899541c578b03805e5ff49fa32c84db4af96f0b/src/upload-blob-zepto.html --------------------------------------------------------------------------------