Commit 55a7312a by zhoubin

代码初始化

parent b3557475
.DS_Store
node_modules
bower_components
demo/config.js
deploy.sh
/.project
/dist/
/*************************************************************
* JSHint Docs:
* http://www.jshint.com/docs
*
* NOTICE: All of our javascript file should pass check of jsHint.
*
* by MaYiqing
*************************************************************/
{
// Global objects
"predef": [
"Q", // global namespace
"model", // UserViewModel
"ko", // knockout.js
"Highcharts", // highcharts.js
"phpjs", // php.js
"_bucketName" // defined in bucket_box_layout.html
],
// Enforcing options
//"quotmark": "single", // always use single quotes
"noarg": true, // forbiden arguments.calle and arguments.caller
"noempty": true, // don't let statements blocks empty
"eqeqeq": true, // always use === to compare
"undef": true, // don't use undefined object
"unused": "vars", // warning when there are some var never use in the file
"curly": true, // always put curly braces around blocks in loops and conditionals
"forin": true, // use hasOwnProperty when use "for(key in obj){}"
"newcap": true, // must capitalize names of constructor functions
// Relaxing options
"expr": true, // suppresses warnings about the use of expressions where function calls
"boss": true, // suppresses warnings about the use of assignments in cases where comparisons are expected
// Enviroments
"browser": true, // globals exposed by modern browsers, like document, window, etc.
"devel": true, // globals that are usually used for logging poor-man's debugging: console, alert, etc.
"jquery": true, // jquery library
"node": true,
"evil": true
}
language: node_js
node_js:
- '4'
cache:
directories:
- node_modules
- bower_components
install:
- npm install
- bower install
script:
- make build
deploy:
provider: npm
email: sdk@qiniu.com
api_key:
secure: d06l6wsuv/YFeY3E0TRGemGjtQ7LZdN9WxiFnseT2exTFkDgMw9EV2uOUWllXNrbxXUY+2ZpDswtYrgZ4exQGfKKQRGFy6/g8oxPgyylVwBz98vlie8NvXXmANoyt7mwQwjninf5/XUiY3cwn9YCaU1jyxcayFdQTex05T++qvc=
on:
tags: true
all : install build dev
install :
npm install
bower install
build :
npm run build
dev :
npm run watch
node demo/server.js
# 源码地址 Qiniu-JavaScript-SDK
============
基于七牛 API 及 Plupload 开发的前端 JavaScript SDK
### 快速导航
- [示例网站](http://jssdk.demo.qiniu.io/)
- [安装与使用](#usage)
- [运行示例](#demo)
- [常见问题](#faq)
### 概述
Qiniu-JavaScript-SDK (下文简称为 JS-SDK)适用于 IE8+、Chrome、Firefox、Safari 等浏览器,基于七牛云存储官方 API 构建,其中上传功能基于 [Plupload](http://www.plupload.com/) 插件封装。开发者基于 JS-SDK 可以方便的从浏览器端上传文件至七牛云存储,并对上传成功后的图片进行丰富的数据处理操作。
不考虑兼容性的情况下,如手机端,建议直接使用 Formdata 结合七牛[表单上传](https://developer.qiniu.com/kodo/manual/form-upload)的方式上传文件。
[ Formdata 上传 demo ](http://jssdk.demo.qiniu.io/formdata)
![ Formdata ](http://oky1vwhqm.bkt.clouddn.com/1486368013.png)
Qiniu-JavaScript-SDK 为客户端 SDK,没有包含 token 生成实现,为了安全,token 建议通过网络从服务端获取,具体生成代码可以参考以下服务端 SDK 的文档。
- [Android](https://developer.qiniu.com/kodo/sdk/android)
- [Java](https://developer.qiniu.com/kodo/sdk/java)
- [PHP](https://developer.qiniu.com/kodo/sdk/php)
- [Python](https://developer.qiniu.com/kodo/sdk/python)
- [Ruby](https://developer.qiniu.com/kodo/sdk/ruby)
- [Go](https://developer.qiniu.com/kodo/sdk/go)
- [Node.js](https://developer.qiniu.com/kodo/sdk/nodejs)
- [C#](https://developer.qiniu.com/kodo/sdk/csharp)
- [C/C++](https://developer.qiniu.com/kodo/sdk/cpp)
- [Objective-C](https://developer.qiniu.com/kodo/sdk/objc)
Qiniu-JavaScript-SDK 的示例 Demo 中的服务器端部分是基于[ Node.js 服务器端 SDK ](https://developer.qiniu.com/kodo/sdk/nodejs) 开发的。
<!--
本 SDK 可使开发者忽略上传底层实现细节,而更多的关注 UI 层的展现。
-->
### 功能简介
- 上传
- html5 模式大于 4M 时可分块上传,小于4M时直传
- 分块上传时,可以断点续上传
- flash、html4 模式直接上传
- 继承了 plupload 的功能,可筛选文件上传、拖曳上传等
- 下载(公开资源)
- 数据处理(图片)
- imageView2(缩略图)
- imageMogr2(高级处理,包含缩放、裁剪、旋转等)
- imageInfo (获取基本信息)
- exif (获取图片 EXIF 信息)
- watermark (文字、图片水印)
- pipeline (管道,可对 imageView2、imageMogr2、watermark 进行链式处理)
### 项目构成介绍
```
├── demo // 示例 Demo
│ ├── images
│ │ └── ...
│ ├── scripts
│ │ └── ...
│ ├── styles
│ │ └── ...
│ ├── views
│ │ └── ...
│ ├── config.js.example
│ └── server.js // 示例 Demo 的服务器端程序
├── dist // SDK 输出目录
│ ├── qiniu.js // 非压缩版
│ ├── qiniu.min.js // 压缩版
│ └── qiniu.min.map // 压缩版的 source map 文件
├── src // SDK 源目录
│ └── qiniu.js // 源文件
├── Gruntfile.js
├── Makefile
├── README.md
├── bower.json
└── package.json
```
<a id="usage"></a>
### 准备
- JS-SDK 的上传功能基于 [Plupload](http://www.plupload.com/) 插件封装的,所以需要[下载 Plupload](http://plupload.com/download)
您也可以访问[ 开放静态文件 CDN ](http://staticfile.org/),搜索 plupload,使用 CDN 加速的静态文件地址。
- 在使用 JS-SDK 之前,您必须先注册一个七牛帐号,并登录控制台获取一对有效的 AccessKey 和 SecretKey,您可以阅读[ 快速入门 ](https://developer.qiniu.com/kodo/manual/console-quickstart)[ 安全机制 ](https://developer.qiniu.com/kodo/manual/security#security) 以进一步了解如何正确使用和管理密钥 。
- JS-SDK 依赖服务端颁发 uptoken,可以通过以下二种方式实现:
- 利用[七牛服务端 SDK ](https://developer.qiniu.com/sdk#sdk)构建后端服务
- 利用七牛底层 API 构建服务,详见七牛[上传策略](https://developer.qiniu.com/kodo/manual/put-policy)[上传凭证](https://developer.qiniu.com/kodo/manual/upload-token)
后端服务应提供一个 URL 地址,供 JS-SDK 初始化使用,前端通过 Ajax 请求该地址后获得 uptoken。Ajax 请求成功后,服务端应返回如下格式的 json:
```
{
"uptoken": "0MLvWPnyya1WtPnXFy9KLyGHyFPNdZceomL..."
}
```
### 安装
支持以下几种安装方式
- 直接使用CDN 加速的静态文件地址,访问[ 开放静态文件 CDN ](http://staticfile.org/),搜索 qiniu
```
https://cdn.staticfile.org/qiniu-JS-SDK/<version>/qiniu.min.js
```
- 使用 Bower 安装
Bower 是一个客户端技术的软件包管理器,它可用于搜索、安装和卸载如 JavaScript、HTML、CSS 之类的网络资源。如果需要更详细的关于 Bower 的使用说明,您可以访问[ Bower 官方网站](http://bower.io/)。
通过 Bower 安装会将 JS-SDK 依赖的 plupload 也一起安装在 `bower_components` 中:
```
bower install qiniu
```
执行之后,JS-SDK 和 plupload 分别在以下位置
```
bower_components
├── plupload
│ └── js
│ ├── moxie.js
│ ├── moxie.min.js
│ ├── plupload.dev.js
│ ├── plupload.full.min.js
│ └── plupload.min.js
└── qiniu
└── dist
├── qiniu.js
├── qiniu.min.js
└── qiniu.min.map
```
- 使用 NPM 安装
NPM 的全称是 Node Package Manager,是一个[ NodeJS ](https://nodejs.org)包管理和分发工具,已经成为了非官方的发布 Node 模块(包)的标准。如果需要更详细的关于 NPM 的使用说明,您可以访问[ NPM 官方网站](https://www.npmjs.com),或对应的[中文网站](http://www.npmjs.com.cn/)
```
npm install qiniu-js
```
执行之后,JS-SDK 在以下位置
```
node_modules
└── qiniu-js
└── dist
├── qiniu.js
├── qiniu.min.js
└── qiniu.min.map
```
- 通过 Github 上的 qiniu/js-sdk 仓库获取
下载最新的[ 发布版本 ](https://github.com/qiniu/js-sdk/releases)并解压 或 直接克隆仓库
```
git clone https://github.com/qiniu/js-sdk.git
```
JS-SDK 在 `dist` 目录中
### 使用
#### 上传功能
1. 在页面中引入 plupload,`plupload.full.min.js`(生产环境)或 引入`plupload.dev.js``moxie.js`(开发调试)
2. 在页面中引入 `qiniu.min.js`(生产环境)或 `qiniu.js`(开发调试)
3. 初始化 uploader,**请确保在执行初始化时,页面已经引入 plupload**
```JavaScript
var uploader = Qiniu.uploader({
disable_statistics_report: false, // 禁止自动发送上传统计信息到七牛,默认允许发送
runtimes: 'html5,flash,html4', // 上传模式,依次退化
browse_button: 'pickfiles', // 上传选择的点选按钮,**必需**
// 在初始化时,uptoken, uptoken_url, uptoken_func 三个参数中必须有一个被设置
// 切如果提供了多个,其优先级为 uptoken > uptoken_url > uptoken_func
// 其中 uptoken 是直接提供上传凭证,uptoken_url 是提供了获取上传凭证的地址,如果需要定制获取 uptoken 的过程则可以设置 uptoken_func
// uptoken : '<Your upload token>', // uptoken 是上传凭证,由其他程序生成
// uptoken_url: '/uptoken', // Ajax 请求 uptoken 的 Url,**强烈建议设置**(服务端提供)
// uptoken_func: function(file){ // 在需要获取 uptoken 时,该方法会被调用
// // do something
// return uptoken;
// },
get_new_uptoken: false, // 设置上传文件的时候是否每次都重新获取新的 uptoken
// downtoken_url: '/downtoken',
// Ajax请求downToken的Url,私有空间时使用,JS-SDK 将向该地址POST文件的key和domain,服务端返回的JSON必须包含`url`字段,`url`值为该文件的下载地址
// unique_names: true, // 默认 false,key 为文件名。若开启该选项,JS-SDK 会为每个文件自动生成key(文件名)
// save_key: true, // 默认 false。若在服务端生成 uptoken 的上传策略中指定了 `save_key`,则开启,SDK在前端将不对key进行任何处理
domain: '<Your bucket domain>', // bucket 域名,下载资源时用到,如:'http://xxx.bkt.clouddn.com/' **必需**
container: 'container', // 上传区域 DOM ID,默认是 browser_button 的父元素,
max_file_size: '100mb', // 最大文件体积限制
flash_swf_url: 'path/of/plupload/Moxie.swf', //引入 flash,相对路径
max_retries: 3, // 上传失败最大重试次数
dragdrop: true, // 开启可拖曳上传
drop_element: 'container', // 拖曳上传区域元素的 ID,拖曳文件或文件夹后可触发上传
chunk_size: '4mb', // 分块上传时,每块的体积
auto_start: true, // 选择文件后自动上传,若关闭需要自己绑定事件触发上传,
//x_vars : {
// 自定义变量,参考http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html
// 'time' : function(up,file) {
// var time = (new Date()).getTime();
// do something with 'time'
// return time;
// },
// 'size' : function(up,file) {
// var size = file.size;
// do something with 'size'
// return size;
// }
//},
init: {
'FilesAdded': function(up, files) {
plupload.each(files, function(file) {
// 文件添加进队列后,处理相关的事情
});
},
'BeforeUpload': function(up, file) {
// 每个文件上传前,处理相关的事情
},
'UploadProgress': function(up, file) {
// 每个文件上传时,处理相关的事情
},
'FileUploaded': function(up, file, info) {
// 每个文件上传成功后,处理相关的事情
// 其中 info.response 是文件上传成功后,服务端返回的json,形式如
// {
// "hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98",
// "key": "gogopher.jpg"
// }
// 参考http://developer.qiniu.com/docs/v6/api/overview/up/response/simple-response.html
// var domain = up.getOption('domain');
// var res = parseJSON(info.response);
// var sourceLink = domain + res.key; 获取上传成功后的文件的Url
},
'Error': function(up, err, errTip) {
//上传出错时,处理相关的事情
},
'UploadComplete': function() {
//队列文件处理完毕后,处理相关的事情
},
'Key': function(up, file) {
// 若想在前端对每个文件的key进行个性化处理,可以配置该函数
// 该配置必须要在 unique_names: false , save_key: false 时才生效
var key = "";
// do something with key here
return key
}
}
});
// domain 为七牛空间(bucket)对应的域名,选择某个空间后,可通过"空间设置->基本设置->域名设置"查看获取
// uploader 为一个 plupload 对象,继承了所有 plupload 的方法,参考http://plupload.com/docs
```
- 如果一个页面中有多个上传实例,可以如下操作
```JavaScript
var option1 = {
key : val ,
……
};
var uploader = Qiniu.uploader(option1);
var Qiniu2 = new QiniuJsSDK();
var option2 = {
key : val ,
……
};
var uploader2 = Qiniu2.uploader(option2);
```
#### 对上传成功的图片进行数据处理
- watermark(水印)
```JavaScript
// key 为每个文件上传成功后,服务端返回的json字段,即资源的最终名字,下同
// key 可在每个文件'FileUploaded'事件被触发时获得
var imgLink = Qiniu.watermark({
mode: 1, // 图片水印
image: 'http://www.b1.qiniudn.com/images/logo-2.png', // 图片水印的Url,mode = 1 时 **必需**
dissolve: 50, // 透明度,取值范围1-100,非必需,下同
gravity: 'SouthWest', // 水印位置,为以下参数[NorthWest、North、NorthEast、West、Center、East、SouthWest、South、SouthEast]之一
dx: 100, // 横轴边距,单位:像素(px)
dy: 100 // 纵轴边距,单位:像素(px)
}, key); // key 为非必需参数,下同
// imgLink 可以赋值给 html 的 img 元素的 src 属性,下同
// 若未指定key,可以通过以下方式获得完整的 imgLink,下同
// imgLink = '<domain>/<key>?' + imgLink
// <domain> 为七牛空间(bucket)对应的域名,选择某个空间后,可通过"空间设置->基本设置->域名设置"查看获取
```
```JavaScript
var imgLink = Qiniu.watermark({
mode: 2, // 文字水印
text: 'hello world !', // 水印文字,mode = 2 时 **必需**
dissolve: 50, // 透明度,取值范围1-100,非必需,下同
gravity: 'SouthWest', // 水印位置,同上
fontsize: 500, // 字体大小,单位: 缇
font: '黑体', // 水印文字字体
dx: 100, // 横轴边距,单位:像素(px)
dy: 100, // 纵轴边距,单位:像素(px)
fill: '#FFF000' // 水印文字颜色,RGB格式,可以是颜色名称
}, key);
```
具体水印参数解释见[水印(watermark)](https://developer.qiniu.com/dora/api/image-watermarking-processing-watermark)
- imageView2
```JavaScript
var imgLink = Qiniu.imageView2({
mode: 3, // 缩略模式,共6种[0-5]
w: 100, // 具体含义由缩略模式决定
h: 100, // 具体含义由缩略模式决定
q: 100, // 新图的图像质量,取值范围:1-100
format: 'png' // 新图的输出格式,取值范围:jpg,gif,png,webp等
}, key);
```
具体缩略参数解释见[图片基本处理(imageView2)](https://developer.qiniu.com/dora/api/basic-processing-images-imageview2)
- imageMogr2
```JavaScript
var imgLink = Qiniu.imageMogr2({
auto-orient: true, // 布尔值,是否根据原图EXIF信息自动旋正,便于后续处理,建议放在首位。
strip: true, // 布尔值,是否去除图片中的元信息
thumbnail: '1000x1000' // 缩放操作参数
crop: '!300x400a10a10', // 裁剪操作参数
gravity: 'NorthWest', // 裁剪锚点参数
quality: 40, // 图片质量,取值范围1-100
rotate: 20, // 旋转角度,取值范围1-360,缺省为不旋转。
format: 'png', // 新图的输出格式,取值范围:jpg,gif,png,webp等
blur:'3x5' // 高斯模糊参数
}, key);
```
具体高级图像处理参数解释见[图像高级处理(imageMogr2)](https://developer.qiniu.com/dora/api/the-advanced-treatment-of-images-imagemogr2)
- imageInfo
```JavaScript
var imageInfoObj = Qiniu.imageInfo(key);
```
具体 imageInfo 解释见[图片基本信息(imageInfo)](https://developer.qiniu.com/dora/api/pictures-basic-information-imageinfo)
Ajax跨域限制,IE系列此函数只支持IE10+
- exif
```JavaScript
var exifOjb = Qiniu.exif(key);
```
具体 exif 解释见[图片EXIF信息(exif)](https://developer.qiniu.com/dora/api/photo-exif-information-exif)
Ajax跨域限制,IE系列此函数只支持IE10+
- pipeline(管道操作)
```JavaScript
var fopArr = [{
fop: 'watermark', // 指定watermark操作
mode: 2, // 此参数同watermark函数的参数,下同。
text: 'hello world !',
dissolve: 50,
gravity: 'SouthWest',
fontsize: 500,
font : '黑体',
dx: 100,
dy: 100,
fill: '#FFF000'
},{
fop: 'imageView2', // 指定imageView2操作
mode: 3, // 此参数同imageView2函数的参数,下同
w: 100,
h: 100,
q: 100,
format: 'png'
},{
fop: 'imageMogr2', // 指定imageMogr2操作
auto-orient: true, // 此参数同imageMogr2函数的参数,下同。
strip: true,
thumbnail: '1000x1000'
crop: '!300x400a10a10',
gravity: 'NorthWest',
quality: 40,
rotate: 20,
format: 'png',
blur:'3x5'
}];
// fopArr 可以为三种类型'watermark'、'imageMogr2'、'imageView2'中的任意1-3个
// 例如只对'watermark'、'imageMogr2'进行管道操作,则如下即可
// var fopArr = [{
// fop: 'watermark', // 指定watermark操作
// mode: 2, // 此参数同watermark函数的参数,下同。
// text: 'hello world !',
// dissolve: 50,
// gravity: 'SouthWest',
// fontsize: 500,
// font : '黑体',
// dx: 100,
// dy: 100,
// fill: '#FFF000'
// },{
// fop: 'imageMogr2', // 指定imageMogr2操作
// auto-orient: true, // 此参数同imageMogr2函数的参数,下同。
// strip: true,
// thumbnail: '1000x1000'
// crop: '!300x400a10a10',
// gravity: 'NorthWest',
// quality: 40,
// rotate: 20,
// format: 'png',
// blur:'3x5'
// }];
var imgLink = Qiniu.pipeline(fopArr, key));
```
具体管道操作解释见[管道操作](https://developer.qiniu.com/dora/manual/processing-mechanism)
<a id="demo"></a>
### 运行示例
1. 进入项目根目录,执行 `make install``npm install & bower install` 安装依赖第三方库
2. 进入 `demo` 目录,按照目录下的 `config.example` 示例,创建 `config.js` 文件,其中,`Access Key``Secret Key` 按如下方式获取
* [开通七牛开发者帐号](https://portal.qiniu.com/signup)
* [登录七牛开发者自助平台,查看 AccessKey 和 SecretKey](https://portal.qiniu.com/user/key) 。
```javascript
module.exports = {
'AccessKey': '<Your Access Key>',
'SecretKey': '<Your Secret Key>',
'Bucket': '<Your Bucket Name>',
'Port': 19110,
'UptokenUrl': '<Your Uptoken_Url>', // demo 启动后会在本地 /uptoken 上提供获取 uptoken 的接口,所以这里可以填 'uptoken'
'Domain': '<Your Bucket Domain>' // Bucket 的外链默认域名,在 Bucket 的内容管理里查看,如:'http://xxx.bkt.clouddn.com/'
}
```
3. 进入项目根目录,执行 `make dev``node demo/server.js` 访问命令行打印出的 demo 地址。
<a id="note"></a>
### 说明
1. JS-SDK 依赖 Plupload,初始化之前请引入 Plupload。
2. JS-SDK 依赖 uptoken,可以直接设置 `uptoken` 、通过提供 Ajax 请求地址 `uptoken_url` 或者通过提供一个能够返回 uptoken 的函数 `uptoken_func` 实现。
3. 如果您想了解更多七牛的上传策略,建议您仔细阅读 [七牛官方文档-上传](https://developer.qiniu.com/kodo/manual/upload-types)
另外,七牛的上传策略是在后端服务指定的,JS-SDK 的 setOption API 只是设置 Plupload 的初始化参数,和上传策略无关。
4. 如果您想了解更多七牛的图片处理,建议您仔细阅读 [七牛官方文档-图片处理](https://developer.qiniu.com/dora/api/image-processing-api)
5. 如果是 https 网站,上传地址为 https://up.qbox.me 否则使用 http://upload.qiniu.com
6. JS-SDK 示例生成 uptotken 时,指定的 `Bucket Name` 为公开空间,所以可以公开访问上传成功后的资源。若您生成 uptoken 时,指定的 `Bucket Name` 为私有空间,那您还需要在服务端进行额外的处理才能访问您上传的资源。具体参见[下载凭证](https://developer.qiniu.com/kodo/manual/download-token)。JS-SDK 数据处理部分功能不适用于私有空间。
<a id="faq"></a>
### 常见问题
七牛提供基于 plupload 插件封装上传的 demo `http://jssdk.demo.qiniu.io/`,如果不需要 plupload 插件可以参考 `https://github.com/iwillwen/qiniu.js/tree/develop`,这里主要针对基于 plupload 插件的方式讲解遇到的一些问题,通过参考 plupload 文档资料,可以对七牛的 demo 进行修改,以满足自己的业务需求,plupload 插件的使用文档可以参考 `http://www.cnblogs.com/2050/p/3913184.html`
**1. 关于上传文件命名问题,可以参考:**
在 main.js 里面,unique_names 是 plupload 插件下面的一个参数,当值为 true 时会为每个上传的文件生成一个唯一的文件名,这个是 plupload 插件自动生成的,如果设置成 false,七牛这边是会以上传的原始名进行命名的。
1. 上传的 scope 为 bucket 的形式,unique_names 参数设置为false,上传后文件的 key 是本地的文件名 abc.txt
2. 上传的 scope 为 bucket 的形式,unique_names 参数设置为 true,plupload 插件会忽略本地文件名,而且这个命名也是没有规律的,上传后文件的 key 是 plupload 插件生成的,比如 Yc7DZRS1m73o.txt。
3. 上传的 scope 为 bucket:key 的形式,上传文件本地的名字需要和 scope 中的 key 是一致的,不然会报错 key doesn‘t match with scope, 注意,这种形式是不能设置 unique_names 为 true 的,因为即使上传文件本地名字为 abc.txt,但是 plupload 会给这个文件赋值另外一个文件名。
4. 上传的 scope 为 bucket,但是 token 中有设定 saveKey,这种形式 save_key 是应该设置为 true,并且上传的本地文件名也是需要和这个 savekey 文件名一致的。
5. 通过 JS 前端设置上传的 key,在 main.js 文件里面设置如下:
```
'Key': function(up, file) {
var key = "";
// do something with key
return key
}
```
这个默认是注释的,若想在前端对每个文件的 key 进行个性化处理,可以配置该函数
该配置必须要在 unique_names: false , save_key: false 时才生效
取消注释后,其优先级要高于:qiniu.js 文件中 getFileKey。
**2. 设置自定义预览样式**
```
// 该设置在ui.js 文件里,默认为
var imageView =‘?imageView2/1/w/100/h/100’
// 可修改成
var imageView = ‘样式符+样式名’
```
**3. 关于设置取消上传可以参考:**
http://stackoverflow.com/questions/11014384/cancel-file-upload-listener
(文件 plupload.dev.js 1950行 removeFile : function(file) 方法)
**4. 限制上传文件的类型:**
这里又分为两种方法:
1. 通过在 token 中设定 mimeLimit 字段限定上传文件的类型,示例
“image/*“ 表示只允许上传图片类型;
“image/jpeg;image/png” 表示只允许上传 jpg 和 png 类型的图片;
“!application/json;text/plain” 表示禁止上传 json 文本和纯文本。(注意最前面的感叹号)
2. 通过 plupload 中设定 filter 参数直接在 JS 前端限定,如下
```
// 可以使用该参数来限制上传文件的类型,大小等,该参数以对象的形式传入,它包括三个属性:
filters : {
max_file_size : '100mb',
prevent_duplicates: true,
// Specify what files to browse for
mime_types: [
{title : "flv files", extensions : "flv"} // 限定flv后缀上传格式上传
{title : "Video files", extensions : "flv,mpg,mpeg,avi,wmv,mov,asf,rm,rmvb,mkv,m4v,mp4"}, // 限定flv,mpg,mpeg,avi,wmv,mov,asf,rm,rmvb,mkv,m4v,mp4后缀格式上传
{title : "Image files", extensions : "jpg,gif,png"}, // 限定jpg,gif,png后缀上传
{title : "Zip files", extensions : "zip"} // 限定zip后缀上传
]
},
```
**5. 设置每次只能选择一个文件**
通过 plupload 插件中的 multi_selection 参数控制,如下
```
// 设置一次只能选择一个文件
multi_selection: false,
```
**6. 设置取消上传,暂停上传**
在 index.html 中加入者两个控制按钮:
```
<a class="btn btn-default btn-lg " id="up_load" href="#" >
<span>确认上传</span>
</a>
<a class="btn btn-default btn-lg " id="stop_load" href="#" >
<span>暂停上传</span>
</a>
```
然后在 main.js 文件里面绑定这两个按钮,添加代码如下:
```
$('#up_load').on('click', function(){
uploader.start();
});
$('#stop_load').on('click', function(){
uploader.stop();
});
```
**7. 取消分片上传**
将 main.js 里面 `chunk_size: '4mb'` 设置 `chunk_size: '0mb'`,注意分片上传默认也只能是 4M,如果设置一个别的分片的大小会出现上传失败。
**8. 取消自动上传**
将 main.js 文件 `auto_start` 参数改成 `auto_start: false`
**9. 关于请求 token 出现跨域**
因为都是建议用户从后端 SDK 获取 token,然后在 main.js 设置参数 uptoken_url: '获取uptoken的url', 这里就有可能出现跨域的现象,此时在服务端添加 response.setHeader("Access-Control-Allow-Origin","*"); 相应头字段即可。
推荐一个关于 [CORS](http://enable-cors.org/) 的网站
<a id="contribute-code"></a>
**10.Android自带的Webview对JS SDK不支持 **
在Android自带的Webview里面引用JS SDK的demo(http://jssdk.demo.qiniu.io/) :
```
public class MainActivity extends Activity {
private WebView webview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webview = (WebView) findViewById(R.id.wv);
webview.getSettings().setJavaScriptEnabled(true);
webview.setWebViewClient(new WebViewClient(){
public boolean shouldOverrideUrlLoading(WebView view, String url){
view.loadUrl(url);
return true;
}
});
webview.loadUrl("http://demos.qiniu.com/demo/simpleuploader/");
}
}
```
但是点击选择文件按钮没有反应,这个是Webview对JS不是很支持造成的,解决方法可以引入这个Webview,jar包地址如下:
https://github.com/delight-im/Android-AdvancedWebView/blob/master/JARs/Android-AdvancedWebView.jar
使用的方法文档上都有写,比较简单:
```
private AdvancedWebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (AdvancedWebView) findViewById(R.id.webview);
mWebView.setListener(this, this);
mWebView.loadUrl("http://jssdk.demo.qiniu.io/");
}
```
**11.关于多个按钮选择文件的Demo **
很多用户都在问JSSDK多文件选择的Demo,其实比较简单,只需要在main.js文件里面多new几个Uploader对象就可以了,然后在主页面上里面写好对应的上传的按钮就可以了
这里直接给出main.js和indxe.html里面需要改动的地方:
main.js里面多new几个uploader对象
```
$(function() {
var uploader = Qiniu.uploader({
runtimes: 'html5,flash,html4',
browse_button: 'pickfiles',
container: 'container',
drop_element: 'container',
max_file_size: '100mb',
flash_swf_url: 'js/plupload/Moxie.swf',
dragdrop: true,
chunk_size: '4mb',
uptoken:'um6IEH7mtwnwkGpjImD08JdxlvViuELhI4mFfoeL:79ApUIePTtKIdVGDHJ9D9BfBnhE=:eyJzY29wZSI6ImphdmFkZW1vIiwiZGVhZGxpbmUiOjE0NTk4ODMyMzV9Cg==',
// uptoken_url: $('#uptoken_url').val(), //当然建议这种通过url的方式获取token
domain: $('#domain').val(),
auto_start: false,
init: {
'FilesAdded': function(up, files) {
$('table').show();
$('#success').hide();
plupload.each(files, function(file) {
var progress = new FileProgress(file, 'fsUploadProgress');
progress.setStatus("等待...");
});
},
'BeforeUpload': function(up, file) {
var progress = new FileProgress(file, 'fsUploadProgress');
var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
if (up.runtime === 'html5' && chunk_size) {
progress.setChunkProgess(chunk_size);
}
},
'UploadProgress': function(up, file) {
var progress = new FileProgress(file, 'fsUploadProgress');
var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
progress.setProgress(file.percent + "%", file.speed, chunk_size);
},
'UploadComplete': function() {
$('#success').show();
},
'FileUploaded': function(up, file, info) {
var progress = new FileProgress(file, 'fsUploadProgress');
progress.setComplete(up, info);
},
'Error': function(up, err, errTip) {
$('table').show();
var progress = new FileProgress(err.file, 'fsUploadProgress');
progress.setError();
progress.setStatus(errTip);
}
}
});
uploader.bind('FileUploaded', function() {
console.log('hello man,a file is uploaded');
});
$('#up_load').on('click', function(){
uploader.start();
});
$('#stop_load').on('click', function(){
uploader.stop();
});
var Q2 = new QiniuJsSDK();
var uploader2 = Q2.uploader({
runtimes: 'html5,flash,html4',
browse_button: 'pickfiles2',
container: 'container2',
drop_element: 'container2',
max_file_size: '100mb',
flash_swf_url: 'js/plupload/Moxie.swf',
dragdrop: true,
chunk_size: '4mb',
uptoken:'um6IEH7mtwnwkGpjImD08JdxlvViuELhI4mFfoeL:79ApUIePTtKIdVGDHJ9D9BfBnhE=:eyJzY29wZSI6ImphdmFkZW1vIiwiZGVhZGxpbmUiOjE0NTk4ODMyMzV9Cg==',
// uptoken_url: $('#uptoken_url').val(), //当然建议这种通过url的方式获取token
domain: $('#domain').val(),
auto_start: false,
init: {
'FilesAdded': function(up, files) {
$('table').show();
$('#success').hide();
plupload.each(files, function(file) {
var progress = new FileProgress(file, 'fsUploadProgress');
progress.setStatus("等待...");
});
},
'BeforeUpload': function(up, file) {
var progress = new FileProgress(file, 'fsUploadProgress');
var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
if (up.runtime === 'html5' && chunk_size) {
progress.setChunkProgess(chunk_size);
}
},
'UploadProgress': function(up, file) {
var progress = new FileProgress(file, 'fsUploadProgress');
var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
progress.setProgress(file.percent + "%", file.speed, chunk_size);
},
'UploadComplete': function() {
$('#success').show();
},
'FileUploaded': function(up, file, info) {
var progress = new FileProgress(file, 'fsUploadProgress');
progress.setComplete(up, info);
},
'Error': function(up, err, errTip) {
$('table').show();
var progress = new FileProgress(err.file, 'fsUploadProgress');
progress.setError();
progress.setStatus(errTip);
}
}
});
uploader2.bind('FileUploaded', function() {
console.log('hello man 2,a file is uploaded');
});
$('#up_load2').on('click', function(){
uploader2.start();
});
$('#stop_load2').on('click', function(){
uploader2.stop();
});
```
相应的index.html文件加入相关按钮:
```
<div id="container">
<a class="btn btn-default btn-lg " id="pickfiles" style="width:160px" href="#" >
<i class="glyphicon glyphicon-plus"></i>
<span>选择文件</span>
</a>
<a class="btn btn-default btn-lg " id="up_load" style="width:160px" href="#" >
<span>确认上传</span>
</a>
<a class="btn btn-default btn-lg " id="stop_load" style="width:160px" href="#" >
<span>暂停上传</span>
</a>
</div>
<div id="container2">
<a class="btn btn-default btn-lg " id="pickfiles2" style="width:160px" href="#" >
<i class="glyphicon glyphicon-plus"></i>
<span>选择文件</span>
</a>
<a class="btn btn-default btn-lg " id="up_load2" style="width:160px" href="#" >
<span>确认上传</span>
</a>
<a class="btn btn-default btn-lg " id="stop_load2" style="width:160px" href="#" >
<span>暂停上传</span>
</a>
</div>
```
### 贡献代码
1. 登录 https://github.com
2. Fork git@github.com:qiniu/js-sdk.git
3. 创建您的特性分支 (git checkout -b new-feature)
4. 提交您的改动 (git commit -am 'Added some features or fixed a bug')
5. 将您的改动记录提交到远程 git 仓库 (git push origin new-feature)
6. 然后到 github 网站的该 git 远程仓库的 new-feature 分支下发起 Pull Request
<a id="license"></a>
### 许可证
> Copyright (c) 2017 qiniu.com
### 基于 MIT 协议发布
# 个性化定制修改
## 源码地址
* https://github.com/qiniu/js-sdk/tree/1.x * https://github.com/qiniu/js-sdk/tree/1.x
# 打包 ## 打包
* 打包命令:npm install & npm build * 打包命令:npm install & npm build
* 使用dist/qiniu.min.js、src/plupload/moxie.js和src/plupload/plupload.dev.js * 使用dist/qiniu.min.js、src/plupload/moxie.js和src/plupload/plupload.dev.js
\ No newline at end of file
{
"name": "qiniu",
"description": "Javascript SDK for Qiniu Resource (Cloud) Storage API",
"version": "v1.0.24",
"main": "dist/qiniu.min.js",
"moduleType": [
"globals"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"keywords": [
"cloud",
"storage",
"s3",
"qiniu",
"upload"
],
"authors": "sdk@qiniu.com",
"contributors": [{
"name": "luoyeshu0507",
"email": "lizhiwei@qiniu.com"
}, {
"name": "codedogfish",
"email": "jackyu@qiniu.com"
}, {
"name": "jinxinxin",
"email": "jinxinxin@qiniu.com"
}],
"homepage": "https://github.com/qiniu/js-sdk",
"repository": {
"type": "git",
"url": "git://github.com/qiniu/js-sdk.git"
},
"devDependencies": {
"bootstrap": "~3.3.6",
"highlight": "~8.9.1",
"jquery": "~1.9.1",
"respond": "~1.4.2",
"vue": "^2.2.1"
},
"private": false
}
module.exports = {
'AccessKey': '<Your Access Key>', // https://portal.qiniu.com/user/key
'SecretKey': '<Your Secret Key>',
'Bucket': '<Your Bucket Name>',
'Port': 19110,
'UptokenUrl': 'uptoken',
'Domain': '<Your Bucket Name>' // bucket domain eg:http://qiniu-plupload.qiniudn.com/
};
// jQuery zepto vue angular 等库皆有 progress 的实现 以jQuery为例:
$(function(){
var $key = $('#key'); // file name eg: the file is image.jpg,but $key='a.jpg', you will upload the file named 'a.jpg'
var $userfile = $('#userfile'); // the file you selected
// upload info
var $selectedFile = $('.selected-file');
var $progress = $(".progress");
var $uploadedResult = $('.uploaded-result');
$("#userfile").change(function() { // you can ues 'onchange' here to uplpad automatically after select a file
$uploadedResult.html('');
var selectedFile = $userfile.val();
if (selectedFile) {
// randomly generate the final file name
var ramdomName = Math.random().toString(36).substr(2) + $userfile.val().match(/\.?[^.\/]+$/);
$key.val(ramdomName);
$selectedFile.html('文件:' + selectedFile);
} else {
return false;
}
var f = new FormData(document.getElementById("testform"));
$.ajax({
url: 'http://upload.qiniu.com/', // Different bucket zone has different upload url, you can get right url by the browser error massage when uploading a file with wrong upload url.
type: 'POST',
data: f,
processData: false,
contentType: false,
xhr: function(){
myXhr = $.ajaxSettings.xhr();
if(myXhr.upload){
myXhr.upload.addEventListener('progress',function(e) {
// console.log(e);
if (e.lengthComputable) {
var percent = e.loaded/e.total*100;
$progress.html('上传:' + e.loaded + "/" + e.total+" bytes. " + percent.toFixed(2) + "%");
}
}, false);
}
return myXhr;
},
success: function(res) {
console.log("成功:" + JSON.stringify(res));
var str = '<span>已上传:' + res.key + '</span>';
if (res.key && res.key.match(/\.(jpg|jpeg|png|gif)$/)) {
str += '<img src="' + domain + res.key + '"/>';
}
$uploadedResult.html(str);
},
error: function(res) {
console.log("失败:" + JSON.stringify(res));
$uploadedResult.html('上传失败:' + res.responseText);
}
});
return false;
});
});
\ No newline at end of file
var hljs=new function(){function l(o){return o.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o<p.length;o++){if(e[p[o]]||p[o]=="no-highlight"){return p[o]}}}function c(q){var o=[];(function p(r,s){for(var t=r.firstChild;t;t=t.nextSibling){if(t.nodeType==3){s+=t.nodeValue.length}else{if(t.nodeName=="BR"){s+=1}else{if(t.nodeType==1){o.push({event:"start",offset:s,node:t});s=p(t,s);o.push({event:"stop",offset:s,node:t})}}}}return s})(q,0);return o}function j(x,v,w){var p=0;var y="";var r=[];function t(){if(x.length&&v.length){if(x[0].offset!=v[0].offset){return(x[0].offset<v[0].offset)?x:v}else{return v[0].event=="start"?x:v}}else{return x.length?x:v}}function s(A){function z(B){return" "+B.nodeName+'="'+l(B.value)+'"'}return"<"+A.nodeName+Array.prototype.map.call(A.attributes,z).join("")+">"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("</"+o.nodeName.toLowerCase()+">")}while(o!=u.node);r.splice(q,1);while(q<r.length){y+=s(r[q]);q++}}}}return y+l(w.substr(p))}function f(q){function o(s,r){return RegExp(s,"m"+(q.cI?"i":"")+(r?"g":""))}function p(y,w){if(y.compiled){return}y.compiled=true;var s=[];if(y.k){var r={};function z(A,t){t.split(" ").forEach(function(B){var C=B.split("|");r[C[0]]=[A,C[1]?Number(C[1]):1];s.push(C[0])})}y.lR=o(y.l||hljs.IR,true);if(typeof y.k=="string"){z("keyword",y.k)}else{for(var x in y.k){if(!y.k.hasOwnProperty(x)){continue}z(x,y.k[x])}}y.k=r}if(w){if(y.bWK){y.b="\\b("+s.join("|")+")\\s"}y.bR=o(y.b?y.b:"\\B|\\b");if(!y.e&&!y.eW){y.e="\\B|\\b"}if(y.e){y.eR=o(y.e)}y.tE=y.e||"";if(y.eW&&w.tE){y.tE+=(y.e?"|":"")+w.tE}}if(y.i){y.iR=o(y.i)}if(y.r===undefined){y.r=1}if(!y.c){y.c=[]}for(var v=0;v<y.c.length;v++){if(y.c[v]=="self"){y.c[v]=y}p(y.c[v],y)}if(y.starts){p(y.starts,w)}var u=[];for(var v=0;v<y.c.length;v++){u.push(y.c[v].b)}if(y.tE){u.push(y.tE)}if(y.i){u.push(y.i)}y.t=u.length?o(u.join("|"),true):{exec:function(t){return null}}}p(q)}function d(D,E){function o(r,M){for(var L=0;L<M.c.length;L++){var K=M.c[L].bR.exec(r);if(K&&K.index==0){return M.c[L]}}}function s(K,r){if(K.e&&K.eR.test(r)){return K}if(K.eW){return s(K.parent,r)}}function t(r,K){return K.i&&K.iR.test(r)}function y(L,r){var K=F.cI?r[0].toLowerCase():r[0];return L.k.hasOwnProperty(K)&&L.k[K]}function G(){var K=l(w);if(!A.k){return K}var r="";var N=0;A.lR.lastIndex=0;var L=A.lR.exec(K);while(L){r+=K.substr(N,L.index-N);var M=y(A,L);if(M){v+=M[1];r+='<span class="'+M[0]+'">'+L[0]+"</span>"}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return'<span class="'+r.language+'">'+r.value+"</span>"}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'<span class="'+L.cN+'">':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+="</span>"}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"<br>")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.cs=function(a){return{k:"abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while ascending descending from get group into join let orderby partial select set value var where yield",c:[{cN:"comment",b:"///",e:"$",rB:true,c:[{cN:"xmlDocTag",b:"///|<!--|-->"},{cN:"xmlDocTag",b:"</?",e:">"}]},a.CLCM,a.CBLCLM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line region endregion pragma checksum"},{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},a.ASM,a.QSM,a.CNM]}}(hljs);hljs.LANGUAGES.ruby=function(e){var a="[a-zA-Z_][a-zA-Z0-9_]*(\\!|\\?)?";var j="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?";var g={keyword:"and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include"};var c={cN:"yardoctag",b:"@[A-Za-z]+"};var k=[{cN:"comment",b:"#",e:"$",c:[c]},{cN:"comment",b:"^\\=begin",e:"^\\=end",c:[c],r:10},{cN:"comment",b:"^__END__",e:"\\n$"}];var d={cN:"subst",b:"#\\{",e:"}",l:a,k:g};var i=[e.BE,d];var b=[{cN:"string",b:"'",e:"'",c:i,r:0},{cN:"string",b:'"',e:'"',c:i,r:0},{cN:"string",b:"%[qw]?\\(",e:"\\)",c:i},{cN:"string",b:"%[qw]?\\[",e:"\\]",c:i},{cN:"string",b:"%[qw]?{",e:"}",c:i},{cN:"string",b:"%[qw]?<",e:">",c:i,r:10},{cN:"string",b:"%[qw]?/",e:"/",c:i,r:10},{cN:"string",b:"%[qw]?%",e:"%",c:i,r:10},{cN:"string",b:"%[qw]?-",e:"-",c:i,r:10},{cN:"string",b:"%[qw]?\\|",e:"\\|",c:i,r:10}];var h={cN:"function",bWK:true,e:" |$|;",k:"def",c:[{cN:"title",b:j,l:a,k:g},{cN:"params",b:"\\(",e:"\\)",l:a,k:g}].concat(k)};var f=k.concat(b.concat([{cN:"class",bWK:true,e:"$|;",k:"class module",c:[{cN:"title",b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?",r:0},{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]}].concat(k)},h,{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:":",c:b.concat([{b:j}]),r:0},{cN:"symbol",b:a+":",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"number",b:"\\?\\w"},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+e.RSR+")\\s*",c:k.concat([{cN:"regexp",b:"/",e:"/[a-z]*",i:"\\n",c:[e.BE,d]}]),r:0}]));d.c=f;h.c[1].c=f;return{l:a,k:g,c:f}}(hljs);hljs.LANGUAGES.javascript=function(a){return{k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const",literal:"true false null undefined NaN Infinity"},c:[a.ASM,a.QSM,a.CLCM,a.CBLCLM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",i:"\\n",c:[{b:"\\\\/"}]},{b:"<",e:">;",sL:"xml"}],r:0},{cN:"function",bWK:true,e:"{",k:"function",c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[a.CLCM,a.CBLCLM],i:"[\"'\\(]"}],i:"\\[|%"}]}}(hljs);hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"<!--",e:"-->",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{title:"style"},c:[b],starts:{e:"</style>",rE:true,sL:"css"}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"title",b:"[^ />]+"},b]}]}}(hljs);hljs.LANGUAGES.http=function(a){return{i:"\\S",c:[{cN:"status",b:"^HTTP/[0-9\\.]+",e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{cN:"request",b:"^[A-Z]+ (.*?) HTTP/[0-9\\.]+$",rB:true,e:"$",c:[{cN:"string",b:" ",e:" ",eB:true,eE:true}]},{cN:"attribute",b:"^\\w",e:": ",eE:true,i:"\\n|\\s|=",starts:{cN:"string",e:"$"}},{b:"\\n\\n",starts:{sL:"",eW:true}}]}}(hljs);hljs.LANGUAGES.java=function(a){return{k:"false synchronized int abstract float private char boolean static null if const for true while long throw strictfp finally protected import native final return void enum else break transient new catch instanceof byte super volatile case assert short package default double public try this switch continue throws",c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",c:[{cN:"javadoctag",b:"@[A-Za-z]+"}],r:10},a.CLCM,a.CBLCLM,a.ASM,a.QSM,{cN:"class",bWK:true,e:"{",k:"class interface",i:":",c:[{bWK:true,k:"extends implements",r:10},{cN:"title",b:a.UIR}]},a.CNM,{cN:"annotation",b:"@[A-Za-z]+"}]}}(hljs);hljs.LANGUAGES.php=function(a){var e={cN:"variable",b:"\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*"};var b=[a.inherit(a.ASM,{i:null}),a.inherit(a.QSM,{i:null}),{cN:"string",b:'b"',e:'"',c:[a.BE]},{cN:"string",b:"b'",e:"'",c:[a.BE]}];var c=[a.BNM,a.CNM];var d={cN:"title",b:a.UIR};return{cI:true,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return implements parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception php_user_filter default die require __FUNCTION__ enddeclare final try this switch continue endfor endif declare unset true false namespace trait goto instanceof insteadof __DIR__ __NAMESPACE__ __halt_compiler",c:[a.CLCM,a.HCM,{cN:"comment",b:"/\\*",e:"\\*/",c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+"}]},{cN:"comment",eB:true,b:"__halt_compiler.+?;",eW:true},{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[a.BE]},{cN:"preprocessor",b:"<\\?php",r:10},{cN:"preprocessor",b:"\\?>"},e,{cN:"function",bWK:true,e:"{",k:"function",i:"\\$|\\[|%",c:[d,{cN:"params",b:"\\(",e:"\\)",c:["self",e,a.CBLCLM].concat(b).concat(c)}]},{cN:"class",bWK:true,e:"{",k:"class",i:"[:\\(\\$]",c:[{bWK:true,eW:true,k:"extends",c:[d]},d]},{b:"=>"}].concat(b).concat(c)}}(hljs);hljs.LANGUAGES.python=function(a){var f={cN:"prompt",b:"^(>>>|\\.\\.\\.) "};var c=[{cN:"string",b:"(u|b)?r?'''",e:"'''",c:[f],r:10},{cN:"string",b:'(u|b)?r?"""',e:'"""',c:[f],r:10},{cN:"string",b:"(u|r|ur)'",e:"'",c:[a.BE],r:10},{cN:"string",b:'(u|r|ur)"',e:'"',c:[a.BE],r:10},{cN:"string",b:"(b|br)'",e:"'",c:[a.BE]},{cN:"string",b:'(b|br)"',e:'"',c:[a.BE]}].concat([a.ASM,a.QSM]);var e={cN:"title",b:a.UIR};var d={cN:"params",b:"\\(",e:"\\)",c:["self",a.CNM,f].concat(c)};var b={bWK:true,e:":",i:"[${=;\\n]",c:[e,d],r:10};return{k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda nonlocal|10",built_in:"None True False Ellipsis NotImplemented"},i:"(</|->|\\?)",c:c.concat([f,a.HCM,a.inherit(b,{cN:"function",k:"def"}),a.inherit(b,{cN:"class",k:"class"}),a.CNM,{cN:"decorator",b:"@",e:"$"},{b:"\\b(print|exec)\\("}])}}(hljs);hljs.LANGUAGES.perl=function(e){var a="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when";var d={cN:"subst",b:"[$@]\\{",e:"\\}",k:a,r:10};var b={cN:"variable",b:"\\$\\d"};var i={cN:"variable",b:"[\\$\\%\\@\\*](\\^\\w\\b|#\\w+(\\:\\:\\w+)*|[^\\s\\w{]|{\\w+}|\\w+(\\:\\:\\w*)*)"};var f=[e.BE,d,b,i];var h={b:"->",c:[{b:e.IR},{b:"{",e:"}"}]};var g={cN:"comment",b:"^(__END__|__DATA__)",e:"\\n$",r:5};var c=[b,i,e.HCM,g,{cN:"comment",b:"^\\=\\w",e:"\\=cut",eW:true},h,{cN:"string",b:"q[qwxr]?\\s*\\(",e:"\\)",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\[",e:"\\]",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\{",e:"\\}",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\|",e:"\\|",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\<",e:"\\>",c:f,r:5},{cN:"string",b:"qw\\s+q",e:"q",c:f,r:5},{cN:"string",b:"'",e:"'",c:[e.BE],r:0},{cN:"string",b:'"',e:'"',c:f,r:0},{cN:"string",b:"`",e:"`",c:[e.BE]},{cN:"string",b:"{\\w+}",r:0},{cN:"string",b:"-?\\w+\\s*\\=\\>",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"("+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,g,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"sub",bWK:true,e:"(\\s*\\(.*?\\))?[;{]",k:"sub",r:5},{cN:"operator",b:"-\\w\\b",r:0}];d.c=c;h.c[1].c=c;return{k:a,c:c}}(hljs);hljs.LANGUAGES.json=function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}}(hljs);hljs.LANGUAGES.cpp=function(a){var b={keyword:"false int float while private char catch export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace unsigned long throw volatile static protected bool template mutable if public friend do return goto auto void enum else break new extern using true class asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue wchar_t inline delete alignof char16_t char32_t constexpr decltype noexcept nullptr static_assert thread_local restrict _Bool complex",built_in:"std string cin cout cerr clog stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr"};return{k:b,i:"</",c:[a.CLCM,a.CBLCLM,a.QSM,{cN:"string",b:"'\\\\?.",e:"'",i:"."},{cN:"number",b:"\\b(\\d+(\\.\\d*)?|\\.\\d+)(u|U|l|L|ul|UL|f|F)"},a.CNM,{cN:"preprocessor",b:"#",e:"$"},{cN:"stl_container",b:"\\b(deque|list|queue|stack|vector|map|set|bitset|multiset|multimap|unordered_map|unordered_set|unordered_multiset|unordered_multimap|array)\\s*<",e:">",k:b,r:10,c:["self"]}]}}(hljs);hljs.LANGUAGES.go=function(a){var b={keyword:"break default func interface select case map struct chan else goto package switch const fallthrough if range type continue for import return var go defer",constant:"true false iota nil",typename:"bool byte complex64 complex128 float32 float64 int8 int16 int32 int64 string uint8 uint16 uint32 uint64 int uint uintptr rune",built_in:"append cap close complex copy imag len make new panic print println real recover delete"};return{k:b,i:"</",c:[a.CLCM,a.CBLCLM,a.QSM,{cN:"string",b:"'",e:"[^\\\\]'",r:0},{cN:"string",b:"`",e:"`"},{cN:"number",b:"[^a-zA-Z_0-9](\\-|\\+)?\\d+(\\.\\d+|\\/\\d+)?((d|e|f|l|s)(\\+|\\-)?\\d+)?",r:0},a.CNM]}}(hljs);
\ No newline at end of file
/**
* @license httpPerformance v1.0.0
* License: MIT
* author: luoyeshu0507
*
* @description
*
* This object provides a utility for detailing the http request performance
*
*/
;(function(window, undefined){
'use strict';
var p = window.performance;
var httpPerformance = {
clear: function() {
p.clearMarks && p.clearMarks();
p.clearMeasures && p.clearMeasures();
p.clearResourceTimings && p.clearResourceTimings();
},
getAll: function() {
var performanceArr = [];
p.getEntries().forEach(function(item) {
performanceArr.push(httpPerformance.formatPerformance(item));
});
return performanceArr;
},
getByName: function(name) {
var performanceArr = [];
p.getEntriesByName(name).forEach(function(item) {
performanceArr.push(httpPerformance.formatPerformance(item));
});
return performanceArr;
},
formatPerformance: function(prt) { // PerformanceResourceTiming
return {
redirect: prt.redirectEnd - prt.redirectStart,
domainLookup: prt.domainLookupEnd - prt.domainLookupStart,
connect: prt.connectEnd - prt.connectStart,
request: prt.responseStart - prt.requestStart,
response: prt.responseEnd - prt.responseStart,
entryType: prt.entryType,
initiatorType: prt.initiatorType,
name: prt.name,
duration: prt.duration,
};
}
};
// support AMD and CMD
if (typeof module !== 'undefined' && module.exports) {
module.exports = httpPerformance;
} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
define('httpPerformance', [], function () {
return httpPerformance;
});
} else {
window.httpPerformance = httpPerformance;
}
})(window);
/*global Qiniu */
/*global plupload */
/*global FileProgress */
/*global hljs */
$(function() {
var uploader = Qiniu.uploader({
disable_statistics_report: false,
runtimes: 'html5,flash,html4',
browse_button: 'pickfiles',
container: 'container',
drop_element: 'container',
max_file_size: '1000mb',
flash_swf_url: 'bower_components/plupload/js/Moxie.swf',
dragdrop: true,
chunk_size: '4mb',
multi_selection: !(moxie.core.utils.Env.OS.toLowerCase() === "ios"),
uptoken_url: $('#uptoken_url').val(),
// uptoken_func: function(){
// var ajax = new XMLHttpRequest();
// ajax.open('GET', $('#uptoken_url').val(), false);
// ajax.setRequestHeader("If-Modified-Since", "0");
// ajax.send();
// if (ajax.status === 200) {
// var res = JSON.parse(ajax.responseText);
// console.log('custom uptoken_func:' + res.uptoken);
// return res.uptoken;
// } else {
// console.log('custom uptoken_func err');
// return '';
// }
// },
domain: $('#domain').val(),
get_new_uptoken: false,
//downtoken_url: '/downtoken',
// unique_names: true,
// save_key: true,
// x_vars: {
// 'id': '1234',
// 'time': function(up, file) {
// var time = (new Date()).getTime();
// // do something with 'time'
// return time;
// },
// },
auto_start: true,
log_level: 5,
init: {
'BeforeChunkUpload': function(up, file) {
console.log("before chunk upload:", file.name);
},
'FilesAdded': function(up, files) {
$('table').show();
$('#success').hide();
plupload.each(files, function(file) {
var progress = new FileProgress(file,
'fsUploadProgress');
progress.setStatus("等待...");
progress.bindUploadCancel(up);
});
},
'BeforeUpload': function(up, file) {
console.log("this is a beforeupload function from init");
var progress = new FileProgress(file, 'fsUploadProgress');
var chunk_size = plupload.parseSize(this.getOption(
'chunk_size'));
if (up.runtime === 'html5' && chunk_size) {
progress.setChunkProgess(chunk_size);
}
},
'UploadProgress': function(up, file) {
var progress = new FileProgress(file, 'fsUploadProgress');
var chunk_size = plupload.parseSize(this.getOption(
'chunk_size'));
progress.setProgress(file.percent + "%", file.speed,
chunk_size);
},
'UploadComplete': function() {
$('#success').show();
},
'FileUploaded': function(up, file, info) {
var progress = new FileProgress(file, 'fsUploadProgress');
console.log("response:", info.response);
progress.setComplete(up, info.response);
},
'Error': function(up, err, errTip) {
$('table').show();
var progress = new FileProgress(err.file, 'fsUploadProgress');
progress.setError();
progress.setStatus(errTip);
}
// ,
// 'Key': function(up, file) {
// var key = "";
// // do something with key
// return key
// }
}
});
//uploader.init();
uploader.bind('BeforeUpload', function() {
console.log("hello man, i am going to upload a file");
});
uploader.bind('FileUploaded', function() {
console.log('hello man,a file is uploaded');
});
$('#container').on(
'dragenter',
function(e) {
e.preventDefault();
$('#container').addClass('draging');
e.stopPropagation();
}
).on('drop', function(e) {
e.preventDefault();
$('#container').removeClass('draging');
e.stopPropagation();
}).on('dragleave', function(e) {
e.preventDefault();
$('#container').removeClass('draging');
e.stopPropagation();
}).on('dragover', function(e) {
e.preventDefault();
$('#container').addClass('draging');
e.stopPropagation();
});
$('#show_code').on('click', function() {
$('#myModal-code').modal();
$('pre code').each(function(i, e) {
hljs.highlightBlock(e);
});
});
$('body').on('click', 'table button.btn', function() {
$(this).parents('tr').next().toggle();
});
var getRotate = function(url) {
if (!url) {
return 0;
}
var arr = url.split('/');
for (var i = 0, len = arr.length; i < len; i++) {
if (arr[i] === 'rotate') {
return parseInt(arr[i + 1], 10);
}
}
return 0;
};
$('#myModal-img .modal-body-footer').find('a').on('click', function() {
var img = $('#myModal-img').find('.modal-body img');
var key = img.data('key');
var oldUrl = img.attr('src');
var originHeight = parseInt(img.data('h'), 10);
var fopArr = [];
var rotate = getRotate(oldUrl);
if (!$(this).hasClass('no-disable-click')) {
$(this).addClass('disabled').siblings().removeClass('disabled');
if ($(this).data('imagemogr') !== 'no-rotate') {
fopArr.push({
'fop': 'imageMogr2',
'auto-orient': true,
'strip': true,
'rotate': rotate,
'format': 'png'
});
}
} else {
$(this).siblings().removeClass('disabled');
var imageMogr = $(this).data('imagemogr');
if (imageMogr === 'left') {
rotate = rotate - 90 < 0 ? rotate + 270 : rotate - 90;
} else if (imageMogr === 'right') {
rotate = rotate + 90 > 360 ? rotate - 270 : rotate + 90;
}
fopArr.push({
'fop': 'imageMogr2',
'auto-orient': true,
'strip': true,
'rotate': rotate,
'format': 'png'
});
}
$('#myModal-img .modal-body-footer').find('a.disabled').each(
function() {
var watermark = $(this).data('watermark');
var imageView = $(this).data('imageview');
var imageMogr = $(this).data('imagemogr');
if (watermark) {
fopArr.push({
fop: 'watermark',
mode: 1,
image: 'http://www.b1.qiniudn.com/images/logo-2.png',
dissolve: 100,
gravity: watermark,
dx: 100,
dy: 100
});
}
if (imageView) {
var height;
switch (imageView) {
case 'large':
height = originHeight;
break;
case 'middle':
height = originHeight * 0.5;
break;
case 'small':
height = originHeight * 0.1;
break;
default:
height = originHeight;
break;
}
fopArr.push({
fop: 'imageView2',
mode: 3,
h: parseInt(height, 10),
q: 100,
format: 'png'
});
}
if (imageMogr === 'no-rotate') {
fopArr.push({
'fop': 'imageMogr2',
'auto-orient': true,
'strip': true,
'rotate': 0,
'format': 'png'
});
}
});
var newUrl = Qiniu.pipeline(fopArr, key);
var newImg = new Image();
img.attr('src', 'images/loading.gif');
newImg.onload = function() {
img.attr('src', newUrl);
img.parent('a').attr('href', newUrl);
};
newImg.src = newUrl;
return false;
});
});
/*global Qiniu */
/*global plupload */
/*global FileProgress */
/*global hljs */
$(function() {
var uploader = Qiniu.uploader({
runtimes: 'html5,flash,html4',
browse_button: 'pickfiles',
container: 'container',
drop_element: 'container',
max_file_size: '100mb',
flash_swf_url: 'js/plupload/Moxie.swf',
dragdrop: true,
chunk_size: '4mb',
uptoken_url: $('#uptoken_url').val(),
domain: $('#domain').val(),
auto_start: true,
init: {
'FilesAdded': function(up, files) {
$('table').show();
$('#success').hide();
plupload.each(files, function(file) {
var progress = new FileProgress(file, 'fsUploadProgress');
progress.setStatus("等待...");
});
},
'BeforeUpload': function(up, file) {
var progress = new FileProgress(file, 'fsUploadProgress');
var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
if (up.runtime === 'html5' && chunk_size) {
progress.setChunkProgess(chunk_size);
}
},
'UploadProgress': function(up, file) {
var progress = new FileProgress(file, 'fsUploadProgress');
var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
progress.setProgress(file.percent + "%", file.speed, chunk_size);
},
'UploadComplete': function() {
$('#success').show();
},
'FileUploaded': function(up, file, info) {
var progress = new FileProgress(file, 'fsUploadProgress');
progress.setComplete(up, info);
},
'Error': function(up, err, errTip) {
$('table').show();
var progress = new FileProgress(err.file, 'fsUploadProgress');
progress.setError();
progress.setStatus(errTip);
}
}
});
uploader.bind('FileUploaded', function() {
console.log('hello man,a file is uploaded');
});
var Q2 = new QiniuJsSDK();
var uploader2 = Q2.uploader({
runtimes: 'html5,flash,html4',
browse_button: 'pickfiles2',
container: 'container2',
drop_element: 'container2',
max_file_size: '100mb',
flash_swf_url: 'js/plupload/Moxie.swf',
dragdrop: true,
chunk_size: '4mb',
uptoken_url: $('#uptoken_url').val(),
domain: $('#domain').val(),
auto_start: true,
init: {
'FilesAdded': function(up, files) {
$('table').show();
$('#success').hide();
plupload.each(files, function(file) {
var progress = new FileProgress(file, 'fsUploadProgress');
progress.setStatus("等待...");
});
},
'BeforeUpload': function(up, file) {
var progress = new FileProgress(file, 'fsUploadProgress');
var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
if (up.runtime === 'html5' && chunk_size) {
progress.setChunkProgess(chunk_size);
}
},
'UploadProgress': function(up, file) {
var progress = new FileProgress(file, 'fsUploadProgress');
var chunk_size = plupload.parseSize(this.getOption('chunk_size'));
progress.setProgress(file.percent + "%", file.speed, chunk_size);
},
'UploadComplete': function() {
$('#success').show();
},
'FileUploaded': function(up, file, info) {
var progress = new FileProgress(file, 'fsUploadProgress');
progress.setComplete(up, info);
},
'Error': function(up, err, errTip) {
$('table').show();
var progress = new FileProgress(err.file, 'fsUploadProgress');
progress.setError();
progress.setStatus(errTip);
}
}
});
uploader2.bind('FileUploaded', function() {
console.log('hello man 2,a file is uploaded');
});
$('#container').on(
'dragenter',
function(e) {
e.preventDefault();
$('#container').addClass('draging');
e.stopPropagation();
}
).on('drop', function(e) {
e.preventDefault();
$('#container').removeClass('draging');
e.stopPropagation();
}).on('dragleave', function(e) {
e.preventDefault();
$('#container').removeClass('draging');
e.stopPropagation();
}).on('dragover', function(e) {
e.preventDefault();
$('#container').addClass('draging');
e.stopPropagation();
});
$('#show_code').on('click', function() {
$('#myModal-code').modal();
$('pre code').each(function(i, e) {
hljs.highlightBlock(e);
});
});
$('body').on('click', 'table button.btn', function() {
$(this).parents('tr').next().toggle();
});
var getRotate = function(url) {
if (!url) {
return 0;
}
var arr = url.split('/');
for (var i = 0, len = arr.length; i < len; i++) {
if (arr[i] === 'rotate') {
return parseInt(arr[i + 1], 10);
}
}
return 0;
};
$('#myModal-img .modal-body-footer').find('a').on('click', function() {
var img = $('#myModal-img').find('.modal-body img');
var key = img.data('key');
var oldUrl = img.attr('src');
var originHeight = parseInt(img.data('h'), 10);
var fopArr = [];
var rotate = getRotate(oldUrl);
if (!$(this).hasClass('no-disable-click')) {
$(this).addClass('disabled').siblings().removeClass('disabled');
if ($(this).data('imagemogr') !== 'no-rotate') {
fopArr.push({
'fop': 'imageMogr2',
'auto-orient': true,
'strip': true,
'rotate': rotate,
'format': 'png'
});
}
} else {
$(this).siblings().removeClass('disabled');
var imageMogr = $(this).data('imagemogr');
if (imageMogr === 'left') {
rotate = rotate - 90 < 0 ? rotate + 270 : rotate - 90;
} else if (imageMogr === 'right') {
rotate = rotate + 90 > 360 ? rotate - 270 : rotate + 90;
}
fopArr.push({
'fop': 'imageMogr2',
'auto-orient': true,
'strip': true,
'rotate': rotate,
'format': 'png'
});
}
$('#myModal-img .modal-body-footer').find('a.disabled').each(function() {
var watermark = $(this).data('watermark');
var imageView = $(this).data('imageview');
var imageMogr = $(this).data('imagemogr');
if (watermark) {
fopArr.push({
fop: 'watermark',
mode: 1,
image: 'http://www.b1.qiniudn.com/images/logo-2.png',
dissolve: 100,
gravity: watermark,
dx: 100,
dy: 100
});
}
if (imageView) {
var height;
switch (imageView) {
case 'large':
height = originHeight;
break;
case 'middle':
height = originHeight * 0.5;
break;
case 'small':
height = originHeight * 0.1;
break;
default:
height = originHeight;
break;
}
fopArr.push({
fop: 'imageView2',
mode: 3,
h: parseInt(height, 10),
q: 100,
format: 'png'
});
}
if (imageMogr === 'no-rotate') {
fopArr.push({
'fop': 'imageMogr2',
'auto-orient': true,
'strip': true,
'rotate': 0,
'format': 'png'
});
}
});
var newUrl = Qiniu.pipeline(fopArr, key);
var newImg = new Image();
img.attr('src', 'images/loading.gif');
newImg.onload = function() {
img.attr('src', newUrl);
img.parent('a').attr('href', newUrl);
};
newImg.src = newUrl;
return false;
});
});
Vue.component('zone-list', {
props: ['hostMap', 'currentZone', 'switchZone', 'selectedHost'],
template: '<div class="uphosts-list"><ul><li v-for="item in hostMap" :class="{on: item.zone == currentZone}" @click="switchZone(item.zone)">{{item.zoneZh}}</li></ul><ul><li v-for="item in hostMap" :class="{on: item.zone == currentZone}"><label v-for="uphost in item.uphosts"><input type="radio" v-model="selectedHost.host" :value="uphost"/>{{uphost}}</label></li></ul></div>'
});
Vue.component('upload-performance', {
props: ['per', 'warning'],
template: '<div class="up-performance"><div class="per-title">上传耗时:<span class="warning" v-if="warning">您好,请使用谷歌、IE9+等高级浏览器获取更详细的数据!</span></div>' +
'<table>' +
'<tr><th>类型</th><th>耗时 / ms</th></tr>' +
'<tr v-if="per.redirect != undefined"><td>重定向:</td><td>{{per.redirect | tofixed(2)}}</td></tr>' +
'<tr v-if="per.domainLookup != undefined"><td>DNS 查询:</td><td>{{per.domainLookup | tofixed(2)}}</td></tr>' +
'<tr v-if="per.connect != undefined"><td>建立连接:</td><td>{{per.connect | tofixed(2)}}</td></tr>' +
'<tr v-if="per.request != undefined"><td>发送数据:</td><td>{{per.request | tofixed(2)}}</td></tr>' +
'<tr v-if="per.response != undefined"><td>接收响应:</td><td>{{per.response | tofixed(2)}}</td></tr>' +
'<tr v-if="per.duration != undefined"><td>总耗时:</td><td>{{per.duration | tofixed(2)}}</td></tr></table>' +
'</div>'
});
Vue.component('up-headers', {
props: ['headers'],
template: '<div class="up-headers"><div class="per-title">响应头:</div><table><tr><th>类型</th><th>值</th></tr><tr v-for="header in headers"><td>{{header.key}}</td><td>{{header.val}}</td></tr></table></div>'
});
Vue.filter('tofixed', function (val, size) {
return val.toFixed(size);
});
var app = new Vue({
el: '#app',
data: {
currentZone: 'z0',
selectedHost: {
host: ''
},
currentToken: '',
loadMessage: '',
hostMap: [
{
zone: 'z0',
zoneZh: '华东',
token: 'anEC5u_72gw1kZPSy3Dsq1lo_DPXyvuPDaj4ePkN:IygENu1FEguSNOeie8-r-rRyf5U=:eyJzY29wZSI6InNkay16MDphLmpwZyIsImZzaXplTGltaXQiOjEwMDAwMDAsImZzaXplTWluIjo4MDAwMDAsImRlYWRsaW5lIjo0NjgxODYyNDk2fQ==',
uphosts: [
'http://up.qiniup.com',
'http://upload.qiniup.com',
'https://up.qiniup.com',
'https://upload.qiniup.com'
]
},
{
zone: 'z1',
zoneZh: '华北',
token: 'anEC5u_72gw1kZPSy3Dsq1lo_DPXyvuPDaj4ePkN:peuYR2ETrDO1KAV_z_IU-58Y038=:eyJzY29wZSI6InNkay16MTphLmpwZyIsImZzaXplTGltaXQiOjEwMDAwMDAsImZzaXplTWluIjo4MDAwMDAsImRlYWRsaW5lIjo0NjgxODYyNDk3fQ==',
uphosts: [
'http://up-z1.qiniup.com',
'http://upload-z1.qiniup.com',
'https://up-z1.qiniup.com',
'https://upload-z1.qiniup.com'
]
},
{
zone: 'z2',
zoneZh: '华南',
token: 'anEC5u_72gw1kZPSy3Dsq1lo_DPXyvuPDaj4ePkN:d8g77lJqYHhHDkv1tJA0gXMf04s=:eyJzY29wZSI6InNkay16MjphLmpwZyIsImZzaXplTGltaXQiOjEwMDAwMDAsImZzaXplTWluIjo4MDAwMDAsImRlYWRsaW5lIjo0NjgxODYyNDk3fQ==',
uphosts: [
'http://up-z2.qiniup.com',
'http://upload-z2.qiniup.com',
'https://up-z2.qiniup.com',
'https://upload-z2.qiniup.com'
]
},
{
zone: 'na0',
zoneZh: '北美',
token: 'anEC5u_72gw1kZPSy3Dsq1lo_DPXyvuPDaj4ePkN:-RsLz4FoCQ2u-lY-4uXR466vq20=:eyJzY29wZSI6InNkay1uYTA6YS5qcGciLCJmc2l6ZUxpbWl0IjoxMDAwMDAwLCJmc2l6ZU1pbiI6ODAwMDAwLCJkZWFkbGluZSI6NDY4MTg2MjQ5N30=',
uphosts: [
'http://up-na0.qiniup.com',
'http://upload-na0.qiniup.com',
'https://up-na0.qiniup.com',
'https://upload-na0.qiniup.com'
]
},
{
zone: 'as0',
zoneZh: '东南亚',
token: 'anEC5u_72gw1kZPSy3Dsq1lo_DPXyvuPDaj4ePkN:3aGthekW5L_3YO9VmSJEoqgDa2c=:eyJzY29wZSI6InNkay1hczA6YS5qcGciLCJkZWFkbGluZSI6MTYzMDU2ODIyNCwiZnNpemVMaW1pdCI6MTAwMDAwMCwidXBob3N0cyI6WyJodHRwOi8vdXAtYXMwLnFpbml1LmNvbSIsImh0dHA6Ly91cGxvYWQtYXMwLnFpbml1LmNvbSIsIi1IIHVwLWFzMC5xaW5pdS5jb20gaHR0cDovLzIzLjI0OC4xNzMuNyJdfQ==',
uphosts: [
'http://up-as0.qiniup.com',
'http://upload-as0.qiniup.com',
'https://up-as0.qiniup.com',
'https://upload-as0.qiniup.com'
]
},
],
isPerformanceSupported: true,
performance: null,
headers: null,
timetags: {},
totalBytes: 0
},
methods: {
renderHtml: function() {},
post: function(opt) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open('POST', opt.url, true);
xmlHttp.setRequestHeader('X-Qiniu-Performance', 'true');
if (opt.headers) {
for (var k in opt.headers) {
xmlHttp.setRequestHeader(k, opt.headers[k]);
}
}
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4) {
if (xmlHttp.status == 200) {
opt.success && opt.success(xmlHttp);
} else {
opt.error && opt.error(xmlHttp.responseText);
}
opt.finally && opt.finally(xmlHttp.status, xmlHttp);
}
};
xmlHttp.upload.onprogress = opt.progress;
this.timetags.beginAjax = + new Date();
xmlHttp.send(opt.data);
},
uploadTest: function() {
if(!this.selectedHost.host) return;
this.resetResult();
var self = this;
httpPerformance.clear();
this.post({
url: this.selectedHost.host,
data: this.mockDate(),
progress: function(e) {
if (e.lengthComputable) {
var percent = e.loaded/e.total*100;
self.loadMessage = '模拟数据上传:' + e.loaded + " / " + e.total+" bytes 完成:" + percent.toFixed(2) + "%";
if (percent === 100) {
self.timetags.afterUpload = + new Date();
self.totalBytes = e.total;
}
}
},
success: function(xhr) {
self.formateHeader(xhr.getAllResponseHeaders());
},
error: function(res) {
self.loadMessage = '上传失败:' + res;
},
finally: function(code, xhr) {
self.timetags.afterAjax = + new Date();
self.getPerformance();
self.formateLog(code, xhr);
}
});
},
mockDate: function(size) {
var f = new FormData(document.getElementById("testform"));
f.append('file', this.dataURLtoBlob(this.randomBase64(size)));
f.append('token', this.currentToken);
return f;
},
randomBase64: function() {
var dataurl = 'data:image/jpeg;base64,';
var len = parseInt(Math.random()*(1330000-1100000+1)+1100000, 10);
while (dataurl.length < len) {
dataurl += Math.random > 0.5 ? '/9j/4AAQSkZJRgABAQAASABIAAD/4Q' : '/9j/4AAQSkZJRgABAQAASABIAAD/4W';
}
return dataurl;
},
dataURLtoBlob: function(dataurl) {
var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while(n--){
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], {type:mime});
},
resetResult: function() {
for (var i = 0; i < this.hostMap.length; i++) {
if (this.hostMap[i].zone === this.currentZone) {
this.currentToken = this.hostMap[i].token;
break;
}
}
this.performance = null;
this.headers = null;
this.timetags = {};
},
switchZone: function(zone) {
this.currentZone = zone;
this.selectedHost.host = '';
},
getPerformance: function() {
if (this.isPerformanceSupported) {
var per = httpPerformance.getByName(this.selectedHost.host + '/');
if(per.length == 2) {
per[1].redirect = per[0].redirect;
per[1].domainLookup = per[0].domainLookup;
per[1].connect = per[0].connect;
}
this.performance = per[1] || per[0]; // 跨域有时候会先发送一个 option 请求,并不是真的上传请求。
} else {
this.performance = {
request: this.timetags.afterUpload - this.timetags.beginAjax,
response: this.timetags.afterAjax - this.timetags.afterUpload,
duration: this.timetags.afterAjax - this.timetags.beginAjax
};
}
},
formateHeader: function(headers) {
var list = [];
headers.match(/.+/mg).map(function(item) {
var index = item.indexOf(':');
var o = {
key: item.substr(0, index),
val: item.substr(index + 1)
};
list.push(o);
});
this.headers = list;
},
formateLog: function(code, xhr) {
// status_code,req_id,host,remote_ip,port,duration,up_time,bytes_sent,up_type
headers = xhr.getAllResponseHeaders();
var req_id = headers.match(/X-Reqid:\s*(\w+)/);
if (req_id && req_id.length === 2) req_id = req_id[1];
var host = this.selectedHost.host;
var port = 80;
if (host) {
if (host.indexOf('https') > -1) port = 443;
host = host.split('//')[1];
}
remote_ip = headers.match(/X-Forwarded-For:[^,]+,\s*([^,]+)/);
if (remote_ip && remote_ip.length === 2) remote_ip = remote_ip[1];
var duration = this.performance.duration.toFixed(2);
var up_time = this.timetags.beginAjax.toString().slice(0,-3);
var bytes_sent = this.totalBytes;
var log = [code, req_id, host, remote_ip, port, duration, up_time, bytes_sent, 'jsperf'];
this.sendLog(log);
},
sendLog: function(log) {
this.post({
url: 'https://uplog.qbox.me/log/2',
data: log.join(','),
headers: {
'Authorization': 'UpToken anEC5u_72gw1kZPSy3Dsq1lo_DPXyvuPDaj4ePkN:-RsLz4FoCQ2u-lY-4uXR466vq20=:eyJzY29wZSI6InNkay1uYTA6YS5qcGciLCJmc2l6ZUxpbWl0IjoxMDAwMDAwLCJmc2l6ZU1pbiI6ODAwMDAwLCJkZWFkbGluZSI6NDY4MTg2MjQ5N30='
}
});
}
},
created: function() {
this.isPerformanceSupported = !!(window.performance && window.performance.getEntries);
}
});
/*global plupload */
/*global qiniu */
function FileProgress(file, targetID) {
this.fileProgressID = file.id;
this.file = file;
this.opacity = 100;
this.height = 0;
this.fileProgressWrapper = $('#' + this.fileProgressID);
if (!this.fileProgressWrapper.length) {
// <div class="progress">
// <div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100" style="width: 20%">
// <span class="sr-only">20% Complete</span>
// </div>
// </div>
this.fileProgressWrapper = $('<tr></tr>');
var Wrappeer = this.fileProgressWrapper;
Wrappeer.attr('id', this.fileProgressID).addClass('progressContainer');
var progressText = $("<td/>");
progressText.addClass('progressName').text(file.name);
var fileSize = plupload.formatSize(file.size).toUpperCase();
var progressSize = $("<td/>");
progressSize.addClass("progressFileSize").text(fileSize);
var progressBarTd = $("<td/>");
var progressBarBox = $("<div/>");
progressBarBox.addClass('info');
var progressBarWrapper = $("<div/>");
progressBarWrapper.addClass("progress progress-striped");
var progressBar = $("<div/>");
progressBar.addClass("progress-bar progress-bar-info")
.attr('role', 'progressbar')
.attr('aria-valuemax', 100)
.attr('aria-valuenow', 0)
.attr('aria-valuein', 0)
.width('0%');
var progressBarPercent = $('<span class=sr-only />');
progressBarPercent.text(fileSize);
var progressCancel = $('<a href=javascript:; />');
progressCancel.show().addClass('progressCancel').text('×');
progressBar.append(progressBarPercent);
progressBarWrapper.append(progressBar);
progressBarBox.append(progressBarWrapper);
progressBarBox.append(progressCancel);
var progressBarStatus = $('<div class="status text-center"/>');
progressBarBox.append(progressBarStatus);
progressBarTd.append(progressBarBox);
Wrappeer.append(progressText);
Wrappeer.append(progressSize);
Wrappeer.append(progressBarTd);
$('#' + targetID).append(Wrappeer);
} else {
this.reset();
}
this.height = this.fileProgressWrapper.offset().top;
this.setTimer(null);
}
FileProgress.prototype.setTimer = function(timer) {
this.fileProgressWrapper.FP_TIMER = timer;
};
FileProgress.prototype.getTimer = function(timer) {
return this.fileProgressWrapper.FP_TIMER || null;
};
FileProgress.prototype.reset = function() {
this.fileProgressWrapper.attr('class', "progressContainer");
this.fileProgressWrapper.find('td .progress .progress-bar-info').attr(
'aria-valuenow', 0).width('0%').find('span').text('');
this.appear();
};
FileProgress.prototype.setChunkProgess = function(chunk_size) {
var chunk_amount = Math.ceil(this.file.size / chunk_size);
if (chunk_amount === 1) {
return false;
}
var viewProgess = $('<button class="btn btn-default">查看分块上传进度</button>');
var progressBarChunkTr = $(
'<tr class="chunk-status-tr"><td colspan=3></td></tr>');
var progressBarChunk = $('<div/>');
for (var i = 1; i <= chunk_amount; i++) {
var col = $('<div class="col-md-2"/>');
var progressBarWrapper = $('<div class="progress progress-striped"></div');
var progressBar = $("<div/>");
progressBar.addClass("progress-bar progress-bar-info text-left")
.attr('role', 'progressbar')
.attr('aria-valuemax', 100)
.attr('aria-valuenow', 0)
.attr('aria-valuein', 0)
.width('0%')
.attr('id', this.file.id + '_' + i)
.text('');
var progressBarStatus = $('<span/>');
progressBarStatus.addClass('chunk-status').text();
progressBarWrapper.append(progressBar);
progressBarWrapper.append(progressBarStatus);
col.append(progressBarWrapper);
progressBarChunk.append(col);
}
if (!this.fileProgressWrapper.find('td:eq(2) .btn-default').length) {
this.fileProgressWrapper.find('td>div').append(viewProgess);
}
progressBarChunkTr.hide().find('td').append(progressBarChunk);
progressBarChunkTr.insertAfter(this.fileProgressWrapper);
};
FileProgress.prototype.setProgress = function(percentage, speed, chunk_size) {
this.fileProgressWrapper.attr('class', "progressContainer green");
var file = this.file;
var uploaded = file.loaded;
var size = plupload.formatSize(uploaded).toUpperCase();
var formatSpeed = plupload.formatSize(speed).toUpperCase();
var progressbar = this.fileProgressWrapper.find('td .progress').find(
'.progress-bar-info');
if (this.fileProgressWrapper.find('.status').text() === '取消上传') {
return;
}
this.fileProgressWrapper.find('.status').text("已上传: " + size + " 上传速度: " +
formatSpeed + "/s");
percentage = parseInt(percentage, 10);
if (file.status !== plupload.DONE && percentage === 100) {
percentage = 99;
}
progressbar.attr('aria-valuenow', percentage).css('width', percentage + '%');
if (chunk_size) {
var chunk_amount = Math.ceil(file.size / chunk_size);
if (chunk_amount === 1) {
return false;
}
var current_uploading_chunk = Math.ceil(uploaded / chunk_size);
var pre_chunk, text;
for (var index = 0; index < current_uploading_chunk; index++) {
pre_chunk = $('#' + file.id + "_" + index);
pre_chunk.width('100%').removeClass().addClass('alert-success').attr(
'aria-valuenow', 100);
text = "块" + index + "上传进度100%";
pre_chunk.next().html(text);
}
var currentProgessBar = $('#' + file.id + "_" + current_uploading_chunk);
var current_chunk_percent;
if (current_uploading_chunk < chunk_amount) {
if (uploaded % chunk_size) {
current_chunk_percent = ((uploaded % chunk_size) / chunk_size * 100).toFixed(
2);
} else {
current_chunk_percent = 100;
currentProgessBar.removeClass().addClass('alert-success');
}
} else {
var last_chunk_size = file.size - chunk_size * (chunk_amount - 1);
var left_file_size = file.size - uploaded;
if (left_file_size % last_chunk_size) {
current_chunk_percent = ((uploaded % chunk_size) / last_chunk_size *
100).toFixed(2);
} else {
current_chunk_percent = 100;
currentProgessBar.removeClass().addClass('alert-success');
}
}
currentProgessBar.width(current_chunk_percent + '%');
currentProgessBar.attr('aria-valuenow', current_chunk_percent);
text = "块" + current_uploading_chunk + "上传进度" + current_chunk_percent +
'%';
currentProgessBar.next().html(text);
}
this.appear();
};
FileProgress.prototype.setComplete = function(up, info) {
var td = this.fileProgressWrapper.find('td:eq(2)'),
tdProgress = td.find('.progress');
var res = $.parseJSON(info);
var url;
if (res.url) {
url = res.url;
str = "<div><strong>Link:</strong><a href=" + res.url +
" target='_blank' > " + res.url + "</a></div>" +
"<div class=hash><strong>Hash:</strong>" + res.hash + "</div>";
} else {
var domain = up.getOption('domain');
url = domain + encodeURI(res.key);
var link = domain + res.key;
str = "<div><strong>Link:</strong><a href=" + url + " target='_blank' > " +
link + "</a></div>" +
"<div class=hash><strong>Hash:</strong>" + res.hash + "</div>";
}
tdProgress.html(str).removeClass().next().next('.status').hide();
td.find('.progressCancel').hide();
var progressNameTd = this.fileProgressWrapper.find('.progressName');
var imageView = '?imageView2/1/w/100/h/100';
var isImage = function(url) {
var res, suffix = "";
var imageSuffixes = ["png", "jpg", "jpeg", "gif", "bmp"];
var suffixMatch = /\.([a-zA-Z0-9]+)(\?|\@|$)/;
if (!url || !suffixMatch.test(url)) {
return false;
}
res = suffixMatch.exec(url);
suffix = res[1].toLowerCase();
for (var i = 0, l = imageSuffixes.length; i < l; i++) {
if (suffix === imageSuffixes[i]) {
return true;
}
}
return false;
};
var isImg = isImage(url);
var Wrapper = $('<div class="Wrapper"/>');
var imgWrapper = $('<div class="imgWrapper col-md-3"/>');
var linkWrapper = $('<a class="linkWrapper" target="_blank"/>');
var showImg = $('<img src="images/loading.gif"/>');
progressNameTd.append(Wrapper);
if (!isImg) {
showImg.attr('src', 'images/default.png');
Wrapper.addClass('default');
imgWrapper.append(showImg);
Wrapper.append(imgWrapper);
} else {
linkWrapper.append(showImg);
imgWrapper.append(linkWrapper);
Wrapper.append(imgWrapper);
var img = new Image();
if (!/imageView/.test(url)) {
url += imageView
}
$(img).attr('src', url);
var height_space = 340;
$(img).on('load', function() {
showImg.attr('src', url);
linkWrapper.attr('href', url).attr('title', '查看原图');
function initImg(url, key, height) {
$('#myModal-img').modal();
var modalBody = $('#myModal-img').find('.modal-body');
if (height <= 300) {
$('#myModal-img').find('.text-warning').show();
}
var newImg = new Image();
modalBody.find('img').attr('src', 'images/loading.gif');
newImg.onload = function() {
modalBody.find('img').attr('src', url).data('key', key).data(
'h', height);
modalBody.find('.modal-body-wrapper').find('a').attr('href',
url);
};
newImg.src = url;
}
var infoWrapper = $('<div class="infoWrapper col-md-6"></div>');
var fopLink = $('<a class="fopLink"/>');
infoWrapper.append(fopLink);
fopLink.on('click', function() {
var key = $(this).data('key');
var height = parseInt($(this).parents('.Wrapper').find(
'.origin-height').text(), 10);
if (height > $(window).height() - height_space) {
height = parseInt($(window).height() - height_space, 10);
} else {
height = parseInt(height, 10) || 300;
//set a default height 300 for ie9-
}
var fopArr = [];
var url = Qiniu.pipeline(fopArr, key);
$('#myModal-img').on('hide.bs.modal', function() {
$('#myModal-img').find('.btn-default').removeClass(
'disabled');
$('#myModal-img').find('.text-warning').hide();
}).on('show.bs.modal', function() {
$('#myModal-img').find('.imageView').find('a:eq(0)').addClass(
'disabled');
$('#myModal-img').find('.watermark').find('a:eq(3)').addClass(
'disabled');
$('#myModal-img').find('.text-warning').hide();
});
initImg(url, key, height);
return false;
});
var ie = Qiniu.detectIEVersion();
}).on('error', function() {
showImg.attr('src', 'default.png');
Wrapper.addClass('default');
});
}
};
FileProgress.prototype.setError = function() {
this.fileProgressWrapper.find('td:eq(2)').attr('class', 'text-warning');
this.fileProgressWrapper.find('td:eq(2) .progress').css('width', 0).hide();
this.fileProgressWrapper.find('button').hide();
this.fileProgressWrapper.next('.chunk-status-tr').hide();
};
FileProgress.prototype.setCancelled = function(manual) {
var progressContainer = 'progressContainer';
if (!manual) {
progressContainer += ' red';
}
this.fileProgressWrapper.attr('class', progressContainer);
this.fileProgressWrapper.find('td .progress').remove();
this.fileProgressWrapper.find('td:eq(2) .btn-default').hide();
this.fileProgressWrapper.find('td:eq(2) .progressCancel').hide();
};
FileProgress.prototype.setStatus = function(status, isUploading) {
if (!isUploading) {
this.fileProgressWrapper.find('.status').text(status).attr('class',
'status text-left');
}
};
// 绑定取消上传事件
FileProgress.prototype.bindUploadCancel = function(up) {
var self = this;
if (up) {
self.fileProgressWrapper.find('td:eq(2) .progressCancel').on('click',
function() {
self.setCancelled(false);
self.setStatus("取消上传");
self.fileProgressWrapper.find('.status').css('left', '0');
up.removeFile(self.file);
});
}
};
FileProgress.prototype.appear = function() {
if (this.getTimer() !== null) {
clearTimeout(this.getTimer());
this.setTimer(null);
}
if (this.fileProgressWrapper[0].filters) {
try {
this.fileProgressWrapper[0].filters.item(
"DXImageTransform.Microsoft.Alpha").opacity = 100;
} catch (e) {
// If it is not set initially, the browser will throw an error. This will set it if it is not set yet.
this.fileProgressWrapper.css('filter',
"progid:DXImageTransform.Microsoft.Alpha(opacity=100)");
}
} else {
this.fileProgressWrapper.css('opacity', 1);
}
this.fileProgressWrapper.css('height', '');
this.height = this.fileProgressWrapper.offset().top;
this.opacity = 100;
this.fileProgressWrapper.show();
};
var qiniu = require('qiniu');
var express = require('express');
var config = require('./config.js');
var app = express();
app.configure(function() {
app.use(express.static(__dirname + '/'));
});
app.set('views', __dirname + '/views');
app.engine('html', require('ejs').renderFile);
app.use(express.urlencoded());
app.use('/bower_components', express.static(__dirname + '/../bower_components'));
app.use('/src', express.static(__dirname + '/../src'));
app.use('/dist', express.static(__dirname + '/../dist'));
var mac = new qiniu.auth.digest.Mac(config.AccessKey, config.SecretKey);
var options = {
scope: config.Bucket,
deleteAfterDays: 1,
};
var putPolicy = new qiniu.rs.PutPolicy(options);
var bucketManager = new qiniu.rs.BucketManager(mac, null);
app.get('/uptoken', function(req, res, next) {
var token = putPolicy.uploadToken(mac);
res.header("Cache-Control", "max-age=0, private, must-revalidate");
res.header("Pragma", "no-cache");
res.header("Expires", 0);
if (token) {
res.json({
uptoken: token
});
}
});
app.post('/downtoken', function(req, res) {
var key = req.body.key;
var domain = req.body.domain;
//trim '/' if the domain's last char is '/'
if (domain.lastIndexOf('/') === domain.length - 1) {
domain = domain.substr(0, domain.length - 1);
}
var deadline = 3600 + Math.floor(Date.now() / 1000);
var privateDownUrl = bucketManager.privateDownloadUrl(domain, key,
deadline);
res.json({
url: privateDownUrl,
});
});
app.get('/', function(req, res) {
res.render('index.html', {
domain: config.Domain,
uptoken_url: config.UptokenUrl
});
});
app.get('/multiple', function(req, res) {
res.render('multiple.html', {
domain: config.Domain,
uptoken_url: config.UptokenUrl
});
});
app.get('/formdata', function(req, res) {
var token = putPolicy.uploadToken(mac);
res.render('formdata.html', {
domain: config.Domain,
uptoken: token
});
});
app.get('/performance', function(req, res) {
var token = putPolicy.uploadToken(mac);
res.render('performance.html', {
uptoken: token
});
});
app.listen(config.Port, function() {
console.log('Listening on port %d\n', config.Port);
console.log(
'▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ Demos ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽ ▽');
console.log(
' ▹▹▹▹▹▹▹▹▹▹▹▹▹▹▹▹ Upload: http://127.0.0.1:%d ◁ ◁ ◁ ◁ ◁ ◁ ◁',
config.Port);
console.log(
' ▹▹▹▹▹▹▹ Multiple upload: http://127.0.0.1:%d/multiple ◁ ◁ ◁',
config.Port);
console.log(
' ▹▹▹▹▹▹▹ Formdata upload: http://127.0.0.1:%d/formdata ◁ ◁ ◁',
config.Port);
console.log(
' ▹▹▹▹▹▹▹ Up Performance: http://127.0.0.1:%d/performance ◁ ◁',
config.Port);
console.log(
'△ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △ △\n'
);
});
#testform{
display: none;
}
body{
padding: 0;
margin:0;
font-family: Tahoma,Arial,Roboto,"Droid Sans","Helvetica Neue","Droid Sans Fallback","Heiti SC",sans-self;
color: #666;
}
.title{
background-color: #0983C7;
line-height: 40px;
text-align: center;
color:#fff;
font-family: '微软雅黑';
}
.container{
width: 100%;
margin:0 auto;
max-width:640px;
background-color: #fff;
border-radius: 10px;
min-height: 600px;
}
label{
display:block;
width: 300px;
height:150px;
margin: 30px auto;
border-radius: 10px;
border:2px dashed #ddd;
overflow: hidden;
cursor: pointer;
}
label span{
display:block;
width:50px;
height:50px;
border-radius: 100%;
border:3px solid #0983C7;
margin:30px auto 15px;
position:relative;
}
label em{
font-style: normal;
text-align: center;
display:block;
}
label span:before, label span:after{
display:block;
content:'';
background-color: #0983C7;
position: absolute;
}
label span:before{
width:3px;
height:30px;
top:10px;
left:24px;
}
label span:after{
width:30px;
height:3px;
top:24px;
left:10px;
}
.selected-file, .progress, .uploaded-result{
line-height: 30px;
margin: 0 20px;
}
.uploaded-result span{
display: block;
}
.uploaded-result img{
display:block;
margin: 20px auto;
width:100%;
max-width: 100%;
}
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
pre code {
display:block;
padding:0.5em;
font:12px Arial;
line-height:2.0em
}
pre .comment,pre .template_comment,pre .diff .header,pre .javadoc {
color:#998;
font-style:italic
}
pre .keyword,pre .css .rule .keyword,pre .winutils,pre .javascript .title,pre .nginx .title,pre .subst,pre .request,pre .status {
//color:#333;
color: #787878;
font-weight:bold
}
pre .number,pre .hexcolor,pre .ruby .constant {
color:#099;
}
pre .string,pre .tag .value,pre .phpdoc,pre .tex .formula {
/*color:#d14*/
color: #409B1C;
}
pre .title,pre .id {
color:#900;
font-weight:bold
}
pre .javascript .title,pre .lisp .title,pre .clojure .title,pre .subst {
font-weight:normal
}
pre .class .title,pre .haskell .type,pre .vhdl .literal,pre .tex .command {
color:#458;
font-weight:bold
}
pre .tag,pre .tag .title,pre .rules .property,pre .django .tag .keyword {
color:#000080;
font-weight:normal
}
pre .attribute,pre .variable,pre .lisp .body {
/*color:#008080*/
color: #FF7800;
}
pre .regexp {
color:#009926
}
pre .class {
color:#458;
font-weight:bold
}
pre .symbol,pre .ruby .symbol .string,pre .lisp .keyword,pre .tex .special,pre .prompt {
color:#990073
}
pre .built_in,pre .lisp .title,pre .clojure .built_in {
color:#0086b3
}
pre .preprocessor,pre .pi,pre .doctype,pre .shebang,pre .cdata {
color:#999;
font-weight:bold
}
pre .deletion {
background:#fdd
}
pre .addition {
background:#dfd
}
pre .diff .change {
background:#0086b3
}
pre .chunk {
color:#aaa
}
body {
font-family: '微软雅黑';
overflow: scroll;
}
h1 {
border-bottom: 1px solid #d2d2d2;
padding-bottom: 15px;
position: relative;
font-weight: bold;
margin-bottom: 15px;
font-family: '微软雅黑';
}
h3,h4 {
color: #d2d2d2;
font-family: '微软雅黑',"Helvetica Neue", Helvetica, Arial, sans-serif;
}
h1 .view_github {
display: block;
font-size: 14px;
line-height: 30px;
position: absolute;
right: 0;
bottom: 0;
border: 1px solid #d2d2d2;
border-bottom: none;
border-radius:0;
}
h1 .view_github:hover,h1 .view_code:hover {
background-color: #f5f5f5;
}
h1 .view_code {
display: block;
font-size: 14px;
line-height: 30px;
position: absolute;
right: 225px;
bottom: 0;
border: 1px solid #d2d2d2;
border-bottom: none;
border-radius:0;
height: 45px;
}
.col-md-12.no-padding {
padding-left:0;
padding-right:0;
}
.info {
position: relative;
height: 100%;
}
tr .progress{
width: 96%;
display: inline-block;
}
tr .progress-bar {
min-height: 20px;
float: none;
}
tr .progress, tr .progress div {
height: 20px;
}
tr .progress .chunk-status {
position: absolute;
left: 24px;
top: 0;
}
tr .progressCancel{
display: inline-block;
float: right;
position: relative;
z-index: 100;
color: #333;
text-decoration: none;
}
.status {
position: absolute;
width: 100%;
top:0;
left: 24px;
}
.tip {
padding-left: 30px;
}
.tip p {
line-height: 1;
}
#container {
margin-bottom: 20px;
border-width: 2px;
border-radius: 3px;
border-color: #dcdcdc;
cursor: default;
}
#container:hover,#container:active {
background-color:#fff;
border-color: #dcdcdc;
-webkit-box-shadow:none;
box-shadow:none;
}
#container.draging {
background-color:#fff;
border-color:#999999;
}
#success {
line-height: 30px;
height: 30px;
margin:20px 0 0 0;
}
#success div {
padding-left: 24px;
}
#container a {
width: 15%;
min-width: 145px;
}
button {
border-radius: 3px;
-webkit-box-shadow: 0 1px 0 rgba(243, 243, 243, 0.75);
box-shadow: 0 1px 0 rgba(243, 243, 243, 0.75);
color: #646464;
border: 1px solid #d2d2d2;
text-align: center;
-webkit-transition: background-color 0.218s, border-color 0.218s, box-shadow 0.218s;
transition: background-color 0.218s, border-color 0.218s, box-shadow 0.218s;
-webkit-user-select: none;
outline: none;
}
td .Wrapper {
margin-top: 20px;
text-align: center;
overflow: hidden;
}
td .Wrapper .infoWrapper{
width:50%;
height:100px;
margin-left: 10px;
text-align: left;
line-height: 25px;
}
td .Wrapper .infoWrapper a:first-child{
margin-right: 10px;
}
td .Wrapper.default {
margin-top: 20px;
text-align: left;
}
td .Wrapper.default img{
width:100px;
height:100px;
}
td .imgWrapper {
position: relative;
width:100px;
height: 100px;
}
td .imgWrapper .linkWrapper {
position: absolute;
left: 15px;
top: 0;
width:100px;
height: 100px;
cursor: pointer;
}
td .imgWrapper .linkWrapper a{
line-height: 50px;
}
table td .hash {
margin-bottom: 20px;
}
pre {
margin-top: 20px;
}
.container .modal-dialog {
width: 45%;
}
.container .modal-dialog .modal-content {
max-height: 90%;
}
.container .modal-dialog .modal-body img{
max-width: 95%;
max-height: 70%;
}
.container .modal-body-footer , .container .modal-body-footer div{
margin-top: 10px;
margin-bottom: 10px;
}
.container .modal-dialog .modal-body-footer {
margin-top: 0px;
}
.container .modal-dialog .modal-body-footer span {
margin-right: 20px;
}
.container .modal-dialog .modal-body-footer .text-warning,.container .modal-dialog .modal-footer span ,.container .modal-dialog .modal-footer a{
font-size: 12px;
}
.body .btn-default {
border-radius: 3px;
-webkit-box-shadow: 0 1px 0 rgba(243, 243, 243, 0.75);
box-shadow: 0 1px 0 rgba(243, 243, 243, 0.75);
color: #646464;
border: 1px solid #d2d2d2;
text-align: center;
-webkit-transition: background-color 0.218s, border-color 0.218s, box-shadow 0.218s;
transition: background-color 0.218s, border-color 0.218s, box-shadow 0.218s;
-webkit-user-select: none;
outline: none;
}
.body .btn-default:hover,.body .btn-default:active,.body .btn-default:focus ,.no-padding button:hover,.no-padding button:active,.no-padding button:focus{
border-color: #bebebe;
text-decoration: none;
background-color: #fff;
color: #262626;
outline: none;
-webkit-box-shadow: 0 1px 0 rgba(230, 230, 230, 0.8);
box-shadow: 0 1px 0 rgba(230, 230, 230, 0.8);
}
a {
cursor: pointer;
}
#testform{
display: none;
}
body{
padding: 0;
margin:0;
font-family: core, "Avenir Next", "Helvetica Neue", Helvetica, Arial, "Source Han Sans SC", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "WenQuanYi MicroHei", sans-serif;
color: #666;
font-size: 14px;
background: url('http://assets.qiniu.com/v2/qiniu-blue-195x105.png') no-repeat #1DA4DE;
}
ul,li {
padding: 0;
margin: 0;
}
td, th{
border: 0 none;
border-bottom: 1px solid #f0f0f0;
}
.hidden{
display: none;
}
.show {
display: inherit;
}
.title{
background-color: #0983C7;
line-height: 40px;
text-align: center;
color:#fff;
}
.container{
width: 100%;
margin:0 auto;
max-width:640px;
background-color: #fff;
border-radius: 10px;
min-height: 530px;
overflow: hidden;
padding-bottom: 30px;
}
.uploaded-result{
line-height: 30px;
margin: 0 20px;
}
.uploaded-result th{
text-align: left;
}
.uploaded-result table {
width: 100%;
word-break: break-all;
}
#canvas{
display:none;
}
.progress{
padding: 20px 20px;
color: #108ee9;
line-height: 26px;
}
.warning{
color: #f0ad4e;
font-weight: normal;
font-size: 12px;
}
.run{
display: block;
width: 120px;
line-height: 26px;
background-color: #1992ea;
border-radius: 6px;
margin: 20px auto;
text-align: center;
color: #fff;
text-decoration: none;
cursor: pointer;
font-size: 14px;
}
.run:hover{
background-color: #1DA4DE;
}
.disabled, .disabled:hover{
background-color: #aaa;
}
.uphosts-list li{
list-style: none;
font-size: 14px;
}
.uphosts-list ul:first-of-type{
text-align: center;
height: 27px;
line-height: 26px;
margin: 20px;
border-bottom: 1px solid #d9d9d9;
}
.uphosts-list ul:first-of-type li{
float: left;
width: 50px;
margin: 0 10px;
cursor: pointer;
}
.uphosts-list ul:first-of-type li.on{
color: #108ee9;
border-bottom: 2px solid #108ee9;
}
.uphosts-list ul:last-child{
margin: 20px;
}
.uphosts-list ul:last-child li{
display: none;
}
.uphosts-list ul:last-child li.on{
display: block;
}
.uphosts-list ul:last-child li label{
display: inline-block;
width: 50%;
padding: 6px 0;
cursor: pointer;
}
.up-performance{
margin-bottom: 20px;
}
.per-title{
font-weight: bold;
color: #108ee9;
}
.up-headers{
}
.up-headers tr td:first-child{
width: 120px;
}
@media screen and (min-width: 640px){
.container{
margin-top:50px;
}
}
@media screen and (max-width: 640px){
body{
background-color:#fff;
}
.uphosts-list ul:last-child li label{
width: 100%;
}
.container{
border-radius: 0;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<title>七牛云 - formdata 上传 demo</title>
<link rel="stylesheet" href="../styles/formdata.css">
<script src="../../bower_components/jquery/jquery.min.js"></script>
<script src="../scripts/formdata.js"></script>
<script>
var domain = "<%= domain %>"; // you bucket domain eg: http://xxx.bkt.clouddn.com
</script>
</head>
<body>
<div class="container">
<div class="title">Formdata 上传 demo</div>
<!-- Document:https://developer.qiniu.com/kodo/manual/form-upload -->
<form id="testform" method="post" enctype="multipart/form-data">
<input name="key" id="key" type="hidden" value="">
<input name="token" type="hidden" value="<%= uptoken %>">
<input id="userfile" name="file" type="file" />
<!-- take photo with phone -->
<!-- <input id="userfile" name="file" accept="image/*" type="file" /> -->
<!-- take video with phone -->
<!-- <input id="userfile" name="file" type="file" accept="video/*"/> -->
<input name="accept" type="hidden" />
</form>
<!-- add file -->
<label for="userfile">
<span></span>
<em>添加文件</em>
</label>
<!-- upload info -->
<div class="selected-file"></div>
<div class="progress"></div>
<div class="uploaded-result"></div>
</div>
</body>
</html>
\ No newline at end of file
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<title>七牛云 - JavaScript SDK</title>
<link href="images/favicon.ico" rel="shortcut icon">
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
<link rel="stylesheet" href="styles/main.css">
<link rel="stylesheet" href="styles/highlight.css">
<!--[if lt IE 9]>
<script src="bower_components/respond/dest/respond.min.js"></script>
<![endif]-->
</head>
<body>
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-6" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">七牛云存储 - JavaScript SDK</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-6">
<ul class="nav navbar-nav">
<li class="active"><a href="#">上传示例</a></li>
<li><a href="http://developer.qiniu.com/code/v6/sdk/javascript.html">SDK 文档</a></li>
<li><a href="/performance">上传速度检测</a></li>
</ul>
</div>
</div>
</nav>
<div class="container" style="padding-top: 60px;">
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="#demo" id="demo-tab" role="tab" data-toggle="tab" aria-controls="demo" aria-expanded="true">示例</a>
</li>
<li role="presentation">
<a href="#code" id="code-tab" role="tab" data-toggle="tab" aria-controls="code">代码</a>
</li>
<li role="presentation">
<a href="#log" id="log-tab" role="tab" data-toggle="tab" aria-controls="log">日志</a>
</li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane fade in active" id="demo" aria-labelledby="demo-tab">
<div class="row" style="margin-top: 20px;">
<input type="hidden" id="domain" value="<%= domain %>">
<input type="hidden" id="uptoken_url" value="<%= uptoken_url %>">
<ul class="tip col-md-12 text-mute">
<li>
<small>
JavaScript SDK 基于 Plupload 开发,可以通过 Html5 或 Flash 等模式上传文件至七牛云存储。
</small>
</li>
<li>
<small>临时上传的空间不定时清空,请勿保存重要文件。</small>
</li>
<li>
<small>Html5模式大于4M文件采用分块上传。</small>
</li>
<li>
<small>上传图片可查看处理效果。</small>
</li>
<li>
<small>本示例限制最大上传文件100M。</small>
</li>
</ul>
<div class="col-md-12">
<div id="container">
<a class="btn btn-default btn-lg " id="pickfiles" href="#" >
<i class="glyphicon glyphicon-plus"></i>
<span>选择文件</span>
</a>
</div>
</div>
<div style="display:none" id="success" class="col-md-12">
<div class="alert-success">
队列全部文件处理完毕
</div>
</div>
<div class="col-md-12 ">
<table class="table table-striped table-hover text-left" style="margin-top:40px;display:none">
<thead>
<tr>
<th class="col-md-4">Filename</th>
<th class="col-md-2">Size</th>
<th class="col-md-6">Detail</th>
</tr>
</thead>
<tbody id="fsUploadProgress">
</tbody>
</table>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane fade" id="code" aria-labelledby="code-tab">
<div class="row" style="margin-top: 20px;">
<div class="col-md-12">
<pre><code>
//引入Plupload 、qiniu.js后
var uploader = Qiniu.uploader({
runtimes: 'html5,flash,html4', //上传模式,依次退化
browse_button: 'pickfiles', //上传选择的点选按钮,**必需**
uptoken_url: '/token', //Ajax请求upToken的Url,**强烈建议设置**(服务端提供)
// uptoken : '<Your upload token>', //若未指定uptoken_url,则必须指定 uptoken ,uptoken由其他程序生成
// unique_names: true, // 默认 false,key为文件名。若开启该选项,SDK为自动生成上传成功后的key(文件名)。
// save_key: true, // 默认 false。若在服务端生成uptoken的上传策略中指定了 `sava_key`,则开启,SDK会忽略对key的处理
domain: 'http://qiniu-plupload.qiniudn.com/', //bucket 域名,下载资源时用到,**必需**
get_new_uptoken: false, //设置上传文件的时候是否每次都重新获取新的token
container: 'container', //上传区域DOM ID,默认是browser_button的父元素,
max_file_size: '100mb', //最大文件体积限制
flash_swf_url: 'js/plupload/Moxie.swf', //引入flash,相对路径
max_retries: 3, //上传失败最大重试次数
dragdrop: true, //开启可拖曳上传
drop_element: 'container', //拖曳上传区域元素的ID,拖曳文件或文件夹后可触发上传
chunk_size: '4mb', //分块上传时,每片的体积
auto_start: true, //选择文件后自动上传,若关闭需要自己绑定事件触发上传
init: {
'FilesAdded': function(up, files) {
plupload.each(files, function(file) {
// 文件添加进队列后,处理相关的事情
});
},
'BeforeUpload': function(up, file) {
// 每个文件上传前,处理相关的事情
},
'UploadProgress': function(up, file) {
// 每个文件上传时,处理相关的事情
},
'FileUploaded': function(up, file, info) {
// 每个文件上传成功后,处理相关的事情
// 其中 info.response 是文件上传成功后,服务端返回的json,形式如
// {
// "hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98",
// "key": "gogopher.jpg"
// }
// 参考http://developer.qiniu.com/docs/v6/api/overview/up/response/simple-response.html
// var domain = up.getOption('domain');
// var res = parseJSON(info.response);
// var sourceLink = domain + res.key; 获取上传成功后的文件的Url
},
'Error': function(up, err, errTip) {
//上传出错时,处理相关的事情
},
'UploadComplete': function() {
//队列文件处理完毕后,处理相关的事情
},
'Key': function(up, file) {
// 若想在前端对每个文件的key进行个性化处理,可以配置该函数
// 该配置必须要在 unique_names: false , save_key: false 时才生效
var key = "";
// do something with key here
return key
}
}
});
// domain 为七牛空间(bucket)对应的域名,选择某个空间后,可通过"空间设置->基本设置->域名设置"查看获取
// uploader 为一个plupload对象,继承了所有plupload的方法,参考http://plupload.com/docs
</code></pre>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane fade" id="log" aria-labelledby="log-tab">
<pre id="qiniu-js-sdk-log"></pre>
</div>
</div>
</div>
<div class="container" style="display: none;">
<div class="text-left col-md-12 wrapper">
<h1 class="text-left col-md-12 ">
七牛云存储 - JavaScript SDK
<a class="btn btn-default view_code" id="show_code">
查看初始化代码
</a>
<a class="btn btn-default view_github" href="https://github.com/qiniupd/qiniu-js-sdk" target="_blank">
<img src="http://qtestbucket.qiniudn.com/GitHub-Mark-32px.png">
View Source on Github
</a>
</h1>
</div>
<div class="body">
<!-- <div class="col-md-12" id="qiniu-js-sdk-log"></div> -->
</div>
<div class="modal fade body" id="myModal-code" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel">查看初始化代码</h4>
</div>
<div class="modal-body">
<pre><code>
//引入Plupload 、qiniu.js后
var uploader = Qiniu.uploader({
runtimes: 'html5,flash,html4', //上传模式,依次退化
browse_button: 'pickfiles', //上传选择的点选按钮,**必需**
uptoken_url: '/token', //Ajax请求upToken的Url,**强烈建议设置**(服务端提供)
// uptoken : '<Your upload token>', //若未指定uptoken_url,则必须指定 uptoken ,uptoken由其他程序生成
// unique_names: true, // 默认 false,key为文件名。若开启该选项,SDK为自动生成上传成功后的key(文件名)。
// save_key: true, // 默认 false。若在服务端生成uptoken的上传策略中指定了 `sava_key`,则开启,SDK会忽略对key的处理
domain: 'http://qiniu-plupload.qiniudn.com/', //bucket 域名,下载资源时用到,**必需**
get_new_uptoken: false, //设置上传文件的时候是否每次都重新获取新的token
container: 'container', //上传区域DOM ID,默认是browser_button的父元素,
max_file_size: '100mb', //最大文件体积限制
flash_swf_url: 'js/plupload/Moxie.swf', //引入flash,相对路径
max_retries: 3, //上传失败最大重试次数
dragdrop: true, //开启可拖曳上传
drop_element: 'container', //拖曳上传区域元素的ID,拖曳文件或文件夹后可触发上传
chunk_size: '4mb', //分块上传时,每片的体积
auto_start: true, //选择文件后自动上传,若关闭需要自己绑定事件触发上传
init: {
'FilesAdded': function(up, files) {
plupload.each(files, function(file) {
// 文件添加进队列后,处理相关的事情
});
},
'BeforeUpload': function(up, file) {
// 每个文件上传前,处理相关的事情
},
'UploadProgress': function(up, file) {
// 每个文件上传时,处理相关的事情
},
'FileUploaded': function(up, file, info) {
// 每个文件上传成功后,处理相关的事情
// 其中 info.response 是文件上传成功后,服务端返回的json,形式如
// {
// "hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98",
// "key": "gogopher.jpg"
// }
// 参考http://developer.qiniu.com/docs/v6/api/overview/up/response/simple-response.html
// var domain = up.getOption('domain');
// var res = parseJSON(info.response);
// var sourceLink = domain + res.key; 获取上传成功后的文件的Url
},
'Error': function(up, err, errTip) {
//上传出错时,处理相关的事情
},
'UploadComplete': function() {
//队列文件处理完毕后,处理相关的事情
},
'Key': function(up, file) {
// 若想在前端对每个文件的key进行个性化处理,可以配置该函数
// 该配置必须要在 unique_names: false , save_key: false 时才生效
var key = "";
// do something with key here
return key
}
}
});
// domain 为七牛空间(bucket)对应的域名,选择某个空间后,可通过"空间设置->基本设置->域名设置"查看获取
// uploader 为一个plupload对象,继承了所有plupload的方法,参考http://plupload.com/docs
</code></pre>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<div class="modal fade body" id="myModal-img" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel">图片效果查看</h4>
</div>
<div class="modal-body">
<div class="modal-body-wrapper text-center">
<a href="" target="_blank" >
<img src="" alt="" data-key="" data-h="">
</a>
</div>
<div class="modal-body-footer">
<div class="watermark">
<span>水印控制:</span>
<a href="#" data-watermark="NorthWest" class="btn btn-default">
左上角
</a>
<a href="#" data-watermark="SouthWest" class="btn btn-default">
左下角
</a>
<a href="#" data-watermark="NorthEast" class="btn btn-default">
右上角
</a>
<a href="#" data-watermark="SouthEast" class="btn btn-default disabled">
右下角
</a>
<a href="#" data-watermark="false" class="btn btn-default">
无水印
</a>
</div>
<div class="imageView2">
<span>缩略控制:</span>
<a href="#" data-imageview="large" class="btn btn-default disabled">
大缩略图
</a>
<a href="#" data-imageview="middle" class="btn btn-default">
中缩略图
</a>
<a href="#" data-imageview="small" class="btn btn-default">
小缩略图
</a>
</div>
<div class="imageMogr2">
<span>高级控制:</span>
<a href="#" data-imagemogr="left" class="btn btn-default no-disable-click" >
逆时针
</a>
<a href="#" data-imagemogr="right" class="btn btn-default no-disable-click">
顺时针
</a>
<a href="#" data-imagemogr="no-rotate" class="btn btn-default">
无旋转
</a>
</div>
<div class="text-warning">
备注:小图片水印效果不明显,建议使用大图片预览水印效果
</div>
</div>
</div>
<div class="modal-footer">
<span class="pull-left">本示例仅演示了简单的图片处理效果,了解更多请点击</span>
<a href="https://github.com/SunLn/qiniu-js-sdk" target="_blank" class="pull-left">本SDK文档</a>
<span class="pull-left"></span>
<a href="http://developer.qiniu.com/docs/v6/api/reference/fop/image/" target="_blank" class="pull-left">七牛官方文档</a>
<button type="button" class="btn btn-primary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="bower_components/jquery/jquery.min.js"></script>
<script type="text/javascript" src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<script type="text/javascript" src="bower_components/plupload/js/i18n/zh_CN.js"></script>
<script type="text/javascript" src="scripts/ui.js"></script>
<script type="text/javascript" src="dist/qiniu.min.js"></script>
<script type="text/javascript" src="scripts/highlight.js"></script>
<script type="text/javascript" src="scripts/main.js"></script>
<script type="text/javascript">hljs.initHighlightingOnLoad();</script>
</body>
</html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>七牛云 - JavaScript SDK</title>
<link href="/images/favicon.ico" rel="shortcut icon">
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css">
<link rel="stylesheet" href="styles/main.css">
<link rel="stylesheet" href="styles/highlight.css">
<!--[if lt IE 9]>
<script src="js/Respond-1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<div class="container">
<div class="text-left col-md-12 wrapper">
<h1 class="text-left col-md-12 ">
七牛云存储 - JavaScript SDK
<a class="btn btn-default view_code" id="show_code">
查看初始化代码
</a>
<a class="btn btn-default view_github" href="https://github.com/qiniupd/qiniu-js-sdk" target="_blank">
<img src="http://qtestbucket.qiniudn.com/GitHub-Mark-32px.png">
View Source on Github
</a>
</h1>
<input type="hidden" id="domain" value="<%= domain %>">
<input type="hidden" id="uptoken_url" value="<%= uptoken_url %>">
<ul class="tip col-md-12 text-mute">
<li>
<small>
JavaScript SDK 基于 Plupload 开发,可以通过 Html5 或 Flash 等模式上传文件至七牛云存储。
</small>
</li>
<li>
<small>临时上传的空间不定时清空,请勿保存重要文件。</small>
</li>
<li>
<small>Html5模式大于4M文件采用分块上传。</small>
</li>
<li>
<small>上传图片可查看处理效果。</small>
</li>
<li>
<small>本示例限制最大上传文件100M。</small>
</li>
</ul>
</div>
<div class="body">
<div class="col-md-12">
<div id="container">
<a class="btn btn-default btn-lg " id="pickfiles" href="#" >
<i class="glyphicon glyphicon-plus"></i>
<sapn>选择文件</sapn>
</a>
</div>
<div id="container2">
<a class="btn btn-default btn-lg " id="pickfiles2" href="#" >
<i class="glyphicon glyphicon-plus"></i>
<sapn>选择文件</sapn>
</a>
</div>
</div>
<div style="display:none" id="success" class="col-md-12">
<div class="alert-success">
队列全部文件处理完毕
</div>
</div>
<div class="col-md-12 ">
<table class="table table-striped table-hover text-left" style="margin-top:40px;display:none">
<thead>
<tr>
<th class="col-md-4">Filename</th>
<th class="col-md-2">Size</th>
<th class="col-md-6">Detail</th>
</tr>
</thead>
<tbody id="fsUploadProgress">
</tbody>
</table>
</div>
</div>
<div class="modal fade body" id="myModal-code" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel">查看初始化代码</h4>
</div>
<div class="modal-body">
<pre><code>
//引入Plupload 、qiniu.js后
var uploader = Qiniu.uploader({
runtimes: 'html5,flash,html4', //上传模式,依次退化
browse_button: 'pickfiles', //上传选择的点选按钮,**必需**
uptoken_url: '/token', //Ajax请求upToken的Url,**强烈建议设置**(服务端提供)
// uptoken : '<Your upload token>', //若未指定uptoken_url,则必须指定 uptoken ,uptoken由其他程序生成
// unique_names: true, // 默认 false,key为文件名。若开启该选项,SDK为自动生成上传成功后的key(文件名)。
// save_key: true, // 默认 false。若在服务端生成uptoken的上传策略中指定了 `sava_key`,则开启,SDK会忽略对key的处理
domain: 'http://qiniu-plupload.qiniudn.com/', //bucket 域名,下载资源时用到,**必需**
container: 'container', //上传区域DOM ID,默认是browser_button的父元素,
max_file_size: '100mb', //最大文件体积限制
flash_swf_url: 'js/plupload/Moxie.swf', //引入flash,相对路径
max_retries: 3, //上传失败最大重试次数
dragdrop: true, //开启可拖曳上传
drop_element: 'container', //拖曳上传区域元素的ID,拖曳文件或文件夹后可触发上传
chunk_size: '4mb', //分块上传时,每片的体积
auto_start: true, //选择文件后自动上传,若关闭需要自己绑定事件触发上传
init: {
'FilesAdded': function(up, files) {
plupload.each(files, function(file) {
// 文件添加进队列后,处理相关的事情
});
},
'BeforeUpload': function(up, file) {
// 每个文件上传前,处理相关的事情
},
'UploadProgress': function(up, file) {
// 每个文件上传时,处理相关的事情
},
'FileUploaded': function(up, file, info) {
// 每个文件上传成功后,处理相关的事情
// 其中 info 是文件上传成功后,服务端返回的json,形式如
// {
// "hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98",
// "key": "gogopher.jpg"
// }
// 参考http://developer.qiniu.com/docs/v6/api/overview/up/response/simple-response.html
// var domain = up.getOption('domain');
// var res = parseJSON(info);
// var sourceLink = domain + res.key; 获取上传成功后的文件的Url
},
'Error': function(up, err, errTip) {
//上传出错时,处理相关的事情
},
'UploadComplete': function() {
//队列文件处理完毕后,处理相关的事情
},
'Key': function(up, file) {
// 若想在前端对每个文件的key进行个性化处理,可以配置该函数
// 该配置必须要在 unique_names: false , save_key: false 时才生效
var key = "";
// do something with key here
return key
}
}
});
// domain 为七牛空间(bucket)对应的域名,选择某个空间后,可通过"空间设置->基本设置->域名设置"查看获取
// uploader 为一个plupload对象,继承了所有plupload的方法,参考http://plupload.com/docs
// 第二个上传实例
var option2 = {
key : val,
……
}
var Q2 = new QiniuJsSDK();
var uploader2 = Q2.uploader(option2);
</code></pre>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
<div class="modal fade body" id="myModal-img" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="myModalLabel">图片效果查看</h4>
</div>
<div class="modal-body">
<div class="modal-body-wrapper text-center">
<a href="" target="_blank" >
<img src="" alt="" data-key="" data-h="">
</a>
</div>
<div class="modal-body-footer">
<div class="watermark">
<span>水印控制:</span>
<a href="#" data-watermark="NorthWest" class="btn btn-default">
左上角
</a>
<a href="#" data-watermark="SouthWest" class="btn btn-default">
左下角
</a>
<a href="#" data-watermark="NorthEast" class="btn btn-default">
右上角
</a>
<a href="#" data-watermark="SouthEast" class="btn btn-default disabled">
右下角
</a>
<a href="#" data-watermark="false" class="btn btn-default">
无水印
</a>
</div>
<div class="imageView2">
<span>缩略控制:</span>
<a href="#" data-imageview="large" class="btn btn-default disabled">
大缩略图
</a>
<a href="#" data-imageview="middle" class="btn btn-default">
中缩略图
</a>
<a href="#" data-imageview="small" class="btn btn-default">
小缩略图
</a>
</div>
<div class="imageMogr2">
<span>高级控制:</span>
<a href="#" data-imagemogr="left" class="btn btn-default no-disable-click" >
逆时针
</a>
<a href="#" data-imagemogr="right" class="btn btn-default no-disable-click">
顺时针
</a>
<a href="#" data-imagemogr="no-rotate" class="btn btn-default">
无旋转
</a>
</div>
<div class="text-warning">
备注:小图片水印效果不明显,建议使用大图片预览水印效果
</div>
</div>
</div>
<div class="modal-footer">
<span class="pull-left">本示例仅演示了简单的图片处理效果,了解更多请点击</span>
<a href="https://github.com/SunLn/qiniu-js-sdk" target="_blank" class="pull-left">本SDK文档</a>
<span class="pull-left"></span>
<a href="http://developer.qiniu.com/docs/v6/api/reference/fop/image/" target="_blank" class="pull-left">七牛官方文档</a>
<button type="button" class="btn btn-primary" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="bower_components/jquery/jquery.min.js"></script>
<script type="text/javascript" src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
<script type="text/javascript" src="bower_components/plupload/js/moxie.js"></script>
<script type="text/javascript" src="bower_components/plupload/js/plupload.dev.js"></script>
<!-- <script type="text/javascript" src="bower_components/plupload/js/plupload.full.min.js"></script> -->
<script type="text/javascript" src="bower_components/plupload/js/i18n/zh_CN.js"></script>
<script type="text/javascript" src="scripts/ui.js"></script>
<script type="text/javascript" src="src/qiniu.js"></script>
<script type="text/javascript" src="scripts/highlight.js"></script>
<script type="text/javascript" src="scripts/multiple.js"></script>
<script type="text/javascript">hljs.initHighlightingOnLoad();</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link rel="stylesheet" href="styles/performance.css">
<title>七牛云 - 上传速度检测</title>
</head>
<body>
<div id="app" class="container">
<div class="title">七牛云上传速度检测</div>
<form id="testform" method="post" enctype="multipart/form-data">
<input name="key" id="key" type="hidden" value="a.jpg">
<input name="accept" type="hidden" />
</form>
<zone-list :host-map="hostMap" :current-zone="currentZone" :switch-zone="switchZone" :selected-host="selectedHost"></zone-list>
<span class="run" :class="{'disabled': !selectedHost.host}" @click="uploadTest">开始检测</span>
<!-- upload info -->
<div class="progress">{{loadMessage}}</div>
<div class="uploaded-result">
<upload-performance :per='performance' v-if="performance" :warning="!isPerformanceSupported"></upload-performance>
<up-headers :headers="headers" v-if="headers"></up-headers>
</div>
</div>
<script src="scripts/http-performance.js"></script>
<script src="bower_components/vue/dist/vue.min.js"></script>
<script src="scripts/performance.js"></script>
</body>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "qiniu-js",
"jsName": "qiniu",
"version": "1.0.24",
"private": false,
"scripts": {
"start": "node demo/server.js",
"watch": "webpack --watch",
"build": "webpack"
},
"description": "Javascript SDK for Qiniu Resource (Cloud) Storage API",
"main": "dist/qiniu.min.js",
"repository": {
"type": "git",
"url": "git://github.com/qiniu/js-sdk.git"
},
"bugs": {
"url": "https://github.com/qiniu/js-sdk/issues"
},
"keywords": [
"cloud",
"storage",
"s3",
"qiniu",
"web-service"
],
"author": "sdk@qiniu.com",
"contributors": [
{
"name": "luoyeshu0507",
"email": "lizhiwei@qiniu.com"
},
{
"name": "codedogfish",
"email": "jackyu@qiniu.com"
},
{
"name": "jinxinxin",
"email": "jinxinxin@qiniu.com"
}
],
"devDependencies": {
"bower": "^1.6.8",
"ejs": "~2.5.5",
"express": "^3.4.8",
"grunt": "~0.4.2",
"grunt-cli": "~0.1.13",
"grunt-contrib-concat": "^0.5.1",
"grunt-contrib-copy": "~0.5.0",
"grunt-contrib-jshint": "~0.7.2",
"grunt-contrib-uglify": "~0.2.2",
"grunt-contrib-watch": "~0.5.3",
"open-browser-webpack-plugin": "0.0.5",
"qiniu": "~7.0.5",
"webpack": "^3.8.1",
"webpack-dev-middleware": "^1.12.0"
},
"license": "MIT"
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
* Plupload - multi-runtime File Uploader
* v2.3.1
*
* Copyright 2013, Moxiecode Systems AB
* Released under GPL License.
*
* License: http://www.plupload.com/license
* Contributing: http://www.plupload.com/contributing
*
* Date: 2017-02-06
*/
;(function (global, factory) {
var extract = function() {
var ctx = {};
factory.apply(ctx, arguments);
return ctx.plupload;
};
if (typeof define === "function" && define.amd) {
define("plupload", ['./moxie'], extract);
} else if (typeof module === "object" && module.exports) {
module.exports = extract(require('../../../js-sdk-master 2/src/moxie'));
} else {
global.plupload = extract(global.moxie);
}
}(this || window, function(moxie) {
/**
* Plupload.js
*
* Copyright 2013, Moxiecode Systems AB
* Released under GPL License.
*
* License: http://www.plupload.com/license
* Contributing: http://www.plupload.com/contributing
*/
;(function(exports, o, undef) {
var delay = window.setTimeout;
var fileFilters = {};
var u = o.core.utils;
var Runtime = o.runtime.Runtime;
// convert plupload features to caps acceptable by mOxie
function normalizeCaps(settings) {
var features = settings.required_features, caps = {};
function resolve(feature, value, strict) {
// Feature notation is deprecated, use caps (this thing here is required for backward compatibility)
var map = {
chunks: 'slice_blob',
jpgresize: 'send_binary_string',
pngresize: 'send_binary_string',
progress: 'report_upload_progress',
multi_selection: 'select_multiple',
dragdrop: 'drag_and_drop',
drop_element: 'drag_and_drop',
headers: 'send_custom_headers',
urlstream_upload: 'send_binary_string',
canSendBinary: 'send_binary',
triggerDialog: 'summon_file_dialog'
};
if (map[feature]) {
caps[map[feature]] = value;
} else if (!strict) {
caps[feature] = value;
}
}
if (typeof(features) === 'string') {
plupload.each(features.split(/\s*,\s*/), function(feature) {
resolve(feature, true);
});
} else if (typeof(features) === 'object') {
plupload.each(features, function(value, feature) {
resolve(feature, value);
});
} else if (features === true) {
// check settings for required features
if (settings.chunk_size && settings.chunk_size > 0) {
caps.slice_blob = true;
}
if (!plupload.isEmptyObj(settings.resize) || settings.multipart === false) {
caps.send_binary_string = true;
}
if (settings.http_method) {
caps.use_http_method = settings.http_method;
}
plupload.each(settings, function(value, feature) {
resolve(feature, !!value, true); // strict check
});
}
return caps;
}
/**
* @module plupload
* @static
*/
var plupload = {
/**
* Plupload version will be replaced on build.
*
* @property VERSION
* @for Plupload
* @static
* @final
*/
VERSION : '2.3.1',
/**
* The state of the queue before it has started and after it has finished
*
* @property STOPPED
* @static
* @final
*/
STOPPED : 1,
/**
* Upload process is running
*
* @property STARTED
* @static
* @final
*/
STARTED : 2,
/**
* File is queued for upload
*
* @property QUEUED
* @static
* @final
*/
QUEUED : 1,
/**
* File is being uploaded
*
* @property UPLOADING
* @static
* @final
*/
UPLOADING : 2,
/**
* File has failed to be uploaded
*
* @property FAILED
* @static
* @final
*/
FAILED : 4,
/**
* File has been uploaded successfully
*
* @property DONE
* @static
* @final
*/
DONE : 5,
// Error constants used by the Error event
/**
* Generic error for example if an exception is thrown inside Silverlight.
*
* @property GENERIC_ERROR
* @static
* @final
*/
GENERIC_ERROR : -100,
/**
* HTTP transport error. For example if the server produces a HTTP status other than 200.
*
* @property HTTP_ERROR
* @static
* @final
*/
HTTP_ERROR : -200,
/**
* Generic I/O error. For example if it wasn't possible to open the file stream on local machine.
*
* @property IO_ERROR
* @static
* @final
*/
IO_ERROR : -300,
/**
* @property SECURITY_ERROR
* @static
* @final
*/
SECURITY_ERROR : -400,
/**
* Initialization error. Will be triggered if no runtime was initialized.
*
* @property INIT_ERROR
* @static
* @final
*/
INIT_ERROR : -500,
/**
* File size error. If the user selects a file that is too large it will be blocked and an error of this type will be triggered.
*
* @property FILE_SIZE_ERROR
* @static
* @final
*/
FILE_SIZE_ERROR : -600,
/**
* File extension error. If the user selects a file that isn't valid according to the filters setting.
*
* @property FILE_EXTENSION_ERROR
* @static
* @final
*/
FILE_EXTENSION_ERROR : -601,
/**
* Duplicate file error. If prevent_duplicates is set to true and user selects the same file again.
*
* @property FILE_DUPLICATE_ERROR
* @static
* @final
*/
FILE_DUPLICATE_ERROR : -602,
/**
* Runtime will try to detect if image is proper one. Otherwise will throw this error.
*
* @property IMAGE_FORMAT_ERROR
* @static
* @final
*/
IMAGE_FORMAT_ERROR : -700,
/**
* While working on files runtime may run out of memory and will throw this error.
*
* @since 2.1.2
* @property MEMORY_ERROR
* @static
* @final
*/
MEMORY_ERROR : -701,
/**
* Each runtime has an upper limit on a dimension of the image it can handle. If bigger, will throw this error.
*
* @property IMAGE_DIMENSIONS_ERROR
* @static
* @final
*/
IMAGE_DIMENSIONS_ERROR : -702,
/**
* Mime type lookup table.
*
* @property mimeTypes
* @type Object
* @final
*/
mimeTypes : u.Mime.mimes,
/**
* In some cases sniffing is the only way around :(
*/
ua: u.Env,
/**
* Gets the true type of the built-in object (better version of typeof).
* @credits Angus Croll (http://javascriptweblog.wordpress.com/)
*
* @method typeOf
* @static
* @param {Object} o Object to check.
* @return {String} Object [[Class]]
*/
typeOf: u.Basic.typeOf,
/**
* Extends the specified object with another object.
*
* @method extend
* @static
* @param {Object} target Object to extend.
* @param {Object..} obj Multiple objects to extend with.
* @return {Object} Same as target, the extended object.
*/
extend : u.Basic.extend,
/**
* Generates an unique ID. This is 99.99% unique since it takes the current time and 5 random numbers.
* The only way a user would be able to get the same ID is if the two persons at the same exact millisecond manages
* to get 5 the same random numbers between 0-65535 it also uses a counter so each call will be guaranteed to be page unique.
* It's more probable for the earth to be hit with an asteriod. You can also if you want to be 100% sure set the plupload.guidPrefix property
* to an user unique key.
*
* @method guid
* @static
* @return {String} Virtually unique id.
*/
guid : u.Basic.guid,
/**
* Get array of DOM Elements by their ids.
*
* @method get
* @param {String} id Identifier of the DOM Element
* @return {Array}
*/
getAll : function get(ids) {
var els = [], el;
if (plupload.typeOf(ids) !== 'array') {
ids = [ids];
}
var i = ids.length;
while (i--) {
el = plupload.get(ids[i]);
if (el) {
els.push(el);
}
}
return els.length ? els : null;
},
/**
Get DOM element by id
@method get
@param {String} id Identifier of the DOM Element
@return {Node}
*/
get: u.Dom.get,
/**
* Executes the callback function for each item in array/object. If you return false in the
* callback it will break the loop.
*
* @method each
* @static
* @param {Object} obj Object to iterate.
* @param {function} callback Callback function to execute for each item.
*/
each : u.Basic.each,
/**
* Returns the absolute x, y position of an Element. The position will be returned in a object with x, y fields.
*
* @method getPos
* @static
* @param {Element} node HTML element or element id to get x, y position from.
* @param {Element} root Optional root element to stop calculations at.
* @return {object} Absolute position of the specified element object with x, y fields.
*/
getPos : u.Dom.getPos,
/**
* Returns the size of the specified node in pixels.
*
* @method getSize
* @static
* @param {Node} node Node to get the size of.
* @return {Object} Object with a w and h property.
*/
getSize : u.Dom.getSize,
/**
* Encodes the specified string.
*
* @method xmlEncode
* @static
* @param {String} s String to encode.
* @return {String} Encoded string.
*/
xmlEncode : function(str) {
var xmlEncodeChars = {'<' : 'lt', '>' : 'gt', '&' : 'amp', '"' : 'quot', '\'' : '#39'}, xmlEncodeRegExp = /[<>&\"\']/g;
return str ? ('' + str).replace(xmlEncodeRegExp, function(chr) {
return xmlEncodeChars[chr] ? '&' + xmlEncodeChars[chr] + ';' : chr;
}) : str;
},
/**
* Forces anything into an array.
*
* @method toArray
* @static
* @param {Object} obj Object with length field.
* @return {Array} Array object containing all items.
*/
toArray : u.Basic.toArray,
/**
* Find an element in array and return its index if present, otherwise return -1.
*
* @method inArray
* @static
* @param {mixed} needle Element to find
* @param {Array} array
* @return {Int} Index of the element, or -1 if not found
*/
inArray : u.Basic.inArray,
/**
Recieve an array of functions (usually async) to call in sequence, each function
receives a callback as first argument that it should call, when it completes. Finally,
after everything is complete, main callback is called. Passing truthy value to the
callback as a first argument will interrupt the sequence and invoke main callback
immediately.
@method inSeries
@static
@param {Array} queue Array of functions to call in sequence
@param {Function} cb Main callback that is called in the end, or in case of error
*/
inSeries: u.Basic.inSeries,
/**
* Extends the language pack object with new items.
*
* @method addI18n
* @static
* @param {Object} pack Language pack items to add.
* @return {Object} Extended language pack object.
*/
addI18n : o.core.I18n.addI18n,
/**
* Translates the specified string by checking for the english string in the language pack lookup.
*
* @method translate
* @static
* @param {String} str String to look for.
* @return {String} Translated string or the input string if it wasn't found.
*/
translate : o.core.I18n.translate,
/**
* Pseudo sprintf implementation - simple way to replace tokens with specified values.
*
* @param {String} str String with tokens
* @return {String} String with replaced tokens
*/
sprintf : u.Basic.sprintf,
/**
* Checks if object is empty.
*
* @method isEmptyObj
* @static
* @param {Object} obj Object to check.
* @return {Boolean}
*/
isEmptyObj : u.Basic.isEmptyObj,
/**
* Checks if specified DOM element has specified class.
*
* @method hasClass
* @static
* @param {Object} obj DOM element like object to add handler to.
* @param {String} name Class name
*/
hasClass : u.Dom.hasClass,
/**
* Adds specified className to specified DOM element.
*
* @method addClass
* @static
* @param {Object} obj DOM element like object to add handler to.
* @param {String} name Class name
*/
addClass : u.Dom.addClass,
/**
* Removes specified className from specified DOM element.
*
* @method removeClass
* @static
* @param {Object} obj DOM element like object to add handler to.
* @param {String} name Class name
*/
removeClass : u.Dom.removeClass,
/**
* Returns a given computed style of a DOM element.
*
* @method getStyle
* @static
* @param {Object} obj DOM element like object.
* @param {String} name Style you want to get from the DOM element
*/
getStyle : u.Dom.getStyle,
/**
* Adds an event handler to the specified object and store reference to the handler
* in objects internal Plupload registry (@see removeEvent).
*
* @method addEvent
* @static
* @param {Object} obj DOM element like object to add handler to.
* @param {String} name Name to add event listener to.
* @param {Function} callback Function to call when event occurs.
* @param {String} (optional) key that might be used to add specifity to the event record.
*/
addEvent : u.Events.addEvent,
/**
* Remove event handler from the specified object. If third argument (callback)
* is not specified remove all events with the specified name.
*
* @method removeEvent
* @static
* @param {Object} obj DOM element to remove event listener(s) from.
* @param {String} name Name of event listener to remove.
* @param {Function|String} (optional) might be a callback or unique key to match.
*/
removeEvent: u.Events.removeEvent,
/**
* Remove all kind of events from the specified object
*
* @method removeAllEvents
* @static
* @param {Object} obj DOM element to remove event listeners from.
* @param {String} (optional) unique key to match, when removing events.
*/
removeAllEvents: u.Events.removeAllEvents,
/**
* Cleans the specified name from national characters (diacritics). The result will be a name with only a-z, 0-9 and _.
*
* @method cleanName
* @static
* @param {String} s String to clean up.
* @return {String} Cleaned string.
*/
cleanName : function(name) {
var i, lookup;
// Replace diacritics
lookup = [
/[\300-\306]/g, 'A', /[\340-\346]/g, 'a',
/\307/g, 'C', /\347/g, 'c',
/[\310-\313]/g, 'E', /[\350-\353]/g, 'e',
/[\314-\317]/g, 'I', /[\354-\357]/g, 'i',
/\321/g, 'N', /\361/g, 'n',
/[\322-\330]/g, 'O', /[\362-\370]/g, 'o',
/[\331-\334]/g, 'U', /[\371-\374]/g, 'u'
];
for (i = 0; i < lookup.length; i += 2) {
name = name.replace(lookup[i], lookup[i + 1]);
}
// Replace whitespace
name = name.replace(/\s+/g, '_');
// Remove anything else
name = name.replace(/[^a-z0-9_\-\.]+/gi, '');
return name;
},
/**
* Builds a full url out of a base URL and an object with items to append as query string items.
*
* @method buildUrl
* @static
* @param {String} url Base URL to append query string items to.
* @param {Object} items Name/value object to serialize as a querystring.
* @return {String} String with url + serialized query string items.
*/
buildUrl: function(url, items) {
var query = '';
plupload.each(items, function(value, name) {
query += (query ? '&' : '') + encodeURIComponent(name) + '=' + encodeURIComponent(value);
});
if (query) {
url += (url.indexOf('?') > 0 ? '&' : '?') + query;
}
return url;
},
/**
* Formats the specified number as a size string for example 1024 becomes 1 KB.
*
* @method formatSize
* @static
* @param {Number} size Size to format as string.
* @return {String} Formatted size string.
*/
formatSize : function(size) {
if (size === undef || /\D/.test(size)) {
return plupload.translate('N/A');
}
function round(num, precision) {
return Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision);
}
var boundary = Math.pow(1024, 4);
// TB
if (size > boundary) {
return round(size / boundary, 1) + " " + plupload.translate('tb');
}
// GB
if (size > (boundary/=1024)) {
return round(size / boundary, 1) + " " + plupload.translate('gb');
}
// MB
if (size > (boundary/=1024)) {
return round(size / boundary, 1) + " " + plupload.translate('mb');
}
// KB
if (size > 1024) {
return Math.round(size / 1024) + " " + plupload.translate('kb');
}
return size + " " + plupload.translate('b');
},
/**
* Parses the specified size string into a byte value. For example 10kb becomes 10240.
*
* @method parseSize
* @static
* @param {String|Number} size String to parse or number to just pass through.
* @return {Number} Size in bytes.
*/
parseSize : u.Basic.parseSizeStr,
/**
* A way to predict what runtime will be choosen in the current environment with the
* specified settings.
*
* @method predictRuntime
* @static
* @param {Object|String} config Plupload settings to check
* @param {String} [runtimes] Comma-separated list of runtimes to check against
* @return {String} Type of compatible runtime
*/
predictRuntime : function(config, runtimes) {
var up, runtime;
up = new plupload.Uploader(config);
runtime = Runtime.thatCan(up.getOption().required_features, runtimes || config.runtimes);
up.destroy();
return runtime;
},
/**
* Registers a filter that will be executed for each file added to the queue.
* If callback returns false, file will not be added.
*
* Callback receives two arguments: a value for the filter as it was specified in settings.filters
* and a file to be filtered. Callback is executed in the context of uploader instance.
*
* @method addFileFilter
* @static
* @param {String} name Name of the filter by which it can be referenced in settings.filters
* @param {String} cb Callback - the actual routine that every added file must pass
*/
addFileFilter: function(name, cb) {
fileFilters[name] = cb;
}
};
plupload.addFileFilter('mime_types', function(filters, file, cb) {
if (filters.length && !filters.regexp.test(file.name)) {
this.trigger('Error', {
code : plupload.FILE_EXTENSION_ERROR,
message : plupload.translate('File extension error.'),
file : file
});
cb(false);
} else {
cb(true);
}
});
plupload.addFileFilter('max_file_size', function(maxSize, file, cb) {
var undef;
maxSize = plupload.parseSize(maxSize);
// Invalid file size
if (file.size !== undef && maxSize && file.size > maxSize) {
this.trigger('Error', {
code : plupload.FILE_SIZE_ERROR,
message : plupload.translate('File size error.'),
file : file
});
cb(false);
} else {
cb(true);
}
});
plupload.addFileFilter('prevent_duplicates', function(value, file, cb) {
if (value) {
var ii = this.files.length;
while (ii--) {
// Compare by name and size (size might be 0 or undefined, but still equivalent for both)
if (file.name === this.files[ii].name && file.size === this.files[ii].size) {
this.trigger('Error', {
code : plupload.FILE_DUPLICATE_ERROR,
message : plupload.translate('Duplicate file error.'),
file : file
});
cb(false);
return;
}
}
}
cb(true);
});
/**
@class Uploader
@constructor
@param {Object} settings For detailed information about each option check documentation.
@param {String|DOMElement} settings.browse_button id of the DOM element or DOM element itself to use as file dialog trigger.
@param {Number|String} [settings.chunk_size=0] Chunk size in bytes to slice the file into. Shorcuts with b, kb, mb, gb, tb suffixes also supported. `e.g. 204800 or "204800b" or "200kb"`. By default - disabled.
@param {String|DOMElement} [settings.container] id of the DOM element or DOM element itself that will be used to wrap uploader structures. Defaults to immediate parent of the `browse_button` element.
@param {String|DOMElement} [settings.drop_element] id of the DOM element or DOM element itself to use as a drop zone for Drag-n-Drop.
@param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message.
@param {Object} [settings.filters={}] Set of file type filters.
@param {String|Number} [settings.filters.max_file_size=0] Maximum file size that the user can pick, in bytes. Optionally supports b, kb, mb, gb, tb suffixes. `e.g. "10mb" or "1gb"`. By default - not set. Dispatches `plupload.FILE_SIZE_ERROR`.
@param {Array} [settings.filters.mime_types=[]] List of file types to accept, each one defined by title and list of extensions. `e.g. {title : "Image files", extensions : "jpg,jpeg,gif,png"}`. Dispatches `plupload.FILE_EXTENSION_ERROR`
@param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`.
@param {String} [settings.flash_swf_url] URL of the Flash swf.
@param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs.
@param {String} [settings.http_method="POST"] HTTP method to use during upload (only PUT or POST allowed).
@param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event.
@param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message.
@param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload.
@param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog.
@param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess.
@param {Object} [settings.resize] Enable resizng of images on client-side. Applies to `image/jpeg` and `image/png` only. `e.g. {width : 200, height : 200, quality : 90, crop: true}`
@param {Number} [settings.resize.width] If image is bigger, it will be resized.
@param {Number} [settings.resize.height] If image is bigger, it will be resized.
@param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100).
@param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally.
@param {String} [settings.runtimes="html5,flash,silverlight,html4"] Comma separated list of runtimes, that Plupload will try in turn, moving to the next if previous fails.
@param {String} [settings.silverlight_xap_url] URL of the Silverlight xap.
@param {Boolean} [settings.send_chunk_number=true] Whether to send chunks and chunk numbers, or total and offset bytes.
@param {Boolean} [settings.send_file_name=true] Whether to send file name as additional argument - 'name' (required for chunked uploads and some other cases where file name cannot be sent via normal ways).
@param {String} settings.url URL of the server-side upload handler.
@param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files.
*/
plupload.Uploader = function(options) {
/**
Fires when the current RunTime has been initialized.
@event Init
@param {plupload.Uploader} uploader Uploader instance sending the event.
*/
/**
Fires after the init event incase you need to perform actions there.
@event PostInit
@param {plupload.Uploader} uploader Uploader instance sending the event.
*/
/**
Fires when the option is changed in via uploader.setOption().
@event OptionChanged
@since 2.1
@param {plupload.Uploader} uploader Uploader instance sending the event.
@param {String} name Name of the option that was changed
@param {Mixed} value New value for the specified option
@param {Mixed} oldValue Previous value of the option
*/
/**
Fires when the silverlight/flash or other shim needs to move.
@event Refresh
@param {plupload.Uploader} uploader Uploader instance sending the event.
*/
/**
Fires when the overall state is being changed for the upload queue.
@event StateChanged
@param {plupload.Uploader} uploader Uploader instance sending the event.
*/
/**
Fires when browse_button is clicked and browse dialog shows.
@event Browse
@since 2.1.2
@param {plupload.Uploader} uploader Uploader instance sending the event.
*/
/**
Fires for every filtered file before it is added to the queue.
@event FileFiltered
@since 2.1
@param {plupload.Uploader} uploader Uploader instance sending the event.
@param {plupload.File} file Another file that has to be added to the queue.
*/
/**
Fires when the file queue is changed. In other words when files are added/removed to the files array of the uploader instance.
@event QueueChanged
@param {plupload.Uploader} uploader Uploader instance sending the event.
*/
/**
Fires after files were filtered and added to the queue.
@event FilesAdded
@param {plupload.Uploader} uploader Uploader instance sending the event.
@param {Array} files Array of file objects that were added to queue by the user.
*/
/**
Fires when file is removed from the queue.
@event FilesRemoved
@param {plupload.Uploader} uploader Uploader instance sending the event.
@param {Array} files Array of files that got removed.
*/
/**
Fires just before a file is uploaded. Can be used to cancel the upload for the specified file
by returning false from the handler.
@event BeforeUpload
@param {plupload.Uploader} uploader Uploader instance sending the event.
@param {plupload.File} file File to be uploaded.
*/
/**
Fires when a file is to be uploaded by the runtime.
@event UploadFile
@param {plupload.Uploader} uploader Uploader instance sending the event.
@param {plupload.File} file File to be uploaded.
*/
/**
Fires while a file is being uploaded. Use this event to update the current file upload progress.
@event UploadProgress
@param {plupload.Uploader} uploader Uploader instance sending the event.
@param {plupload.File} file File that is currently being uploaded.
*/
/**
* Fires just before a chunk is uploaded. This event enables you to override settings
* on the uploader instance before the chunk is uploaded.
*
* @event BeforeChunkUpload
* @param {plupload.Uploader} uploader Uploader instance sending the event.
* @param {plupload.File} file File to be uploaded.
* @param {Object} args POST params to be sent.
* @param {Blob} chunkBlob Current blob.
* @param {offset} offset Current offset.
*/
/**
Fires when file chunk is uploaded.
@event ChunkUploaded
@param {plupload.Uploader} uploader Uploader instance sending the event.
@param {plupload.File} file File that the chunk was uploaded for.
@param {Object} result Object with response properties.
@param {Number} result.offset The amount of bytes the server has received so far, including this chunk.
@param {Number} result.total The size of the file.
@param {String} result.response The response body sent by the server.
@param {Number} result.status The HTTP status code sent by the server.
@param {String} result.responseHeaders All the response headers as a single string.
*/
/**
Fires when a file is successfully uploaded.
@event FileUploaded
@param {plupload.Uploader} uploader Uploader instance sending the event.
@param {plupload.File} file File that was uploaded.
@param {Object} result Object with response properties.
@param {String} result.response The response body sent by the server.
@param {Number} result.status The HTTP status code sent by the server.
@param {String} result.responseHeaders All the response headers as a single string.
*/
/**
Fires when all files in a queue are uploaded.
@event UploadComplete
@param {plupload.Uploader} uploader Uploader instance sending the event.
@param {Array} files Array of file objects that was added to queue/selected by the user.
*/
/**
Fires when a error occurs.
@event Error
@param {plupload.Uploader} uploader Uploader instance sending the event.
@param {Object} error Contains code, message and sometimes file and other details.
@param {Number} error.code The plupload error code.
@param {String} error.message Description of the error (uses i18n).
*/
/**
Fires when destroy method is called.
@event Destroy
@param {plupload.Uploader} uploader Uploader instance sending the event.
*/
var uid = plupload.guid()
, settings
, files = []
, preferred_caps = {}
, fileInputs = []
, fileDrops = []
, startTime
, total
, disabled = false
, xhr
;
// Private methods
function uploadNext() {
var file, count = 0, i;
if (this.state == plupload.STARTED) {
// Find first QUEUED file
for (i = 0; i < files.length; i++) {
if (!file && files[i].status == plupload.QUEUED) {
file = files[i];
if (this.trigger("BeforeUpload", file)) {
file.status = plupload.UPLOADING;
this.trigger("UploadFile", file);
}
} else {
count++;
}
}
// All files are DONE or FAILED
if (count == files.length) {
if (this.state !== plupload.STOPPED) {
this.state = plupload.STOPPED;
this.trigger("StateChanged");
}
this.trigger("UploadComplete", files);
}
}
}
function calcFile(file) {
file.percent = file.size > 0 ? Math.ceil(file.loaded / file.size * 100) : 100;
calc();
}
function calc() {
var i, file;
var loaded;
var loadedDuringCurrentSession = 0;
// Reset stats
total.reset();
// Check status, size, loaded etc on all files
for (i = 0; i < files.length; i++) {
file = files[i];
if (file.size !== undef) {
// We calculate totals based on original file size
total.size += file.origSize;
// Since we cannot predict file size after resize, we do opposite and
// interpolate loaded amount to match magnitude of total
loaded = file.loaded * file.origSize / file.size;
if (!file.completeTimestamp || file.completeTimestamp > startTime) {
loadedDuringCurrentSession += loaded;
}
total.loaded += loaded;
} else {
total.size = undef;
}
if (file.status == plupload.DONE) {
total.uploaded++;
} else if (file.status == plupload.FAILED) {
total.failed++;
} else {
total.queued++;
}
}
// If we couldn't calculate a total file size then use the number of files to calc percent
if (total.size === undef) {
total.percent = files.length > 0 ? Math.ceil(total.uploaded / files.length * 100) : 0;
} else {
total.bytesPerSec = Math.ceil(loadedDuringCurrentSession / ((+new Date() - startTime || 1) / 1000.0));
total.percent = total.size > 0 ? Math.ceil(total.loaded / total.size * 100) : 0;
}
}
function getRUID() {
var ctrl = fileInputs[0] || fileDrops[0];
if (ctrl) {
return ctrl.getRuntime().uid;
}
return false;
}
function runtimeCan(file, cap) {
if (file.ruid) {
var info = Runtime.getInfo(file.ruid);
if (info) {
return info.can(cap);
}
}
return false;
}
function bindEventListeners() {
this.bind('FilesAdded FilesRemoved', function(up) {
up.trigger('QueueChanged');
up.refresh();
});
this.bind('CancelUpload', onCancelUpload);
this.bind('BeforeUpload', onBeforeUpload);
this.bind('UploadFile', onUploadFile);
this.bind('UploadProgress', onUploadProgress);
this.bind('StateChanged', onStateChanged);
this.bind('QueueChanged', calc);
this.bind('Error', onError);
this.bind('FileUploaded', onFileUploaded);
this.bind('Destroy', onDestroy);
}
function initControls(settings, cb) {
var self = this, inited = 0, queue = [];
// common settings
var options = {
runtime_order: settings.runtimes,
required_caps: settings.required_features,
preferred_caps: preferred_caps,
swf_url: settings.flash_swf_url,
xap_url: settings.silverlight_xap_url
};
// add runtime specific options if any
plupload.each(settings.runtimes.split(/\s*,\s*/), function(runtime) {
if (settings[runtime]) {
options[runtime] = settings[runtime];
}
});
// initialize file pickers - there can be many
if (settings.browse_button) {
plupload.each(settings.browse_button, function(el) {
queue.push(function(cb) {
var fileInput = new o.file.FileInput(plupload.extend({}, options, {
accept: settings.filters.mime_types,
name: settings.file_data_name,
multiple: settings.multi_selection,
container: settings.container,
browse_button: el
}));
fileInput.onready = function() {
var info = Runtime.getInfo(this.ruid);
// for backward compatibility
plupload.extend(self.features, {
chunks: info.can('slice_blob'),
multipart: info.can('send_multipart'),
multi_selection: info.can('select_multiple')
});
inited++;
fileInputs.push(this);
cb();
};
fileInput.onchange = function() {
self.addFile(this.files);
};
fileInput.bind('mouseenter mouseleave mousedown mouseup', function(e) {
if (!disabled) {
if (settings.browse_button_hover) {
if ('mouseenter' === e.type) {
plupload.addClass(el, settings.browse_button_hover);
} else if ('mouseleave' === e.type) {
plupload.removeClass(el, settings.browse_button_hover);
}
}
if (settings.browse_button_active) {
if ('mousedown' === e.type) {
plupload.addClass(el, settings.browse_button_active);
} else if ('mouseup' === e.type) {
plupload.removeClass(el, settings.browse_button_active);
}
}
}
});
fileInput.bind('mousedown', function() {
self.trigger('Browse');
});
fileInput.bind('error runtimeerror', function() {
fileInput = null;
cb();
});
fileInput.init();
});
});
}
// initialize drop zones
if (settings.drop_element) {
plupload.each(settings.drop_element, function(el) {
queue.push(function(cb) {
var fileDrop = new o.file.FileDrop(plupload.extend({}, options, {
drop_zone: el
}));
fileDrop.onready = function() {
var info = Runtime.getInfo(this.ruid);
// for backward compatibility
plupload.extend(self.features, {
chunks: info.can('slice_blob'),
multipart: info.can('send_multipart'),
dragdrop: info.can('drag_and_drop')
});
inited++;
fileDrops.push(this);
cb();
};
fileDrop.ondrop = function() {
self.addFile(this.files);
};
fileDrop.bind('error runtimeerror', function() {
fileDrop = null;
cb();
});
fileDrop.init();
});
});
}
plupload.inSeries(queue, function() {
if (typeof(cb) === 'function') {
cb(inited);
}
});
}
function resizeImage(blob, params, cb) {
var img = new o.image.Image();
try {
img.onload = function() {
// no manipulation required if...
if (params.width > this.width &&
params.height > this.height &&
params.quality === undef &&
params.preserve_headers &&
!params.crop
) {
this.destroy();
return cb(blob);
}
// otherwise downsize
img.downsize(params.width, params.height, params.crop, params.preserve_headers);
};
img.onresize = function() {
cb(this.getAsBlob(blob.type, params.quality));
this.destroy();
};
img.onerror = function() {
cb(blob);
};
img.load(blob);
} catch(ex) {
cb(blob);
}
}
function setOption(option, value, init) {
var self = this, reinitRequired = false;
function _setOption(option, value, init) {
var oldValue = settings[option];
switch (option) {
case 'max_file_size':
if (option === 'max_file_size') {
settings.max_file_size = settings.filters.max_file_size = value;
}
break;
case 'chunk_size':
if (value = plupload.parseSize(value)) {
settings[option] = value;
settings.send_file_name = true;
}
break;
case 'multipart':
settings[option] = value;
if (!value) {
settings.send_file_name = true;
}
break;
case 'http_method':
settings[option] = value.toUpperCase() === 'PUT' ? 'PUT' : 'POST';
break;
case 'unique_names':
settings[option] = value;
if (value) {
settings.send_file_name = true;
}
break;
case 'filters':
// for sake of backward compatibility
if (plupload.typeOf(value) === 'array') {
value = {
mime_types: value
};
}
if (init) {
plupload.extend(settings.filters, value);
} else {
settings.filters = value;
}
// if file format filters are being updated, regenerate the matching expressions
if (value.mime_types) {
if (plupload.typeOf(value.mime_types) === 'string') {
value.mime_types = o.core.utils.Mime.mimes2extList(value.mime_types);
}
value.mime_types.regexp = (function(filters) {
var extensionsRegExp = [];
plupload.each(filters, function(filter) {
plupload.each(filter.extensions.split(/,/), function(ext) {
if (/^\s*\*\s*$/.test(ext)) {
extensionsRegExp.push('\\.*');
} else {
extensionsRegExp.push('\\.' + ext.replace(new RegExp('[' + ('/^$.*+?|()[]{}\\'.replace(/./g, '\\$&')) + ']', 'g'), '\\$&'));
}
});
});
return new RegExp('(' + extensionsRegExp.join('|') + ')$', 'i');
}(value.mime_types));
settings.filters.mime_types = value.mime_types;
}
break;
case 'resize':
if (value) {
settings.resize = plupload.extend({
preserve_headers: true,
crop: false
}, value);
} else {
settings.resize = false;
}
break;
case 'prevent_duplicates':
settings.prevent_duplicates = settings.filters.prevent_duplicates = !!value;
break;
// options that require reinitialisation
case 'container':
case 'browse_button':
case 'drop_element':
value = 'container' === option
? plupload.get(value)
: plupload.getAll(value)
;
case 'runtimes':
case 'multi_selection':
case 'flash_swf_url':
case 'silverlight_xap_url':
settings[option] = value;
if (!init) {
reinitRequired = true;
}
break;
default:
settings[option] = value;
}
if (!init) {
self.trigger('OptionChanged', option, value, oldValue);
}
}
if (typeof(option) === 'object') {
plupload.each(option, function(value, option) {
_setOption(option, value, init);
});
} else {
_setOption(option, value, init);
}
if (init) {
// Normalize the list of required capabilities
settings.required_features = normalizeCaps(plupload.extend({}, settings));
// Come up with the list of capabilities that can affect default mode in a multi-mode runtimes
preferred_caps = normalizeCaps(plupload.extend({}, settings, {
required_features: true
}));
} else if (reinitRequired) {
self.trigger('Destroy');
initControls.call(self, settings, function(inited) {
if (inited) {
self.runtime = Runtime.getInfo(getRUID()).type;
self.trigger('Init', { runtime: self.runtime });
self.trigger('PostInit');
} else {
self.trigger('Error', {
code : plupload.INIT_ERROR,
message : plupload.translate('Init error.')
});
}
});
}
}
// Internal event handlers
function onBeforeUpload(up, file) {
// Generate unique target filenames
if (up.settings.unique_names) {
var matches = file.name.match(/\.([^.]+)$/), ext = "part";
if (matches) {
ext = matches[1];
}
file.target_name = file.id + '.' + ext;
}
}
function onUploadFile(up, file) {
var url = up.settings.url
, chunkSize = up.settings.chunk_size
, retries = up.settings.max_retries
, features = up.features
, offset = 0
, blob
;
// make sure we start at a predictable offset
if (file.loaded) {
offset = file.loaded = chunkSize ? chunkSize * Math.floor(file.loaded / chunkSize) : 0;
}
function handleError() {
if (retries-- > 0) {
delay(uploadNextChunk, 1000);
} else {
file.loaded = offset; // reset all progress
up.trigger('Error', {
code : plupload.HTTP_ERROR,
message : plupload.translate('HTTP Error.'),
file : file,
response : xhr.responseText,
status : xhr.status,
responseHeaders: xhr.getAllResponseHeaders()
});
}
}
function uploadNextChunk() {
var chunkBlob, args = {}, curChunkSize;
// make sure that file wasn't cancelled and upload is not stopped in general
if (file.status !== plupload.UPLOADING || up.state === plupload.STOPPED) {
return;
}
// send additional 'name' parameter only if required
if (up.settings.send_file_name) {
args.name = file.target_name || file.name;
}
if (chunkSize && features.chunks && blob.size > chunkSize) { // blob will be of type string if it was loaded in memory
curChunkSize = Math.min(chunkSize, blob.size - offset);
chunkBlob = blob.slice(offset, offset + curChunkSize);
} else {
curChunkSize = blob.size;
chunkBlob = blob;
}
// If chunking is enabled add corresponding args, no matter if file is bigger than chunk or smaller
if (chunkSize && features.chunks) {
// Setup query string arguments
if (up.settings.send_chunk_number) {
args.chunk = Math.ceil(offset / chunkSize);
args.chunks = Math.ceil(blob.size / chunkSize);
} else { // keep support for experimental chunk format, just in case
args.offset = offset;
args.total = blob.size;
}
}
if (up.trigger('BeforeChunkUpload', file, args, chunkBlob, offset)) {
uploadChunk(args, chunkBlob, curChunkSize);
}
}
function uploadChunk(args, chunkBlob, curChunkSize) {
var formData;
xhr = new o.xhr.XMLHttpRequest();
// Do we have upload progress support
if (xhr.upload) {
xhr.upload.onprogress = function(e) {
file.loaded = Math.min(file.size, offset + e.loaded);
up.trigger('UploadProgress', file);
};
}
xhr.onload = function() {
// check if upload made itself through
if (xhr.status >= 400) {
handleError();
return;
}
retries = up.settings.max_retries; // reset the counter
// Handle chunk response
if (curChunkSize < blob.size) {
chunkBlob.destroy();
offset += curChunkSize;
file.loaded = Math.min(offset, blob.size);
up.trigger('ChunkUploaded', file, {
offset : file.loaded,
total : blob.size,
response : xhr.responseText,
status : xhr.status,
responseHeaders: xhr.getAllResponseHeaders()
});
// stock Android browser doesn't fire upload progress events, but in chunking mode we can fake them
if (plupload.ua.browser === 'Android Browser') {
// doesn't harm in general, but is not required anywhere else
up.trigger('UploadProgress', file);
}
} else {
file.loaded = file.size;
}
chunkBlob = formData = null; // Free memory
// Check if file is uploaded
if (!offset || offset >= blob.size) {
// If file was modified, destory the copy
if (file.size != file.origSize) {
blob.destroy();
blob = null;
}
up.trigger('UploadProgress', file);
file.status = plupload.DONE;
file.completeTimestamp = +new Date();
up.trigger('FileUploaded', file, {
response : xhr.responseText,
status : xhr.status,
responseHeaders: xhr.getAllResponseHeaders()
});
} else {
// Still chunks left
delay(uploadNextChunk, 1); // run detached, otherwise event handlers interfere
}
};
xhr.onerror = function() {
handleError();
};
xhr.onloadend = function() {
this.destroy();
xhr = null;
};
// Build multipart request
if (up.settings.multipart && features.multipart) {
xhr.open(up.settings.http_method, url, true);
// Set custom headers
plupload.each(up.settings.headers, function(value, name) {
xhr.setRequestHeader(name, value);
});
formData = new o.xhr.FormData();
// Add multipart params
plupload.each(plupload.extend(args, up.settings.multipart_params), function(value, name) {
formData.append(name, value);
});
// Add file and send it
formData.append(up.settings.file_data_name, chunkBlob);
xhr.send(formData, {
runtime_order: up.settings.runtimes,
required_caps: up.settings.required_features,
preferred_caps: preferred_caps,
swf_url: up.settings.flash_swf_url,
xap_url: up.settings.silverlight_xap_url
});
} else {
// if no multipart, send as binary stream
url = plupload.buildUrl(up.settings.url, plupload.extend(args, up.settings.multipart_params));
xhr.open(up.settings.http_method, url, true);
// Set custom headers
plupload.each(up.settings.headers, function(value, name) {
xhr.setRequestHeader(name, value);
});
// do not set Content-Type, if it was defined previously (see #1203)
if (!xhr.hasRequestHeader('Content-Type')) {
xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // Binary stream header
}
xhr.send(chunkBlob, {
runtime_order: up.settings.runtimes,
required_caps: up.settings.required_features,
preferred_caps: preferred_caps,
swf_url: up.settings.flash_swf_url,
xap_url: up.settings.silverlight_xap_url
});
}
}
blob = file.getSource();
// Start uploading chunks
if (!plupload.isEmptyObj(up.settings.resize) && runtimeCan(blob, 'send_binary_string') && plupload.inArray(blob.type, ['image/jpeg', 'image/png']) !== -1) {
// Resize if required
resizeImage.call(this, blob, up.settings.resize, function(resizedBlob) {
blob = resizedBlob;
file.size = resizedBlob.size;
uploadNextChunk();
});
} else {
uploadNextChunk();
}
}
function onUploadProgress(up, file) {
calcFile(file);
}
function onStateChanged(up) {
if (up.state == plupload.STARTED) {
// Get start time to calculate bps
startTime = (+new Date());
} else if (up.state == plupload.STOPPED) {
// Reset currently uploading files
for (var i = up.files.length - 1; i >= 0; i--) {
if (up.files[i].status == plupload.UPLOADING) {
up.files[i].status = plupload.QUEUED;
calc();
}
}
}
}
function onCancelUpload() {
if (xhr) {
xhr.abort();
}
}
function onFileUploaded(up) {
calc();
// Upload next file but detach it from the error event
// since other custom listeners might want to stop the queue
delay(function() {
uploadNext.call(up);
}, 1);
}
function onError(up, err) {
if (err.code === plupload.INIT_ERROR) {
up.destroy();
}
// Set failed status if an error occured on a file
else if (err.code === plupload.HTTP_ERROR) {
err.file.status = plupload.FAILED;
err.file.completeTimestamp = +new Date();
calcFile(err.file);
// Upload next file but detach it from the error event
// since other custom listeners might want to stop the queue
if (up.state == plupload.STARTED) { // upload in progress
up.trigger('CancelUpload');
delay(function() {
uploadNext.call(up);
}, 1);
}
}
}
function onDestroy(up) {
up.stop();
// Purge the queue
plupload.each(files, function(file) {
file.destroy();
});
files = [];
if (fileInputs.length) {
plupload.each(fileInputs, function(fileInput) {
fileInput.destroy();
});
fileInputs = [];
}
if (fileDrops.length) {
plupload.each(fileDrops, function(fileDrop) {
fileDrop.destroy();
});
fileDrops = [];
}
preferred_caps = {};
disabled = false;
startTime = xhr = null;
total.reset();
}
// Default settings
settings = {
chunk_size: 0,
file_data_name: 'file',
filters: {
mime_types: [],
prevent_duplicates: false,
max_file_size: 0
},
flash_swf_url: 'js/Moxie.swf',
http_method: 'POST',
max_retries: 0,
multipart: true,
multi_selection: true,
resize: false,
runtimes: Runtime.order,
send_file_name: true,
send_chunk_number: true,
silverlight_xap_url: 'js/Moxie.xap'
};
setOption.call(this, options, null, true);
// Inital total state
total = new plupload.QueueProgress();
// Add public methods
plupload.extend(this, {
/**
* Unique id for the Uploader instance.
*
* @property id
* @type String
*/
id : uid,
uid : uid, // mOxie uses this to differentiate between event targets
/**
* Current state of the total uploading progress. This one can either be plupload.STARTED or plupload.STOPPED.
* These states are controlled by the stop/start methods. The default value is STOPPED.
*
* @property state
* @type Number
*/
state : plupload.STOPPED,
/**
* Map of features that are available for the uploader runtime. Features will be filled
* before the init event is called, these features can then be used to alter the UI for the end user.
* Some of the current features that might be in this map is: dragdrop, chunks, jpgresize, pngresize.
*
* @property features
* @type Object
*/
features : {},
/**
* Current runtime name.
*
* @property runtime
* @type String
*/
runtime : null,
/**
* Current upload queue, an array of File instances.
*
* @property files
* @type Array
* @see plupload.File
*/
files : files,
/**
* Object with name/value settings.
*
* @property settings
* @type Object
*/
settings : settings,
/**
* Total progess information. How many files has been uploaded, total percent etc.
*
* @property total
* @type plupload.QueueProgress
*/
total : total,
/**
* Initializes the Uploader instance and adds internal event listeners.
*
* @method init
*/
init : function() {
var self = this, opt, preinitOpt, err;
preinitOpt = self.getOption('preinit');
if (typeof(preinitOpt) == "function") {
preinitOpt(self);
} else {
plupload.each(preinitOpt, function(func, name) {
self.bind(name, func);
});
}
bindEventListeners.call(self);
// Check for required options
plupload.each(['container', 'browse_button', 'drop_element'], function(el) {
if (self.getOption(el) === null) {
err = {
code : plupload.INIT_ERROR,
message : plupload.sprintf(plupload.translate("%s specified, but cannot be found."), el)
}
return false;
}
});
if (err) {
return self.trigger('Error', err);
}
if (!settings.browse_button && !settings.drop_element) {
return self.trigger('Error', {
code : plupload.INIT_ERROR,
message : plupload.translate("You must specify either browse_button or drop_element.")
});
}
initControls.call(self, settings, function(inited) {
var initOpt = self.getOption('init');
if (typeof(initOpt) == "function") {
initOpt(self);
} else {
plupload.each(initOpt, function(func, name) {
self.bind(name, func);
});
}
if (inited) {
self.runtime = Runtime.getInfo(getRUID()).type;
self.trigger('Init', { runtime: self.runtime });
self.trigger('PostInit');
} else {
self.trigger('Error', {
code : plupload.INIT_ERROR,
message : plupload.translate('Init error.')
});
}
});
},
/**
* Set the value for the specified option(s).
*
* @method setOption
* @since 2.1
* @param {String|Object} option Name of the option to change or the set of key/value pairs
* @param {Mixed} [value] Value for the option (is ignored, if first argument is object)
*/
setOption: function(option, value) {
setOption.call(this, option, value, !this.runtime); // until runtime not set we do not need to reinitialize
},
/**
* Get the value for the specified option or the whole configuration, if not specified.
*
* @method getOption
* @since 2.1
* @param {String} [option] Name of the option to get
* @return {Mixed} Value for the option or the whole set
*/
getOption: function(option) {
if (!option) {
return settings;
}
return settings[option];
},
/**
* Refreshes the upload instance by dispatching out a refresh event to all runtimes.
* This would for example reposition flash/silverlight shims on the page.
*
* @method refresh
*/
refresh : function() {
if (fileInputs.length) {
plupload.each(fileInputs, function(fileInput) {
fileInput.trigger('Refresh');
});
}
this.trigger('Refresh');
},
/**
* Starts uploading the queued files.
*
* @method start
*/
start : function() {
if (this.state != plupload.STARTED) {
this.state = plupload.STARTED;
this.trigger('StateChanged');
uploadNext.call(this);
}
},
/**
* Stops the upload of the queued files.
*
* @method stop
*/
stop : function() {
if (this.state != plupload.STOPPED) {
this.state = plupload.STOPPED;
this.trigger('StateChanged');
this.trigger('CancelUpload');
}
},
/**
* Disables/enables browse button on request.
*
* @method disableBrowse
* @param {Boolean} disable Whether to disable or enable (default: true)
*/
disableBrowse : function() {
disabled = arguments[0] !== undef ? arguments[0] : true;
if (fileInputs.length) {
plupload.each(fileInputs, function(fileInput) {
fileInput.disable(disabled);
});
}
this.trigger('DisableBrowse', disabled);
},
/**
* Returns the specified file object by id.
*
* @method getFile
* @param {String} id File id to look for.
* @return {plupload.File} File object or undefined if it wasn't found;
*/
getFile : function(id) {
var i;
for (i = files.length - 1; i >= 0; i--) {
if (files[i].id === id) {
return files[i];
}
}
},
/**
* Adds file to the queue programmatically. Can be native file, instance of Plupload.File,
* instance of mOxie.File, input[type="file"] element, or array of these. Fires FilesAdded,
* if any files were added to the queue. Otherwise nothing happens.
*
* @method addFile
* @since 2.0
* @param {plupload.File|mOxie.File|File|Node|Array} file File or files to add to the queue.
* @param {String} [fileName] If specified, will be used as a name for the file
*/
addFile : function(file, fileName) {
var self = this
, queue = []
, filesAdded = []
, ruid
;
function filterFile(file, cb) {
var queue = [];
plupload.each(self.settings.filters, function(rule, name) {
if (fileFilters[name]) {
queue.push(function(cb) {
fileFilters[name].call(self, rule, file, function(res) {
cb(!res);
});
});
}
});
plupload.inSeries(queue, cb);
}
/**
* @method resolveFile
* @private
* @param {moxie.file.File|moxie.file.Blob|plupload.File|File|Blob|input[type="file"]} file
*/
function resolveFile(file) {
var type = plupload.typeOf(file);
// moxie.file.File
if (file instanceof o.file.File) {
if (!file.ruid && !file.isDetached()) {
if (!ruid) { // weird case
return false;
}
file.ruid = ruid;
file.connectRuntime(ruid);
}
resolveFile(new plupload.File(file));
}
// moxie.file.Blob
else if (file instanceof o.file.Blob) {
resolveFile(file.getSource());
file.destroy();
}
// plupload.File - final step for other branches
else if (file instanceof plupload.File) {
if (fileName) {
file.name = fileName;
}
queue.push(function(cb) {
// run through the internal and user-defined filters, if any
filterFile(file, function(err) {
if (!err) {
// make files available for the filters by updating the main queue directly
files.push(file);
// collect the files that will be passed to FilesAdded event
filesAdded.push(file);
self.trigger("FileFiltered", file);
}
delay(cb, 1); // do not build up recursions or eventually we might hit the limits
});
});
}
// native File or blob
else if (plupload.inArray(type, ['file', 'blob']) !== -1) {
resolveFile(new o.file.File(null, file));
}
// input[type="file"]
else if (type === 'node' && plupload.typeOf(file.files) === 'filelist') {
// if we are dealing with input[type="file"]
plupload.each(file.files, resolveFile);
}
// mixed array of any supported types (see above)
else if (type === 'array') {
fileName = null; // should never happen, but unset anyway to avoid funny situations
plupload.each(file, resolveFile);
}
}
ruid = getRUID();
resolveFile(file);
if (queue.length) {
plupload.inSeries(queue, function() {
// if any files left after filtration, trigger FilesAdded
if (filesAdded.length) {
self.trigger("FilesAdded", filesAdded);
}
});
}
},
/**
* Removes a specific file.
*
* @method removeFile
* @param {plupload.File|String} file File to remove from queue.
*/
removeFile : function(file) {
var id = typeof(file) === 'string' ? file : file.id;
for (var i = files.length - 1; i >= 0; i--) {
if (files[i].id === id) {
return this.splice(i, 1)[0];
}
}
},
/**
* Removes part of the queue and returns the files removed. This will also trigger the FilesRemoved and QueueChanged events.
*
* @method splice
* @param {Number} start (Optional) Start index to remove from.
* @param {Number} length (Optional) Lengh of items to remove.
* @return {Array} Array of files that was removed.
*/
splice : function(start, length) {
// Splice and trigger events
var removed = files.splice(start === undef ? 0 : start, length === undef ? files.length : length);
// if upload is in progress we need to stop it and restart after files are removed
var restartRequired = false;
if (this.state == plupload.STARTED) { // upload in progress
plupload.each(removed, function(file) {
if (file.status === plupload.UPLOADING) {
restartRequired = true; // do not restart, unless file that is being removed is uploading
return false;
}
});
if (restartRequired) {
this.stop();
}
}
this.trigger("FilesRemoved", removed);
// Dispose any resources allocated by those files
plupload.each(removed, function(file) {
file.destroy();
});
if (restartRequired) {
this.start();
}
return removed;
},
/**
Dispatches the specified event name and its arguments to all listeners.
@method trigger
@param {String} name Event name to fire.
@param {Object..} Multiple arguments to pass along to the listener functions.
*/
// override the parent method to match Plupload-like event logic
dispatchEvent: function(type) {
var list, args, result;
type = type.toLowerCase();
list = this.hasEventListener(type);
if (list) {
// sort event list by priority
list.sort(function(a, b) { return b.priority - a.priority; });
// first argument should be current plupload.Uploader instance
args = [].slice.call(arguments);
args.shift();
args.unshift(this);
for (var i = 0; i < list.length; i++) {
// Fire event, break chain if false is returned
if (list[i].fn.apply(list[i].scope, args) === false) {
return false;
}
}
}
return true;
},
/**
Check whether uploader has any listeners to the specified event.
@method hasEventListener
@param {String} name Event name to check for.
*/
/**
Adds an event listener by name.
@method bind
@param {String} name Event name to listen for.
@param {function} fn Function to call ones the event gets fired.
@param {Object} [scope] Optional scope to execute the specified function in.
@param {Number} [priority=0] Priority of the event handler - handlers with higher priorities will be called first
*/
bind: function(name, fn, scope, priority) {
// adapt moxie EventTarget style to Plupload-like
plupload.Uploader.prototype.bind.call(this, name, fn, priority, scope);
},
/**
Removes the specified event listener.
@method unbind
@param {String} name Name of event to remove.
@param {function} fn Function to remove from listener.
*/
/**
Removes all event listeners.
@method unbindAll
*/
/**
* Destroys Plupload instance and cleans after itself.
*
* @method destroy
*/
destroy : function() {
this.trigger('Destroy');
settings = total = null; // purge these exclusively
this.unbindAll();
}
});
};
plupload.Uploader.prototype = o.core.EventTarget.instance;
/**
* Constructs a new file instance.
*
* @class File
* @constructor
*
* @param {Object} file Object containing file properties
* @param {String} file.name Name of the file.
* @param {Number} file.size File size.
*/
plupload.File = (function() {
var filepool = {};
function PluploadFile(file) {
plupload.extend(this, {
/**
* File id this is a globally unique id for the specific file.
*
* @property id
* @type String
*/
id: plupload.guid(),
/**
* File name for example "myfile.gif".
*
* @property name
* @type String
*/
name: file.name || file.fileName,
/**
* File type, `e.g image/jpeg`
*
* @property type
* @type String
*/
type: file.type || '',
/**
* File size in bytes (may change after client-side manupilation).
*
* @property size
* @type Number
*/
size: file.size || file.fileSize,
/**
* Original file size in bytes.
*
* @property origSize
* @type Number
*/
origSize: file.size || file.fileSize,
/**
* Number of bytes uploaded of the files total size.
*
* @property loaded
* @type Number
*/
loaded: 0,
/**
* Number of percentage uploaded of the file.
*
* @property percent
* @type Number
*/
percent: 0,
/**
* Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE.
*
* @property status
* @type Number
* @see plupload
*/
status: plupload.QUEUED,
/**
* Date of last modification.
*
* @property lastModifiedDate
* @type {String}
*/
lastModifiedDate: file.lastModifiedDate || (new Date()).toLocaleString(), // Thu Aug 23 2012 19:40:00 GMT+0400 (GET)
/**
* Set when file becomes plupload.DONE or plupload.FAILED. Is used to calculate proper plupload.QueueProgress.bytesPerSec.
* @private
* @property completeTimestamp
* @type {Number}
*/
completeTimestamp: 0,
/**
* Returns native window.File object, when it's available.
*
* @method getNative
* @return {window.File} or null, if plupload.File is of different origin
*/
getNative: function() {
var file = this.getSource().getSource();
return plupload.inArray(plupload.typeOf(file), ['blob', 'file']) !== -1 ? file : null;
},
/**
* Returns mOxie.File - unified wrapper object that can be used across runtimes.
*
* @method getSource
* @return {mOxie.File} or null
*/
getSource: function() {
if (!filepool[this.id]) {
return null;
}
return filepool[this.id];
},
/**
* Destroys plupload.File object.
*
* @method destroy
*/
destroy: function() {
var src = this.getSource();
if (src) {
src.destroy();
delete filepool[this.id];
}
}
});
filepool[this.id] = file;
}
return PluploadFile;
}());
/**
* Constructs a queue progress.
*
* @class QueueProgress
* @constructor
*/
plupload.QueueProgress = function() {
var self = this; // Setup alias for self to reduce code size when it's compressed
/**
* Total queue file size.
*
* @property size
* @type Number
*/
self.size = 0;
/**
* Total bytes uploaded.
*
* @property loaded
* @type Number
*/
self.loaded = 0;
/**
* Number of files uploaded.
*
* @property uploaded
* @type Number
*/
self.uploaded = 0;
/**
* Number of files failed to upload.
*
* @property failed
* @type Number
*/
self.failed = 0;
/**
* Number of files yet to be uploaded.
*
* @property queued
* @type Number
*/
self.queued = 0;
/**
* Total percent of the uploaded bytes.
*
* @property percent
* @type Number
*/
self.percent = 0;
/**
* Bytes uploaded per second.
*
* @property bytesPerSec
* @type Number
*/
self.bytesPerSec = 0;
/**
* Resets the progress to its initial values.
*
* @method reset
*/
self.reset = function() {
self.size = self.loaded = self.uploaded = self.failed = self.queued = self.percent = self.bytesPerSec = 0;
};
};
exports.plupload = plupload;
}(this, moxie));
}));
\ No newline at end of file
/*!
* qiniu-js-sdk v@VERSION
*
* Copyright 2015 by Qiniu
* Released under GPL V2 License.
*
* GitHub: http://github.com/qiniu/js-sdk
*
* Date: @DATE
*/
/*global plupload ,moxie*/
/*global ActiveXObject */
/*exported Qiniu */
/*exported QiniuJsSDK */
;(function (global) {
/**
* Creates new cookie or removes cookie with negative expiration
* @param key The key or identifier for the store
* @param value Contents of the store
* @param exp Expiration - creation defaults to 30 days
*/
function createCookie(key, value, exp) {
var date = new Date();
date.setTime(date.getTime() + (exp * 24 * 60 * 60 * 1000));
var expires = "; expires=" + date.toGMTString();
document.cookie = key + "=" + value + expires + "; path=/";
}
/**
* Returns contents of cookie
* @param key The key or identifier for the store
*/
function readCookie(key) {
var nameEQ = key + "=";
var ca = document.cookie.split(';');
for (var i = 0, max = ca.length; i < max; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) === 0) {
return c.substring(nameEQ.length, c.length);
}
}
return null;
}
// if current browser is not support localStorage
// use cookie to make a polyfill
if (!window.localStorage) {
window.localStorage = {
setItem: function (key, value) {
createCookie(key, value, 30);
},
getItem: function (key) {
return readCookie(key);
},
removeItem: function (key) {
createCookie(key, '', -1);
}
};
}
function QiniuJsSDK() {
var moxie = require('./plupload/moxie');
window.moxie = moxie;
var plupload = require('./plupload/plupload.dev');
window.plupload = plupload;
var that = this;
/**
* detect IE version
* if current browser is not IE
* it will return false
* else
* it will return version of current IE browser
* @return {Number|Boolean} IE version or false
*/
this.detectIEVersion = function () {
var v = 4,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while (
div.innerHTML = '<!--[if gt IE ' + v + ']><i></i><![endif]-->',
all[0]
) {
v++;
}
return v > 4 ? v : false;
};
var logger = {
MUTE: 0,
FATA: 1,
ERROR: 2,
WARN: 3,
INFO: 4,
DEBUG: 5,
TRACE: 6,
level: 0
};
function log(type, args) {
var header = "[qiniu-js-sdk][" + type + "]";
var msg = header;
for (var i = 0; i < args.length; i++) {
if (typeof args[i] === "string") {
msg += " " + args[i];
} else {
msg += " " + that.stringifyJSON(args[i]);
}
}
if (that.detectIEVersion()) {
// http://stackoverflow.com/questions/5538972/console-log-apply-not-working-in-ie9
//var log = Function.prototype.bind.call(console.log, console);
//log.apply(console, args);
console.log(msg);
} else {
args.unshift(header);
console.log.apply(console, args);
}
if (document.getElementById('qiniu-js-sdk-log')) {
document.getElementById('qiniu-js-sdk-log').innerHTML += '<p>' + msg + '</p>';
}
}
function makeLogFunc(code) {
var func = code.toLowerCase();
logger[func] = function () {
// logger[func].history = logger[func].history || [];
// logger[func].history.push(arguments);
if (window.console && window.console.log && logger.level >= logger[code]) {
var args = Array.prototype.slice.call(arguments);
log(func, args);
}
};
}
for (var property in logger) {
if (logger.hasOwnProperty(property) && (typeof logger[property]) === "number" && !logger.hasOwnProperty(property.toLowerCase())) {
makeLogFunc(property);
}
}
var qiniuUploadUrl;
if (window.location.protocol === 'https:') {
qiniuUploadUrl = 'https://upload.qiniup.com';
} else {
// qiniuUploadUrl = 'http://upload.qiniup.com';
qiniuUploadUrl = 'https://upload.qiniup.com';
}
/**
* qiniu upload urls
* 'qiniuUploadUrls' is used to change target when current url is not avaliable
* @type {Array}
*/
/*var qiniuUploadUrls = [
"http://upload.qiniup.com",
"http://up.qiniup.com"
];*/
var qiniuUploadUrls = [
"https://upload.qiniup.com",
"https://upload.qiniup.com"
];
/*var qiniuUpHosts = {
"http": [
"http://upload.qiniup.com",
"http://up.qiniup.com"
],
"https": [
"https://upload.qiniup.com"
]
};*/
var qiniuUpHosts = {
"http": [
"https://upload.qiniup.com",
"https://upload.qiniup.com"
],
"https": [
"https://upload.qiniup.com"
]
};
var changeUrlTimes = 0;
function StatisticsLogger() {
// api to collect upload logs
var qiniuCollectUploadLogUrl = "https://uplog.qbox.me/log/3";
/**
* { log: string, status: number }[] status: 0 待处理, 1 正在发送, 2 发送完毕
*/
var queue = [];
var TaskStatus = {
waiting: 0,
processing: 1,
finished: 2
};
/**
* send logs to statistics server
*
* @param {number} code status code
* @param {string} req_id request id
* @param {string} host
* @param {string} remote_ip
* @param {string} port
* @param {string} duration
* @param {string} up_time
* @param {number} bytes_sent uploaded size (bytes)
* @param {string} up_type js sdk runtime: html5, html4, flash
* @param {number} file_size file total size (bytes)
*/
this.log = function (code, req_id, host, remote_ip, port, duration, up_time, bytes_sent, up_type, file_size) {
var log = Array.prototype.join.call(arguments, ',');
queue.push({
log: log,
status: TaskStatus.waiting
});
logger.debug("[STATISTICS] send log to statistics server", log);
};
function tick() {
var unFinishedTasks = [];
for (var i = 0; i < queue.length; i++) {
if (queue[i].status !== TaskStatus.finished) {
unFinishedTasks.push(queue[i]);
}
if (queue[i].status === TaskStatus.waiting) {
send(queue[i]);
}
}
queue = unFinishedTasks;
}
function send(task) {
task.status = TaskStatus.processing;
var ajax = that.createAjax();
ajax.open('POST', qiniuCollectUploadLogUrl, true);
ajax.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
ajax.setRequestHeader('Authorization', 'UpToken ' + that.token);
ajax.onreadystatechange = function () {
if (ajax.readyState === 4) {
if (ajax.status === 200) {
logger.debug("[STATISTICS] successfully report log to server");
task.status = TaskStatus.finished;
} else {
logger.debug("[STATISTICS] report log to server failed");
task.status = TaskStatus.waiting;
}
}
};
ajax.send(task.log);
}
// start a timer to report
setInterval(tick, 1000);
}
var statisticsLogger = new StatisticsLogger();
var ExtraErrors = {
ZeroSizeFile: -6,
InvalidToken: -5,
InvalidArgument: -4,
InvalidFile: -3,
Cancelled: -2,
NetworkError: -1,
UnknownError: 0,
TimedOut: -1001,
UnknownHost: -1003,
CannotConnectToHost: -1004,
NetworkConnectionLost: -1005
};
/**
* reset upload url
* if current page protocal is https
* it will always return 'https://up.qbox.me'
* else
* it will set 'qiniuUploadUrl' value with 'qiniuUploadUrls' looply
*/
this.resetUploadUrl = function (num) {
logger.debug('num: ' + num);
if( num == 0) {
logger.debug("use main uphost");
var hosts = qiniuUpHosts.main;
qiniuUploadUrl = window.location.protocol === 'https:' ? "https://" + hosts[0] : "http://" + hosts[0];
} else {
logger.debug("use backup uphost");
var hosts = qiniuUpHosts.backup;
if( num % 2 == 0) {
qiniuUploadUrl = window.location.protocol === 'https:' ? "https://" + hosts[1] : "http://" + hosts[1];
} else {
qiniuUploadUrl = window.location.protocol === 'https:' ? "https://" + hosts[0] : "http://" + hosts[0];
}
}
//qiniuUploadUrl = window.location.protocol === 'https:' ? "https://" + hosts[0] : "http://" + hosts[0];
logger.debug('resetUploadUrl: ' + qiniuUploadUrl);
};
// this.resetUploadUrl();
/**
* is image
* @param {String} url of a file
* @return {Boolean} file is a image or not
*/
this.isImage = function (url) {
url = url.split(/[?#]/)[0];
return (/\.(png|jpg|jpeg|gif|bmp)$/i).test(url);
};
/**
* get file extension
* @param {String} filename
* @return {String} file extension
* @example
* input: test.txt
* output: txt
*/
this.getFileExtension = function (filename) {
var tempArr = filename.split(".");
var ext;
if (tempArr.length === 1 || (tempArr[0] === "" && tempArr.length === 2)) {
ext = "";
} else {
ext = tempArr.pop().toLowerCase(); //get the extension and make it lower-case
}
return ext;
};
/**
* encode string by utf8
* @param {String} string to encode
* @return {String} encoded string
*/
this.utf8_encode = function (argString) {
// http://kevin.vanzonneveld.net
// + original by: Webtoolkit.info (http://www.webtoolkit.info/)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: sowberry
// + tweaked by: Jack
// + bugfixed by: Onno Marsman
// + improved by: Yves Sucaet
// + bugfixed by: Onno Marsman
// + bugfixed by: Ulrich
// + bugfixed by: Rafal Kukawski
// + improved by: kirilloid
// + bugfixed by: kirilloid
// * example 1: this.utf8_encode('Kevin van Zonneveld');
// * returns 1: 'Kevin van Zonneveld'
if (argString === null || typeof argString === 'undefined') {
return '';
}
var string = (argString + ''); // .replace(/\r\n/g, '\n').replace(/\r/g, '\n');
var utftext = '',
start, end, stringl = 0;
start = end = 0;
stringl = string.length;
for (var n = 0; n < stringl; n++) {
var c1 = string.charCodeAt(n);
var enc = null;
if (c1 < 128) {
end++;
} else if (c1 > 127 && c1 < 2048) {
enc = String.fromCharCode(
(c1 >> 6) | 192, (c1 & 63) | 128
);
} else if (c1 & 0xF800 ^ 0xD800 > 0) {
enc = String.fromCharCode(
(c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
);
} else { // surrogate pairs
if (c1 & 0xFC00 ^ 0xD800 > 0) {
throw new RangeError('Unmatched trail surrogate at ' + n);
}
var c2 = string.charCodeAt(++n);
if (c2 & 0xFC00 ^ 0xDC00 > 0) {
throw new RangeError('Unmatched lead surrogate at ' + (n - 1));
}
c1 = ((c1 & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000;
enc = String.fromCharCode(
(c1 >> 18) | 240, ((c1 >> 12) & 63) | 128, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128
);
}
if (enc !== null) {
if (end > start) {
utftext += string.slice(start, end);
}
utftext += enc;
start = end = n + 1;
}
}
if (end > start) {
utftext += string.slice(start, stringl);
}
return utftext;
};
this.base64_decode = function (data) {
// http://kevin.vanzonneveld.net
// + original by: Tyler Akins (http://rumkin.com)
// + improved by: Thunder.m
// + input by: Aman Gupta
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Onno Marsman
// + bugfixed by: Pellentesque Malesuada
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + input by: Brett Zamir (http://brett-zamir.me)
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
// * returns 1: 'Kevin van Zonneveld'
// mozilla has this native
// - but breaks in 2.0.0.12!
//if (typeof this.window['atob'] == 'function') {
// return atob(data);
//}
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
ac = 0,
dec = "",
tmp_arr = [];
if (!data) {
return data;
}
data += '';
do { // unpack four hexets into three octets using index points in b64
h1 = b64.indexOf(data.charAt(i++));
h2 = b64.indexOf(data.charAt(i++));
h3 = b64.indexOf(data.charAt(i++));
h4 = b64.indexOf(data.charAt(i++));
bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
o1 = bits >> 16 & 0xff;
o2 = bits >> 8 & 0xff;
o3 = bits & 0xff;
if (h3 === 64) {
tmp_arr[ac++] = String.fromCharCode(o1);
} else if (h4 === 64) {
tmp_arr[ac++] = String.fromCharCode(o1, o2);
} else {
tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
}
} while (i < data.length);
dec = tmp_arr.join('');
return dec;
};
/**
* encode data by base64
* @param {String} data to encode
* @return {String} encoded data
*/
this.base64_encode = function (data) {
// http://kevin.vanzonneveld.net
// + original by: Tyler Akins (http://rumkin.com)
// + improved by: Bayron Guevara
// + improved by: Thunder.m
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Pellentesque Malesuada
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// - depends on: this.utf8_encode
// * example 1: this.base64_encode('Kevin van Zonneveld');
// * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
// mozilla has this native
// - but breaks in 2.0.0.12!
//if (typeof this.window['atob'] == 'function') {
// return atob(data);
//}
var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
ac = 0,
enc = '',
tmp_arr = [];
if (!data) {
return data;
}
data = this.utf8_encode(data + '');
do { // pack three octets into four hexets
o1 = data.charCodeAt(i++);
o2 = data.charCodeAt(i++);
o3 = data.charCodeAt(i++);
bits = o1 << 16 | o2 << 8 | o3;
h1 = bits >> 18 & 0x3f;
h2 = bits >> 12 & 0x3f;
h3 = bits >> 6 & 0x3f;
h4 = bits & 0x3f;
// use hexets to index into b64, and append result to encoded string
tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
} while (i < data.length);
enc = tmp_arr.join('');
switch (data.length % 3) {
case 1:
enc = enc.slice(0, -2) + '==';
break;
case 2:
enc = enc.slice(0, -1) + '=';
break;
}
return enc;
};
/**
* encode string in url by base64
* @param {String} string in url
* @return {String} encoded string
*/
this.URLSafeBase64Encode = function (v) {
v = this.base64_encode(v);
return v.replace(/\//g, '_').replace(/\+/g, '-');
};
this.URLSafeBase64Decode = function (v) {
v = v.replace(/_/g, '/').replace(/-/g, '+');
return this.base64_decode(v);
};
// TODO: use mOxie
/**
* craete object used to AJAX
* @return {Object}
*/
this.createAjax = function (argument) {
var xmlhttp = {};
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
return xmlhttp;
};
// TODO: enhance IE compatibility
/**
* parse json string to javascript object
* @param {String} json string
* @return {Object} object
*/
this.parseJSON = function (data) {
// Attempt to parse using the native JSON parser first
if (window.JSON && window.JSON.parse) {
return window.JSON.parse(data);
}
//var rx_one = /^[\],:{}\s]*$/,
// rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
// rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
// rx_four = /(?:^|:|,)(?:\s*\[)+/g,
var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
//var json;
var text = String(data);
rx_dangerous.lastIndex = 0;
if (rx_dangerous.test(text)) {
text = text.replace(rx_dangerous, function (a) {
return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// todo 使用一下判断,增加安全性
//if (
// rx_one.test(
// text
// .replace(rx_two, '@')
// .replace(rx_three, ']')
// .replace(rx_four, '')
// )
//) {
// return eval('(' + text + ')');
//}
return eval('(' + text + ')');
};
/**
* parse javascript object to json string
* @param {Object} object
* @return {String} json string
*/
this.stringifyJSON = function (obj) {
// Attempt to parse using the native JSON parser first
if (window.JSON && window.JSON.stringify) {
return window.JSON.stringify(obj);
}
switch (typeof (obj)) {
case 'string':
return '"' + obj.replace(/(["\\])/g, '\\$1') + '"';
case 'array':
return '[' + obj.map(that.stringifyJSON).join(',') + ']';
case 'object':
if (obj instanceof Array) {
var strArr = [];
var len = obj.length;
for (var i = 0; i < len; i++) {
strArr.push(that.stringifyJSON(obj[i]));
}
return '[' + strArr.join(',') + ']';
} else if (obj === null) {
return 'null';
} else {
var string = [];
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
string.push(that.stringifyJSON(property) + ':' + that.stringifyJSON(obj[property]));
}
}
return '{' + string.join(',') + '}';
}
break;
case 'number':
return obj;
case false:
return obj;
case 'boolean':
return obj;
}
};
/**
* trim space beside text
* @param {String} untrimed string
* @return {String} trimed string
*/
this.trim = function (text) {
return text === null ? "" : text.replace(/^\s+|\s+$/g, '');
};
/**
* create a uploader by QiniuJsSDK
* @param {object} options to create a new uploader
* @return {object} uploader
*/
this.uploader = function (op) {
/********** inner function define start **********/
// according the different condition to reset chunk size
// and the upload strategy according with the chunk size
// when chunk size is zero will cause to direct upload
// see the statement binded on 'BeforeUpload' event
var reset_chunk_size = function () {
var ie = that.detectIEVersion();
var BLOCK_BITS, MAX_CHUNK_SIZE, chunk_size;
// case Safari 5、Windows 7、iOS 7 set isSpecialSafari to true
var isSpecialSafari = (moxie.core.utils.Env.browser === "Safari" && moxie.core.utils.Env.version <= 5 && moxie.core.utils.Env.os === "Windows" && moxie.core.utils.Env.osVersion === "7") || (moxie.core.utils.Env.browser === "Safari" && moxie.core.utils.Env.os === "iOS" && moxie.core.utils.Env.osVersion === "7");
// case IE 9-,chunk_size is not empty and flash is included in runtimes
// set op.chunk_size to zero
//if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) {
if (ie && ie < 9 && op.chunk_size && op.runtimes.indexOf('flash') >= 0) {
// link: http://www.plupload.com/docs/Frequently-Asked-Questions#when-to-use-chunking-and-when-not
// when plupload chunk_size setting is't null ,it cause bug in ie8/9 which runs flash runtimes (not support html5) .
op.chunk_size = 0;
} else if (isSpecialSafari) {
// win7 safari / iOS7 safari have bug when in chunk upload mode
// reset chunk_size to 0
// disable chunk in special version safari
op.chunk_size = 0;
} else {
BLOCK_BITS = 20;
MAX_CHUNK_SIZE = 4 << BLOCK_BITS; //4M
chunk_size = plupload.parseSize(op.chunk_size);
if (chunk_size > MAX_CHUNK_SIZE) {
op.chunk_size = MAX_CHUNK_SIZE;
}
// qiniu service max_chunk_size is 4m
// reset chunk_size to max_chunk_size(4m) when chunk_size > 4m
}
// if op.chunk_size set 0 will be cause to direct upload
};
var getHosts = function (hosts) {
var result = [];
var uploadIndex = -1;
for (var i = 0; i < hosts.length; i++) {
var host = hosts[i];
if (host.indexOf("upload") !== -1) {
uploadIndex = i;
}
if (host.indexOf('-H') === 0) {
result.push(host.split(' ')[2]);
} else {
result.push(host);
}
}
if (uploadIndex !== -1) {
//make upload domains first
var uploadDomain = result[uploadIndex];
result[uploadIndex] = result[0];
result[0] = uploadDomain;
}
return result;
};
var getPutPolicy = function (uptoken) {
var segments = uptoken.split(":");
var ak = segments[0];
var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2]));
putPolicy.ak = ak;
if (putPolicy.scope.indexOf(":") >= 0) {
putPolicy.bucket = putPolicy.scope.split(":")[0];
putPolicy.key = putPolicy.scope.split(":")[1];
} else {
putPolicy.bucket = putPolicy.scope;
}
return putPolicy;
};
var getUpHosts = function (uptoken) {
var putPolicy = getPutPolicy(uptoken);
// var uphosts_url = "//uc.qbox.me/v1/query?ak="+ak+"&bucket="+putPolicy.scope;
// IE9 does not support protocol relative url
var uphosts_url = window.location.protocol + "//api.qiniu.com/v2/query?ak=" + putPolicy.ak + "&bucket=" + putPolicy.bucket;
logger.debug("putPolicy: ", putPolicy);
logger.debug("get uphosts from: ", uphosts_url);
var ie = that.detectIEVersion();
var ajax;
if (ie && ie <= 9) {
ajax = new moxie.xhr.XMLHttpRequest();
moxie.core.utils.Env.swf_url = op.flash_swf_url;
} else {
ajax = that.createAjax();
}
ajax.open('GET', uphosts_url, false);
var onreadystatechange = function () {
logger.debug("ajax.readyState: ", ajax.readyState);
if (ajax.readyState === 4) {
logger.debug("ajax.status: ", ajax.status);
if (ajax.status < 400) {
var res = that.parseJSON(ajax.responseText);
qiniuUpHosts.main = res.up.acc.main;
qiniuUpHosts.backup = res.up.acc.backup;
logger.debug("get new uphosts: ", qiniuUpHosts);
that.resetUploadUrl(0);
} else {
logger.error("get uphosts error: ", ajax.responseText);
}
}
};
if (ie && ie <= 9) {
ajax.bind('readystatechange', onreadystatechange);
} else {
ajax.onreadystatechange = onreadystatechange;
}
ajax.send();
// ajax.send();
// if (ajax.status < 400) {
// var res = that.parseJSON(ajax.responseText);
// qiniuUpHosts.http = getHosts(res.http.up);
// qiniuUpHosts.https = getHosts(res.https.up);
// logger.debug("get new uphosts: ", qiniuUpHosts);
// that.resetUploadUrl();
// } else {
// logger.error("get uphosts error: ", ajax.responseText);
// }
return;
};
var getUptoken = function (file) {
if (!that.token || (op.uptoken_url && that.tokenInfo.isExpired())) {
return getNewUpToken(file);
} else {
return that.token;
}
};
// getNewUptoken maybe called at Init Event or BeforeUpload Event
// case Init Event, the file param of getUptken will be set a null value
// if op.uptoken has value, set uptoken with op.uptoken
// else if op.uptoken_url has value, set uptoken from op.uptoken_url
// else if op.uptoken_func has value, set uptoken by result of op.uptoken_func
var getNewUpToken = function (file) {
if (op.uptoken) {
that.token = op.uptoken;
} else if (op.uptoken_url) {
logger.debug("get uptoken from: ", that.uptoken_url);
// TODO: use mOxie
var ajax = that.createAjax();
ajax.open('GET', that.uptoken_url, false);
// ajax.setRequestHeader("If-Modified-Since", "0");
// ajax.onreadystatechange = function() {
// if (ajax.readyState === 4 && ajax.status === 200) {
// var res = that.parseJSON(ajax.responseText);
// that.token = res.uptoken;
// }
// };
ajax.send();
if (ajax.status === 200) {
var res = that.parseJSON(ajax.responseText);
that.token = res.uptoken;
var segments = that.token.split(":");
var putPolicy = that.parseJSON(that.URLSafeBase64Decode(segments[2]));
if (!that.tokenMap) {
that.tokenMap = {};
}
var getTimestamp = function (time) {
return Math.ceil(time.getTime() / 1000);
};
var serverTime = getTimestamp(new Date(ajax.getResponseHeader("date")));
var clientTime = getTimestamp(new Date());
that.tokenInfo = {
serverDelay: clientTime - serverTime,
deadline: putPolicy.deadline,
isExpired: function () {
var leftTime = this.deadline - getTimestamp(new Date()) + this.serverDelay;
return leftTime < 600;
}
};
logger.debug("get new uptoken: ", that.token);
logger.debug("get token info: ", that.tokenInfo);
} else {
logger.error("get uptoken error: ", ajax.responseText);
}
} else if (op.uptoken_func) {
logger.debug("get uptoken from uptoken_func");
that.token = op.uptoken_func(file);
logger.debug("get new uptoken: ", that.token);
} else {
logger.error("one of [uptoken, uptoken_url, uptoken_func] settings in options is required!");
}
if (that.token) {
getUpHosts(that.token);
}
return that.token;
};
// get file key according with the user passed options
var getFileKey = function (up, file, func) {
// WARNING
// When you set the key in putPolicy by "scope": "bucket:key"
// You should understand the risk of override a file in the bucket
// So the code below that automatically get key from uptoken has been commented
// var putPolicy = getPutPolicy(that.token)
// if (putPolicy.key) {
// logger.debug("key is defined in putPolicy.scope: ", putPolicy.key)
// return putPolicy.key
// }
var key = '',
unique_names = false;
if (!op.save_key) {
unique_names = up.getOption && up.getOption('unique_names');
unique_names = unique_names || (up.settings && up.settings.unique_names);
if (unique_names) {
var ext = that.getFileExtension(file.name);
key = ext ? file.id + '.' + ext : file.id;
} else if (typeof func === 'function') {
key = func(up, file);
} else {
key = file.name;
}
}
return key;
};
var getDomainFromUrl = function (url) {
if (url && url.match) {
var groups = url.match(/^https?:\/\/([^:^/]*)/);
return groups ? groups[1] : "";
}
return "";
};
var getPortFromUrl = function (url) {
if (url && url.match) {
var groups = url.match(/(^https?)/);
if (!groups) {
return "";
}
var type = groups[1];
groups = url.match(/^https?:\/\/([^:^/]*):(\d*)/);
if (groups) {
return groups[2];
} else if (type === "http") {
return "80";
} else {
return "443";
}
}
return "";
};
/********** inner function define end **********/
if (op.log_level) {
logger.level = op.log_level;
}
if (!op.domain) {
throw 'domain setting in options is required!';
}
if (!op.browse_button) {
throw 'browse_button setting in options is required!';
}
if (!op.uptoken && !op.uptoken_url && !op.uptoken_func) {
throw 'one of [uptoken, uptoken_url, uptoken_func] settings in options is required!';
}
logger.debug("init uploader start");
logger.debug("environment: ", moxie.core.utils.Env);
logger.debug("userAgent: ", navigator.userAgent);
var option = {};
// hold the handler from user passed options
var _Error_Handler = op.init && op.init.Error;
var _FileUploaded_Handler = op.init && op.init.FileUploaded;
// replace the handler for intercept
op.init.Error = function () {};
op.init.FileUploaded = function () {};
that.uptoken_url = op.uptoken_url;
that.token = '';
that.key_handler = typeof op.init.Key === 'function' ? op.init.Key : '';
this.domain = op.domain;
// TODO: ctx is global in scope of a uploader instance
// this maybe cause error
var ctx = '';
var speedCalInfo = {
isResumeUpload: false,
resumeFilesize: 0,
startTime: '',
currentTime: ''
};
reset_chunk_size();
logger.debug("invoke reset_chunk_size()");
logger.debug("op.chunk_size: ", op.chunk_size);
var defaultSetting = {
url: qiniuUploadUrl,
multipart_params: {
token: ''
}
};
var ie = that.detectIEVersion();
// case IE 9-
// add accept in multipart params
if (ie && ie <= 9) {
defaultSetting.multipart_params.accept = 'text/plain; charset=utf-8';
logger.debug("add accept text/plain in multipart params");
}
// compose options with user passed options and default setting
plupload.extend(option, op, defaultSetting);
logger.debug("option: ", option);
// create a new uploader with composed options
var uploader = new plupload.Uploader(option);
logger.debug("new plupload.Uploader(option)");
// bind getNewUpToken to 'Init' event
uploader.bind('Init', function (up, params) {
logger.debug("Init event activated");
// if op.get_new_uptoken is not true
// invoke getNewUptoken when uploader init
// else
// getNewUptoken everytime before a new file upload
if (!op.get_new_uptoken) {
getNewUpToken(null);
}
//getNewUpToken(null);
});
logger.debug("bind Init event");
// bind 'FilesAdded' event
// when file be added and auto_start has set value
// uploader will auto start upload the file
uploader.bind('FilesAdded', function (up, files) {
logger.debug("FilesAdded event activated");
var auto_start = up.getOption && up.getOption('auto_start');
auto_start = auto_start || (up.settings && up.settings.auto_start);
logger.debug("auto_start: ", auto_start);
logger.debug("files: ", files);
// detect is iOS
var is_ios = function () {
if (moxie.core.utils.Env.OS.toLowerCase() === "ios") {
return true;
} else {
return false;
}
};
// if current env os is iOS change file name to [time].[ext]
if (is_ios()) {
for (var i = 0; i < files.length; i++) {
var file = files[i];
var ext = that.getFileExtension(file.name);
file.name = file.id + "." + ext;
}
}
if (auto_start) {
setTimeout(function () {
up.start();
logger.debug("invoke up.start()");
}, 0);
// up.start();
// plupload.each(files, function(i, file) {
// up.start();
// logger.debug("invoke up.start()")
// logger.debug("file: ", file);
// });
}
up.refresh(); // Reposition Flash/Silverlight
});
logger.debug("bind FilesAdded event");
// bind 'BeforeUpload' event
// intercept the process of upload
// - prepare uptoken
// - according the chunk size to make differnt upload strategy
// - resume upload with the last breakpoint of file
uploader.bind('BeforeUpload', function (up, file) {
logger.debug("BeforeUpload event activated");
file._start_at = new Date();
// add a key named speed for file object
file.speed = file.speed || 0;
ctx = '';
if (op.get_new_uptoken) {
getNewUpToken(file);
}
var directUpload = function (up, file, func) {
speedCalInfo.startTime = new Date().getTime();
var multipart_params_obj;
if (op.save_key) {
multipart_params_obj = {
'token': that.token
};
} else {
multipart_params_obj = {
'key': getFileKey(up, file, func),
'token': that.token
};
}
var ie = that.detectIEVersion();
// case IE 9-
// add accept in multipart params
if (ie && ie <= 9) {
multipart_params_obj.accept = 'text/plain; charset=utf-8';
logger.debug("add accept text/plain in multipart params");
}
logger.debug("directUpload multipart_params_obj: ", multipart_params_obj);
var x_vars = op.x_vars;
if (x_vars !== undefined && typeof x_vars === 'object') {
for (var x_key in x_vars) {
if (x_vars.hasOwnProperty(x_key)) {
if (typeof x_vars[x_key] === 'function') {
multipart_params_obj['x:' + x_key] = x_vars[x_key](up, file);
} else if (typeof x_vars[x_key] !== 'object') {
multipart_params_obj['x:' + x_key] = x_vars[x_key];
}
}
}
}
up.setOption({
'url': qiniuUploadUrl,
'multipart': true,
'chunk_size': is_android_weixin_or_qq() ? op.max_file_size : undefined,
'multipart_params': multipart_params_obj
});
};
// detect is weixin or qq inner browser
var is_android_weixin_or_qq = function () {
var ua = navigator.userAgent.toLowerCase();
if ((ua.match(/MicroMessenger/i) || moxie.core.utils.Env.browser === "QQBrowser" || ua.match(/V1_AND_SQ/i)) && moxie.core.utils.Env.OS.toLowerCase() === "android") {
return true;
} else {
return false;
}
};
var chunk_size = up.getOption && up.getOption('chunk_size');
chunk_size = chunk_size || (up.settings && up.settings.chunk_size);
logger.debug("uploader.runtime: ", uploader.runtime);
logger.debug("chunk_size: ", chunk_size);
// TODO: flash support chunk upload
if ((uploader.runtime === 'html5' || uploader.runtime === 'flash') && chunk_size) {
if (file.size < chunk_size || is_android_weixin_or_qq()) {
logger.debug("directUpload because file.size < chunk_size || is_android_weixin_or_qq()");
// direct upload if file size is less then the chunk size
directUpload(up, file, that.key_handler);
} else {
// TODO: need a polifill to make it work in IE 9-
// ISSUE: if file.name is existed in localStorage
// but not the same file maybe cause error
var localFileInfo = localStorage.getItem(file.name);
var blockSize = chunk_size;
if (localFileInfo) {
// TODO: although only the html5 runtime will enter this statement
// but need uniform way to make convertion between string and json
localFileInfo = that.parseJSON(localFileInfo);
var now = (new Date()).getTime();
var before = localFileInfo.time || 0;
var aDay = 24 * 60 * 60 * 1000; // milliseconds of one day
// if the last upload time is within one day
// will upload continuously follow the last breakpoint
// else
// will reupload entire file
if (now - before < aDay) {
if (localFileInfo.percent !== 100) {
if (file.size === localFileInfo.total) {
// TODO: if file.name and file.size is the same
// but not the same file will cause error
file.percent = localFileInfo.percent;
file.loaded = localFileInfo.offset;
ctx = localFileInfo.ctx;
// set speed info
speedCalInfo.isResumeUpload = true;
speedCalInfo.resumeFilesize = localFileInfo.offset;
// set block size
if (localFileInfo.offset + blockSize > file.size) {
blockSize = file.size - localFileInfo.offset;
}
} else {
// remove file info when file.size is conflict with file info
localStorage.removeItem(file.name);
}
} else {
// remove file info when upload percent is 100%
// avoid 499 bug
localStorage.removeItem(file.name);
}
} else {
// remove file info when last upload time is over one day
localStorage.removeItem(file.name);
}
}
speedCalInfo.startTime = new Date().getTime();
var multipart_params_obj = {};
var ie = that.detectIEVersion();
// case IE 9-
// add accept in multipart params
if (ie && ie <= 9) {
multipart_params_obj.accept = 'text/plain; charset=utf-8';
logger.debug("add accept text/plain in multipart params");
}
// TODO: to support bput
// http://developer.qiniu.com/docs/v6/api/reference/up/bput.html
up.setOption({
'url': qiniuUploadUrl + '/mkblk/' + blockSize,
'multipart': false,
'chunk_size': chunk_size,
'required_features': "chunks",
'headers': {
'Authorization': 'UpToken ' + getUptoken(file)
},
'multipart_params': multipart_params_obj
});
}
} else {
logger.debug("directUpload because uploader.runtime !== 'html5' || uploader.runtime !== 'flash' || !chunk_size");
// direct upload if runtime is not html5
directUpload(up, file, that.key_handler);
}
});
logger.debug("bind BeforeUpload event");
// bind 'UploadProgress' event
// calculate upload speed
uploader.bind('UploadProgress', function (up, file) {
logger.trace("UploadProgress event activated");
speedCalInfo.currentTime = new Date().getTime();
var timeUsed = speedCalInfo.currentTime - speedCalInfo.startTime; // ms
var fileUploaded = file.loaded || 0;
if (speedCalInfo.isResumeUpload) {
fileUploaded = file.loaded - speedCalInfo.resumeFilesize;
}
file.speed = (fileUploaded / timeUsed * 1000).toFixed(0) || 0; // unit: byte/s
});
logger.debug("bind UploadProgress event");
// bind 'ChunkUploaded' event
// store the chunk upload info and set next chunk upload url
uploader.bind('ChunkUploaded', function (up, file, info) {
logger.debug("ChunkUploaded event activated");
logger.debug("ChunkUploaded file: ", file);
logger.debug("ChunkUploaded info: ", info);
var res = that.parseJSON(info.response);
logger.debug("ChunkUploaded res: ", res);
// ctx should look like '[chunk01_ctx],[chunk02_ctx],[chunk03_ctx],...'
ctx = ctx ? ctx + ',' + res.ctx : res.ctx;
var leftSize = info.total - info.offset;
var chunk_size = up.getOption && up.getOption('chunk_size');
chunk_size = chunk_size || (up.settings && up.settings.chunk_size);
if (leftSize < chunk_size) {
up.setOption({
'url': qiniuUploadUrl + '/mkblk/' + leftSize
});
logger.debug("up.setOption url: ", qiniuUploadUrl + '/mkblk/' + leftSize);
}
up.setOption({
'headers': {
'Authorization': 'UpToken ' + getUptoken(file)
}
});
localStorage.setItem(file.name, that.stringifyJSON({
ctx: ctx,
percent: file.percent,
total: info.total,
offset: info.offset,
time: (new Date()).getTime()
}));
});
logger.debug("bind ChunkUploaded event");
var retries = op.max_retries;
// if error is unkown switch upload url and retry
var unknow_error_retry = function (file) {
if (retries-- > 0) {
setTimeout(function () {
that.resetUploadUrl(retries);
file.status = plupload.QUEUED;
uploader.stop();
uploader.start();
}, 0);
return true;
} else {
retries = qiniuUploadUrls.length;
return false;
}
};
// bind 'Error' event
// check the err.code and return the errTip
uploader.bind('Error', (function (_Error_Handler) {
return function (up, err) {
logger.error("Error event activated");
logger.error("err: ", err);
var nowTime = new Date();
var errTip = '';
var file = err.file;
if (file) {
switch (err.code) {
case plupload.FAILED:
errTip = '上传失败。请稍后再试。';
break;
case plupload.FILE_SIZE_ERROR:
var max_file_size = up.getOption && up.getOption('max_file_size');
max_file_size = max_file_size || (up.settings && up.settings.max_file_size);
errTip = '浏览器最大可上传' + max_file_size + '。更大文件请使用命令行工具。';
break;
case plupload.FILE_EXTENSION_ERROR:
errTip = '文件验证失败。请稍后重试。';
break;
case plupload.HTTP_ERROR:
if (err.response === '') {
// Fix parseJSON error ,when http error is like net::ERR_ADDRESS_UNREACHABLE
errTip = err.message || '未知网络错误。';
if (!unknow_error_retry(file)) {
return;
}
break;
}
var errorObj = that.parseJSON(err.response);
var errorText = errorObj.error;
switch (err.status) {
case 400:
errTip = "请求报文格式错误。";
break;
case 401:
errTip = "客户端认证授权失败。请重试或提交反馈。";
break;
case 405:
errTip = "客户端请求错误。请重试或提交反馈。";
break;
case 579:
errTip = "资源上传成功,但回调失败。";
break;
case 599:
errTip = "网络连接异常。请重试或提交反馈。";
if (!unknow_error_retry(file)) {
return;
}
break;
case 614:
errTip = "文件已存在。";
try {
errorObj = that.parseJSON(errorObj.error);
errorText = errorObj.error || 'file exists';
} catch (e) {
errorText = errorObj.error || 'file exists';
}
break;
case 631:
errTip = "指定空间不存在。";
break;
case 701:
errTip = "上传数据块校验出错。请重试或提交反馈。";
break;
default:
errTip = "未知错误。";
if (!unknow_error_retry(file)) {
return;
}
break;
}
errTip = errTip + '(' + err.status + ':' + errorText + ')';
break;
case plupload.SECURITY_ERROR:
errTip = '安全配置错误。请联系网站管理员。';
break;
case plupload.GENERIC_ERROR:
errTip = '上传失败。请稍后再试。';
break;
case plupload.IO_ERROR:
errTip = '上传失败。请稍后再试。';
break;
case plupload.INIT_ERROR:
errTip = '网站配置错误。请联系网站管理员。';
uploader.destroy();
break;
default:
errTip = err.message + err.details;
if (!unknow_error_retry(file)) {
return;
}
break;
}
if (_Error_Handler) {
_Error_Handler(up, err, errTip);
}
}
up.refresh(); // Reposition Flash/Silverlight
// add send log for upload error
if (!op.disable_statistics_report) {
var matchedGroups = (err && err.responseHeaders && err.responseHeaders.match) ? err.responseHeaders.match(/(X-Reqid\:\ )([\w\.\%-]*)/) : [];
console.log(err);
var req_id = matchedGroups[2].replace(/[\r\n]/g,"");
var errcode = plupload.HTTP_ERROR ? err.status : err.code;
var startAt = file._start_at ? file._start_at.getTime() : nowTime.getTime();
statisticsLogger.log(
errcode === 0 ? ExtraErrors.NetworkError : errcode,
req_id,
getDomainFromUrl(up.settings.url),
undefined,
getPortFromUrl(up.settings.url),
(nowTime.getTime() - startAt)/1000,
parseInt(startAt/1000),
err.file.size * (err.file.percent / 100),
"jssdk-" + up.runtime,
file.size
);
}
};
})(_Error_Handler));
logger.debug("bind Error event");
// bind 'FileUploaded' event
// intercept the complete of upload
// - get downtoken from downtoken_url if bucket is private
// - invoke mkfile api to compose chunks if upload strategy is chunk upload
uploader.bind('FileUploaded', (function (_FileUploaded_Handler) {
return function (up, file, info) {
logger.debug("FileUploaded event activated");
logger.debug("FileUploaded file: ", file);
logger.debug("FileUploaded info: ", info);
var nowTime = new Date();
var last_step = function (up, file, info) {
logger.debug("FileUploaded last step:", info);
if (op.downtoken_url) {
// if op.dowontoken_url is not empty
// need get downtoken before invoke the _FileUploaded_Handler
var ajax_downtoken = that.createAjax();
ajax_downtoken.open('POST', op.downtoken_url, true);
ajax_downtoken.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
ajax_downtoken.onreadystatechange = function () {
if (ajax_downtoken.readyState === 4) {
if (ajax_downtoken.status === 200) {
var res_downtoken;
try {
res_downtoken = that.parseJSON(ajax_downtoken.responseText);
} catch (e) {
throw ('invalid json format');
}
var info_extended = {};
plupload.extend(info_extended, that.parseJSON(info.response), res_downtoken);
info.response = that.stringifyJSON(info_extended);
if (_FileUploaded_Handler) {
_FileUploaded_Handler(up, file, info.response);
}
} else {
uploader.trigger('Error', {
status: ajax_downtoken.status,
response: ajax_downtoken.responseText,
file: file,
code: plupload.HTTP_ERROR
});
}
}
};
ajax_downtoken.send('key=' + that.parseJSON(info.response).key + '&domain=' + op.domain);
} else if (_FileUploaded_Handler) {
_FileUploaded_Handler(up, file, info.response);
}
};
var res = that.parseJSON(info.response);
ctx = ctx ? ctx : res.ctx;
// if ctx is not empty
// that means the upload strategy is chunk upload
// before the invoke the last_step
// we need request the mkfile to compose all uploaded chunks
// else
// invoke the last_step
logger.debug("ctx: ", ctx);
if (ctx) {
var key = '';
logger.debug("save_key: ", op.save_key);
if (!op.save_key) {
key = getFileKey(up, file, that.key_handler);
key = key ? '/key/' + that.URLSafeBase64Encode(key) : '';
}
var fname = '/fname/' + that.URLSafeBase64Encode(file.name);
logger.debug("op.x_vars: ", op.x_vars);
var x_vars = op.x_vars,
x_val = '',
x_vars_url = '';
if (x_vars !== undefined && typeof x_vars === 'object') {
for (var x_key in x_vars) {
if (x_vars.hasOwnProperty(x_key)) {
if (typeof x_vars[x_key] === 'function') {
x_val = that.URLSafeBase64Encode(x_vars[x_key](up, file));
} else if (typeof x_vars[x_key] !== 'object') {
x_val = that.URLSafeBase64Encode(x_vars[x_key]);
}
x_vars_url += '/x:' + x_key + '/' + x_val;
}
}
}
var url = qiniuUploadUrl + '/mkfile/' + file.size + key + fname + x_vars_url;
var ie = that.detectIEVersion();
var ajax;
if (ie && ie <= 9) {
ajax = new moxie.xhr.XMLHttpRequest();
moxie.core.utils.Env.swf_url = op.flash_swf_url;
} else {
ajax = that.createAjax();
}
ajax.open('POST', url, true);
ajax.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8');
console.log('uptoken:'+that.token);
ajax.setRequestHeader('Authorization', 'UpToken ' + that.token);
var onreadystatechange = function () {
logger.debug("ajax.readyState: ", ajax.readyState);
if (ajax.readyState === 4) {
localStorage.removeItem(file.name);
var ajaxInfo;
if (ajax.status === 200) {
ajaxInfo = {
status: ajax.status,
response: ajax.responseText,
responseHeaders: ajax.getAllResponseHeaders(),
};
logger.debug("mkfile is success: ", ajaxInfo);
last_step(up, file, ajaxInfo);
} else {
ajaxInfo = {
status: ajax.status,
response: ajax.responseText,
file: file,
code: -200,
responseHeaders: ajax.getAllResponseHeaders()
};
logger.debug("mkfile is error: ", ajaxInfo);
uploader.trigger('Error', ajaxInfo);
}
}
};
if (ie && ie <= 9) {
ajax.bind('readystatechange', onreadystatechange);
} else {
ajax.onreadystatechange = onreadystatechange;
}
ajax.send(ctx);
logger.debug("mkfile: ", url);
} else {
last_step(up, file, info);
}
// send statistics log
if (!op.disable_statistics_report) {
console.log(info.responseHeaders);
var req_id = info.responseHeaders.match(/(X-Reqid\:\ )([\w\.\%-]*)/i)[2].replace(/[\r\n]/g,"");
var startAt = file._start_at ? file._start_at.getTime() : nowTime.getTime();
statisticsLogger.log(
info.status,
req_id,
getDomainFromUrl(up.settings.url),
undefined,
getPortFromUrl(up.settings.url),
(nowTime.getTime() - startAt)/1000,
parseInt(startAt/1000),
file.size,
"jssdk-" + up.runtime,
file.size
);
}
};
})(_FileUploaded_Handler));
logger.debug("bind FileUploaded event");
// bind 'FilesRemoved' event
// intercept the cancel of upload
// used to send statistics log to server
uploader.bind('FilesRemoved', function (up, files) {
var nowTime = new Date();
// add cancel log
if (!op.disable_statistics_report) {
for (var i = 0; i < files.length; i++) {
statisticsLogger.log(
ExtraErrors.Cancelled,
undefined,
getDomainFromUrl(up.settings.url),
undefined,
getPortFromUrl(up.settings.url),
(nowTime.getTime() - files[i]._start_at.getTime())/1000,
files[i]._start_at.getTime()/1000,
files[i].size * files[i].percent / 100,
"jssdk-" + up.runtime,
files[i].size
);
}
}
});
logger.debug("bind FilesRemoved event");
// init uploader
uploader.init();
logger.debug("invoke uploader.init()");
logger.debug("init uploader end");
return uploader;
};
/**
* get url by key
* @param {String} key of file
* @return {String} url of file
*/
this.getUrl = function (key) {
if (!key) {
return false;
}
key = encodeURI(key);
var domain = this.domain;
if (domain.slice(domain.length - 1) !== '/') {
domain = domain + '/';
}
return domain + key;
};
}
var Qiniu = new QiniuJsSDK();
global.Qiniu = Qiniu;
global.QiniuJsSDK = QiniuJsSDK;
if (typeof module !== 'undefined' && module.exports) {
module.exports = QiniuJsSDK;
} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
// register as 'qiniu-js', consistent with npm package name
define('qiniu-js', ['./plupload/moxie.js','./plupload/plupload.dev.js'], function () {
return QiniuJsSDK;
});
} else {
global.QiniuJsSDK = QiniuJsSDK;
}
})(window);
var path = require('path');
var webpack = require('webpack');
module.exports = {
entry: path.resolve(__dirname, 'src/qiniu.js'),
output: {
path: path.resolve(__dirname, 'dist/'),
filename: 'qiniu.min.js',
},
devtool: 'inline-source-map',
plugins: [
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
}),
]
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment