├── README.md ├── demo1 ├── index.html └── server.js ├── demo2 ├── index.html └── server.js ├── demo3 ├── index.html └── moxie.min.js ├── demo4 ├── index.html ├── moxie.min.js └── server.js ├── demo5 ├── index.html ├── moxie.min.js ├── plupload.full.min.js ├── plupload.min.js └── server.js ├── demo6 ├── index.html ├── plupload.full.min.js └── server.js ├── demo7 ├── images │ ├── ui-icons_444444_256x240.png │ ├── ui-icons_555555_256x240.png │ ├── ui-icons_777620_256x240.png │ ├── ui-icons_777777_256x240.png │ ├── ui-icons_cc0000_256x240.png │ └── ui-icons_ffffff_256x240.png ├── img │ ├── loading.gif │ └── plupload.png ├── index.html ├── jquery-ui.css ├── jquery-ui.min.js ├── jquery.min.js ├── jquery.ui.plupload.css ├── jquery.ui.plupload.js ├── plupload.dev.js ├── plupload.full.min.js └── server.js ├── demo8 ├── client.js ├── index.html ├── moxie.min.js └── server.js ├── demo9 ├── index.html ├── plupload.full.min.js └── server.js ├── docs └── compile.md └── img ├── 1.1.png ├── 1.2.png ├── 1.22.png ├── 1.3.png ├── 1.4.png ├── 3.1.png ├── 6.1.png ├── 6.2.png ├── 6.3.png ├── 6.4.jpg ├── 7.1.png ├── 7.png ├── 8.png └── 9.png /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript文件上传详解 2 | 3 | ~~~sh 4 | git clone https://github.com/ktont/javascript-file-upload 5 | node javascript-file-upload/demo6/server.js 6 | ~~~ 7 | 8 | ## 目录 9 | 10 | > [demo1 form 表单,原生的文件上传方式](#demo1) 11 | > [demo2 plupload 的原理](#demo2) 12 | > [demo3 mOxie 文件选取和文件预览](#demo3) 13 | > [demo4 mOxie 文件上传,进度提示](#demo4) 14 | > [demo5 使用 plupload 实现了图片上传](#demo5) 15 | > [demo6 断点续传](#demo6) 16 | > [demo7 plupload 之 Ui Widget 的示例](#demo7) 17 | > [demo8 服务端限速](#demo8) 18 | > [demo9 FAQ 批量上传的时候,如何为每个文件添加一个id](#demo9) 19 | > [总结](#end) 20 | 21 | 本教程包含7个 demo,它们循序渐进、由浅入深地讲解**文件上传**。每个 demo 都被精心设计,都是可执行的。 22 | 因为我刚做完并上线了一个真实的**文件上传**程序,所以有些 demo 对实际生产有指导意义。 23 | 24 | 除了前端的**上传**部分。后端的**接收**部分也由我们一手操办,并且没有用现成的包而是亲自去解析数据,因为我想让你更清晰的看到 **http 协议**。 25 | 26 | 在运行 demo 的时候,请将网络速度调低,这样,我们就可以清楚的看到 **http 的交互过程**。 27 | 调低网络速度的方法之一,是用 chrome 的 debugger 工具,下文会有详细的图示。 28 | 29 | ~~~bash 30 | windows 31 | 下载zip文件,然后解压到c盘 32 | c:\> cd javascript-file-upload-master 33 | c:\> node demo1\server.js 34 | 35 | linux or mac 36 | $ git clone https://github.com/ktont/javascript-file-upload 37 | $ cd javascript-file-upload 38 | $ sudo node demo1/server.js 39 | 类推,运行demo2的时候,去执行demo2下的server.js。 40 | $ sudo node demo2/server.js 41 | ~~~ 42 | 43 | 然后在浏览器中(建议 chrome)打开 [http://localhost](http://localhost) 44 | 45 | __ERROR__: 如果你遇到 EADDRINUSE 的错误,那是因为80端口已经被其它诸如 apache、nginx 的进程占用了。 46 | 可以在启动的时候指定端口, 比如端口3000。 47 | 48 | $ node demo1/server.js 3000 49 | 50 | __ERROR__: 如果遇到 EACCES 的错误,请用 sudo 权限运行它。 51 | 52 | $ sudo node demo1/server.js 53 | 54 | ## 1、form表单,原生文件上传方式 55 | 56 | 首先,来看第一个例子。 57 | 它是用原生的文件提交方法,前端只有一段 html 而没有 js。我们的目的是**观察** http 协议。 58 | 59 | 前端 index.html,使用一个 input 标签进行文件选择,然后使用 form 表单发送数据。 60 | 后端 server.js,对表单发过来的数据进行解析,把协议格式打印出来。 61 | 62 | 点击 选择文件 后 63 | 64 | 65 | 66 | 在点击 Upload 按钮之前,对网络进行限速,方便观察数据传输的过程。打开 debugger 67 | 68 | 69 | 70 | 点击后,选取一个较慢的 71 | 72 | 73 | 74 | 服务端会打印下面的提示,注意红框中的 token,它用来表示二进制数据的边界。 75 | 76 | 77 | 你在 server.js 中可以看到解析 http 数据的 formidable 函数。 78 | 可以调试它,用来学习 http 协议。 79 | 80 | 上传完成后 81 | 82 | 83 | 84 | __TIP__: 观察,它是我们本次学习之旅的主要方法。 85 | 你一定要运行每个例子,看到它们运行,观察它们的行为。 86 | 这样,就熟悉了这个技术。 87 | 88 | ## 2、plupload的原理 89 | `plupload` 是一个文件上传的前端插件。 90 | 91 | [它的主页](http://www.plupload.com) [它的github地址](https://github.com/moxiecode/plupload) 92 | 93 | demo2并没有使用 `plupload`,事实上它是自己实现了 `plupload`,它本身就相当于 `plupload` 的 v0.01 版本。 94 | 95 | 通过 v0.01,这20行代码来一窥 `plupload` 的原理。而不是去读 `plupload` 的上万行代码, 96 | 真是,两岸猿声啼不住,轻舟已过万重山,一日千里。 97 | 98 | `plupload` 的原理,就是拿到文件句柄后,自己发送(XMLHttpRequest)文件。 99 | 尽量控制整个过程,从中加入自己实现的功能,这就是它的想法。 100 | 101 | * 比如,图片预览,是在拿到文件以后在新的 canvas 上画出新的尺寸。 102 | * 比如,断点续传,是在拿到文件以后 slice 文件,从断点处开始读取。 103 | 104 | 这些操作,都有个前提,就是要拿到文件。否则,一切无从做起。 105 | 106 | ## 3、mOxie文件选取和文件预览 107 | 108 | 这个例子没有服务端,请直接用浏览器打开 `demo3/index.html`。然后选取图片,就可以看到预览。 109 | 这样避免你想当然的认为,预览是服务端辅助的。 110 | 111 | NOTE: h5 是无法直接在 img 标签中嵌入本地图片预览的。本例使用 canvas 在页面上画出缩略图。 112 | 113 | 114 | 115 | 文件预览一般的做法是,先上传图片,然后从图片服务器上下载 thumbnail,这么做是有缺点的,预览要先上传才能看到(可能人们更喜欢先看到再决定要不要上传)。但是这里采用的做法不同,它在本地进行预览,但这势必会增加一些 cpu 的开销,因为预览的实质是进行了图片压缩(要么服务端压缩要么客户端压缩而已)。 116 | 117 | 实际生产中,采用哪一种做法,要看需求,或者看你方便的程度。如果需求中要求节省流量,或有上传前删除功能,那就采用本地预览(也就是本例的做法)。如果服务器能存储压缩后的 thumbnail,且压力不大,速度够快,那就用服务端预览。 118 | 119 | 另外, 120 | 当你看到 mOxie 的时候,可能会觉得莫名其妙。是这样的 121 | 122 | 打开 http://www.plupload.com/docs/ 123 | 124 | 文档的最后一段话如下 125 | > * Low-level pollyfills (mOxie) 126 | > * Plupload API 127 | > * UI Widget 128 | > * Queue Widget 129 | 130 | 其实我写本文的初衷,是为了解释这四句话。 131 | 我跟你一样,一开始读不懂。 132 | 133 | 这四句话的意思是 134 | `plupload` 有四个安装等级 - 初级,中级,高级,长级 135 | 136 | * 初级,叫 moxie.min.js,插件大小77k到106k不等(神马鬼?为什么不等的原因参见 [编译 mOxie](docs/compile.md) 一节)。 137 | 其中提到的 pollyfills 应为 **polyfills**,是帮助老浏览器跟上 h5 步伐的插件,叫 **h5 垫片**,用 js 提升老浏览器的 api,抹平浏览器间的差异。所以 mOxie 其实是个通用前端库。 138 | * 中级,plupload.full.min.js,插件大小123k 139 | 打开它看一下,发现它其实是 moxie.min.js 和一个叫 plupload.min.js 的文件合并到一起而已。 140 | 所以 `plupload` 其实是在 mOxie 的基础上,封装了一下文件上传 api,专业文件上传前端库。 141 | * 高级,它依赖 142 | jquery 137k 143 | jquery ui 282k 144 | plupload 123k 145 | plupload ui 30k 146 | 一共约600k的大小。帮助你实现 ui,叫 widget - 小组件。 147 | * 巨级,它和高级差不多,也是实现一套 ui。区别是 ui 是队列,前者的 ui 是块和列表。 148 | 149 | 那么回过头,再来看这个例子。这个例子只是演示文件选择,它没有上传的功能。 150 | 只有文件选择功能的 `mOxie` 插件的大小为77k,比正常功能要小30%。为什么呢? 151 | 152 | 因为 `mOxie` 是一个可以自定义的前端库,如果有些功能不需要,比如 silverlight,那么就可以不把它们编到目标中。 参见 [编译 mOxie](docs/compile.md) 153 | 154 | 那么 `mOxie` 都做了什么呢,为甚么有77k这么大(大吗?)的体积。它提供文件预览功能、图片压缩功能、国际化支持(就是 i18n )等。同时,上面也提到,它解决浏览器的兼容性问题。 155 | 156 | ## 4、mOxie文件上传,进度提示 157 | 158 | 这个例子只使用 `mOxie` 提供的功能,实现了**文件上传**。 159 | 160 | ~~~ 161 | $ ls -l demo[3-4]/moxie.min.js 162 | -rw-r--r-- ktont staff 73499 13:53 demo3/moxie.min.js 163 | -rw-r--r-- ktont staff 77782 13:58 demo4/moxie.min.js 164 | ~~~ 165 | 您会发现,本例中的 `mOxie` 库比上一例多了4k,那是因为在编译的时候加入了 XMLHttpRequest 的支持。 166 | 所以 demo4 中的 moxie.min.js 就是 `plupload` 库能投入生产的最精简版本。参见 [编译 mOxie](docs/compile.md) 167 | 168 | 您可以在这个 demo 的基础上实现自己的文件上传。相比 `Plupload API`,它更灵活,您可能更喜欢在这个**层次**上编写应用。当然,灵活性的对立面是复杂度,它们之间的平衡点因人而异。 169 | 170 | ### 怎么判断文件上传成功? 171 | 172 | - 无论上传成功或者失败,结束时都会触发 loadend 事件 173 | - 如果上传过程中服务器崩溃,那么 loadend 事件的附加值 info.status 为 0。也可以根据 xhr.status 为 0 174 | - 如果上传过程中用户 abort,那么结果将依赖于服务器的处理。服务器有可能返回成功也可能返回失败。 175 | - 如果上传过程中网络中断或者超时,那么 那么 loadend 事件的附加值 info.status 为 0。也可以根据 xhr.status 为 0 176 | - 如果服务器返回的值 info.status == 200,则说明上传成功。否则,失败 177 | 178 | NOTE: 关于 abort 179 | 180 | 这似乎是一个bug,因为当用户主动 abort 时,并没有触发 abort 事件。 181 | 所以你要想正确的 abort 那么就要注意了。手工 abort() 后,要标记失败了。 182 | 最好用 Promise,abort() 后,立即返回 reject() 183 | 184 | ## 5、使用plupload实现了图片上传 185 | 186 | 这个例子,比较实际一点,使用 `Plupload API`。`Plupload API` 主要在 `mOxie` 上实现一套事件驱动的机制。 187 | 188 | 同时,顺带演习上传的暂停和重传。为甚么在这里演习暂停和重传呢? 189 | 190 | 为了区分下个例子 -- 断点续传。断点续传是指,**重启了电脑后断点续传**。 191 | 192 | 断点续传在上传大文件的场景下,很有用。 193 | 比如我上传一个电影,中间关闭了电脑,然后睡个觉。醒来后可以继续传。 194 | 195 | 下一个例子演示断点续传。 196 | 197 | 而本例的重传是说,不重启浏览器的前提下,重新传文件。它会从头再来,之前传的会丢弃。 198 | 实际场景中,用来重传图片这种小文件。 199 | 因为小文件一个封包或几个封包就发送完了,没必要断点续传,也没法儿**断**了。 200 | 201 | ## 6、断点续传 202 | 203 | 是时候请出你的硬盘女神啦!运行本程序需要一个大文件,而电影文件再合适不过了。 204 | 205 | 206 | 207 | 选取文件后,并没有立即上传。而是去服务器询问上次传输的断点。 208 | 在本例中,服务端会返回一个50到100的随机值,它表示百分比,用来模拟实际情况中的**上次的断点**。 209 | 210 | 例如,下面图片中,上次的断点是94% 211 | 212 | 213 | 214 | 你可能会误认为服务器会从94%的地方把数据存起来,不是的, 215 | 它的意思是告诉客户端,**请从文件94%的地方把剩下的数据发送过来**。 216 | 217 | 服务端的情况 218 | 219 | 220 | 221 | 222 | 本例中使用的**块**大小是1兆字节,这个配置在 `index.html` 的19行 223 | > chunk_size: '1mb' 224 | 225 | 上图中,两个绿色框之间是一次独立的 **http 交互过程**,它用来发送一个**块**。 226 | 本例中的文件一共4G多,会切成4千多个块。产生4千多次 http 交互来发送它们。 227 | 相比不分块而一次 http 发送完所有数据,这么做会有些网络性能损耗。但是不分块的缺点是非常明显的。 228 | 229 | 如果真的不分块,单 http 发送所有数据。假设网络异常,服务端 hanging,客户端此时开启另一个链接 retry。 230 | retry 首先询问服务端**上次的断点**,然后从该断点处继续发送。 231 | 之前 hanging 的链接可能已经 hang up,也可能没有,这取决于服务端的超时时间。 232 | 233 | 此时,服务端就会面临一个尴尬的选择,必须关闭之前 hanging 的链接 234 | 。因为如果不关闭,网络中残留的数据可能继续写入文件,导致数据错乱。 235 | 服务端一般请求间是无法操作的,一个请求不能操作其它请求。 236 | 237 | 238 | 239 | 虽然,实际上几乎不会出现上面的情况,但是它不严谨。并且, 240 | http 协议是一个应用层协议。http 协议在 **application** 和 **network transfer** 更靠近 **application**。大多数 http 服务器都会帮你做封包的拼解工作,而让你从网络层传输层解放出来。如果达不到这一点,http 的处理还是和 tcp 一样麻烦,那 http 就不应该存在。[参考 http协议](https://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html) 241 | 242 | 然而,如果分块来传输,就不会遇到这个问题。如果链接 hang up。那么整个请求的数据统统丢弃,偏移仍然在当前**块**。 243 | 244 | 话说回来,所以要把文件数据拆分成一个较小的单元来用 http 传输,并且 245 | * 用块发送可以降低 token 冲突的概率。上传文件是使用一个随机 token 来标记数据边界(第一个绿框)的。 246 | 当文件大的时候,会有可能遇到和 token 一样的字符串。但是,分块传,会每次都换一个 token。 247 | * 适当的**块**大小,有助于浏览器读取文件。比如本例中 chrome 用的是 slice 读取文件,我们不能指望它很智能,塞给它一个很大的文件,让它很好的处理。有些浏览器对文件大小有限制,甚至在传大文件的时候会卡死。 248 | 249 | 上图中,红色的框表示当前传输的是**第几块**数据。因为服务端给了随机值94%,所以这里是4261的尾部 -- 4005。 250 | 251 | 黄色的框表示一共有多少块数据。当红色和黄色相等的时候,表示文件传输完成。 252 | 253 | 灰色的框表示传输的二进制数据,数据的边界由第一个绿框定义。这个时候,这次 http 交互就完成了,链接会被关闭。紧接着会是下一块数据,一个全新的 http 交互,token 也会是一个新的。 254 | 255 | 断点续传的关键在于 --**从文件的指定偏移处读取** (__ZHUANGBI__: c语言中 fseek) 256 | 257 | 但是浏览器提供给前端的功能都是受限的,没有 `fseek`,而是提供了一个 `slice` 功能。 258 | 比如,`slice(off, off+1024)` 用来读取 off 处的1024字节数据。 259 | 还能凑合着用吧,那我们每次读一块数据,然后发送,再读下一块,再发送。。。 260 | 261 | 突然发现,这不就是失传已久的 **socket 编程** 吗?搞一个 **缓冲**,撸一串数据然后发出去,再撸一串数据再发出去。 262 | 263 | 好吧!幸亏不是让我们写这种恶心的数据解析工作,`plupload` 已经给我们写好了,我刚撸起的袖管赶紧放了下去。 264 | 265 | 266 | ## 7、plupload ui widget的示例 267 | 268 | 这个例子,用来展示 plupload 的 `UI Widget`。 269 | 270 | 271 | 272 | 在 index.html 中,ui 部分只需安置一个 div 273 | >
274 | 275 | plupload 会在这个 div 中,自动**安插**一个 ui 组件,就是图片中展示的那个。 276 | 277 | 这样极大的方便了开发,你可能一句 js 都没写,就实现了复杂的图片上传。 278 | 当然,你可以定制这个组件,那样需要一些学习成本,并且挺高的。所以,如果你想要一个轻量,自定义的 ui 组件的时候,就需要自己设计 ui 了。 279 | 比如下面这样的 280 | 281 | 282 | 283 | 在上面这个组件中,要求 284 | 285 | * 最多只能选n张 286 | * 更好的用户体验 287 | * 可以删除节点 288 | * 上传失败后可以重传 289 | * 页面大小控制在100k以内 290 | 291 | ## 8、nodejs上传显示进度、服务端限速 292 | 293 | 本示例研究了两个小技术 294 | * nodejs上传显示进度 295 | * 服务端限速 296 | 297 | ``` 298 | node demo8/client.js 299 | 得找一个大文件才能看出效果 300 | ``` 301 | 302 | 这两个小技术,用 stream 就能搞定。 303 | 304 | nodejs上传显示进度,用 readable stream 的 data 事件 305 | 服务端限速 用 readable stream 的 once() pause() resume(),实现有些精巧,自己去看吧。它每秒限定 64k。调整 timer 可以实现任意速率限速。 306 | 307 | 另外,demo8/client.js 研究了 github.com/request/request 库如何 abort 一次上传 308 | 309 | 在控制台直接输入几个回车,然后输入 abort ,就会 abort 这次上传。如图: 310 | 311 | 312 | 313 | ## 9、FAQ 批量上传的时候,如何为每个文件添加一个id 314 | 315 | 这是个真实的案例。 316 | 317 | 有人问我如何实现下面两点: 318 | - 批量上传的时候,如何为每个文件添加一个uuid 319 | - 如果文件小于 10M,则没有必要分块传输 320 | 321 | 这两个问题是同一个问题。答案很简单。 322 | 323 | 使用 plupload 的 BeforeUpload 事件 和 setOption 方法 324 | 325 | 326 | 327 | ## 总结 328 | 329 | 这篇文章好像变成了对 plupload 的讲解。 330 | 331 | 其实,我的初衷是探讨 web 环境下文件上传。 332 | 333 | 但是,在随后2年的开发中,我又开发了桌面端,服务端的文件上传和下载。它们 334 | 的相关经验也记录在这里了。 demo8 就是这时候产生的。 335 | 336 | 337 | 338 | -------------------------------------------------------------------------------- /demo1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | demo 1 5 | 6 | 7 |
8 |
9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /demo1/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | $ node server.js 3 | or 4 | $ node server.js 1234 5 | | 6 | V 7 | port 8 | */ 9 | var http = require("http"), 10 | url = require("url"), 11 | path = require("path"), 12 | fs = require("fs"); 13 | 14 | var port = process.argv[2] || 80; 15 | 16 | function formidable(req, res) { 17 | // parse a file upload 18 | var expect = 'sp'; 19 | var begin = new Date(); 20 | var sp, cont, type, total = 0; 21 | req.on('data', function(tr) { 22 | while(1) { 23 | switch(expect) { 24 | case 'sp': 25 | var idx = tr.indexOf('\r\n'); 26 | if(idx == -1) return; 27 | sp = tr.slice(0, idx).toString(); 28 | tr = tr.slice(idx+2); 29 | console.log('sp:', sp); 30 | expect = 'content'; 31 | break; 32 | case 'content': 33 | var idx = tr.indexOf('\r\n'); 34 | cont = tr.slice(0, idx).toString(); 35 | console.log('content:', cont); 36 | if(/Content-Disposition: ?form-data;.*filename="/.test(cont)) { 37 | expect = 'type'; 38 | tr = tr.slice(idx+2); 39 | } else { 40 | expect = 'value'; 41 | tr = tr.slice(idx+4); 42 | } 43 | break; 44 | case 'value': 45 | var idx = tr.indexOf('\r\n'); 46 | value = tr.slice(0, idx).toString(); 47 | tr = tr.slice(idx+2); 48 | console.log('value:', value); 49 | expect = 'sp'; 50 | break; 51 | case 'type': 52 | var idx = tr.indexOf('\r\n'); 53 | type = tr.slice(0, idx).toString(); 54 | tr = tr.slice(idx+4); 55 | console.log('type:', type); 56 | expect = 'end'; 57 | break; 58 | case 'end': 59 | var idx = tr.indexOf('\r\n'+sp); 60 | process.stdout.write('.'); 61 | if(idx >= 0) { 62 | total += idx; 63 | } else total += tr.length; 64 | return; 65 | } 66 | } 67 | }).on('end',function() { 68 | console.log('\ntotal:', total); 69 | var spendTm = new Date() - begin; 70 | res.end(` 71 | 72 | 73 |

${cont}

74 |

total: ${total}

75 |

upload speed: ${parseInt((total*8)/(spendTm/1000))} bps

76 |

upload speed: ${parseInt((total/1024/1024)/(spendTm/1000))} Mbyte per second

77 | `); 78 | }); 79 | } 80 | 81 | var mimeTypes = { 82 | "htm": "text/html", 83 | "html": "text/html", 84 | "jpeg": "image/jpeg", 85 | "jpg": "image/jpeg", 86 | "png": "image/png", 87 | "gif": "image/gif", 88 | "js": "text/javascript", 89 | "css": "text/css"}; 90 | 91 | var virtualDirectories = { 92 | //"images": "../images/" 93 | }; 94 | 95 | process.chdir(__dirname); 96 | 97 | http.createServer(function(req, res) { 98 | if(req.url == '/upload' && req.method.toLowerCase() == 'post') { 99 | console.log('post', req.url); 100 | formidable(req, res); 101 | return; 102 | } 103 | 104 | if(req.url == '/big') { 105 | res.setHeader('Content-Length', "4423129088"); 106 | res.setHeader('Content-Type', 'application/octet-stream'); 107 | res.setHeader('Content-Disposition', `attachment; filename=CentOS-6.2.ios`); 108 | fs.createReadStream('/Users/zhi.liang/Downloads/CentOS-6.2-x86_64-bin-DVD1.iso').pipe(res); 109 | return; 110 | } 111 | 112 | if(req.url == '/small') { 113 | res.setHeader('Content-Length', "6393"); 114 | res.setHeader('Content-Type', 'application/octet-stream'); 115 | res.setHeader('Content-Disposition', `attachment; filename=etc.passwd.txt`); 116 | fs.createReadStream('/etc/passwd').pipe(res); 117 | return; 118 | } 119 | 120 | var uri = url.parse(req.url).pathname 121 | , filename = path.join(process.cwd(), uri) 122 | , root = uri.split("/")[1] 123 | , virtualDirectory; 124 | 125 | virtualDirectory = virtualDirectories[root]; 126 | if(virtualDirectory){ 127 | uri = uri.slice(root.length + 1, uri.length); 128 | filename = path.join(virtualDirectory ,uri); 129 | } 130 | 131 | fs.exists(filename, function(exists) { 132 | if(!exists) { 133 | res.writeHead(404, {"Content-Type": "text/plain"}); 134 | res.write("404 Not Found\n"); 135 | res.end(); 136 | console.error('404: ' + filename); 137 | return; 138 | } 139 | 140 | if (fs.statSync(filename).isDirectory()) filename += '/index.html'; 141 | 142 | fs.readFile(filename, "binary", function(err, file) { 143 | if(err) { 144 | res.writeHead(500, {"Content-Type": "text/plain"}); 145 | res.write(err + "\n"); 146 | res.end(); 147 | console.error('500: ' + filename); 148 | return; 149 | } 150 | 151 | var mimeType = mimeTypes[path.extname(filename).split(".")[1]]; 152 | res.writeHead(200, {"Content-Type": mimeType}); 153 | res.write(file, "binary"); 154 | res.end(); 155 | console.log('200: ' + filename + ' as ' + mimeType); 156 | }); 157 | }); 158 | }).listen(parseInt(port, 10)); 159 | 160 | console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown"); 161 | -------------------------------------------------------------------------------- /demo2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | demo 2 5 | 6 | 7 | 8 | 9 | 33 |

34 | 
35 | 
36 | 


--------------------------------------------------------------------------------
/demo2/server.js:
--------------------------------------------------------------------------------
  1 | /*
  2 | $ node server.js 
  3 | or 
  4 | $ node server.js 1234 
  5 | */
  6 | var http = require("http"),
  7 |     url = require("url"),
  8 |     path = require("path"),
  9 |     fs = require("fs")
 10 |     port = process.argv[2] || 80;
 11 | 
 12 | function formidable(req, res) {
 13 |     // parse a file upload
 14 |     var expect = 'sp';
 15 |     var sp, cont, type, total = 0;
 16 |     req.on('data', function(tr) {
 17 |         //console.log('trunk:', tr, tr.toString());
 18 |         while(1) {
 19 |             switch(expect) {
 20 |                 case 'sp':
 21 |                     var idx = tr.indexOf('\r\n');
 22 |                     if(idx == -1) return;
 23 |                     sp = tr.slice(0, idx).toString();
 24 |                     tr = tr.slice(idx+2);
 25 |                     console.log(sp);
 26 |                     expect = 'content';
 27 |                     break;
 28 |                 case 'content':
 29 |                     var idx = tr.indexOf('\r\n');
 30 |                     cont = tr.slice(0, idx).toString();
 31 |                     console.log(cont);
 32 |                     if(/Content-Disposition: ?form-data;.*filename="/.test(cont)) {
 33 |                         expect = 'type';
 34 |                         tr = tr.slice(idx+2);
 35 |                     } else {
 36 |                         expect = 'value';
 37 |                         tr = tr.slice(idx+4);
 38 |                     }
 39 |                     break;
 40 |                 case 'value':
 41 |                     var idx = tr.indexOf('\r\n');
 42 |                     value = tr.slice(0, idx).toString();
 43 |                     tr = tr.slice(idx+2);
 44 |                     console.log(value);
 45 |                     expect = 'sp';
 46 |                     break;
 47 |                 case 'type':
 48 |                     var idx = tr.indexOf('\r\n');
 49 |                     type = tr.slice(0, idx).toString();
 50 |                     tr = tr.slice(idx+4);
 51 |                     console.log(type);
 52 |                     expect = 'end';
 53 |                     break;
 54 |                 case 'end':
 55 |                     var idx = tr.indexOf('\r\n'+sp);
 56 |                     process.stdout.write('.');
 57 |                     if(idx >= 0) {
 58 |                         total += idx;
 59 |                     } else total += tr.length;
 60 |                     return;
 61 |             }
 62 |         }
 63 |     }).on('end',function() {
 64 |         console.log('\ntotal:', total);
 65 |         res.end(`
 66 |             ${cont}
 67 |             total: ${total}
 68 |         `);
 69 |     });
 70 | }
 71 | 
 72 | var mimeTypes = {
 73 |     "htm": "text/html",
 74 |     "html": "text/html",
 75 |     "jpeg": "image/jpeg",
 76 |     "jpg": "image/jpeg",
 77 |     "png": "image/png",
 78 |     "gif": "image/gif",
 79 |     "js": "text/javascript",
 80 |     "css": "text/css"};
 81 | 
 82 | var virtualDirectories = {
 83 |     //"images": "../images/"
 84 |   };
 85 | 
 86 | process.chdir(__dirname);
 87 | http.createServer(function(request, response) {
 88 |   //console.log('0000000000', request.url);
 89 |   if (request.url == '/upload' && request.method.toLowerCase() == 'post') {
 90 |      console.log('upload', request.url);
 91 |      formidable(request, response);
 92 |      return;
 93 |   }
 94 | 
 95 |   var uri = url.parse(request.url).pathname
 96 |     , filename = path.join(process.cwd(), uri)
 97 |     , root = uri.split("/")[1]
 98 |     , virtualDirectory;
 99 |   
100 |   virtualDirectory = virtualDirectories[root];
101 |   if(virtualDirectory){
102 |     uri = uri.slice(root.length + 1, uri.length);
103 |     filename = path.join(virtualDirectory ,uri);
104 |   }
105 | 
106 |   fs.exists(filename, function(exists) {
107 |     if(!exists) {
108 |       response.writeHead(404, {"Content-Type": "text/plain"});
109 |       response.write("404 Not Found\n");
110 |       response.end();
111 |       console.error('404: ' + filename);
112 |       return;
113 |     }
114 | 
115 |     if (fs.statSync(filename).isDirectory()) filename += '/index.html';
116 | 
117 |     fs.readFile(filename, "binary", function(err, file) {
118 |       if(err) {        
119 |         response.writeHead(500, {"Content-Type": "text/plain"});
120 |         response.write(err + "\n");
121 |         response.end();
122 |         console.error('500: ' + filename);
123 |         return;
124 |       }
125 | 
126 |       var mimeType = mimeTypes[path.extname(filename).split(".")[1]];
127 |       response.writeHead(200, {"Content-Type": mimeType});
128 |       response.write(file, "binary");
129 |       response.end();
130 |       console.log('200: ' + filename + ' as ' + mimeType);
131 |     });
132 |   });
133 | }).listen(parseInt(port, 10));
134 | 
135 | console.log("Static file server running at\n  => http://localhost:" + port + "/\nCTRL + C to shutdown");
136 | 


--------------------------------------------------------------------------------
/demo3/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 |     demo 3
 5 | 
 6 | 
 7 | 
 8 | 
 9 | 
10 | 
11 | Browse... 12 |
13 | 14 | 15 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /demo4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | demo 4 5 | 6 | 7 | 8 | 9 | 10 |
11 | Browse... 12 |
13 | 14 | 15 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /demo4/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | $ node server.js 3 | or 4 | $ node server.js 1234 5 | */ 6 | var http = require("http"), 7 | url = require("url"), 8 | path = require("path"), 9 | fs = require("fs") 10 | port = process.argv[2] || 80; 11 | 12 | function formidable(req, res) { 13 | // parse a file upload 14 | var expect = 'sp'; 15 | var sp, cont, type, total = 0; 16 | req.on('data', function(tr) { 17 | //console.log('trunk:', tr, tr.toString()); 18 | while(1) { 19 | switch(expect) { 20 | case 'sp': 21 | var idx = tr.indexOf('\r\n'); 22 | sp = tr.slice(0, idx).toString(); 23 | tr = tr.slice(idx+2); 24 | console.log(sp); 25 | expect = 'content'; 26 | break; 27 | case 'content': 28 | var idx = tr.indexOf('\r\n'); 29 | cont = tr.slice(0, idx).toString(); 30 | console.log(cont); 31 | if(/Content-Disposition: ?form-data;.*filename="/.test(cont)) { 32 | expect = 'type'; 33 | tr = tr.slice(idx+2); 34 | } else { 35 | expect = 'value'; 36 | tr = tr.slice(idx+4); 37 | } 38 | break; 39 | case 'value': 40 | var idx = tr.indexOf('\r\n'); 41 | value = tr.slice(0, idx).toString(); 42 | tr = tr.slice(idx+2); 43 | console.log(value); 44 | expect = 'sp'; 45 | break; 46 | case 'type': 47 | var idx = tr.indexOf('\r\n'); 48 | type = tr.slice(0, idx).toString(); 49 | tr = tr.slice(idx+4); 50 | console.log(type); 51 | expect = 'end'; 52 | break; 53 | case 'end': 54 | var idx = tr.indexOf('\r\n'+sp); 55 | console.log('data length:', tr.length); 56 | if(idx >= 0) { 57 | total += idx; 58 | } else total += tr.length; 59 | return; 60 | } 61 | } 62 | }).on('end',function() { 63 | console.log('total:', total); 64 | //res.statusCode = 404; 65 | //res.end('not fonddound'); 66 | res.end(` 67 | 68 | 69 |

${cont}

70 |

total: ${total}

71 | `); 72 | 73 | }); 74 | } 75 | 76 | var mimeTypes = { 77 | "htm": "text/html", 78 | "html": "text/html", 79 | "jpeg": "image/jpeg", 80 | "jpg": "image/jpeg", 81 | "png": "image/png", 82 | "gif": "image/gif", 83 | "js": "text/javascript", 84 | "css": "text/css"}; 85 | 86 | var virtualDirectories = { 87 | //"images": "../images/" 88 | }; 89 | 90 | process.chdir(__dirname); 91 | http.createServer(function(request, response) { 92 | if (request.url == '/upload' && request.method.toLowerCase() == 'post') { 93 | console.log('upload', request.url); 94 | formidable(request, response); 95 | return; 96 | } 97 | 98 | var uri = url.parse(request.url).pathname 99 | , filename = path.join(process.cwd(), uri) 100 | , root = uri.split("/")[1] 101 | , virtualDirectory; 102 | 103 | virtualDirectory = virtualDirectories[root]; 104 | if(virtualDirectory){ 105 | uri = uri.slice(root.length + 1, uri.length); 106 | filename = path.join(virtualDirectory ,uri); 107 | } 108 | 109 | fs.exists(filename, function(exists) { 110 | if(!exists) { 111 | response.writeHead(404, {"Content-Type": "text/plain"}); 112 | response.write("404 Not Found\n"); 113 | response.end(); 114 | console.error('404: ' + filename); 115 | return; 116 | } 117 | 118 | if (fs.statSync(filename).isDirectory()) filename += '/index.html'; 119 | 120 | fs.readFile(filename, "binary", function(err, file) { 121 | if(err) { 122 | response.writeHead(500, {"Content-Type": "text/plain"}); 123 | response.write(err + "\n"); 124 | response.end(); 125 | console.error('500: ' + filename); 126 | return; 127 | } 128 | 129 | var mimeType = mimeTypes[path.extname(filename).split(".")[1]]; 130 | response.writeHead(200, {"Content-Type": mimeType}); 131 | response.write(file, "binary"); 132 | response.end(); 133 | console.log('200: ' + filename + ' as ' + mimeType); 134 | }); 135 | }); 136 | }).listen(parseInt(port, 10)); 137 | 138 | console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown"); 139 | -------------------------------------------------------------------------------- /demo5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | demo 5 5 | 6 | 7 | 8 | 9 | 10 |
11 | Browse... 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /demo5/plupload.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Plupload - multi-runtime File Uploader 3 | * v2.2.1 4 | * 5 | * Copyright 2013, Moxiecode Systems AB 6 | * Released under GPL License. 7 | * 8 | * License: http://www.plupload.com/license 9 | * Contributing: http://www.plupload.com/contributing 10 | * 11 | * Date: 2016-11-23 12 | */ 13 | !function(e,t){var i=function(){var e={};return t.apply(e,arguments),e.plupload};"function"==typeof define&&define.amd?define("plupload",["./moxie"],i):"object"==typeof module&&module.exports?module.exports=i(require("./moxie")):e.plupload=i(e.moxie)}(this||window,function(e){!function(e,t,i){function n(e){function t(e,t,i){var r={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"};r[e]?n[r[e]]=t:i||(n[e]=t)}var i=e.required_features,n={};return"string"==typeof i?l.each(i.split(/\s*,\s*/),function(e){t(e,!0)}):"object"==typeof i?l.each(i,function(e,i){t(i,e)}):i===!0&&(e.chunk_size>0&&(n.slice_blob=!0),l.isEmptyObj(e.resize)&&e.multipart||(n.send_binary_string=!0),l.each(e,function(e,i){t(i,!!e,!0)})),n}var r=window.setTimeout,s={},a=t.core.utils,o=t.runtime.Runtime,l={VERSION:"2.2.1",STOPPED:1,STARTED:2,QUEUED:1,UPLOADING:2,FAILED:4,DONE:5,GENERIC_ERROR:-100,HTTP_ERROR:-200,IO_ERROR:-300,SECURITY_ERROR:-400,INIT_ERROR:-500,FILE_SIZE_ERROR:-600,FILE_EXTENSION_ERROR:-601,FILE_DUPLICATE_ERROR:-602,IMAGE_FORMAT_ERROR:-700,MEMORY_ERROR:-701,IMAGE_DIMENSIONS_ERROR:-702,mimeTypes:a.Mime.mimes,ua:a.Env,typeOf:a.Basic.typeOf,extend:a.Basic.extend,guid:a.Basic.guid,getAll:function(e){var t,i=[];"array"!==l.typeOf(e)&&(e=[e]);for(var n=e.length;n--;)t=l.get(e[n]),t&&i.push(t);return i.length?i:null},get:a.Dom.get,each:a.Basic.each,getPos:a.Dom.getPos,getSize:a.Dom.getSize,xmlEncode:function(e){var t={"<":"lt",">":"gt","&":"amp",'"':"quot","'":"#39"},i=/[<>&\"\']/g;return e?(""+e).replace(i,function(e){return t[e]?"&"+t[e]+";":e}):e},toArray:a.Basic.toArray,inArray:a.Basic.inArray,inSeries:a.Basic.inSeries,addI18n:t.core.I18n.addI18n,translate:t.core.I18n.translate,sprintf:a.Basic.sprintf,isEmptyObj:a.Basic.isEmptyObj,hasClass:a.Dom.hasClass,addClass:a.Dom.addClass,removeClass:a.Dom.removeClass,getStyle:a.Dom.getStyle,addEvent:a.Events.addEvent,removeEvent:a.Events.removeEvent,removeAllEvents:a.Events.removeAllEvents,cleanName:function(e){var t,i;for(i=[/[\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"],t=0;t0?"&":"?")+i),e},formatSize:function(e){function t(e,t){return Math.round(e*Math.pow(10,t))/Math.pow(10,t)}if(e===i||/\D/.test(e))return l.translate("N/A");var n=Math.pow(1024,4);return e>n?t(e/n,1)+" "+l.translate("tb"):e>(n/=1024)?t(e/n,1)+" "+l.translate("gb"):e>(n/=1024)?t(e/n,1)+" "+l.translate("mb"):e>1024?Math.round(e/1024)+" "+l.translate("kb"):e+" "+l.translate("b")},parseSize:a.Basic.parseSizeStr,predictRuntime:function(e,t){var i,n;return i=new l.Uploader(e),n=o.thatCan(i.getOption().required_features,t||e.runtimes),i.destroy(),n},addFileFilter:function(e,t){s[e]=t}};l.addFileFilter("mime_types",function(e,t,i){e.length&&!e.regexp.test(t.name)?(this.trigger("Error",{code:l.FILE_EXTENSION_ERROR,message:l.translate("File extension error."),file:t}),i(!1)):i(!0)}),l.addFileFilter("max_file_size",function(e,t,i){var n;e=l.parseSize(e),t.size!==n&&e&&t.size>e?(this.trigger("Error",{code:l.FILE_SIZE_ERROR,message:l.translate("File size error."),file:t}),i(!1)):i(!0)}),l.addFileFilter("prevent_duplicates",function(e,t,i){if(e)for(var n=this.files.length;n--;)if(t.name===this.files[n].name&&t.size===this.files[n].size)return this.trigger("Error",{code:l.FILE_DUPLICATE_ERROR,message:l.translate("Duplicate file error."),file:t}),i(!1),void 0;i(!0)}),l.Uploader=function(e){function a(){var e,t,i=0;if(this.state==l.STARTED){for(t=0;t0?Math.ceil(100*(e.loaded/e.size)):100,d()}function d(){var e,t;for(w.reset(),e=0;e0?Math.ceil(100*(w.uploaded/T.length)):0:(w.bytesPerSec=Math.ceil(w.loaded/((+new Date-S||1)/1e3)),w.percent=w.size>0?Math.ceil(100*(w.loaded/w.size)):0)}function c(){var e=A[0]||P[0];return e?e.getRuntime().uid:!1}function f(e,t){if(e.ruid){var i=o.getInfo(e.ruid);if(i)return i.can(t)}return!1}function p(){this.bind("FilesAdded FilesRemoved",function(e){e.trigger("QueueChanged"),e.refresh()}),this.bind("CancelUpload",y),this.bind("BeforeUpload",m),this.bind("UploadFile",E),this.bind("UploadProgress",v),this.bind("StateChanged",b),this.bind("QueueChanged",d),this.bind("Error",z),this.bind("FileUploaded",R),this.bind("Destroy",O)}function g(e,i){var n=this,r=0,s=[],a={runtime_order:e.runtimes,required_caps:e.required_features,preferred_caps:F,swf_url:e.flash_swf_url,xap_url:e.silverlight_xap_url};l.each(e.runtimes.split(/\s*,\s*/),function(t){e[t]&&(a[t]=e[t])}),e.browse_button&&l.each(e.browse_button,function(i){s.push(function(s){var u=new t.file.FileInput(l.extend({},a,{accept:e.filters.mime_types,name:e.file_data_name,multiple:e.multi_selection,container:e.container,browse_button:i}));u.onready=function(){var e=o.getInfo(this.ruid);l.extend(n.features,{chunks:e.can("slice_blob"),multipart:e.can("send_multipart"),multi_selection:e.can("select_multiple")}),r++,A.push(this),s()},u.onchange=function(){n.addFile(this.files)},u.bind("mouseenter mouseleave mousedown mouseup",function(t){U||(e.browse_button_hover&&("mouseenter"===t.type?l.addClass(i,e.browse_button_hover):"mouseleave"===t.type&&l.removeClass(i,e.browse_button_hover)),e.browse_button_active&&("mousedown"===t.type?l.addClass(i,e.browse_button_active):"mouseup"===t.type&&l.removeClass(i,e.browse_button_active)))}),u.bind("mousedown",function(){n.trigger("Browse")}),u.bind("error runtimeerror",function(){u=null,s()}),u.init()})}),e.drop_element&&l.each(e.drop_element,function(e){s.push(function(i){var s=new t.file.FileDrop(l.extend({},a,{drop_zone:e}));s.onready=function(){var e=o.getInfo(this.ruid);l.extend(n.features,{chunks:e.can("slice_blob"),multipart:e.can("send_multipart"),dragdrop:e.can("drag_and_drop")}),r++,P.push(this),i()},s.ondrop=function(){n.addFile(this.files)},s.bind("error runtimeerror",function(){s=null,i()}),s.init()})}),l.inSeries(s,function(){"function"==typeof i&&i(r)})}function h(e,n,r){var s=new t.image.Image;try{s.onload=function(){return n.width>this.width&&n.height>this.height&&n.quality===i&&n.preserve_headers&&!n.crop?(this.destroy(),r(e)):(s.downsize(n.width,n.height,n.crop,n.preserve_headers),void 0)},s.onresize=function(){r(this.getAsBlob(e.type,n.quality)),this.destroy()},s.onerror=function(){r(e)},s.load(e)}catch(a){r(e)}}function _(e,i,r){function s(e,i,n){var r=I[e];switch(e){case"max_file_size":"max_file_size"===e&&(I.max_file_size=I.filters.max_file_size=i);break;case"chunk_size":(i=l.parseSize(i))&&(I[e]=i,I.send_file_name=!0);break;case"multipart":I[e]=i,i||(I.send_file_name=!0);break;case"unique_names":I[e]=i,i&&(I.send_file_name=!0);break;case"filters":"array"===l.typeOf(i)&&(i={mime_types:i}),n?l.extend(I.filters,i):I.filters=i,i.mime_types&&("string"===l.typeOf(i.mime_types)&&(i.mime_types=t.core.utils.Mime.mimes2extList(i.mime_types)),i.mime_types.regexp=function(e){var t=[];return l.each(e,function(e){l.each(e.extensions.split(/,/),function(e){/^\s*\*\s*$/.test(e)?t.push("\\.*"):t.push("\\."+e.replace(new RegExp("["+"/^$.*+?|()[]{}\\".replace(/./g,"\\$&")+"]","g"),"\\$&"))})}),new RegExp("("+t.join("|")+")$","i")}(i.mime_types),I.filters.mime_types=i.mime_types);break;case"resize":I.resize=i?l.extend({preserve_headers:!0,crop:!1},i):!1;break;case"prevent_duplicates":I.prevent_duplicates=I.filters.prevent_duplicates=!!i;break;case"container":case"browse_button":case"drop_element":i="container"===e?l.get(i):l.getAll(i);case"runtimes":case"multi_selection":case"flash_swf_url":case"silverlight_xap_url":I[e]=i,n||(u=!0);break;default:I[e]=i}n||a.trigger("OptionChanged",e,i,r)}var a=this,u=!1;"object"==typeof e?l.each(e,function(e,t){s(t,e,r)}):s(e,i,r),r?(I.required_features=n(l.extend({},I)),F=n(l.extend({},I,{required_features:!0}))):u&&(a.trigger("Destroy"),g.call(a,I,function(e){e?(a.runtime=o.getInfo(c()).type,a.trigger("Init",{runtime:a.runtime}),a.trigger("PostInit")):a.trigger("Error",{code:l.INIT_ERROR,message:l.translate("Init error.")})}))}function m(e,t){if(e.settings.unique_names){var i=t.name.match(/\.([^.]+)$/),n="part";i&&(n=i[1]),t.target_name=t.id+"."+n}}function E(e,i){function n(){d-->0?r(s,1e3):(i.loaded=p,e.trigger("Error",{code:l.HTTP_ERROR,message:l.translate("HTTP Error."),file:i,response:D.responseText,status:D.status,responseHeaders:D.getAllResponseHeaders()}))}function s(){var f,g,h,_={};i.status===l.UPLOADING&&e.state!==l.STOPPED&&(e.settings.send_file_name&&(_.name=i.target_name||i.name),u&&c.chunks&&a.size>u?(h=Math.min(u,a.size-p),f=a.slice(p,p+h)):(h=a.size,f=a),u&&c.chunks&&(e.settings.send_chunk_number?(_.chunk=Math.ceil(p/u),_.chunks=Math.ceil(a.size/u)):(_.offset=p,_.total=a.size)),D=new t.xhr.XMLHttpRequest,D.upload&&(D.upload.onprogress=function(t){i.loaded=Math.min(i.size,p+t.loaded),e.trigger("UploadProgress",i)}),D.onload=function(){return D.status>=400?(n(),void 0):(d=e.settings.max_retries,h=a.size?(i.size!=i.origSize&&(a.destroy(),a=null),e.trigger("UploadProgress",i),i.status=l.DONE,e.trigger("FileUploaded",i,{response:D.responseText,status:D.status,responseHeaders:D.getAllResponseHeaders()})):r(s,1),void 0)},D.onerror=function(){n()},D.onloadend=function(){this.destroy(),D=null},e.settings.multipart&&c.multipart?(D.open("post",o,!0),l.each(e.settings.headers,function(e,t){D.setRequestHeader(t,e)}),g=new t.xhr.FormData,l.each(l.extend(_,e.settings.multipart_params),function(e,t){g.append(t,e)}),g.append(e.settings.file_data_name,f),D.send(g,{runtime_order:e.settings.runtimes,required_caps:e.settings.required_features,preferred_caps:F,swf_url:e.settings.flash_swf_url,xap_url:e.settings.silverlight_xap_url})):(o=l.buildUrl(e.settings.url,l.extend(_,e.settings.multipart_params)),D.open("post",o,!0),l.each(e.settings.headers,function(e,t){D.setRequestHeader(t,e)}),D.hasRequestHeader("Content-Type")||D.setRequestHeader("Content-Type","application/octet-stream"),D.send(f,{runtime_order:e.settings.runtimes,required_caps:e.settings.required_features,preferred_caps:F,swf_url:e.settings.flash_swf_url,xap_url:e.settings.silverlight_xap_url})))}var a,o=e.settings.url,u=e.settings.chunk_size,d=e.settings.max_retries,c=e.features,p=0;i.loaded&&(p=i.loaded=u?u*Math.floor(i.loaded/u):0),a=i.getSource(),!l.isEmptyObj(e.settings.resize)&&f(a,"send_binary_string")&&-1!==l.inArray(a.type,["image/jpeg","image/png"])?h.call(this,a,e.settings.resize,function(e){a=e,i.size=e.size,s()}):s()}function v(e,t){u(t)}function b(e){if(e.state==l.STARTED)S=+new Date;else if(e.state==l.STOPPED)for(var t=e.files.length-1;t>=0;t--)e.files[t].status==l.UPLOADING&&(e.files[t].status=l.QUEUED,d())}function y(){D&&D.abort()}function R(e){d(),r(function(){a.call(e)},1)}function z(e,t){t.code===l.INIT_ERROR?e.destroy():t.code===l.HTTP_ERROR&&(t.file.status=l.FAILED,u(t.file),e.state==l.STARTED&&(e.trigger("CancelUpload"),r(function(){a.call(e)},1)))}function O(e){e.stop(),l.each(T,function(e){e.destroy()}),T=[],A.length&&(l.each(A,function(e){e.destroy()}),A=[]),P.length&&(l.each(P,function(e){e.destroy()}),P=[]),F={},U=!1,S=D=null,w.reset()}var I,S,w,D,x=l.guid(),T=[],F={},A=[],P=[],U=!1;I={runtimes:o.order,max_retries:0,chunk_size:0,multipart:!0,multi_selection:!0,file_data_name:"file",flash_swf_url:"js/Moxie.swf",silverlight_xap_url:"js/Moxie.xap",filters:{mime_types:[],prevent_duplicates:!1,max_file_size:0},resize:!1,send_file_name:!0,send_chunk_number:!0},_.call(this,e,null,!0),w=new l.QueueProgress,l.extend(this,{id:x,uid:x,state:l.STOPPED,features:{},runtime:null,files:T,settings:I,total:w,init:function(){var e,t,i=this;return e=i.getOption("preinit"),"function"==typeof e?e(i):l.each(e,function(e,t){i.bind(t,e)}),p.call(i),l.each(["container","browse_button","drop_element"],function(e){return null===i.getOption(e)?(t={code:l.INIT_ERROR,message:l.sprintf(l.translate("%s specified, but cannot be found."),e)},!1):void 0}),t?i.trigger("Error",t):I.browse_button||I.drop_element?(g.call(i,I,function(e){var t=i.getOption("init");"function"==typeof t?t(i):l.each(t,function(e,t){i.bind(t,e)}),e?(i.runtime=o.getInfo(c()).type,i.trigger("Init",{runtime:i.runtime}),i.trigger("PostInit")):i.trigger("Error",{code:l.INIT_ERROR,message:l.translate("Init error.")})}),void 0):i.trigger("Error",{code:l.INIT_ERROR,message:l.translate("You must specify either browse_button or drop_element.")})},setOption:function(e,t){_.call(this,e,t,!this.runtime)},getOption:function(e){return e?I[e]:I},refresh:function(){A.length&&l.each(A,function(e){e.trigger("Refresh")}),this.trigger("Refresh")},start:function(){this.state!=l.STARTED&&(this.state=l.STARTED,this.trigger("StateChanged"),a.call(this))},stop:function(){this.state!=l.STOPPED&&(this.state=l.STOPPED,this.trigger("StateChanged"),this.trigger("CancelUpload"))},disableBrowse:function(){U=arguments[0]!==i?arguments[0]:!0,A.length&&l.each(A,function(e){e.disable(U)}),this.trigger("DisableBrowse",U)},getFile:function(e){var t;for(t=T.length-1;t>=0;t--)if(T[t].id===e)return T[t]},addFile:function(e,i){function n(e,t){var i=[];l.each(u.settings.filters,function(t,n){s[n]&&i.push(function(i){s[n].call(u,t,e,function(e){i(!e)})})}),l.inSeries(i,t)}function a(e){var s=l.typeOf(e);if(e instanceof t.file.File){if(!e.ruid&&!e.isDetached()){if(!o)return!1;e.ruid=o,e.connectRuntime(o)}a(new l.File(e))}else e instanceof t.file.Blob?(a(e.getSource()),e.destroy()):e instanceof l.File?(i&&(e.name=i),d.push(function(t){n(e,function(i){i||(T.push(e),f.push(e),u.trigger("FileFiltered",e)),r(t,1)})})):-1!==l.inArray(s,["file","blob"])?a(new t.file.File(null,e)):"node"===s&&"filelist"===l.typeOf(e.files)?l.each(e.files,a):"array"===s&&(i=null,l.each(e,a))}var o,u=this,d=[],f=[];o=c(),a(e),d.length&&l.inSeries(d,function(){f.length&&u.trigger("FilesAdded",f)})},removeFile:function(e){for(var t="string"==typeof e?e:e.id,i=T.length-1;i>=0;i--)if(T[i].id===t)return this.splice(i,1)[0]},splice:function(e,t){var n=T.splice(e===i?0:e,t===i?T.length:t),r=!1;return this.state==l.STARTED&&(l.each(n,function(e){return e.status===l.UPLOADING?(r=!0,!1):void 0}),r&&this.stop()),this.trigger("FilesRemoved",n),l.each(n,function(e){e.destroy()}),r&&this.start(),n},dispatchEvent:function(e){var t,i;if(e=e.toLowerCase(),t=this.hasEventListener(e)){t.sort(function(e,t){return t.priority-e.priority}),i=[].slice.call(arguments),i.shift(),i.unshift(this);for(var n=0;n= 0) { 57 | console.log('end'); 58 | } 59 | return; 60 | } 61 | } 62 | }).on('end',function() { 63 | res.end(` 64 | 65 | 66 | ${cont}`); 67 | }); 68 | } 69 | 70 | var mimeTypes = { 71 | "htm": "text/html", 72 | "html": "text/html", 73 | "jpeg": "image/jpeg", 74 | "jpg": "image/jpeg", 75 | "png": "image/png", 76 | "gif": "image/gif", 77 | "js": "text/javascript", 78 | "css": "text/css"}; 79 | 80 | var virtualDirectories = { 81 | //"images": "../images/" 82 | }; 83 | 84 | process.chdir(__dirname); 85 | http.createServer(function(request, response) { 86 | //console.log('0000000000', request.url); 87 | if (request.url == '/upload' && request.method.toLowerCase() == 'post') { 88 | console.log('upload', request.url); 89 | formidable(request, response); 90 | return; 91 | } 92 | 93 | var uri = url.parse(request.url).pathname 94 | , filename = path.join(process.cwd(), uri) 95 | , root = uri.split("/")[1] 96 | , virtualDirectory; 97 | 98 | virtualDirectory = virtualDirectories[root]; 99 | if(virtualDirectory){ 100 | uri = uri.slice(root.length + 1, uri.length); 101 | filename = path.join(virtualDirectory ,uri); 102 | } 103 | 104 | fs.exists(filename, function(exists) { 105 | if(!exists) { 106 | response.writeHead(404, {"Content-Type": "text/plain"}); 107 | response.write("404 Not Found\n"); 108 | response.end(); 109 | console.error('404: ' + filename); 110 | return; 111 | } 112 | 113 | if (fs.statSync(filename).isDirectory()) filename += '/index.html'; 114 | 115 | fs.readFile(filename, "binary", function(err, file) { 116 | if(err) { 117 | response.writeHead(500, {"Content-Type": "text/plain"}); 118 | response.write(err + "\n"); 119 | response.end(); 120 | console.error('500: ' + filename); 121 | return; 122 | } 123 | 124 | var mimeType = mimeTypes[path.extname(filename).split(".")[1]]; 125 | response.writeHead(200, {"Content-Type": mimeType}); 126 | response.write(file, "binary"); 127 | response.end(); 128 | console.log('200: ' + filename + ' as ' + mimeType); 129 | }); 130 | }); 131 | }).listen(parseInt(port, 10)); 132 | 133 | console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown"); 134 | -------------------------------------------------------------------------------- /demo6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | demo 6 5 | 6 | 7 | 8 |
    9 | 10 |
    11 | Browse... 12 |
    13 | 14 | 15 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /demo6/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | $ node server.js 3 | or 4 | $ node server.js 1234 5 | | 6 | V 7 | port 8 | */ 9 | var http = require("http"), 10 | url = require("url"), 11 | path = require("path"), 12 | fs = require("fs"); 13 | 14 | var port = process.argv[2] || 80; 15 | 16 | function formidable(req, res) { 17 | // parse a file upload 18 | var total = 0; 19 | req.on('data', function(tr) { 20 | while(1) { 21 | if(tr.length == 0) return; 22 | var idx = tr.indexOf('\r\n'); 23 | if(idx == -1) { 24 | process.stdout.write('data:'+tr.length); 25 | total += tr.length; 26 | return; 27 | } 28 | if(idx > 2000) { 29 | process.stdout.write('data:'+tr.length); 30 | total += idx; 31 | tr = tr.slice(idx+2); 32 | continue; 33 | } 34 | var flag = 0; 35 | for(var i = 0; i= 0x20 && b <= 0x7a)) { 38 | total += idx+2; 39 | tr = tr.slice(idx+2); 40 | flag = 1; 41 | break; 42 | } 43 | } 44 | if(flag) { 45 | continue; 46 | } 47 | var str = tr.slice(0, idx).toString(); 48 | if(/\<.*\>/.test(str)) { 49 | total += idx+2; 50 | tr = tr.slice(idx+2); 51 | continue; 52 | } 53 | console.log(str); 54 | tr = tr.slice(idx+2); 55 | } 56 | return; 57 | }).on('end',function() { 58 | console.log('\ntotal:', total); 59 | res.end('total: ' + total); 60 | }); 61 | } 62 | 63 | function getRandom(start, end) { 64 | return Math.floor(Math.random() * (end - start)) + start; 65 | } 66 | 67 | var mimeTypes = { 68 | "htm": "text/html", 69 | "html": "text/html", 70 | "jpeg": "image/jpeg", 71 | "jpg": "image/jpeg", 72 | "png": "image/png", 73 | "gif": "image/gif", 74 | "js": "text/javascript", 75 | "css": "text/css"}; 76 | 77 | var virtualDirectories = { 78 | //"images": "../images/" 79 | }; 80 | 81 | process.chdir(__dirname); 82 | http.createServer(function(request, response) { 83 | if (request.url == '/upload' && request.method.toLowerCase() == 'post') { 84 | console.log('post', request.url); 85 | formidable(request, response); 86 | return; 87 | } 88 | 89 | if (request.url == '/offset') { 90 | response.end(getRandom(100, 50)+''); 91 | return; 92 | } 93 | 94 | var uri = url.parse(request.url).pathname 95 | , filename = path.join(process.cwd(), uri) 96 | , root = uri.split("/")[1] 97 | , virtualDirectory; 98 | 99 | virtualDirectory = virtualDirectories[root]; 100 | if(virtualDirectory){ 101 | uri = uri.slice(root.length + 1, uri.length); 102 | filename = path.join(virtualDirectory ,uri); 103 | } 104 | 105 | fs.exists(filename, function(exists) { 106 | if(!exists) { 107 | response.writeHead(404, {"Content-Type": "text/plain"}); 108 | response.write("404 Not Found\n"); 109 | response.end(); 110 | console.error('404: ' + filename); 111 | return; 112 | } 113 | 114 | if (fs.statSync(filename).isDirectory()) filename += '/index.html'; 115 | 116 | fs.readFile(filename, "binary", function(err, file) { 117 | if(err) { 118 | response.writeHead(500, {"Content-Type": "text/plain"}); 119 | response.write(err + "\n"); 120 | response.end(); 121 | console.error('500: ' + filename); 122 | return; 123 | } 124 | 125 | var mimeType = mimeTypes[path.extname(filename).split(".")[1]]; 126 | response.writeHead(200, {"Content-Type": mimeType}); 127 | response.write(file, "binary"); 128 | response.end(); 129 | console.log('200: ' + filename + ' as ' + mimeType); 130 | }); 131 | }); 132 | }).listen(parseInt(port, 10)); 133 | 134 | console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown"); 135 | -------------------------------------------------------------------------------- /demo7/images/ui-icons_444444_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/demo7/images/ui-icons_444444_256x240.png -------------------------------------------------------------------------------- /demo7/images/ui-icons_555555_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/demo7/images/ui-icons_555555_256x240.png -------------------------------------------------------------------------------- /demo7/images/ui-icons_777620_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/demo7/images/ui-icons_777620_256x240.png -------------------------------------------------------------------------------- /demo7/images/ui-icons_777777_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/demo7/images/ui-icons_777777_256x240.png -------------------------------------------------------------------------------- /demo7/images/ui-icons_cc0000_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/demo7/images/ui-icons_cc0000_256x240.png -------------------------------------------------------------------------------- /demo7/images/ui-icons_ffffff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/demo7/images/ui-icons_ffffff_256x240.png -------------------------------------------------------------------------------- /demo7/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/demo7/img/loading.gif -------------------------------------------------------------------------------- /demo7/img/plupload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/demo7/img/plupload.png -------------------------------------------------------------------------------- /demo7/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Plupload: UI Widget 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 |
    16 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /demo7/jquery-ui.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.12.1 - 2017-02-05 2 | * http://jqueryui.com 3 | * Includes: draggable.css, core.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css 4 | * To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif 5 | * Copyright jQuery Foundation and other contributors; Licensed MIT */ 6 | 7 | .ui-draggable-handle { 8 | -ms-touch-action: none; 9 | touch-action: none; 10 | } 11 | /* Layout helpers 12 | ----------------------------------*/ 13 | .ui-helper-hidden { 14 | display: none; 15 | } 16 | .ui-helper-hidden-accessible { 17 | border: 0; 18 | clip: rect(0 0 0 0); 19 | height: 1px; 20 | margin: -1px; 21 | overflow: hidden; 22 | padding: 0; 23 | position: absolute; 24 | width: 1px; 25 | } 26 | .ui-helper-reset { 27 | margin: 0; 28 | padding: 0; 29 | border: 0; 30 | outline: 0; 31 | line-height: 1.3; 32 | text-decoration: none; 33 | font-size: 100%; 34 | list-style: none; 35 | } 36 | .ui-helper-clearfix:before, 37 | .ui-helper-clearfix:after { 38 | content: ""; 39 | display: table; 40 | border-collapse: collapse; 41 | } 42 | .ui-helper-clearfix:after { 43 | clear: both; 44 | } 45 | .ui-helper-zfix { 46 | width: 100%; 47 | height: 100%; 48 | top: 0; 49 | left: 0; 50 | position: absolute; 51 | opacity: 0; 52 | filter:Alpha(Opacity=0); /* support: IE8 */ 53 | } 54 | 55 | .ui-front { 56 | z-index: 100; 57 | } 58 | 59 | 60 | /* Interaction Cues 61 | ----------------------------------*/ 62 | .ui-state-disabled { 63 | cursor: default !important; 64 | pointer-events: none; 65 | } 66 | 67 | 68 | /* Icons 69 | ----------------------------------*/ 70 | .ui-icon { 71 | display: inline-block; 72 | vertical-align: middle; 73 | margin-top: -.25em; 74 | position: relative; 75 | text-indent: -99999px; 76 | overflow: hidden; 77 | background-repeat: no-repeat; 78 | } 79 | 80 | .ui-widget-icon-block { 81 | left: 50%; 82 | margin-left: -8px; 83 | display: block; 84 | } 85 | 86 | /* Misc visuals 87 | ----------------------------------*/ 88 | 89 | /* Overlays */ 90 | .ui-widget-overlay { 91 | position: fixed; 92 | top: 0; 93 | left: 0; 94 | width: 100%; 95 | height: 100%; 96 | } 97 | .ui-resizable { 98 | position: relative; 99 | } 100 | .ui-resizable-handle { 101 | position: absolute; 102 | font-size: 0.1px; 103 | display: block; 104 | -ms-touch-action: none; 105 | touch-action: none; 106 | } 107 | .ui-resizable-disabled .ui-resizable-handle, 108 | .ui-resizable-autohide .ui-resizable-handle { 109 | display: none; 110 | } 111 | .ui-resizable-n { 112 | cursor: n-resize; 113 | height: 7px; 114 | width: 100%; 115 | top: -5px; 116 | left: 0; 117 | } 118 | .ui-resizable-s { 119 | cursor: s-resize; 120 | height: 7px; 121 | width: 100%; 122 | bottom: -5px; 123 | left: 0; 124 | } 125 | .ui-resizable-e { 126 | cursor: e-resize; 127 | width: 7px; 128 | right: -5px; 129 | top: 0; 130 | height: 100%; 131 | } 132 | .ui-resizable-w { 133 | cursor: w-resize; 134 | width: 7px; 135 | left: -5px; 136 | top: 0; 137 | height: 100%; 138 | } 139 | .ui-resizable-se { 140 | cursor: se-resize; 141 | width: 12px; 142 | height: 12px; 143 | right: 1px; 144 | bottom: 1px; 145 | } 146 | .ui-resizable-sw { 147 | cursor: sw-resize; 148 | width: 9px; 149 | height: 9px; 150 | left: -5px; 151 | bottom: -5px; 152 | } 153 | .ui-resizable-nw { 154 | cursor: nw-resize; 155 | width: 9px; 156 | height: 9px; 157 | left: -5px; 158 | top: -5px; 159 | } 160 | .ui-resizable-ne { 161 | cursor: ne-resize; 162 | width: 9px; 163 | height: 9px; 164 | right: -5px; 165 | top: -5px; 166 | } 167 | .ui-selectable { 168 | -ms-touch-action: none; 169 | touch-action: none; 170 | } 171 | .ui-selectable-helper { 172 | position: absolute; 173 | z-index: 100; 174 | border: 1px dotted black; 175 | } 176 | .ui-sortable-handle { 177 | -ms-touch-action: none; 178 | touch-action: none; 179 | } 180 | .ui-accordion .ui-accordion-header { 181 | display: block; 182 | cursor: pointer; 183 | position: relative; 184 | margin: 2px 0 0 0; 185 | padding: .5em .5em .5em .7em; 186 | font-size: 100%; 187 | } 188 | .ui-accordion .ui-accordion-content { 189 | padding: 1em 2.2em; 190 | border-top: 0; 191 | overflow: auto; 192 | } 193 | .ui-autocomplete { 194 | position: absolute; 195 | top: 0; 196 | left: 0; 197 | cursor: default; 198 | } 199 | .ui-menu { 200 | list-style: none; 201 | padding: 0; 202 | margin: 0; 203 | display: block; 204 | outline: 0; 205 | } 206 | .ui-menu .ui-menu { 207 | position: absolute; 208 | } 209 | .ui-menu .ui-menu-item { 210 | margin: 0; 211 | cursor: pointer; 212 | /* support: IE10, see #8844 */ 213 | list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); 214 | } 215 | .ui-menu .ui-menu-item-wrapper { 216 | position: relative; 217 | padding: 3px 1em 3px .4em; 218 | } 219 | .ui-menu .ui-menu-divider { 220 | margin: 5px 0; 221 | height: 0; 222 | font-size: 0; 223 | line-height: 0; 224 | border-width: 1px 0 0 0; 225 | } 226 | .ui-menu .ui-state-focus, 227 | .ui-menu .ui-state-active { 228 | margin: -1px; 229 | } 230 | 231 | /* icon support */ 232 | .ui-menu-icons { 233 | position: relative; 234 | } 235 | .ui-menu-icons .ui-menu-item-wrapper { 236 | padding-left: 2em; 237 | } 238 | 239 | /* left-aligned */ 240 | .ui-menu .ui-icon { 241 | position: absolute; 242 | top: 0; 243 | bottom: 0; 244 | left: .2em; 245 | margin: auto 0; 246 | } 247 | 248 | /* right-aligned */ 249 | .ui-menu .ui-menu-icon { 250 | left: auto; 251 | right: 0; 252 | } 253 | .ui-button { 254 | padding: .4em 1em; 255 | display: inline-block; 256 | position: relative; 257 | line-height: normal; 258 | margin-right: .1em; 259 | cursor: pointer; 260 | vertical-align: middle; 261 | text-align: center; 262 | -webkit-user-select: none; 263 | -moz-user-select: none; 264 | -ms-user-select: none; 265 | user-select: none; 266 | 267 | /* Support: IE <= 11 */ 268 | overflow: visible; 269 | } 270 | 271 | .ui-button, 272 | .ui-button:link, 273 | .ui-button:visited, 274 | .ui-button:hover, 275 | .ui-button:active { 276 | text-decoration: none; 277 | } 278 | 279 | /* to make room for the icon, a width needs to be set here */ 280 | .ui-button-icon-only { 281 | width: 2em; 282 | box-sizing: border-box; 283 | text-indent: -9999px; 284 | white-space: nowrap; 285 | } 286 | 287 | /* no icon support for input elements */ 288 | input.ui-button.ui-button-icon-only { 289 | text-indent: 0; 290 | } 291 | 292 | /* button icon element(s) */ 293 | .ui-button-icon-only .ui-icon { 294 | position: absolute; 295 | top: 50%; 296 | left: 50%; 297 | margin-top: -8px; 298 | margin-left: -8px; 299 | } 300 | 301 | .ui-button.ui-icon-notext .ui-icon { 302 | padding: 0; 303 | width: 2.1em; 304 | height: 2.1em; 305 | text-indent: -9999px; 306 | white-space: nowrap; 307 | 308 | } 309 | 310 | input.ui-button.ui-icon-notext .ui-icon { 311 | width: auto; 312 | height: auto; 313 | text-indent: 0; 314 | white-space: normal; 315 | padding: .4em 1em; 316 | } 317 | 318 | /* workarounds */ 319 | /* Support: Firefox 5 - 40 */ 320 | input.ui-button::-moz-focus-inner, 321 | button.ui-button::-moz-focus-inner { 322 | border: 0; 323 | padding: 0; 324 | } 325 | .ui-controlgroup { 326 | vertical-align: middle; 327 | display: inline-block; 328 | } 329 | .ui-controlgroup > .ui-controlgroup-item { 330 | float: left; 331 | margin-left: 0; 332 | margin-right: 0; 333 | } 334 | .ui-controlgroup > .ui-controlgroup-item:focus, 335 | .ui-controlgroup > .ui-controlgroup-item.ui-visual-focus { 336 | z-index: 9999; 337 | } 338 | .ui-controlgroup-vertical > .ui-controlgroup-item { 339 | display: block; 340 | float: none; 341 | width: 100%; 342 | margin-top: 0; 343 | margin-bottom: 0; 344 | text-align: left; 345 | } 346 | .ui-controlgroup-vertical .ui-controlgroup-item { 347 | box-sizing: border-box; 348 | } 349 | .ui-controlgroup .ui-controlgroup-label { 350 | padding: .4em 1em; 351 | } 352 | .ui-controlgroup .ui-controlgroup-label span { 353 | font-size: 80%; 354 | } 355 | .ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item { 356 | border-left: none; 357 | } 358 | .ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item { 359 | border-top: none; 360 | } 361 | .ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content { 362 | border-right: none; 363 | } 364 | .ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content { 365 | border-bottom: none; 366 | } 367 | 368 | /* Spinner specific style fixes */ 369 | .ui-controlgroup-vertical .ui-spinner-input { 370 | 371 | /* Support: IE8 only, Android < 4.4 only */ 372 | width: 75%; 373 | width: calc( 100% - 2.4em ); 374 | } 375 | .ui-controlgroup-vertical .ui-spinner .ui-spinner-up { 376 | border-top-style: solid; 377 | } 378 | 379 | .ui-checkboxradio-label .ui-icon-background { 380 | box-shadow: inset 1px 1px 1px #ccc; 381 | border-radius: .12em; 382 | border: none; 383 | } 384 | .ui-checkboxradio-radio-label .ui-icon-background { 385 | width: 16px; 386 | height: 16px; 387 | border-radius: 1em; 388 | overflow: visible; 389 | border: none; 390 | } 391 | .ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon, 392 | .ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon { 393 | background-image: none; 394 | width: 8px; 395 | height: 8px; 396 | border-width: 4px; 397 | border-style: solid; 398 | } 399 | .ui-checkboxradio-disabled { 400 | pointer-events: none; 401 | } 402 | .ui-datepicker { 403 | width: 17em; 404 | padding: .2em .2em 0; 405 | display: none; 406 | } 407 | .ui-datepicker .ui-datepicker-header { 408 | position: relative; 409 | padding: .2em 0; 410 | } 411 | .ui-datepicker .ui-datepicker-prev, 412 | .ui-datepicker .ui-datepicker-next { 413 | position: absolute; 414 | top: 2px; 415 | width: 1.8em; 416 | height: 1.8em; 417 | } 418 | .ui-datepicker .ui-datepicker-prev-hover, 419 | .ui-datepicker .ui-datepicker-next-hover { 420 | top: 1px; 421 | } 422 | .ui-datepicker .ui-datepicker-prev { 423 | left: 2px; 424 | } 425 | .ui-datepicker .ui-datepicker-next { 426 | right: 2px; 427 | } 428 | .ui-datepicker .ui-datepicker-prev-hover { 429 | left: 1px; 430 | } 431 | .ui-datepicker .ui-datepicker-next-hover { 432 | right: 1px; 433 | } 434 | .ui-datepicker .ui-datepicker-prev span, 435 | .ui-datepicker .ui-datepicker-next span { 436 | display: block; 437 | position: absolute; 438 | left: 50%; 439 | margin-left: -8px; 440 | top: 50%; 441 | margin-top: -8px; 442 | } 443 | .ui-datepicker .ui-datepicker-title { 444 | margin: 0 2.3em; 445 | line-height: 1.8em; 446 | text-align: center; 447 | } 448 | .ui-datepicker .ui-datepicker-title select { 449 | font-size: 1em; 450 | margin: 1px 0; 451 | } 452 | .ui-datepicker select.ui-datepicker-month, 453 | .ui-datepicker select.ui-datepicker-year { 454 | width: 45%; 455 | } 456 | .ui-datepicker table { 457 | width: 100%; 458 | font-size: .9em; 459 | border-collapse: collapse; 460 | margin: 0 0 .4em; 461 | } 462 | .ui-datepicker th { 463 | padding: .7em .3em; 464 | text-align: center; 465 | font-weight: bold; 466 | border: 0; 467 | } 468 | .ui-datepicker td { 469 | border: 0; 470 | padding: 1px; 471 | } 472 | .ui-datepicker td span, 473 | .ui-datepicker td a { 474 | display: block; 475 | padding: .2em; 476 | text-align: right; 477 | text-decoration: none; 478 | } 479 | .ui-datepicker .ui-datepicker-buttonpane { 480 | background-image: none; 481 | margin: .7em 0 0 0; 482 | padding: 0 .2em; 483 | border-left: 0; 484 | border-right: 0; 485 | border-bottom: 0; 486 | } 487 | .ui-datepicker .ui-datepicker-buttonpane button { 488 | float: right; 489 | margin: .5em .2em .4em; 490 | cursor: pointer; 491 | padding: .2em .6em .3em .6em; 492 | width: auto; 493 | overflow: visible; 494 | } 495 | .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { 496 | float: left; 497 | } 498 | 499 | /* with multiple calendars */ 500 | .ui-datepicker.ui-datepicker-multi { 501 | width: auto; 502 | } 503 | .ui-datepicker-multi .ui-datepicker-group { 504 | float: left; 505 | } 506 | .ui-datepicker-multi .ui-datepicker-group table { 507 | width: 95%; 508 | margin: 0 auto .4em; 509 | } 510 | .ui-datepicker-multi-2 .ui-datepicker-group { 511 | width: 50%; 512 | } 513 | .ui-datepicker-multi-3 .ui-datepicker-group { 514 | width: 33.3%; 515 | } 516 | .ui-datepicker-multi-4 .ui-datepicker-group { 517 | width: 25%; 518 | } 519 | .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, 520 | .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { 521 | border-left-width: 0; 522 | } 523 | .ui-datepicker-multi .ui-datepicker-buttonpane { 524 | clear: left; 525 | } 526 | .ui-datepicker-row-break { 527 | clear: both; 528 | width: 100%; 529 | font-size: 0; 530 | } 531 | 532 | /* RTL support */ 533 | .ui-datepicker-rtl { 534 | direction: rtl; 535 | } 536 | .ui-datepicker-rtl .ui-datepicker-prev { 537 | right: 2px; 538 | left: auto; 539 | } 540 | .ui-datepicker-rtl .ui-datepicker-next { 541 | left: 2px; 542 | right: auto; 543 | } 544 | .ui-datepicker-rtl .ui-datepicker-prev:hover { 545 | right: 1px; 546 | left: auto; 547 | } 548 | .ui-datepicker-rtl .ui-datepicker-next:hover { 549 | left: 1px; 550 | right: auto; 551 | } 552 | .ui-datepicker-rtl .ui-datepicker-buttonpane { 553 | clear: right; 554 | } 555 | .ui-datepicker-rtl .ui-datepicker-buttonpane button { 556 | float: left; 557 | } 558 | .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, 559 | .ui-datepicker-rtl .ui-datepicker-group { 560 | float: right; 561 | } 562 | .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, 563 | .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { 564 | border-right-width: 0; 565 | border-left-width: 1px; 566 | } 567 | 568 | /* Icons */ 569 | .ui-datepicker .ui-icon { 570 | display: block; 571 | text-indent: -99999px; 572 | overflow: hidden; 573 | background-repeat: no-repeat; 574 | left: .5em; 575 | top: .3em; 576 | } 577 | .ui-dialog { 578 | position: absolute; 579 | top: 0; 580 | left: 0; 581 | padding: .2em; 582 | outline: 0; 583 | } 584 | .ui-dialog .ui-dialog-titlebar { 585 | padding: .4em 1em; 586 | position: relative; 587 | } 588 | .ui-dialog .ui-dialog-title { 589 | float: left; 590 | margin: .1em 0; 591 | white-space: nowrap; 592 | width: 90%; 593 | overflow: hidden; 594 | text-overflow: ellipsis; 595 | } 596 | .ui-dialog .ui-dialog-titlebar-close { 597 | position: absolute; 598 | right: .3em; 599 | top: 50%; 600 | width: 20px; 601 | margin: -10px 0 0 0; 602 | padding: 1px; 603 | height: 20px; 604 | } 605 | .ui-dialog .ui-dialog-content { 606 | position: relative; 607 | border: 0; 608 | padding: .5em 1em; 609 | background: none; 610 | overflow: auto; 611 | } 612 | .ui-dialog .ui-dialog-buttonpane { 613 | text-align: left; 614 | border-width: 1px 0 0 0; 615 | background-image: none; 616 | margin-top: .5em; 617 | padding: .3em 1em .5em .4em; 618 | } 619 | .ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { 620 | float: right; 621 | } 622 | .ui-dialog .ui-dialog-buttonpane button { 623 | margin: .5em .4em .5em 0; 624 | cursor: pointer; 625 | } 626 | .ui-dialog .ui-resizable-n { 627 | height: 2px; 628 | top: 0; 629 | } 630 | .ui-dialog .ui-resizable-e { 631 | width: 2px; 632 | right: 0; 633 | } 634 | .ui-dialog .ui-resizable-s { 635 | height: 2px; 636 | bottom: 0; 637 | } 638 | .ui-dialog .ui-resizable-w { 639 | width: 2px; 640 | left: 0; 641 | } 642 | .ui-dialog .ui-resizable-se, 643 | .ui-dialog .ui-resizable-sw, 644 | .ui-dialog .ui-resizable-ne, 645 | .ui-dialog .ui-resizable-nw { 646 | width: 7px; 647 | height: 7px; 648 | } 649 | .ui-dialog .ui-resizable-se { 650 | right: 0; 651 | bottom: 0; 652 | } 653 | .ui-dialog .ui-resizable-sw { 654 | left: 0; 655 | bottom: 0; 656 | } 657 | .ui-dialog .ui-resizable-ne { 658 | right: 0; 659 | top: 0; 660 | } 661 | .ui-dialog .ui-resizable-nw { 662 | left: 0; 663 | top: 0; 664 | } 665 | .ui-draggable .ui-dialog-titlebar { 666 | cursor: move; 667 | } 668 | .ui-progressbar { 669 | height: 2em; 670 | text-align: left; 671 | overflow: hidden; 672 | } 673 | .ui-progressbar .ui-progressbar-value { 674 | margin: -1px; 675 | height: 100%; 676 | } 677 | .ui-progressbar .ui-progressbar-overlay { 678 | background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw=="); 679 | height: 100%; 680 | filter: alpha(opacity=25); /* support: IE8 */ 681 | opacity: 0.25; 682 | } 683 | .ui-progressbar-indeterminate .ui-progressbar-value { 684 | background-image: none; 685 | } 686 | .ui-selectmenu-menu { 687 | padding: 0; 688 | margin: 0; 689 | position: absolute; 690 | top: 0; 691 | left: 0; 692 | display: none; 693 | } 694 | .ui-selectmenu-menu .ui-menu { 695 | overflow: auto; 696 | overflow-x: hidden; 697 | padding-bottom: 1px; 698 | } 699 | .ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup { 700 | font-size: 1em; 701 | font-weight: bold; 702 | line-height: 1.5; 703 | padding: 2px 0.4em; 704 | margin: 0.5em 0 0 0; 705 | height: auto; 706 | border: 0; 707 | } 708 | .ui-selectmenu-open { 709 | display: block; 710 | } 711 | .ui-selectmenu-text { 712 | display: block; 713 | margin-right: 20px; 714 | overflow: hidden; 715 | text-overflow: ellipsis; 716 | } 717 | .ui-selectmenu-button.ui-button { 718 | text-align: left; 719 | white-space: nowrap; 720 | width: 14em; 721 | } 722 | .ui-selectmenu-icon.ui-icon { 723 | float: right; 724 | margin-top: 0; 725 | } 726 | .ui-slider { 727 | position: relative; 728 | text-align: left; 729 | } 730 | .ui-slider .ui-slider-handle { 731 | position: absolute; 732 | z-index: 2; 733 | width: 1.2em; 734 | height: 1.2em; 735 | cursor: default; 736 | -ms-touch-action: none; 737 | touch-action: none; 738 | } 739 | .ui-slider .ui-slider-range { 740 | position: absolute; 741 | z-index: 1; 742 | font-size: .7em; 743 | display: block; 744 | border: 0; 745 | background-position: 0 0; 746 | } 747 | 748 | /* support: IE8 - See #6727 */ 749 | .ui-slider.ui-state-disabled .ui-slider-handle, 750 | .ui-slider.ui-state-disabled .ui-slider-range { 751 | filter: inherit; 752 | } 753 | 754 | .ui-slider-horizontal { 755 | height: .8em; 756 | } 757 | .ui-slider-horizontal .ui-slider-handle { 758 | top: -.3em; 759 | margin-left: -.6em; 760 | } 761 | .ui-slider-horizontal .ui-slider-range { 762 | top: 0; 763 | height: 100%; 764 | } 765 | .ui-slider-horizontal .ui-slider-range-min { 766 | left: 0; 767 | } 768 | .ui-slider-horizontal .ui-slider-range-max { 769 | right: 0; 770 | } 771 | 772 | .ui-slider-vertical { 773 | width: .8em; 774 | height: 100px; 775 | } 776 | .ui-slider-vertical .ui-slider-handle { 777 | left: -.3em; 778 | margin-left: 0; 779 | margin-bottom: -.6em; 780 | } 781 | .ui-slider-vertical .ui-slider-range { 782 | left: 0; 783 | width: 100%; 784 | } 785 | .ui-slider-vertical .ui-slider-range-min { 786 | bottom: 0; 787 | } 788 | .ui-slider-vertical .ui-slider-range-max { 789 | top: 0; 790 | } 791 | .ui-spinner { 792 | position: relative; 793 | display: inline-block; 794 | overflow: hidden; 795 | padding: 0; 796 | vertical-align: middle; 797 | } 798 | .ui-spinner-input { 799 | border: none; 800 | background: none; 801 | color: inherit; 802 | padding: .222em 0; 803 | margin: .2em 0; 804 | vertical-align: middle; 805 | margin-left: .4em; 806 | margin-right: 2em; 807 | } 808 | .ui-spinner-button { 809 | width: 1.6em; 810 | height: 50%; 811 | font-size: .5em; 812 | padding: 0; 813 | margin: 0; 814 | text-align: center; 815 | position: absolute; 816 | cursor: default; 817 | display: block; 818 | overflow: hidden; 819 | right: 0; 820 | } 821 | /* more specificity required here to override default borders */ 822 | .ui-spinner a.ui-spinner-button { 823 | border-top-style: none; 824 | border-bottom-style: none; 825 | border-right-style: none; 826 | } 827 | .ui-spinner-up { 828 | top: 0; 829 | } 830 | .ui-spinner-down { 831 | bottom: 0; 832 | } 833 | .ui-tabs { 834 | position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ 835 | padding: .2em; 836 | } 837 | .ui-tabs .ui-tabs-nav { 838 | margin: 0; 839 | padding: .2em .2em 0; 840 | } 841 | .ui-tabs .ui-tabs-nav li { 842 | list-style: none; 843 | float: left; 844 | position: relative; 845 | top: 0; 846 | margin: 1px .2em 0 0; 847 | border-bottom-width: 0; 848 | padding: 0; 849 | white-space: nowrap; 850 | } 851 | .ui-tabs .ui-tabs-nav .ui-tabs-anchor { 852 | float: left; 853 | padding: .5em 1em; 854 | text-decoration: none; 855 | } 856 | .ui-tabs .ui-tabs-nav li.ui-tabs-active { 857 | margin-bottom: -1px; 858 | padding-bottom: 1px; 859 | } 860 | .ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, 861 | .ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, 862 | .ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { 863 | cursor: text; 864 | } 865 | .ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { 866 | cursor: pointer; 867 | } 868 | .ui-tabs .ui-tabs-panel { 869 | display: block; 870 | border-width: 0; 871 | padding: 1em 1.4em; 872 | background: none; 873 | } 874 | .ui-tooltip { 875 | padding: 8px; 876 | position: absolute; 877 | z-index: 9999; 878 | max-width: 300px; 879 | } 880 | body .ui-tooltip { 881 | border-width: 2px; 882 | } 883 | 884 | /* Component containers 885 | ----------------------------------*/ 886 | .ui-widget { 887 | font-family: Arial,Helvetica,sans-serif; 888 | font-size: 1em; 889 | } 890 | .ui-widget .ui-widget { 891 | font-size: 1em; 892 | } 893 | .ui-widget input, 894 | .ui-widget select, 895 | .ui-widget textarea, 896 | .ui-widget button { 897 | font-family: Arial,Helvetica,sans-serif; 898 | font-size: 1em; 899 | } 900 | .ui-widget.ui-widget-content { 901 | border: 1px solid #c5c5c5; 902 | } 903 | .ui-widget-content { 904 | border: 1px solid #dddddd; 905 | background: #ffffff; 906 | color: #333333; 907 | } 908 | .ui-widget-content a { 909 | color: #333333; 910 | } 911 | .ui-widget-header { 912 | border: 1px solid #dddddd; 913 | background: #e9e9e9; 914 | color: #333333; 915 | font-weight: bold; 916 | } 917 | .ui-widget-header a { 918 | color: #333333; 919 | } 920 | 921 | /* Interaction states 922 | ----------------------------------*/ 923 | .ui-state-default, 924 | .ui-widget-content .ui-state-default, 925 | .ui-widget-header .ui-state-default, 926 | .ui-button, 927 | 928 | /* We use html here because we need a greater specificity to make sure disabled 929 | works properly when clicked or hovered */ 930 | html .ui-button.ui-state-disabled:hover, 931 | html .ui-button.ui-state-disabled:active { 932 | border: 1px solid #c5c5c5; 933 | background: #f6f6f6; 934 | font-weight: normal; 935 | color: #454545; 936 | } 937 | .ui-state-default a, 938 | .ui-state-default a:link, 939 | .ui-state-default a:visited, 940 | a.ui-button, 941 | a:link.ui-button, 942 | a:visited.ui-button, 943 | .ui-button { 944 | color: #454545; 945 | text-decoration: none; 946 | } 947 | .ui-state-hover, 948 | .ui-widget-content .ui-state-hover, 949 | .ui-widget-header .ui-state-hover, 950 | .ui-state-focus, 951 | .ui-widget-content .ui-state-focus, 952 | .ui-widget-header .ui-state-focus, 953 | .ui-button:hover, 954 | .ui-button:focus { 955 | border: 1px solid #cccccc; 956 | background: #ededed; 957 | font-weight: normal; 958 | color: #2b2b2b; 959 | } 960 | .ui-state-hover a, 961 | .ui-state-hover a:hover, 962 | .ui-state-hover a:link, 963 | .ui-state-hover a:visited, 964 | .ui-state-focus a, 965 | .ui-state-focus a:hover, 966 | .ui-state-focus a:link, 967 | .ui-state-focus a:visited, 968 | a.ui-button:hover, 969 | a.ui-button:focus { 970 | color: #2b2b2b; 971 | text-decoration: none; 972 | } 973 | 974 | .ui-visual-focus { 975 | box-shadow: 0 0 3px 1px rgb(94, 158, 214); 976 | } 977 | .ui-state-active, 978 | .ui-widget-content .ui-state-active, 979 | .ui-widget-header .ui-state-active, 980 | a.ui-button:active, 981 | .ui-button:active, 982 | .ui-button.ui-state-active:hover { 983 | border: 1px solid #003eff; 984 | background: #007fff; 985 | font-weight: normal; 986 | color: #ffffff; 987 | } 988 | .ui-icon-background, 989 | .ui-state-active .ui-icon-background { 990 | border: #003eff; 991 | background-color: #ffffff; 992 | } 993 | .ui-state-active a, 994 | .ui-state-active a:link, 995 | .ui-state-active a:visited { 996 | color: #ffffff; 997 | text-decoration: none; 998 | } 999 | 1000 | /* Interaction Cues 1001 | ----------------------------------*/ 1002 | .ui-state-highlight, 1003 | .ui-widget-content .ui-state-highlight, 1004 | .ui-widget-header .ui-state-highlight { 1005 | border: 1px solid #dad55e; 1006 | background: #fffa90; 1007 | color: #777620; 1008 | } 1009 | .ui-state-checked { 1010 | border: 1px solid #dad55e; 1011 | background: #fffa90; 1012 | } 1013 | .ui-state-highlight a, 1014 | .ui-widget-content .ui-state-highlight a, 1015 | .ui-widget-header .ui-state-highlight a { 1016 | color: #777620; 1017 | } 1018 | .ui-state-error, 1019 | .ui-widget-content .ui-state-error, 1020 | .ui-widget-header .ui-state-error { 1021 | border: 1px solid #f1a899; 1022 | background: #fddfdf; 1023 | color: #5f3f3f; 1024 | } 1025 | .ui-state-error a, 1026 | .ui-widget-content .ui-state-error a, 1027 | .ui-widget-header .ui-state-error a { 1028 | color: #5f3f3f; 1029 | } 1030 | .ui-state-error-text, 1031 | .ui-widget-content .ui-state-error-text, 1032 | .ui-widget-header .ui-state-error-text { 1033 | color: #5f3f3f; 1034 | } 1035 | .ui-priority-primary, 1036 | .ui-widget-content .ui-priority-primary, 1037 | .ui-widget-header .ui-priority-primary { 1038 | font-weight: bold; 1039 | } 1040 | .ui-priority-secondary, 1041 | .ui-widget-content .ui-priority-secondary, 1042 | .ui-widget-header .ui-priority-secondary { 1043 | opacity: .7; 1044 | filter:Alpha(Opacity=70); /* support: IE8 */ 1045 | font-weight: normal; 1046 | } 1047 | .ui-state-disabled, 1048 | .ui-widget-content .ui-state-disabled, 1049 | .ui-widget-header .ui-state-disabled { 1050 | opacity: .35; 1051 | filter:Alpha(Opacity=35); /* support: IE8 */ 1052 | background-image: none; 1053 | } 1054 | .ui-state-disabled .ui-icon { 1055 | filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ 1056 | } 1057 | 1058 | /* Icons 1059 | ----------------------------------*/ 1060 | 1061 | /* states and images */ 1062 | .ui-icon { 1063 | width: 16px; 1064 | height: 16px; 1065 | } 1066 | .ui-icon, 1067 | .ui-widget-content .ui-icon { 1068 | background-image: url("images/ui-icons_444444_256x240.png"); 1069 | } 1070 | .ui-widget-header .ui-icon { 1071 | background-image: url("images/ui-icons_444444_256x240.png"); 1072 | } 1073 | .ui-state-hover .ui-icon, 1074 | .ui-state-focus .ui-icon, 1075 | .ui-button:hover .ui-icon, 1076 | .ui-button:focus .ui-icon { 1077 | background-image: url("images/ui-icons_555555_256x240.png"); 1078 | } 1079 | .ui-state-active .ui-icon, 1080 | .ui-button:active .ui-icon { 1081 | background-image: url("images/ui-icons_ffffff_256x240.png"); 1082 | } 1083 | .ui-state-highlight .ui-icon, 1084 | .ui-button .ui-state-highlight.ui-icon { 1085 | background-image: url("images/ui-icons_777620_256x240.png"); 1086 | } 1087 | .ui-state-error .ui-icon, 1088 | .ui-state-error-text .ui-icon { 1089 | background-image: url("images/ui-icons_cc0000_256x240.png"); 1090 | } 1091 | .ui-button .ui-icon { 1092 | background-image: url("images/ui-icons_777777_256x240.png"); 1093 | } 1094 | 1095 | /* positioning */ 1096 | .ui-icon-blank { background-position: 16px 16px; } 1097 | .ui-icon-caret-1-n { background-position: 0 0; } 1098 | .ui-icon-caret-1-ne { background-position: -16px 0; } 1099 | .ui-icon-caret-1-e { background-position: -32px 0; } 1100 | .ui-icon-caret-1-se { background-position: -48px 0; } 1101 | .ui-icon-caret-1-s { background-position: -65px 0; } 1102 | .ui-icon-caret-1-sw { background-position: -80px 0; } 1103 | .ui-icon-caret-1-w { background-position: -96px 0; } 1104 | .ui-icon-caret-1-nw { background-position: -112px 0; } 1105 | .ui-icon-caret-2-n-s { background-position: -128px 0; } 1106 | .ui-icon-caret-2-e-w { background-position: -144px 0; } 1107 | .ui-icon-triangle-1-n { background-position: 0 -16px; } 1108 | .ui-icon-triangle-1-ne { background-position: -16px -16px; } 1109 | .ui-icon-triangle-1-e { background-position: -32px -16px; } 1110 | .ui-icon-triangle-1-se { background-position: -48px -16px; } 1111 | .ui-icon-triangle-1-s { background-position: -65px -16px; } 1112 | .ui-icon-triangle-1-sw { background-position: -80px -16px; } 1113 | .ui-icon-triangle-1-w { background-position: -96px -16px; } 1114 | .ui-icon-triangle-1-nw { background-position: -112px -16px; } 1115 | .ui-icon-triangle-2-n-s { background-position: -128px -16px; } 1116 | .ui-icon-triangle-2-e-w { background-position: -144px -16px; } 1117 | .ui-icon-arrow-1-n { background-position: 0 -32px; } 1118 | .ui-icon-arrow-1-ne { background-position: -16px -32px; } 1119 | .ui-icon-arrow-1-e { background-position: -32px -32px; } 1120 | .ui-icon-arrow-1-se { background-position: -48px -32px; } 1121 | .ui-icon-arrow-1-s { background-position: -65px -32px; } 1122 | .ui-icon-arrow-1-sw { background-position: -80px -32px; } 1123 | .ui-icon-arrow-1-w { background-position: -96px -32px; } 1124 | .ui-icon-arrow-1-nw { background-position: -112px -32px; } 1125 | .ui-icon-arrow-2-n-s { background-position: -128px -32px; } 1126 | .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } 1127 | .ui-icon-arrow-2-e-w { background-position: -160px -32px; } 1128 | .ui-icon-arrow-2-se-nw { background-position: -176px -32px; } 1129 | .ui-icon-arrowstop-1-n { background-position: -192px -32px; } 1130 | .ui-icon-arrowstop-1-e { background-position: -208px -32px; } 1131 | .ui-icon-arrowstop-1-s { background-position: -224px -32px; } 1132 | .ui-icon-arrowstop-1-w { background-position: -240px -32px; } 1133 | .ui-icon-arrowthick-1-n { background-position: 1px -48px; } 1134 | .ui-icon-arrowthick-1-ne { background-position: -16px -48px; } 1135 | .ui-icon-arrowthick-1-e { background-position: -32px -48px; } 1136 | .ui-icon-arrowthick-1-se { background-position: -48px -48px; } 1137 | .ui-icon-arrowthick-1-s { background-position: -64px -48px; } 1138 | .ui-icon-arrowthick-1-sw { background-position: -80px -48px; } 1139 | .ui-icon-arrowthick-1-w { background-position: -96px -48px; } 1140 | .ui-icon-arrowthick-1-nw { background-position: -112px -48px; } 1141 | .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } 1142 | .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } 1143 | .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } 1144 | .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } 1145 | .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } 1146 | .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } 1147 | .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } 1148 | .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } 1149 | .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } 1150 | .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } 1151 | .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } 1152 | .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } 1153 | .ui-icon-arrowreturn-1-w { background-position: -64px -64px; } 1154 | .ui-icon-arrowreturn-1-n { background-position: -80px -64px; } 1155 | .ui-icon-arrowreturn-1-e { background-position: -96px -64px; } 1156 | .ui-icon-arrowreturn-1-s { background-position: -112px -64px; } 1157 | .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } 1158 | .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } 1159 | .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } 1160 | .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } 1161 | .ui-icon-arrow-4 { background-position: 0 -80px; } 1162 | .ui-icon-arrow-4-diag { background-position: -16px -80px; } 1163 | .ui-icon-extlink { background-position: -32px -80px; } 1164 | .ui-icon-newwin { background-position: -48px -80px; } 1165 | .ui-icon-refresh { background-position: -64px -80px; } 1166 | .ui-icon-shuffle { background-position: -80px -80px; } 1167 | .ui-icon-transfer-e-w { background-position: -96px -80px; } 1168 | .ui-icon-transferthick-e-w { background-position: -112px -80px; } 1169 | .ui-icon-folder-collapsed { background-position: 0 -96px; } 1170 | .ui-icon-folder-open { background-position: -16px -96px; } 1171 | .ui-icon-document { background-position: -32px -96px; } 1172 | .ui-icon-document-b { background-position: -48px -96px; } 1173 | .ui-icon-note { background-position: -64px -96px; } 1174 | .ui-icon-mail-closed { background-position: -80px -96px; } 1175 | .ui-icon-mail-open { background-position: -96px -96px; } 1176 | .ui-icon-suitcase { background-position: -112px -96px; } 1177 | .ui-icon-comment { background-position: -128px -96px; } 1178 | .ui-icon-person { background-position: -144px -96px; } 1179 | .ui-icon-print { background-position: -160px -96px; } 1180 | .ui-icon-trash { background-position: -176px -96px; } 1181 | .ui-icon-locked { background-position: -192px -96px; } 1182 | .ui-icon-unlocked { background-position: -208px -96px; } 1183 | .ui-icon-bookmark { background-position: -224px -96px; } 1184 | .ui-icon-tag { background-position: -240px -96px; } 1185 | .ui-icon-home { background-position: 0 -112px; } 1186 | .ui-icon-flag { background-position: -16px -112px; } 1187 | .ui-icon-calendar { background-position: -32px -112px; } 1188 | .ui-icon-cart { background-position: -48px -112px; } 1189 | .ui-icon-pencil { background-position: -64px -112px; } 1190 | .ui-icon-clock { background-position: -80px -112px; } 1191 | .ui-icon-disk { background-position: -96px -112px; } 1192 | .ui-icon-calculator { background-position: -112px -112px; } 1193 | .ui-icon-zoomin { background-position: -128px -112px; } 1194 | .ui-icon-zoomout { background-position: -144px -112px; } 1195 | .ui-icon-search { background-position: -160px -112px; } 1196 | .ui-icon-wrench { background-position: -176px -112px; } 1197 | .ui-icon-gear { background-position: -192px -112px; } 1198 | .ui-icon-heart { background-position: -208px -112px; } 1199 | .ui-icon-star { background-position: -224px -112px; } 1200 | .ui-icon-link { background-position: -240px -112px; } 1201 | .ui-icon-cancel { background-position: 0 -128px; } 1202 | .ui-icon-plus { background-position: -16px -128px; } 1203 | .ui-icon-plusthick { background-position: -32px -128px; } 1204 | .ui-icon-minus { background-position: -48px -128px; } 1205 | .ui-icon-minusthick { background-position: -64px -128px; } 1206 | .ui-icon-close { background-position: -80px -128px; } 1207 | .ui-icon-closethick { background-position: -96px -128px; } 1208 | .ui-icon-key { background-position: -112px -128px; } 1209 | .ui-icon-lightbulb { background-position: -128px -128px; } 1210 | .ui-icon-scissors { background-position: -144px -128px; } 1211 | .ui-icon-clipboard { background-position: -160px -128px; } 1212 | .ui-icon-copy { background-position: -176px -128px; } 1213 | .ui-icon-contact { background-position: -192px -128px; } 1214 | .ui-icon-image { background-position: -208px -128px; } 1215 | .ui-icon-video { background-position: -224px -128px; } 1216 | .ui-icon-script { background-position: -240px -128px; } 1217 | .ui-icon-alert { background-position: 0 -144px; } 1218 | .ui-icon-info { background-position: -16px -144px; } 1219 | .ui-icon-notice { background-position: -32px -144px; } 1220 | .ui-icon-help { background-position: -48px -144px; } 1221 | .ui-icon-check { background-position: -64px -144px; } 1222 | .ui-icon-bullet { background-position: -80px -144px; } 1223 | .ui-icon-radio-on { background-position: -96px -144px; } 1224 | .ui-icon-radio-off { background-position: -112px -144px; } 1225 | .ui-icon-pin-w { background-position: -128px -144px; } 1226 | .ui-icon-pin-s { background-position: -144px -144px; } 1227 | .ui-icon-play { background-position: 0 -160px; } 1228 | .ui-icon-pause { background-position: -16px -160px; } 1229 | .ui-icon-seek-next { background-position: -32px -160px; } 1230 | .ui-icon-seek-prev { background-position: -48px -160px; } 1231 | .ui-icon-seek-end { background-position: -64px -160px; } 1232 | .ui-icon-seek-start { background-position: -80px -160px; } 1233 | /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ 1234 | .ui-icon-seek-first { background-position: -80px -160px; } 1235 | .ui-icon-stop { background-position: -96px -160px; } 1236 | .ui-icon-eject { background-position: -112px -160px; } 1237 | .ui-icon-volume-off { background-position: -128px -160px; } 1238 | .ui-icon-volume-on { background-position: -144px -160px; } 1239 | .ui-icon-power { background-position: 0 -176px; } 1240 | .ui-icon-signal-diag { background-position: -16px -176px; } 1241 | .ui-icon-signal { background-position: -32px -176px; } 1242 | .ui-icon-battery-0 { background-position: -48px -176px; } 1243 | .ui-icon-battery-1 { background-position: -64px -176px; } 1244 | .ui-icon-battery-2 { background-position: -80px -176px; } 1245 | .ui-icon-battery-3 { background-position: -96px -176px; } 1246 | .ui-icon-circle-plus { background-position: 0 -192px; } 1247 | .ui-icon-circle-minus { background-position: -16px -192px; } 1248 | .ui-icon-circle-close { background-position: -32px -192px; } 1249 | .ui-icon-circle-triangle-e { background-position: -48px -192px; } 1250 | .ui-icon-circle-triangle-s { background-position: -64px -192px; } 1251 | .ui-icon-circle-triangle-w { background-position: -80px -192px; } 1252 | .ui-icon-circle-triangle-n { background-position: -96px -192px; } 1253 | .ui-icon-circle-arrow-e { background-position: -112px -192px; } 1254 | .ui-icon-circle-arrow-s { background-position: -128px -192px; } 1255 | .ui-icon-circle-arrow-w { background-position: -144px -192px; } 1256 | .ui-icon-circle-arrow-n { background-position: -160px -192px; } 1257 | .ui-icon-circle-zoomin { background-position: -176px -192px; } 1258 | .ui-icon-circle-zoomout { background-position: -192px -192px; } 1259 | .ui-icon-circle-check { background-position: -208px -192px; } 1260 | .ui-icon-circlesmall-plus { background-position: 0 -208px; } 1261 | .ui-icon-circlesmall-minus { background-position: -16px -208px; } 1262 | .ui-icon-circlesmall-close { background-position: -32px -208px; } 1263 | .ui-icon-squaresmall-plus { background-position: -48px -208px; } 1264 | .ui-icon-squaresmall-minus { background-position: -64px -208px; } 1265 | .ui-icon-squaresmall-close { background-position: -80px -208px; } 1266 | .ui-icon-grip-dotted-vertical { background-position: 0 -224px; } 1267 | .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } 1268 | .ui-icon-grip-solid-vertical { background-position: -32px -224px; } 1269 | .ui-icon-grip-solid-horizontal { background-position: -48px -224px; } 1270 | .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } 1271 | .ui-icon-grip-diagonal-se { background-position: -80px -224px; } 1272 | 1273 | 1274 | /* Misc visuals 1275 | ----------------------------------*/ 1276 | 1277 | /* Corner radius */ 1278 | .ui-corner-all, 1279 | .ui-corner-top, 1280 | .ui-corner-left, 1281 | .ui-corner-tl { 1282 | border-top-left-radius: 3px; 1283 | } 1284 | .ui-corner-all, 1285 | .ui-corner-top, 1286 | .ui-corner-right, 1287 | .ui-corner-tr { 1288 | border-top-right-radius: 3px; 1289 | } 1290 | .ui-corner-all, 1291 | .ui-corner-bottom, 1292 | .ui-corner-left, 1293 | .ui-corner-bl { 1294 | border-bottom-left-radius: 3px; 1295 | } 1296 | .ui-corner-all, 1297 | .ui-corner-bottom, 1298 | .ui-corner-right, 1299 | .ui-corner-br { 1300 | border-bottom-right-radius: 3px; 1301 | } 1302 | 1303 | /* Overlays */ 1304 | .ui-widget-overlay { 1305 | background: #aaaaaa; 1306 | opacity: .3; 1307 | filter: Alpha(Opacity=30); /* support: IE8 */ 1308 | } 1309 | .ui-widget-shadow { 1310 | -webkit-box-shadow: 0px 0px 5px #666666; 1311 | box-shadow: 0px 0px 5px #666666; 1312 | } 1313 | -------------------------------------------------------------------------------- /demo7/jquery.ui.plupload.css: -------------------------------------------------------------------------------- 1 | /* 2 | Plupload 3 | ------------------------------------------------------------------- */ 4 | 5 | .plupload_wrapper * { 6 | box-sizing: content-box; 7 | } 8 | 9 | .plupload_button { 10 | cursor: pointer; 11 | outline: none; 12 | } 13 | 14 | .plupload_wrapper { 15 | font: normal 11px Verdana,sans-serif; 16 | width: 100%; 17 | min-width: 520px; 18 | line-height: 12px; 19 | } 20 | 21 | .plupload_container { 22 | _height: 300px; 23 | min-height: 300px; 24 | position: relative; 25 | } 26 | 27 | .plupload_filelist_footer {border-width: 1px 0 0 0} 28 | .plupload_file {border-width: 0 0 1px 0} 29 | .plupload_container .plupload_header {border-width: 0 0 1px 0; position: relative;} 30 | 31 | .plupload_delete .ui-icon, 32 | .plupload_done .ui-icon, 33 | .plupload_failed .ui-icon { 34 | cursor:pointer; 35 | } 36 | 37 | .plupload_header_content { 38 | height: 56px; 39 | padding: 0 160px 0 60px; 40 | position: relative; 41 | } 42 | 43 | .plupload_logo { 44 | width: 40px; 45 | height: 40px; 46 | background: url('../img/plupload.png') no-repeat 0 0; 47 | position: absolute; 48 | top: 8px; 49 | left: 8px; 50 | } 51 | 52 | .plupload_header_content_bw .plupload_logo { 53 | background-position: -40px 0; 54 | } 55 | 56 | .plupload_header_title { 57 | font: normal 18px sans-serif; 58 | line-height: 19px; 59 | padding: 6px 0 3px; 60 | } 61 | 62 | .plupload_header_text { 63 | font: normal 12px sans-serif; 64 | } 65 | 66 | .plupload_view_switch { 67 | position: absolute; 68 | right: 16px; 69 | bottom: 8px; 70 | margin: 0; 71 | display: none; 72 | } 73 | 74 | .plupload_view_switch .ui-button { 75 | margin-right: -0.31em; 76 | } 77 | 78 | .plupload_content { 79 | position: absolute; 80 | top: 86px; 81 | bottom: 44px; 82 | left: 0; 83 | right: 0; 84 | overflow-y: auto; 85 | width: 100%; 86 | } 87 | 88 | .plupload_filelist { 89 | border-collapse: collapse; 90 | border-left: none; 91 | border-right: none; 92 | margin: 0; 93 | padding: 0; 94 | width: 100%; 95 | -moz-user-select: none; 96 | -webkit-user-select: none; 97 | user-select: none; 98 | } 99 | 100 | .plupload_filelist_content { 101 | padding: 0; 102 | margin: 0; 103 | } 104 | 105 | .plupload_cell {padding: 8px 6px;} 106 | 107 | .plupload_file { 108 | list-style: none; 109 | display: block; 110 | position: relative; 111 | overflow: hidden; 112 | line-height: 12px; 113 | } 114 | 115 | .plupload_file_thumb { 116 | position: relative; 117 | background-image: none; 118 | background-color: #eee; 119 | } 120 | 121 | .plupload_thumb_loading { 122 | background: #eee url(../img/loading.gif) center no-repeat; 123 | } 124 | 125 | .plupload_thumb_loading .plupload_file_dummy, 126 | .plupload_thumb_embedded .plupload_file_dummy { 127 | display: none; 128 | } 129 | 130 | .plupload_file_name { 131 | overflow: hidden; 132 | text-overflow: ellipsis; 133 | white-space: nowrap; 134 | } 135 | 136 | .plupload_filelist_header { 137 | border-top: none; 138 | } 139 | 140 | .plupload_filelist_footer { 141 | position: absolute; 142 | bottom: 0; 143 | left: 0; 144 | right: 0; 145 | } 146 | 147 | .plupload_buttons { 148 | position: relative; 149 | } 150 | 151 | /* list view */ 152 | .plupload_view_list .plupload_file { 153 | border-left: none; 154 | border-right: none; 155 | border-top: none; 156 | height: 29px; 157 | width: 100% !important; 158 | /* fix IE6 vertical white-space bug */ 159 | _float: left; 160 | _clear: left; 161 | } 162 | 163 | .plupload_view_list div.plupload_file_size, 164 | .plupload_view_list div.plupload_file_status, 165 | .plupload_view_list div.plupload_file_action { 166 | padding: 8px 6px; 167 | position: absolute; 168 | top: 0; 169 | right: 0; 170 | } 171 | 172 | .plupload_view_list div.plupload_file_name { 173 | margin-right: 156px; 174 | padding: 8px 6px; 175 | _width: 75%; 176 | } 177 | 178 | .plupload_view_list div.plupload_file_size { 179 | right: 28px; 180 | } 181 | 182 | .plupload_view_list div.plupload_file_status { 183 | right: 82px; 184 | } 185 | 186 | .plupload_view_list .plupload_file_rename { 187 | margin-left: -2px; 188 | } 189 | 190 | .plupload_view_list .plupload_file_size, 191 | .plupload_view_list .plupload_file_status, 192 | .plupload_filelist_footer .plupload_file_size, 193 | .plupload_filelist_footer .plupload_file_status { 194 | text-align: right; 195 | width: 52px; 196 | } 197 | 198 | .plupload_view_list .plupload_file_thumb { 199 | position: absolute; 200 | top: -999px; 201 | } 202 | 203 | .plupload_view_list .plupload_file_progress { 204 | display: none; 205 | } 206 | 207 | 208 | /* thumbs view */ 209 | .plupload_view_thumbs .plupload_content { 210 | top: 57px; 211 | } 212 | 213 | .plupload_view_thumbs .plupload_filelist_header { 214 | display: none; 215 | } 216 | 217 | .plupload_view_thumbs .plupload_file { 218 | padding: 6px; 219 | margin: 10px; 220 | border: 1px solid #fff; 221 | float: left; 222 | } 223 | 224 | .plupload_view_thumbs .plupload_file_thumb, 225 | .plupload_view_thumbs .plupload_file_dummy { 226 | text-align: center; 227 | overflow: hidden; 228 | } 229 | 230 | .plupload_view_thumbs .plupload_file_dummy { 231 | font-size: 21px; 232 | font-weight: bold; 233 | text-transform: lowercase; 234 | overflow: hidden; 235 | border: none; 236 | position: absolute; 237 | top: 0; 238 | left: 0; 239 | width: 100%; 240 | height: 100%; 241 | } 242 | 243 | .plupload_view_thumbs div.plupload_file_action { 244 | position: absolute; 245 | top: 0; 246 | right: 0; 247 | } 248 | 249 | .plupload_view_thumbs div.plupload_file_name { 250 | padding: 0; 251 | font-weight: bold; 252 | } 253 | 254 | .plupload_view_thumbs .plupload_file_rename { 255 | padding: 1px 0; 256 | width: 100% !important; 257 | } 258 | 259 | .plupload_view_thumbs div.plupload_file_size { 260 | font-size: 0.8em; 261 | font-weight: normal; 262 | } 263 | 264 | .plupload_view_thumbs div.plupload_file_status { 265 | position: relative; 266 | height: 3px; 267 | overflow: hidden; 268 | text-indent: -999px; 269 | margin-bottom: 3px; 270 | } 271 | 272 | .plupload_view_thumbs div.plupload_file_progress { 273 | border: none; 274 | height: 100%; 275 | } 276 | 277 | .plupload .ui-sortable-helper, 278 | .plupload .ui-sortable .plupload_file { 279 | cursor:move; 280 | } 281 | 282 | .plupload_file_action {width: 16px;} 283 | .plupload_file_name { 284 | overflow: hidden; 285 | padding-left: 10px; 286 | } 287 | 288 | .plupload_file_rename { 289 | border: none; 290 | font: normal 11px Verdana, sans-serif; 291 | padding: 1px 2px; 292 | line-height: 11px; 293 | height: 11px; 294 | } 295 | 296 | .plupload_progress {width: 60px;} 297 | .plupload_progress_container {padding: 1px;} 298 | 299 | 300 | /* Floats */ 301 | 302 | .plupload_right {float: right;} 303 | .plupload_left {float: left;} 304 | .plupload_clear,.plupload_clearer {clear: both;} 305 | .plupload_clearer, .plupload_progress_bar { 306 | display: block; 307 | font-size: 0; 308 | line-height: 0; 309 | } 310 | .plupload_clearer {height: 0;} 311 | 312 | /* Misc */ 313 | .plupload_hidden {display: none !important;} 314 | 315 | .plupload_droptext { 316 | position: absolute; 317 | top: 0; 318 | left: 0; 319 | right: 0; 320 | bottom: 0; 321 | background: transparent; 322 | text-align: center; 323 | vertical-align: middle; 324 | border: 0; 325 | line-height: 160px; 326 | display: none; 327 | } 328 | 329 | .plupload_dropbox .plupload_droptext { 330 | display: block; 331 | } 332 | 333 | .plupload_buttons, .plupload_upload_status {float: left} 334 | 335 | .plupload_message { 336 | position: absolute; 337 | top: -1px; 338 | left: -1px; 339 | height: 100%; 340 | width: 100%; 341 | } 342 | 343 | .plupload_message p { 344 | padding:0.7em; 345 | margin:0; 346 | } 347 | 348 | .plupload_message strong { 349 | font-weight: bold; 350 | } 351 | 352 | .plupload_message i { 353 | font-style: italic; 354 | } 355 | 356 | .plupload_message p span.ui-icon { 357 | float: left; 358 | margin-right: 0.3em; 359 | } 360 | 361 | .plupload_header_content .ui-state-error, 362 | .plupload_header_content .ui-state-highlight { 363 | border:none; 364 | } 365 | 366 | .plupload_message_close { 367 | position:absolute; 368 | top:5px; 369 | right:5px; 370 | cursor:pointer; 371 | } 372 | 373 | .plupload .ui-sortable-placeholder { 374 | height:35px; 375 | } 376 | -------------------------------------------------------------------------------- /demo7/jquery.ui.plupload.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jquery.ui.plupload.js 3 | * 4 | * Copyright 2013, Moxiecode Systems AB 5 | * Released under GPL License. 6 | * 7 | * License: http://www.plupload.com/license 8 | * Contributing: http://www.plupload.com/contributing 9 | * 10 | * Depends: 11 | * jquery.ui.core.js 12 | * jquery.ui.widget.js 13 | * jquery.ui.button.js 14 | * jquery.ui.progressbar.js 15 | * 16 | * Optionally: 17 | * jquery.ui.sortable.js 18 | */ 19 | 20 | /* global jQuery:true */ 21 | 22 | /** 23 | jQuery UI based implementation of the Plupload API - multi-runtime file uploading API. 24 | 25 | To use the widget you must include _jQuery_ and _jQuery UI_ bundle (including `ui.core`, `ui.widget`, `ui.button`, 26 | `ui.progressbar` and `ui.sortable`). 27 | 28 | In general the widget is designed the way that you do not usually need to do anything to it after you instantiate it. 29 | But! You still can intervenue, to some extent, in case you need to. Although, due to the fact that widget is based on 30 | _jQuery UI_ widget factory, there are some specifics. See examples below for more details. 31 | 32 | @example 33 | 34 |
    35 |

    Your browser doesn't have Flash, Silverlight or HTML5 support.

    36 |
    37 | 38 | 50 | 51 | @example 52 | // Invoking methods: 53 | $('#uploader').plupload(options); 54 | 55 | // Display welcome message in the notification area 56 | $('#uploader').plupload('notify', 'info', "This might be obvious, but you need to click 'Add Files' to add some files."); 57 | 58 | @example 59 | // Subscribing to the events... 60 | // ... on initialization: 61 | $('#uploader').plupload({ 62 | ... 63 | viewchanged: function(event, args) { 64 | // stuff ... 65 | } 66 | }); 67 | // ... or after initialization 68 | $('#uploader').on("viewchanged", function(event, args) { 69 | // stuff ... 70 | }); 71 | 72 | @class UI.Plupload 73 | @constructor 74 | @param {Object} settings For detailed information about each option check documentation. 75 | @param {String} settings.url URL of the server-side upload handler. 76 | @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. 77 | @param {String} [settings.file_data_name="file"] Name for the file field in Multipart formated message. 78 | @param {Object} [settings.filters={}] Set of file type filters. 79 | @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` 80 | @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`. 81 | @param {Boolean} [settings.filters.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`. 82 | @param {Number} [settings.filters.max_file_count=0] Limit the number of files that can reside in the queue at the same time (default is 0 - no limit). 83 | @param {String} [settings.flash_swf_url] URL of the Flash swf. 84 | @param {Object} [settings.headers] Custom headers to send with the upload. Hash of name/value pairs. 85 | @param {Number|String} [settings.max_file_size] 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`. 86 | @param {Number} [settings.max_retries=0] How many times to retry the chunk or file, before triggering Error event. 87 | @param {Boolean} [settings.multipart=true] Whether to send file and additional parameters as Multipart formated message. 88 | @param {Object} [settings.multipart_params] Hash of key/value pairs to send with every file upload. 89 | @param {Boolean} [settings.multi_selection=true] Enable ability to select multiple files at once in file dialog. 90 | @param {Boolean} [settings.prevent_duplicates=false] Do not let duplicates into the queue. Dispatches `plupload.FILE_DUPLICATE_ERROR`. 91 | @param {String|Object} [settings.required_features] Either comma-separated list or hash of required features that chosen runtime should absolutely possess. 92 | @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}` 93 | @param {Number} [settings.resize.width] If image is bigger, it will be resized. 94 | @param {Number} [settings.resize.height] If image is bigger, it will be resized. 95 | @param {Number} [settings.resize.quality=90] Compression quality for jpegs (1-100). 96 | @param {Boolean} [settings.resize.crop=false] Whether to crop images to exact dimensions. By default they will be resized proportionally. 97 | @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. 98 | @param {String} [settings.silverlight_xap_url] URL of the Silverlight xap. 99 | @param {Boolean} [settings.unique_names=false] If true will generate unique filenames for uploaded files. 100 | 101 | @param {Boolean} [settings.autostart=false] Whether to auto start uploading right after file selection. 102 | @param {Boolean} [settings.dragdrop=true] Enable ability to add file to the queue by drag'n'dropping them from the desktop. 103 | @param {Boolean} [settings.rename=false] Enable ability to rename files in the queue. 104 | @param {Boolean} [settings.sortable=false] Enable ability to sort files in the queue, changing their uploading priority. 105 | @param {Object} [settings.buttons] Control the visibility of functional buttons. 106 | @param {Boolean} [settings.buttons.browse=true] Display browse button. 107 | @param {Boolean} [settings.buttons.start=true] Display start button. 108 | @param {Boolean} [settings.buttons.stop=true] Display stop button. 109 | @param {Object} [settings.views] Control various views of the file queue. 110 | @param {Boolean} [settings.views.list=true] Enable list view. 111 | @param {Boolean} [settings.views.thumbs=false] Enable thumbs view. 112 | @param {String} [settings.views.default='list'] Default view. 113 | @param {Boolean} [settings.views.remember=true] Whether to remember the current view (requires jQuery Cookie plugin). 114 | @param {Boolean} [settings.multiple_queues=true] Re-activate the widget after each upload procedure. 115 | */ 116 | ;(function(window, document, plupload, o, $) { 117 | 118 | /** 119 | Dispatched when the widget is initialized and ready. 120 | 121 | @event ready 122 | @param {plupload.Uploader} uploader Uploader instance sending the event. 123 | */ 124 | 125 | /** 126 | Dispatched when file dialog is closed. 127 | 128 | @event selected 129 | @param {plupload.Uploader} uploader Uploader instance sending the event. 130 | @param {Array} files Array of selected files represented by plupload.File objects 131 | */ 132 | 133 | /** 134 | Dispatched when file dialog is closed. 135 | 136 | @event removed 137 | @param {plupload.Uploader} uploader Uploader instance sending the event. 138 | @param {Array} files Array of removed files represented by plupload.File objects 139 | */ 140 | 141 | /** 142 | Dispatched when upload is started. 143 | 144 | @event started 145 | @param {plupload.Uploader} uploader Uploader instance sending the event. 146 | */ 147 | 148 | /** 149 | Dispatched when upload is stopped. 150 | 151 | @event stopped 152 | @param {plupload.Uploader} uploader Uploader instance sending the event. 153 | */ 154 | 155 | /** 156 | Dispatched during the upload process. 157 | 158 | @event progress 159 | @param {plupload.Uploader} uploader Uploader instance sending the event. 160 | @param {plupload.File} file File that is being uploaded (includes loaded and percent properties among others). 161 | @param {Number} size Total file size in bytes. 162 | @param {Number} loaded Number of bytes uploaded of the files total size. 163 | @param {Number} percent Number of percentage uploaded of the file. 164 | */ 165 | 166 | /** 167 | Dispatched when file is uploaded. 168 | 169 | @event uploaded 170 | @param {plupload.Uploader} uploader Uploader instance sending the event. 171 | @param {plupload.File} file File that was uploaded. 172 | @param {Enum} status Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE. 173 | */ 174 | 175 | /** 176 | Dispatched when upload of the whole queue is complete. 177 | 178 | @event complete 179 | @param {plupload.Uploader} uploader Uploader instance sending the event. 180 | @param {Array} files Array of uploaded files represented by plupload.File objects 181 | */ 182 | 183 | /** 184 | Dispatched when the view is changed, e.g. from `list` to `thumbs` or vice versa. 185 | 186 | @event viewchanged 187 | @param {plupload.Uploader} uploader Uploader instance sending the event. 188 | @param {String} type Current view type. 189 | */ 190 | 191 | /** 192 | Dispatched when error of some kind is detected. 193 | 194 | @event error 195 | @param {plupload.Uploader} uploader Uploader instance sending the event. 196 | @param {String} error Error message. 197 | @param {plupload.File} file File that was uploaded. 198 | @param {Enum} status Status constant matching the plupload states QUEUED, UPLOADING, FAILED, DONE. 199 | */ 200 | 201 | var uploaders = {}; 202 | 203 | function _(str) { 204 | return plupload.translate(str) || str; 205 | } 206 | 207 | function renderUI(obj) { 208 | obj.id = obj.attr('id'); 209 | 210 | obj.html( 211 | '
    ' + 212 | '
    ' + 213 | '
    ' + 214 | '
    ' + 215 | '' + 216 | '
    ' + _("Select files") + '
    ' + 217 | '
    ' + _("Add files to the upload queue and click the start button.") + '
    ' + 218 | '
    ' + 219 | '' + 220 | '' + 221 | '
    ' + 222 | '
    ' + 223 | '
    ' + 224 | 225 | '' + 226 | '' + 227 | '' + 228 | '' + 229 | '' + 230 | '' + 231 | '' + 232 | '
    ' + _('Filename') + '' + _('Status') + '' + _('Size') + ' 
    ' + 233 | 234 | '
    ' + 235 | '
    ' + _("Drag files here.") + '
    ' + 236 | '
    ' + 237 | '
     
    ' + 238 | '
    ' + 239 | 240 | '' + 241 | '' + 242 | '' + 259 | '' + 260 | '' + 261 | '' + 262 | '' + 263 | '' + 264 | 265 | '
    ' + 266 | '' + 267 | '
    ' 268 | ); 269 | } 270 | 271 | 272 | $.widget("ui.plupload", { 273 | 274 | widgetEventPrefix: '', 275 | 276 | contents_bak: '', 277 | 278 | options: { 279 | browse_button_hover: 'ui-state-hover', 280 | browse_button_active: 'ui-state-active', 281 | 282 | filters: {}, 283 | 284 | // widget specific 285 | buttons: { 286 | browse: true, 287 | start: true, 288 | stop: true 289 | }, 290 | 291 | views: { 292 | list: true, 293 | thumbs: false, 294 | active: 'list', 295 | remember: true // requires: https://github.com/carhartl/jquery-cookie, otherwise disabled even if set to true 296 | }, 297 | 298 | thumb_width: 100, 299 | thumb_height: 60, 300 | 301 | multiple_queues: true, // re-use widget by default 302 | dragdrop : true, 303 | autostart: false, 304 | sortable: false, 305 | rename: false 306 | }, 307 | 308 | FILE_COUNT_ERROR: -9001, 309 | 310 | _create: function() { 311 | var id = this.element.attr('id'); 312 | if (!id) { 313 | id = plupload.guid(); 314 | this.element.attr('id', id); 315 | } 316 | this.id = id; 317 | 318 | // backup the elements initial state 319 | this.contents_bak = this.element.html(); 320 | renderUI(this.element); 321 | 322 | // container, just in case 323 | this.container = $('.plupload_container', this.element).attr('id', id + '_container'); 324 | 325 | this.content = $('.plupload_content', this.element); 326 | 327 | if ($.fn.resizable) { 328 | this.container.resizable({ 329 | handles: 's', 330 | minHeight: 300 331 | }); 332 | } 333 | 334 | // list of files, may become sortable 335 | this.filelist = $('.plupload_filelist_content', this.container) 336 | .attr({ 337 | id: id + '_filelist', 338 | unselectable: 'on' 339 | }); 340 | 341 | 342 | // buttons 343 | this.browse_button = $('.plupload_add', this.container).attr('id', id + '_browse'); 344 | this.start_button = $('.plupload_start', this.container).attr('id', id + '_start'); 345 | this.stop_button = $('.plupload_stop', this.container).attr('id', id + '_stop'); 346 | this.thumbs_switcher = $('#' + id + '_view_thumbs'); 347 | this.list_switcher = $('#' + id + '_view_list'); 348 | 349 | if ($.ui.button) { 350 | this.browse_button.button({ 351 | icons: { primary: 'ui-icon-circle-plus' }, 352 | disabled: true 353 | }); 354 | 355 | this.start_button.button({ 356 | icons: { primary: 'ui-icon-circle-arrow-e' }, 357 | disabled: true 358 | }); 359 | 360 | this.stop_button.button({ 361 | icons: { primary: 'ui-icon-circle-close' } 362 | }); 363 | 364 | this.list_switcher.button({ 365 | text: false, 366 | icons: { secondary: "ui-icon-grip-dotted-horizontal" } 367 | }); 368 | 369 | this.thumbs_switcher.button({ 370 | text: false, 371 | icons: { secondary: "ui-icon-image" } 372 | }); 373 | } 374 | 375 | // progressbar 376 | this.progressbar = $('.plupload_progress_container', this.container); 377 | 378 | if ($.ui.progressbar) { 379 | this.progressbar.progressbar(); 380 | } 381 | 382 | // counter 383 | this.counter = $('.plupload_count', this.element) 384 | .attr({ 385 | id: id + '_count', 386 | name: id + '_count' 387 | }); 388 | 389 | // initialize uploader instance 390 | this._initUploader(); 391 | }, 392 | 393 | _initUploader: function() { 394 | var self = this 395 | , id = this.id 396 | , uploader 397 | , options = { 398 | container: id + '_buttons', 399 | browse_button: id + '_browse' 400 | } 401 | ; 402 | 403 | $('.plupload_buttons', this.element).attr('id', id + '_buttons'); 404 | 405 | if (self.options.dragdrop) { 406 | this.filelist.parent().attr('id', this.id + '_dropbox'); 407 | options.drop_element = this.id + '_dropbox'; 408 | } 409 | 410 | this.filelist.on('click', function(e) { 411 | if ($(e.target).hasClass('plupload_action_icon')) { 412 | self.removeFile($(e.target).closest('.plupload_file').attr('id')); 413 | e.preventDefault(); 414 | } 415 | }); 416 | 417 | uploader = this.uploader = uploaders[id] = new plupload.Uploader($.extend(this.options, options)); 418 | 419 | // retrieve full normalized set of options 420 | this.options = uploader.getOption(); 421 | 422 | if (self.options.views.thumbs) { 423 | uploader.settings.required_features.display_media = true; 424 | } 425 | 426 | // for backward compatibility 427 | if (self.options.max_file_count) { 428 | plupload.extend(uploader.getOption('filters'), { 429 | max_file_count: self.options.max_file_count 430 | }); 431 | } 432 | 433 | plupload.addFileFilter('max_file_count', function(maxCount, file, cb) { 434 | if (maxCount <= this.files.length - (this.total.uploaded + this.total.failed)) { 435 | self.browse_button.button('disable'); 436 | this.disableBrowse(); 437 | 438 | this.trigger('Error', { 439 | code : self.FILE_COUNT_ERROR, 440 | message : _("File count error."), 441 | file : file 442 | }); 443 | cb(false); 444 | } else { 445 | cb(true); 446 | } 447 | }); 448 | 449 | 450 | uploader.bind('Error', function(up, err) { 451 | var message, details = ""; 452 | 453 | message = '' + err.message + ''; 454 | 455 | switch (err.code) { 456 | case plupload.FILE_EXTENSION_ERROR: 457 | details = plupload.sprintf(_("File: %s"), err.file.name); 458 | break; 459 | 460 | case plupload.FILE_SIZE_ERROR: 461 | details = plupload.sprintf(_("File: %s, size: %d, max file size: %d"), err.file.name, plupload.formatSize(err.file.size), plupload.formatSize(plupload.parseSize(up.getOption('filters').max_file_size))); 462 | break; 463 | 464 | case plupload.FILE_DUPLICATE_ERROR: 465 | details = plupload.sprintf(_("%s already present in the queue."), err.file.name); 466 | break; 467 | 468 | case self.FILE_COUNT_ERROR: 469 | details = plupload.sprintf(_("Upload element accepts only %d file(s) at a time. Extra files were stripped."), up.getOption('filters').max_file_count || 0); 470 | break; 471 | 472 | case plupload.IMAGE_FORMAT_ERROR : 473 | details = _("Image format either wrong or not supported."); 474 | break; 475 | 476 | case plupload.IMAGE_MEMORY_ERROR : 477 | details = _("Runtime ran out of available memory."); 478 | break; 479 | 480 | /* // This needs a review 481 | case plupload.IMAGE_DIMENSIONS_ERROR : 482 | details = plupload.sprintf(_('Resoultion out of boundaries! %s runtime supports images only up to %wx%hpx.'), up.runtime, up.features.maxWidth, up.features.maxHeight); 483 | break; */ 484 | 485 | case plupload.HTTP_ERROR: 486 | details = _("Upload URL might be wrong or doesn't exist."); 487 | break; 488 | } 489 | 490 | message += "
    " + details + ""; 491 | 492 | self._trigger('error', null, { up: up, error: err } ); 493 | 494 | // do not show UI if no runtime can be initialized 495 | if (err.code === plupload.INIT_ERROR) { 496 | setTimeout(function() { 497 | self.destroy(); 498 | }, 1); 499 | } else { 500 | self.notify('error', message); 501 | } 502 | }); 503 | 504 | 505 | uploader.bind('PostInit', function(up) { 506 | // all buttons are optional, so they can be disabled and hidden 507 | if (!self.options.buttons.browse) { 508 | self.browse_button.button('disable').hide(); 509 | up.disableBrowse(true); 510 | } else { 511 | self.browse_button.button('enable'); 512 | } 513 | 514 | if (!self.options.buttons.start) { 515 | self.start_button.button('disable').hide(); 516 | } 517 | 518 | if (!self.options.buttons.stop) { 519 | self.stop_button.button('disable').hide(); 520 | } 521 | 522 | if (!self.options.unique_names && self.options.rename) { 523 | self._enableRenaming(); 524 | } 525 | 526 | if (self.options.dragdrop && up.features.dragdrop) { 527 | self.filelist.parent().addClass('plupload_dropbox'); 528 | } 529 | 530 | self._enableViewSwitcher(); 531 | 532 | self.start_button.click(function(e) { 533 | if (!$(this).button('option', 'disabled')) { 534 | self.start(); 535 | } 536 | e.preventDefault(); 537 | }); 538 | 539 | self.stop_button.click(function(e) { 540 | self.stop(); 541 | e.preventDefault(); 542 | }); 543 | 544 | self._trigger('ready', null, { up: up }); 545 | }); 546 | 547 | // uploader internal events must run first 548 | uploader.init(); 549 | 550 | uploader.bind('FileFiltered', function(up, file) { 551 | self._addFiles(file); 552 | }); 553 | 554 | uploader.bind('FilesAdded', function(up, files) { 555 | self._trigger('selected', null, { up: up, files: files } ); 556 | 557 | // re-enable sortable 558 | if (self.options.sortable && $.ui.sortable) { 559 | self._enableSortingList(); 560 | } 561 | 562 | self._trigger('updatelist', null, { filelist: self.filelist }); 563 | 564 | if (self.options.autostart) { 565 | // set a little delay to make sure that QueueChanged triggered by the core has time to complete 566 | setTimeout(function() { 567 | self.start(); 568 | }, 10); 569 | } 570 | }); 571 | 572 | uploader.bind('FilesRemoved', function(up, files) { 573 | // destroy sortable if enabled 574 | if ($.ui.sortable && self.options.sortable) { 575 | $('tbody', self.filelist).sortable('destroy'); 576 | } 577 | 578 | $.each(files, function(i, file) { 579 | $('#' + file.id).toggle("highlight", function() { 580 | $(this).remove(); 581 | }); 582 | }); 583 | 584 | if (up.files.length) { 585 | // re-initialize sortable 586 | if (self.options.sortable && $.ui.sortable) { 587 | self._enableSortingList(); 588 | } 589 | } 590 | 591 | self._trigger('updatelist', null, { filelist: self.filelist }); 592 | self._trigger('removed', null, { up: up, files: files } ); 593 | }); 594 | 595 | uploader.bind('QueueChanged', function() { 596 | self._handleState(); 597 | }); 598 | 599 | uploader.bind('StateChanged', function(up) { 600 | self._handleState(); 601 | if (plupload.STARTED === up.state) { 602 | self._trigger('started', null, { up: this.uploader }); 603 | } else if (plupload.STOPPED === up.state) { 604 | self._trigger('stopped', null, { up: this.uploader }); 605 | } 606 | }); 607 | 608 | uploader.bind('UploadFile', function(up, file) { 609 | self._handleFileStatus(file); 610 | }); 611 | 612 | uploader.bind('FileUploaded', function(up, file, result) { 613 | self._handleFileStatus(file); 614 | self._trigger('uploaded', null, { up: up, file: file, result: result } ); 615 | }); 616 | 617 | uploader.bind('UploadProgress', function(up, file) { 618 | self._handleFileStatus(file); 619 | self._updateTotalProgress(); 620 | self._trigger('progress', null, { up: up, file: file } ); 621 | }); 622 | 623 | uploader.bind('UploadComplete', function(up, files) { 624 | self._addFormFields(); 625 | self._trigger('complete', null, { up: up, files: files } ); 626 | }); 627 | }, 628 | 629 | 630 | _setOption: function(key, value) { 631 | var self = this; 632 | 633 | if (key == 'buttons' && typeof(value) == 'object') { 634 | value = $.extend(self.options.buttons, value); 635 | 636 | if (!value.browse) { 637 | self.browse_button.button('disable').hide(); 638 | self.uploader.disableBrowse(true); 639 | } else { 640 | self.browse_button.button('enable').show(); 641 | self.uploader.disableBrowse(false); 642 | } 643 | 644 | if (!value.start) { 645 | self.start_button.button('disable').hide(); 646 | } else { 647 | self.start_button.button('enable').show(); 648 | } 649 | 650 | if (!value.stop) { 651 | self.stop_button.button('disable').hide(); 652 | } else { 653 | self.start_button.button('enable').show(); 654 | } 655 | } 656 | 657 | self.uploader.setOption(key, value); 658 | }, 659 | 660 | 661 | /** 662 | Start upload. Triggers `start` event. 663 | 664 | @method start 665 | */ 666 | start: function() { 667 | this.uploader.start(); 668 | }, 669 | 670 | 671 | /** 672 | Stop upload. Triggers `stop` event. 673 | 674 | @method stop 675 | */ 676 | stop: function() { 677 | this.uploader.stop(); 678 | }, 679 | 680 | 681 | /** 682 | Enable browse button. 683 | 684 | @method enable 685 | */ 686 | enable: function() { 687 | this.browse_button.button('enable'); 688 | this.uploader.disableBrowse(false); 689 | }, 690 | 691 | 692 | /** 693 | Disable browse button. 694 | 695 | @method disable 696 | */ 697 | disable: function() { 698 | this.browse_button.button('disable'); 699 | this.uploader.disableBrowse(true); 700 | }, 701 | 702 | 703 | /** 704 | Retrieve file by its unique id. 705 | 706 | @method getFile 707 | @param {String} id Unique id of the file 708 | @return {plupload.File} 709 | */ 710 | getFile: function(id) { 711 | var file; 712 | 713 | if (typeof id === 'number') { 714 | file = this.uploader.files[id]; 715 | } else { 716 | file = this.uploader.getFile(id); 717 | } 718 | return file; 719 | }, 720 | 721 | /** 722 | Return array of files currently in the queue. 723 | 724 | @method getFiles 725 | @return {Array} Array of files in the queue represented by plupload.File objects 726 | */ 727 | getFiles: function() { 728 | return this.uploader.files; 729 | }, 730 | 731 | 732 | /** 733 | Remove the file from the queue. 734 | 735 | @method removeFile 736 | @param {plupload.File|String} file File to remove, might be specified directly or by its unique id 737 | */ 738 | removeFile: function(file) { 739 | if (plupload.typeOf(file) === 'string') { 740 | file = this.getFile(file); 741 | } 742 | this.uploader.removeFile(file); 743 | }, 744 | 745 | 746 | /** 747 | Clear the file queue. 748 | 749 | @method clearQueue 750 | */ 751 | clearQueue: function() { 752 | this.uploader.splice(); 753 | }, 754 | 755 | 756 | /** 757 | Retrieve internal plupload.Uploader object (usually not required). 758 | 759 | @method getUploader 760 | @return {plupload.Uploader} 761 | */ 762 | getUploader: function() { 763 | return this.uploader; 764 | }, 765 | 766 | 767 | /** 768 | Trigger refresh procedure, specifically browse_button re-measure and re-position operations. 769 | Might get handy, when UI Widget is placed within the popup, that is constantly hidden and shown 770 | again - without calling this method after each show operation, dialog trigger might get displaced 771 | and disfunctional. 772 | 773 | @method refresh 774 | */ 775 | refresh: function() { 776 | this.uploader.refresh(); 777 | }, 778 | 779 | 780 | /** 781 | Display a message in notification area. 782 | 783 | @method notify 784 | @param {Enum} type Type of the message, either `error` or `info` 785 | @param {String} message The text message to display. 786 | */ 787 | notify: function(type, message) { 788 | var popup = $( 789 | '
    ' + 790 | '' + 791 | '

    ' + message + '

    ' + 792 | '
    ' 793 | ); 794 | 795 | popup 796 | .addClass('ui-state-' + (type === 'error' ? 'error' : 'highlight')) 797 | .find('p .ui-icon') 798 | .addClass('ui-icon-' + (type === 'error' ? 'alert' : 'info')) 799 | .end() 800 | .find('.plupload_message_close') 801 | .click(function() { 802 | popup.remove(); 803 | }) 804 | .end(); 805 | 806 | $('.plupload_header', this.container).append(popup); 807 | }, 808 | 809 | 810 | /** 811 | Destroy the widget, the uploader, free associated resources and bring back original html. 812 | 813 | @method destroy 814 | */ 815 | destroy: function() { 816 | // destroy uploader instance 817 | this.uploader.destroy(); 818 | 819 | // unbind all button events 820 | $('.plupload_button', this.element).unbind(); 821 | 822 | // destroy buttons 823 | if ($.ui.button) { 824 | $('.plupload_add, .plupload_start, .plupload_stop', this.container) 825 | .button('destroy'); 826 | } 827 | 828 | // destroy progressbar 829 | if ($.ui.progressbar) { 830 | this.progressbar.progressbar('destroy'); 831 | } 832 | 833 | // destroy sortable behavior 834 | if ($.ui.sortable && this.options.sortable) { 835 | $('tbody', this.filelist).sortable('destroy'); 836 | } 837 | 838 | // restore the elements initial state 839 | this.element 840 | .empty() 841 | .html(this.contents_bak); 842 | this.contents_bak = ''; 843 | 844 | $.Widget.prototype.destroy.apply(this); 845 | }, 846 | 847 | 848 | _handleState: function() { 849 | var up = this.uploader 850 | , filesPending = up.files.length - (up.total.uploaded + up.total.failed) 851 | , maxCount = up.getOption('filters').max_file_count || 0 852 | ; 853 | 854 | if (plupload.STARTED === up.state) { 855 | $([]) 856 | .add(this.stop_button) 857 | .add('.plupload_started') 858 | .removeClass('plupload_hidden'); 859 | 860 | this.start_button.button('disable'); 861 | 862 | if (!this.options.multiple_queues) { 863 | this.browse_button.button('disable'); 864 | up.disableBrowse(); 865 | } 866 | 867 | $('.plupload_upload_status', this.element).html(plupload.sprintf(_('Uploaded %d/%d files'), up.total.uploaded, up.files.length)); 868 | $('.plupload_header_content', this.element).addClass('plupload_header_content_bw'); 869 | } 870 | else if (plupload.STOPPED === up.state) { 871 | $([]) 872 | .add(this.stop_button) 873 | .add('.plupload_started') 874 | .addClass('plupload_hidden'); 875 | 876 | if (filesPending) { 877 | this.start_button.button('enable'); 878 | } else { 879 | this.start_button.button('disable'); 880 | } 881 | 882 | if (this.options.multiple_queues) { 883 | $('.plupload_header_content', this.element).removeClass('plupload_header_content_bw'); 884 | } 885 | 886 | // if max_file_count defined, only that many files can be queued at once 887 | if (this.options.multiple_queues && maxCount && maxCount > filesPending) { 888 | this.browse_button.button('enable'); 889 | up.disableBrowse(false); 890 | } 891 | 892 | this._updateTotalProgress(); 893 | } 894 | 895 | if (up.total.queued === 0) { 896 | $('.ui-button-text', this.browse_button).html(_('Add Files')); 897 | } else { 898 | $('.ui-button-text', this.browse_button).html(plupload.sprintf(_('%d files queued'), up.total.queued)); 899 | } 900 | 901 | up.refresh(); 902 | }, 903 | 904 | 905 | _handleFileStatus: function(file) { 906 | var $file = $('#' + file.id), actionClass, iconClass; 907 | 908 | // since this method might be called asynchronously, file row might not yet be rendered 909 | if (!$file.length) { 910 | return; 911 | } 912 | 913 | switch (file.status) { 914 | case plupload.DONE: 915 | actionClass = 'plupload_done'; 916 | iconClass = 'plupload_action_icon ui-icon ui-icon-circle-check'; 917 | break; 918 | 919 | case plupload.FAILED: 920 | actionClass = 'ui-state-error plupload_failed'; 921 | iconClass = 'plupload_action_icon ui-icon ui-icon-alert'; 922 | break; 923 | 924 | case plupload.QUEUED: 925 | actionClass = 'plupload_delete'; 926 | iconClass = 'plupload_action_icon ui-icon ui-icon-circle-minus'; 927 | break; 928 | 929 | case plupload.UPLOADING: 930 | actionClass = 'ui-state-highlight plupload_uploading'; 931 | iconClass = 'plupload_action_icon ui-icon ui-icon-circle-arrow-w'; 932 | 933 | // scroll uploading file into the view if its bottom boundary is out of it 934 | var scroller = $('.plupload_scroll', this.container) 935 | , scrollTop = scroller.scrollTop() 936 | , scrollerHeight = scroller.height() 937 | , rowOffset = $file.position().top + $file.height() 938 | ; 939 | 940 | if (scrollerHeight < rowOffset) { 941 | scroller.scrollTop(scrollTop + rowOffset - scrollerHeight); 942 | } 943 | 944 | // Set file specific progress 945 | $file 946 | .find('.plupload_file_percent') 947 | .html(file.percent + '%') 948 | .end() 949 | .find('.plupload_file_progress') 950 | .css('width', file.percent + '%') 951 | .end() 952 | .find('.plupload_file_size') 953 | .html(plupload.formatSize(file.size)); 954 | break; 955 | } 956 | actionClass += ' ui-state-default plupload_file'; 957 | 958 | $file 959 | .attr('class', actionClass) 960 | .find('.plupload_action_icon') 961 | .attr('class', iconClass); 962 | }, 963 | 964 | 965 | _updateTotalProgress: function() { 966 | var up = this.uploader; 967 | 968 | // Scroll to end of file list 969 | this.filelist[0].scrollTop = this.filelist[0].scrollHeight; 970 | 971 | this.progressbar.progressbar('value', up.total.percent); 972 | 973 | this.element 974 | .find('.plupload_total_status') 975 | .html(up.total.percent + '%') 976 | .end() 977 | .find('.plupload_total_file_size') 978 | .html(plupload.formatSize(up.total.size)) 979 | .end() 980 | .find('.plupload_upload_status') 981 | .html(plupload.sprintf(_('Uploaded %d/%d files'), up.total.uploaded, up.files.length)); 982 | }, 983 | 984 | 985 | _displayThumbs: function() { 986 | var self = this 987 | , tw, th // thumb width/height 988 | , cols 989 | , num = 0 // number of simultaneously visible thumbs 990 | , thumbs = [] // array of thumbs to preload at any given moment 991 | , loading = false 992 | ; 993 | 994 | if (!this.options.views.thumbs) { 995 | return; 996 | } 997 | 998 | 999 | function onLast(el, eventName, cb) { 1000 | var timer; 1001 | 1002 | el.on(eventName, function() { 1003 | clearTimeout(timer); 1004 | timer = setTimeout(function() { 1005 | clearTimeout(timer); 1006 | cb(); 1007 | }, 300); 1008 | }); 1009 | } 1010 | 1011 | 1012 | // calculate number of simultaneously visible thumbs 1013 | function measure() { 1014 | if (!tw || !th) { 1015 | var wrapper = $('.plupload_file:eq(0)', self.filelist); 1016 | tw = wrapper.outerWidth(true); 1017 | th = wrapper.outerHeight(true); 1018 | } 1019 | 1020 | var aw = self.content.width(), ah = self.content.height(); 1021 | cols = Math.floor(aw / tw); 1022 | num = cols * (Math.ceil(ah / th) + 1); 1023 | } 1024 | 1025 | 1026 | function pickThumbsToLoad() { 1027 | // calculate index of virst visible thumb 1028 | var startIdx = Math.floor(self.content.scrollTop() / th) * cols; 1029 | // get potentially visible thumbs that are not yet visible 1030 | thumbs = $('.plupload_file .plupload_file_thumb', self.filelist) 1031 | .slice(startIdx, startIdx + num) 1032 | .filter('.plupload_thumb_toload') 1033 | .get(); 1034 | } 1035 | 1036 | 1037 | function init() { 1038 | function mpl() { // measure, pick, load 1039 | if (self.view_mode !== 'thumbs') { 1040 | return; 1041 | } 1042 | measure(); 1043 | pickThumbsToLoad(); 1044 | lazyLoad(); 1045 | } 1046 | 1047 | if ($.fn.resizable) { 1048 | onLast(self.container, 'resize', mpl); 1049 | } 1050 | 1051 | onLast(self.window, 'resize', mpl); 1052 | onLast(self.content, 'scroll', mpl); 1053 | 1054 | self.element.on('viewchanged selected', mpl); 1055 | 1056 | mpl(); 1057 | } 1058 | 1059 | 1060 | function preloadThumb(file, cb) { 1061 | var img = new o.image.Image(); 1062 | var resolveUrl = o.core.utils.Url.resolveUrl; 1063 | 1064 | img.onload = function() { 1065 | var thumb = $('#' + file.id + ' .plupload_file_thumb', self.filelist); 1066 | this.embed(thumb[0], { 1067 | width: self.options.thumb_width, 1068 | height: self.options.thumb_height, 1069 | crop: true, 1070 | preserveHeaders: false, 1071 | swf_url: resolveUrl(self.options.flash_swf_url), 1072 | xap_url: resolveUrl(self.options.silverlight_xap_url) 1073 | }); 1074 | }; 1075 | 1076 | img.bind("embedded error", function(e) { 1077 | $('#' + file.id, self.filelist) 1078 | .find('.plupload_file_thumb') 1079 | .removeClass('plupload_thumb_loading') 1080 | .addClass('plupload_thumb_' + e.type) 1081 | ; 1082 | this.destroy(); 1083 | setTimeout(cb, 1); // detach, otherwise ui might hang (in SilverLight for example) 1084 | }); 1085 | 1086 | $('#' + file.id, self.filelist) 1087 | .find('.plupload_file_thumb') 1088 | .removeClass('plupload_thumb_toload') 1089 | .addClass('plupload_thumb_loading') 1090 | ; 1091 | img.load(file.getSource()); 1092 | } 1093 | 1094 | 1095 | function lazyLoad() { 1096 | if (self.view_mode !== 'thumbs' || loading) { 1097 | return; 1098 | } 1099 | 1100 | pickThumbsToLoad(); 1101 | if (!thumbs.length) { 1102 | return; 1103 | } 1104 | 1105 | loading = true; 1106 | 1107 | preloadThumb(self.getFile($(thumbs.shift()).closest('.plupload_file').attr('id')), function() { 1108 | loading = false; 1109 | lazyLoad(); 1110 | }); 1111 | } 1112 | 1113 | // this has to run only once to measure structures and bind listeners 1114 | this.element.on('selected', function onselected() { 1115 | self.element.off('selected', onselected); 1116 | init(); 1117 | }); 1118 | }, 1119 | 1120 | 1121 | _addFiles: function(files) { 1122 | var self = this, file_html, html = ''; 1123 | 1124 | file_html = '
  • ' + 1125 | '
    ' + 1126 | '
    {ext}
    ' + 1127 | '
    ' + 1128 | '
    ' + 1129 | '
    ' + 1130 | '{percent} ' + 1131 | '
    ' + 1132 | '
    ' + 1133 | '{name} ' + 1134 | '
    ' + 1135 | '
    ' + 1136 | '
    ' + 1137 | '
    ' + 1138 | '
    {size}
    ' + 1139 | '
    ' + 1140 | '
  • '; 1141 | 1142 | if (plupload.typeOf(files) !== 'array') { 1143 | files = [files]; 1144 | } 1145 | 1146 | $.each(files, function(i, file) { 1147 | var ext = o.core.utils.Mime.getFileExtension(file.name) || 'none'; 1148 | 1149 | html += file_html.replace(/\{(\w+)\}/g, function($0, $1) { 1150 | switch ($1) { 1151 | case 'thumb_width': 1152 | case 'thumb_height': 1153 | return self.options[$1]; 1154 | 1155 | case 'size': 1156 | return plupload.formatSize(file.size); 1157 | 1158 | case 'ext': 1159 | return ext; 1160 | 1161 | default: 1162 | return file[$1] || ''; 1163 | } 1164 | }); 1165 | }); 1166 | 1167 | self.filelist.append(html); 1168 | }, 1169 | 1170 | 1171 | _addFormFields: function() { 1172 | var self = this; 1173 | 1174 | // re-add from fresh 1175 | $('.plupload_file_fields', this.filelist).html(''); 1176 | 1177 | plupload.each(this.uploader.files, function(file, count) { 1178 | var fields = '' 1179 | , id = self.id + '_' + count 1180 | ; 1181 | 1182 | if (file.target_name) { 1183 | fields += ''; 1184 | } 1185 | fields += ''; 1186 | fields += ''; 1187 | 1188 | $('#' + file.id).find('.plupload_file_fields').html(fields); 1189 | }); 1190 | 1191 | this.counter.val(this.uploader.files.length); 1192 | }, 1193 | 1194 | 1195 | _viewChanged: function(view) { 1196 | // update or write a new cookie 1197 | if (this.options.views.remember && $.cookie) { 1198 | $.cookie('plupload_ui_view', view, { expires: 7, path: '/' }); 1199 | } 1200 | 1201 | // ugly fix for IE6 - make content area stretchable 1202 | if (plupload.ua.browser === 'IE' && plupload.ua.version < 7) { 1203 | this.content.attr('style', 'height:expression(document.getElementById("' + this.id + '_container' + '").clientHeight - ' + (view === 'list' ? 132 : 102) + ')'); 1204 | } 1205 | 1206 | this.container.removeClass('plupload_view_list plupload_view_thumbs').addClass('plupload_view_' + view); 1207 | this.view_mode = view; 1208 | this._trigger('viewchanged', null, { view: view }); 1209 | }, 1210 | 1211 | 1212 | _enableViewSwitcher: function() { 1213 | var self = this 1214 | , view 1215 | , switcher = $('.plupload_view_switch', this.container) 1216 | , buttons 1217 | , button 1218 | ; 1219 | 1220 | plupload.each(['list', 'thumbs'], function(view) { 1221 | if (!self.options.views[view]) { 1222 | switcher.find('[for="' + self.id + '_view_' + view + '"], #'+ self.id +'_view_' + view).remove(); 1223 | } 1224 | }); 1225 | 1226 | // check if any visible left 1227 | buttons = switcher.find('.plupload_button'); 1228 | 1229 | if (buttons.length === 1) { 1230 | switcher.hide(); 1231 | view = buttons.eq(0).data('view'); 1232 | this._viewChanged(view); 1233 | } else if ($.ui.button && buttons.length > 1) { 1234 | if (this.options.views.remember && $.cookie) { 1235 | view = $.cookie('plupload_ui_view'); 1236 | } 1237 | 1238 | // if wierd case, bail out to default 1239 | if (!~plupload.inArray(view, ['list', 'thumbs'])) { 1240 | view = this.options.views.active; 1241 | } 1242 | 1243 | switcher 1244 | .show() 1245 | .buttonset() 1246 | .find('.ui-button') 1247 | .click(function(e) { 1248 | view = $(this).data('view'); 1249 | self._viewChanged(view); 1250 | e.preventDefault(); // avoid auto scrolling to widget in IE and FF (see #850) 1251 | }); 1252 | 1253 | // if view not active - happens when switcher wasn't clicked manually 1254 | button = switcher.find('[for="' + self.id + '_view_'+view+'"]'); 1255 | if (button.length) { 1256 | button.trigger('click'); 1257 | } 1258 | } else { 1259 | switcher.show(); 1260 | this._viewChanged(this.options.views.active); 1261 | } 1262 | 1263 | // initialize thumb viewer if requested 1264 | if (this.options.views.thumbs) { 1265 | this._displayThumbs(); 1266 | } 1267 | }, 1268 | 1269 | 1270 | _enableRenaming: function() { 1271 | var self = this; 1272 | 1273 | this.filelist.dblclick(function(e) { 1274 | var nameSpan = $(e.target), nameInput, file, parts, name, ext = ""; 1275 | 1276 | if (!nameSpan.hasClass('plupload_file_name_wrapper')) { 1277 | return; 1278 | } 1279 | 1280 | // Get file name and split out name and extension 1281 | file = self.uploader.getFile(nameSpan.closest('.plupload_file')[0].id); 1282 | name = file.name; 1283 | parts = /^(.+)(\.[^.]+)$/.exec(name); 1284 | if (parts) { 1285 | name = parts[1]; 1286 | ext = parts[2]; 1287 | } 1288 | 1289 | // Display input element 1290 | nameInput = $('').width(nameSpan.width()).insertAfter(nameSpan.hide()); 1291 | nameInput.val(name).blur(function() { 1292 | nameSpan.show().parent().scrollLeft(0).end().next().remove(); 1293 | }).keydown(function(e) { 1294 | var nameInput = $(this); 1295 | 1296 | if ($.inArray(e.keyCode, [13, 27]) !== -1) { 1297 | e.preventDefault(); 1298 | 1299 | // Rename file and glue extension back on 1300 | if (e.keyCode === 13) { 1301 | file.name = nameInput.val() + ext; 1302 | nameSpan.html(file.name); 1303 | } 1304 | nameInput.blur(); 1305 | } 1306 | })[0].focus(); 1307 | }); 1308 | }, 1309 | 1310 | 1311 | _enableSortingList: function() { 1312 | var self = this; 1313 | 1314 | if ($('.plupload_file', this.filelist).length < 2) { 1315 | return; 1316 | } 1317 | 1318 | // destroy sortable if enabled 1319 | $('tbody', this.filelist).sortable('destroy'); 1320 | 1321 | // enable 1322 | this.filelist.sortable({ 1323 | items: '.plupload_delete', 1324 | 1325 | cancel: 'object, .plupload_clearer', 1326 | 1327 | stop: function() { 1328 | var files = []; 1329 | 1330 | $.each($(this).sortable('toArray'), function(i, id) { 1331 | files[files.length] = self.uploader.getFile(id); 1332 | }); 1333 | 1334 | files.unshift(files.length); 1335 | files.unshift(0); 1336 | 1337 | // re-populate files array 1338 | Array.prototype.splice.apply(self.uploader.files, files); 1339 | } 1340 | }); 1341 | } 1342 | }); 1343 | 1344 | } (window, document, plupload, moxie, jQuery)); 1345 | -------------------------------------------------------------------------------- /demo7/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | $ node server.js 3 | or 4 | $ node server.js 1234 5 | | 6 | V 7 | port 8 | */ 9 | var http = require("http"), 10 | url = require("url"), 11 | path = require("path"), 12 | fs = require("fs"); 13 | port = process.argv[2] || 80; 14 | 15 | function formidable(req, res) { 16 | // parse a file upload 17 | var total = 0; 18 | req.on('data', function(tr) { 19 | while(1) { 20 | if(tr.length == 0) return; 21 | var idx = tr.indexOf('\r\n'); 22 | if(idx == -1) { 23 | process.stdout.write('data:'+tr.length); 24 | total += tr.length; 25 | return; 26 | } 27 | if(idx > 2000) { 28 | process.stdout.write('data:'+tr.length); 29 | total += idx; 30 | tr = tr.slice(idx+2); 31 | continue; 32 | } 33 | var flag = 0; 34 | for(var i = 0; i= 0x20 && b <= 0x7a)) { 37 | total += idx+2; 38 | tr = tr.slice(idx+2); 39 | flag = 1; 40 | break; 41 | } 42 | } 43 | if(flag) { 44 | continue; 45 | } 46 | var str = tr.slice(0, idx).toString(); 47 | if(/\<.*\>/.test(str)) { 48 | total += idx+2; 49 | tr = tr.slice(idx+2); 50 | continue; 51 | } 52 | console.log(str); 53 | tr = tr.slice(idx+2); 54 | } 55 | return; 56 | }).on('end',function() { 57 | console.log('\ntotal:', total); 58 | res.end('total: ' + total); 59 | }); 60 | } 61 | 62 | var mimeTypes = { 63 | "htm": "text/html", 64 | "html": "text/html", 65 | "jpeg": "image/jpeg", 66 | "jpg": "image/jpeg", 67 | "png": "image/png", 68 | "gif": "image/gif", 69 | "js": "text/javascript", 70 | "css": "text/css"}; 71 | 72 | var virtualDirectories = { 73 | //"images": "../images/" 74 | }; 75 | 76 | process.chdir(__dirname); 77 | http.createServer(function(request, response) { 78 | if (request.url == '/upload' && request.method.toLowerCase() == 'post') { 79 | console.log('post', request.url); 80 | formidable(request, response); 81 | return; 82 | } 83 | 84 | var uri = url.parse(request.url).pathname 85 | , filename = path.join(process.cwd(), uri) 86 | , root = uri.split("/")[1] 87 | , virtualDirectory; 88 | 89 | virtualDirectory = virtualDirectories[root]; 90 | if(virtualDirectory){ 91 | uri = uri.slice(root.length + 1, uri.length); 92 | filename = path.join(virtualDirectory ,uri); 93 | } 94 | 95 | fs.exists(filename, function(exists) { 96 | if(!exists) { 97 | response.writeHead(404, {"Content-Type": "text/plain"}); 98 | response.write("404 Not Found\n"); 99 | response.end(); 100 | console.error('404: ' + filename); 101 | return; 102 | } 103 | 104 | if (fs.statSync(filename).isDirectory()) filename += '/index.html'; 105 | 106 | fs.readFile(filename, "binary", function(err, file) { 107 | if(err) { 108 | response.writeHead(500, {"Content-Type": "text/plain"}); 109 | response.write(err + "\n"); 110 | response.end(); 111 | console.error('500: ' + filename); 112 | return; 113 | } 114 | 115 | var mimeType = mimeTypes[path.extname(filename).split(".")[1]]; 116 | response.writeHead(200, {"Content-Type": mimeType}); 117 | response.write(file, "binary"); 118 | response.end(); 119 | console.log('200: ' + filename + ' as ' + mimeType); 120 | }); 121 | }); 122 | }).listen(parseInt(port, 10)); 123 | 124 | console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown"); 125 | -------------------------------------------------------------------------------- /demo8/client.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | const fs = require('fs'); 3 | 4 | //'/Users/zhi.liang/Downloads/googlechrome_mac_55.0.2883.95.dmg' 5 | const pathname = process.argv[2]; 6 | if(!pathname) { 7 | console.log('Usage: 请选择一个文件'); 8 | process.exit(1); 9 | } 10 | 11 | var stream = fs 12 | .createReadStream(pathname); 13 | 14 | var total = fs.statSync(pathname).size 15 | var current = 0; 16 | 17 | var formData = { 18 | file: stream, 19 | }; 20 | 21 | function _progress(chunk) { 22 | current += chunk.length; 23 | // console.log('progress', total, current, chunk.length, 24 | // ((current/total)*100).toString().slice(0,4)+'%'); 25 | process.stdout.write(((current/total)*100).toString().slice(0,4)+'%'); 26 | } 27 | 28 | var req = request.post({ 29 | url:'http://localhost/upload', 30 | formData: formData 31 | },(err, res, body) => { 32 | if (err) { 33 | //stream.removeAllListeners('data', _progress); 34 | stream.removeAllListeners(['data']); 35 | return console.error('upload failed:', err); 36 | } 37 | console.log('Upload successful! Server responded with:', body); 38 | process.exit(0); 39 | }); 40 | 41 | req.on('error', function(err) { 42 | console.log('error event', err); 43 | }); 44 | 45 | req.on('abort', function() { 46 | console.log('abort event'); 47 | }); 48 | 49 | stream.on('data', _progress); 50 | //stream.removeAllListeners(['data']); 51 | 52 | process.stdin.on('data', function(t) { 53 | var N = t.slice(0, t.length - 1).toString(); 54 | if(N === 'abort') { 55 | console.log('==============you input abort, begin abort req============'); 56 | req.abort(); 57 | } 58 | }); 59 | -------------------------------------------------------------------------------- /demo8/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | demo 8 5 | 6 | 7 | 8 |
      9 | 10 |
      11 | Browse... 12 |
      13 | 14 | 15 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /demo8/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | NodeJS Static Http Server - http://github.com/thedigitalself/node-static-http-server/ 3 | By James Wanga - The Digital Self 4 | Licensed under a Creative Commons Attribution 3.0 Unported License. 5 | A simple, nodeJS, http development server that trivializes serving static files. 6 | This server is HEAVILY based on work done by Ryan Florence(https://github.com/rpflorence) (https://gist.github.com/701407). I merged this code with suggestions on handling varied MIME types found at Stackoverflow (http://stackoverflow.com/questions/7268033/basic-static-file-server-in-nodejs). 7 | To run the server simply place the server.js file in the root of your web application and issue the command 8 | $ node server.js 9 | or 10 | $ node server.js 1234 11 | with "1234" being a custom port number" 12 | Your web application will be served at http://localhost:8888 by default or http://localhost:1234 with "1234" being the custom port you passed. 13 | Mime Types: 14 | You can add to the mimeTypes has to serve more file types. 15 | Virtual Directories: 16 | Add to the virtualDirectories hash if you have resources that are not children of the root directory 17 | */ 18 | var http = require("http"), 19 | url = require("url"), 20 | path = require("path"), 21 | fs = require("fs") 22 | port = process.argv[2] || 80; 23 | 24 | function formidable(req, res) { 25 | req.setMaxListeners(0); 26 | // parse a file upload 27 | var total = 0; 28 | (function scan(n) { 29 | req.resume(); 30 | req.once('data', function(chunk) { 31 | console.log('chunk', chunk.length, req === this); 32 | total += chunk.length; 33 | this.pause(); 34 | }); 35 | setTimeout(scan, 1000, n+1); 36 | })(0); 37 | 38 | req.on('end', () => { 39 | res.end(` 40 | 41 | 42 | ${total}`); 43 | }); 44 | } 45 | 46 | var mimeTypes = { 47 | "htm": "text/html", 48 | "html": "text/html", 49 | "jpeg": "image/jpeg", 50 | "jpg": "image/jpeg", 51 | "png": "image/png", 52 | "gif": "image/gif", 53 | "js": "text/javascript", 54 | "css": "text/css"}; 55 | 56 | var virtualDirectories = { 57 | //"images": "../images/" 58 | }; 59 | 60 | process.chdir(__dirname); 61 | http.createServer(function(request, response) { 62 | //console.log('0000000000', request.url); 63 | if (request.url == '/upload' && request.method.toLowerCase() == 'post') { 64 | console.log('upload', request.url); 65 | formidable(request, response); 66 | return; 67 | } 68 | 69 | var uri = url.parse(request.url).pathname 70 | , filename = path.join(process.cwd(), uri) 71 | , root = uri.split("/")[1] 72 | , virtualDirectory; 73 | 74 | virtualDirectory = virtualDirectories[root]; 75 | if(virtualDirectory){ 76 | uri = uri.slice(root.length + 1, uri.length); 77 | filename = path.join(virtualDirectory ,uri); 78 | } 79 | 80 | fs.exists(filename, function(exists) { 81 | if(!exists) { 82 | response.writeHead(404, {"Content-Type": "text/plain"}); 83 | response.write("404 Not Found\n"); 84 | response.end(); 85 | console.error('404: ' + filename); 86 | return; 87 | } 88 | 89 | if (fs.statSync(filename).isDirectory()) filename += '/index.html'; 90 | 91 | fs.readFile(filename, "binary", function(err, file) { 92 | if(err) { 93 | response.writeHead(500, {"Content-Type": "text/plain"}); 94 | response.write(err + "\n"); 95 | response.end(); 96 | console.error('500: ' + filename); 97 | return; 98 | } 99 | 100 | var mimeType = mimeTypes[path.extname(filename).split(".")[1]]; 101 | response.writeHead(200, {"Content-Type": mimeType}); 102 | response.write(file, "binary"); 103 | response.end(); 104 | console.log('200: ' + filename + ' as ' + mimeType); 105 | }); 106 | }); 107 | }).listen(parseInt(port, 10)); 108 | 109 | console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown"); 110 | -------------------------------------------------------------------------------- /demo9/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | demo 9 5 | 6 | 7 | 8 |
        9 | 10 |
        11 | Browse... 12 |
        13 | 14 | 15 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /demo9/server.js: -------------------------------------------------------------------------------- 1 | /* 2 | $ node server.js 3 | or 4 | $ node server.js 1234 5 | | 6 | V 7 | port 8 | */ 9 | var http = require("http"), 10 | url = require("url"), 11 | path = require("path"), 12 | fs = require("fs"); 13 | 14 | var port = process.argv[2] || 80; 15 | 16 | function formidable(req, res) { 17 | // parse a file upload 18 | var total = 0; 19 | req.on('data', function(tr) { 20 | while(1) { 21 | if(tr.length == 0) return; 22 | var idx = tr.indexOf('\r\n'); 23 | if(idx == -1) { 24 | process.stdout.write('data:'+tr.length); 25 | total += tr.length; 26 | return; 27 | } 28 | if(idx > 2000) { 29 | process.stdout.write('data:'+tr.length); 30 | total += idx; 31 | tr = tr.slice(idx+2); 32 | continue; 33 | } 34 | var flag = 0; 35 | for(var i = 0; i= 0x20 && b <= 0x7a)) { 38 | total += idx+2; 39 | tr = tr.slice(idx+2); 40 | flag = 1; 41 | break; 42 | } 43 | } 44 | if(flag) { 45 | continue; 46 | } 47 | var str = tr.slice(0, idx).toString(); 48 | if(/\<.*\>/.test(str)) { 49 | total += idx+2; 50 | tr = tr.slice(idx+2); 51 | continue; 52 | } 53 | console.log(str); 54 | tr = tr.slice(idx+2); 55 | } 56 | return; 57 | }).on('end',function() { 58 | console.log('\ntotal:', total); 59 | res.end('total: ' + total); 60 | }); 61 | } 62 | 63 | function getRandom(start, end) { 64 | return Math.floor(Math.random() * (end - start)) + start; 65 | } 66 | 67 | var mimeTypes = { 68 | "htm": "text/html", 69 | "html": "text/html", 70 | "jpeg": "image/jpeg", 71 | "jpg": "image/jpeg", 72 | "png": "image/png", 73 | "gif": "image/gif", 74 | "js": "text/javascript", 75 | "css": "text/css"}; 76 | 77 | var virtualDirectories = { 78 | //"images": "../images/" 79 | }; 80 | 81 | process.chdir(__dirname); 82 | http.createServer(function(request, response) { 83 | if (request.url == '/upload' && request.method.toLowerCase() == 'post') { 84 | console.log('post', request.url); 85 | formidable(request, response); 86 | return; 87 | } 88 | 89 | if (request.url == '/offset') { 90 | //response.end(getRandom(100, 50)+''); 91 | response.end('0'); 92 | return; 93 | } 94 | 95 | var uri = url.parse(request.url).pathname 96 | , filename = path.join(process.cwd(), uri) 97 | , root = uri.split("/")[1] 98 | , virtualDirectory; 99 | 100 | virtualDirectory = virtualDirectories[root]; 101 | if(virtualDirectory){ 102 | uri = uri.slice(root.length + 1, uri.length); 103 | filename = path.join(virtualDirectory ,uri); 104 | } 105 | 106 | fs.exists(filename, function(exists) { 107 | if(!exists) { 108 | response.writeHead(404, {"Content-Type": "text/plain"}); 109 | response.write("404 Not Found\n"); 110 | response.end(); 111 | console.error('404: ' + filename); 112 | return; 113 | } 114 | 115 | if (fs.statSync(filename).isDirectory()) filename += '/index.html'; 116 | 117 | fs.readFile(filename, "binary", function(err, file) { 118 | if(err) { 119 | response.writeHead(500, {"Content-Type": "text/plain"}); 120 | response.write(err + "\n"); 121 | response.end(); 122 | console.error('500: ' + filename); 123 | return; 124 | } 125 | 126 | var mimeType = mimeTypes[path.extname(filename).split(".")[1]]; 127 | response.writeHead(200, {"Content-Type": mimeType}); 128 | response.write(file, "binary"); 129 | response.end(); 130 | console.log('200: ' + filename + ' as ' + mimeType); 131 | }); 132 | }); 133 | }).listen(parseInt(port, 10)); 134 | 135 | console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown"); 136 | -------------------------------------------------------------------------------- /docs/compile.md: -------------------------------------------------------------------------------- 1 | # 编译moxie 2 | 3 | [官网的编译方法](https://github.com/moxiecode/moxie) 4 | 5 | 你可能会遇到下面的问题 6 | ~~~ 7 | 18:06:34 0$ jake 8 | -bash: jake: command not found 9 | ~~~ 10 | 解决办法是 11 | ~~~ 12 | 18:09:11 0$ node node_modules/jake/bin/cli.js mkjs . 13 | rm -rf bin/js 14 | Writing source version output to: bin/js/moxie.js 15 | Writing development version to: bin/js/moxie.dev.js 16 | Writing coverage version output to: bin/js/moxie.cov.js 17 | Writing minified version output to: bin/js/moxie.min.js 18 | ~~~ 19 | 20 | ## 最小编译 21 | node node_modules/jake/bin/cli.js mkjs[file/FileInput] runtimes=html5 . 22 | 23 | 编译结果 38k 24 | 25 | ## demo3的编译方法 - 图片预览 26 | node node_modules/jake/bin/cli.js mkjs[file/FileInput,image/Image] runtimes=html5 . 27 | 28 | 编译结果 73k 29 | 30 | ## demo4的编译方法 - 最小的上传库 31 | node node_modules/jake/bin/cli.js mkjs[file/FileInput,image/Image,xhr/XMLHttpRequest] runtimes=html5 . 32 | 33 | 编译结果 77k 34 | 35 | ## 默认的编译方法 36 | node node_modules/jake/bin/cli.js mkjs . 37 | 38 | 编译结果 107k 39 | -------------------------------------------------------------------------------- /img/1.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/1.1.png -------------------------------------------------------------------------------- /img/1.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/1.2.png -------------------------------------------------------------------------------- /img/1.22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/1.22.png -------------------------------------------------------------------------------- /img/1.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/1.3.png -------------------------------------------------------------------------------- /img/1.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/1.4.png -------------------------------------------------------------------------------- /img/3.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/3.1.png -------------------------------------------------------------------------------- /img/6.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/6.1.png -------------------------------------------------------------------------------- /img/6.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/6.2.png -------------------------------------------------------------------------------- /img/6.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/6.3.png -------------------------------------------------------------------------------- /img/6.4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/6.4.jpg -------------------------------------------------------------------------------- /img/7.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/7.1.png -------------------------------------------------------------------------------- /img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/7.png -------------------------------------------------------------------------------- /img/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/8.png -------------------------------------------------------------------------------- /img/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ktont/javascript-file-upload/cc2c1eafb73ddbdc18fb593086aebc09270cc694/img/9.png --------------------------------------------------------------------------------